/* $Id: Controller.mm,v 1.27 2004/02/18 17:07:20 titer Exp $ This file is part of the HandBrake source code. Homepage: . It may be used under the terms of the GNU General Public License. */ #include #include #include #include #include #include "Controller.h" #define _(a) NSLocalizedString(a,nil) static void _Scanning( void * data, int title, int titleCount ); static void _ScanDone( void * data, HBList * titleList ); static void _Encoding( void * data, float position, int pass, int passCount, float curFrameRate, float avgFrameRate, int remainingTime ); static void _RipDone( void * data, int result ); /******************************* * HBController implementation * *******************************/ @implementation HBController - (void) applicationDidFinishLaunching: (NSNotification *) notification { /* Init libhb */ HBCallbacks callbacks; callbacks.data = self; callbacks.scanning = _Scanning; callbacks.scanDone = _ScanDone; callbacks.encoding = _Encoding; callbacks.ripDone = _RipDone; fHandle = HBInit( 1, 0 ); HBSetCallbacks( fHandle, callbacks ); [fPictureGLView SetHandle: fHandle]; /* Detect drives mounted after the app is started */ [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self selector: @selector( DetectDrives: ) name: NSWorkspaceDidMountNotification object: nil]; } - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *) app { /* Clean up */ HBClose( &fHandle ); return NSTerminateNow; } - (void) awakeFromNib { /* Strings for the Scan view */ [fScWelcomeField setStringValue: _( @"Welcome to HandBrake" )]; [fScSelectField setStringValue: _( @"Select a DVD:" )]; [fScDetectedCell setTitle: _( @"Detected volume" )]; [fScDetectedPopUp removeAllItems]; [fScFolderCell setTitle: _( @"DVD Folder" )]; [fScBrowseButton setTitle: _( @"Browse" )]; [fScStatusField setStringValue: @""]; [fScOpenButton setTitle: _( @"Open" )]; /* Strings for the Rip view */ /* General box */ [fRipGeneralField setStringValue: _( @"General" )]; [fRipTitleField setStringValue: _( @"DVD title" )]; [fRipTitlePopUp removeAllItems]; [fRipFormatField setStringValue: _( @"Output format" )]; [fRipFormatPopUp removeAllItems]; [fRipFormatPopUp addItemWithTitle: _( @"MP4 file / MPEG-4 video / AAC audio" )]; [fRipFormatPopUp addItemWithTitle: _( @"AVI file / MPEG-4 video / MP3 audio" )]; [fRipFormatPopUp addItemWithTitle: _( @"AVI file / H264 video / MP3 audio" )]; [fRipFormatPopUp addItemWithTitle: _( @"OGM file / MPEG-4 video / Vorbis audio" )]; [fRipFileField1 setStringValue: _( @"File" )]; [fRipFileField2 setStringValue: [NSString stringWithFormat: @"%@/Desktop/Movie.mp4", NSHomeDirectory()]]; [fRipBrowseButton setTitle: _( @"Browse" )]; /* Video box */ [fRipVideoField setStringValue: _( @"Video" )]; [fRipEncoderField setStringValue: _( @"MPEG-4 encoder" )]; [fRipEncoderPopUp removeAllItems]; [fRipEncoderPopUp addItemWithTitle: @"FFmpeg"]; [fRipEncoderPopUp addItemWithTitle: @"XviD"]; [fRipBitrateField setStringValue: _( @"Bitrate" )]; [fRipCustomCell setTitle: _( @"Custom (kbps)" )]; [fRipCustomField setIntValue: 1024]; [fRipTargetCell setTitle: _( @"Target size (MB)" )]; [fRipTargetField setIntValue: 700]; [fRipTwoPassCheck setTitle: _( @"2-pass encoding" )]; [fRipCropButton setTitle: _( @"Crop & Scale..." )]; /* Audio box */ [fRipAudioField setStringValue: _( @"Audio" )]; [fRipLang1Field setStringValue: _( @"Language 1" )]; [fRipLang1PopUp removeAllItems]; [fRipLang2Field setStringValue: _( @"Language 2 (optional)" )]; [fRipLang2PopUp removeAllItems]; [fRipAudBitField setStringValue: _( @"Bitrate (kbps)" )]; [fRipAudBitPopUp removeAllItems]; [fRipAudBitPopUp addItemWithTitle: @"32"]; [fRipAudBitPopUp addItemWithTitle: @"40"]; [fRipAudBitPopUp addItemWithTitle: @"48"]; [fRipAudBitPopUp addItemWithTitle: @"56"]; [fRipAudBitPopUp addItemWithTitle: @"64"]; [fRipAudBitPopUp addItemWithTitle: @"80"]; [fRipAudBitPopUp addItemWithTitle: @"96"]; [fRipAudBitPopUp addItemWithTitle: @"112"]; [fRipAudBitPopUp addItemWithTitle: @"128"]; [fRipAudBitPopUp addItemWithTitle: @"160"]; [fRipAudBitPopUp addItemWithTitle: @"192"]; [fRipAudBitPopUp addItemWithTitle: @"224"]; [fRipAudBitPopUp addItemWithTitle: @"256"]; [fRipAudBitPopUp addItemWithTitle: @"320"]; [fRipAudBitPopUp selectItemWithTitle: @"128"]; /* Bottom */ [fRipStatusField setStringValue: @""]; [fRipInfoField setStringValue: @""]; [fRipPauseButton setTitle: _( @"Pause" )]; [fRipRipButton setTitle: _( @"Rip" )]; /* Strings for the crop panel */ [fWidthField1 setStringValue: _( @"Picture width" )]; [fDeinterlaceCheck setTitle: _( @"Deinterlace picture" )]; [fTopField1 setStringValue: _( @"Top cropping" )]; [fBottomField1 setStringValue: _( @"Bottom cropping" )]; [fLeftField1 setStringValue: _( @"Left cropping" )]; [fRightField1 setStringValue: _( @"Right cropping" )]; [fPreviousButton setTitle: _( @"Previous" )]; [fNextButton setTitle: _( @"Next" )]; [fAutocropButton setTitle: _( @"Autocrop" )]; [fOpenGLCheck setTitle: _( @"Useless OpenGL effects" )]; [fInfoField setStringValue: @""]; [fCloseButton setTitle: _( @"Close" )]; [self VideoMatrixChanged: self]; /* Show the scan view */ [fWindow setContentSize: [fScView frame].size]; [fWindow setContentView: fScView]; [fWindow center]; /* Detect DVD drives */ [self DetectDrives: nil]; [self ScanMatrixChanged: self]; } - (BOOL) windowShouldClose: (id) sender { /* Stop the application when the user closes the window */ [NSApp terminate: self]; return YES; } - (IBAction) BrowseDVD: (id) sender { /* Open a panel to let the user choose and update the text field */ NSOpenPanel * panel = [NSOpenPanel openPanel]; [panel setAllowsMultipleSelection: NO]; [panel setCanChooseFiles: NO]; [panel setCanChooseDirectories: YES ]; [panel beginSheetForDirectory: nil file: nil types: nil modalForWindow: fWindow modalDelegate: self didEndSelector: @selector( BrowseDVDDone:returnCode:contextInfo: ) contextInfo: nil]; } - (void) BrowseDVDDone: (NSOpenPanel *) sheet returnCode: (int) returnCode contextInfo: (void *) contextInfo { if( returnCode == NSOKButton ) { [fScFolderField setStringValue: [[sheet filenames] objectAtIndex: 0]]; } } - (IBAction) VideoMatrixChanged: (id) sender; { if( ![fRipVideoMatrix isEnabled] ) { [fRipCustomField setEnabled: NO]; [fRipTargetField setEnabled: NO]; return; } if( ![fRipVideoMatrix selectedRow] ) { [fRipCustomField setEnabled: YES]; [fRipTargetField setEnabled: NO]; } else { [fRipCustomField setEnabled: NO]; [fRipTargetField setEnabled: YES]; [fRipTargetField UpdateBitrate]; } } - (IBAction) BrowseFile: (id) sender { /* Open a panel to let the user choose and update the text field */ NSSavePanel * panel = [NSSavePanel savePanel]; [panel beginSheetForDirectory: nil file: nil modalForWindow: fWindow modalDelegate: self didEndSelector: @selector( BrowseFileDone:returnCode:contextInfo: ) contextInfo: nil]; } - (void) BrowseFileDone: (NSSavePanel *) sheet returnCode: (int) returnCode contextInfo: (void *) contextInfo { if( returnCode == NSOKButton ) { [fRipFileField2 setStringValue: [sheet filename]]; [self FormatPopUpChanged: self]; } } - (IBAction) Scan: (id) sender { [fScMatrix setEnabled: NO]; [fScDetectedPopUp setEnabled: NO]; [fScFolderField setEnabled: NO]; [fScBrowseButton setEnabled: NO]; [fScProgress setIndeterminate: YES]; [fScProgress startAnimation: self]; [fScOpenButton setEnabled: NO]; [fScStatusField setStringValue: _( @"Opening device..." )]; /* Ask libhb to start scanning the specified volume */ if( ![fScMatrix selectedRow] ) { /* DVD drive */ HBScanDVD( fHandle, [[fScDetectedPopUp titleOfSelectedItem] cString], 0 ); } else { /* DVD folder */ HBScanDVD( fHandle, [[fScFolderField stringValue] cString], 0 ); } } - (IBAction) ShowPicturePanel: (id) sender { HBTitle * title = (HBTitle*) HBListItemAt( fTitleList, [fRipTitlePopUp indexOfSelectedItem] ); [fPictureGLView SetTitle: title]; fPicture = 0; [fPictureGLView ShowPicture: fPicture animate: HB_ANIMATE_NONE]; [fWidthStepper setValueWraps: NO]; [fWidthStepper setIncrement: 16]; [fWidthStepper setMinValue: 16]; [fWidthStepper setMaxValue: title->outWidthMax]; [fWidthStepper setIntValue: title->outWidth]; [fWidthField2 setIntValue: title->outWidth]; [fDeinterlaceCheck setState: title->deinterlace ? NSOnState : NSOffState]; [fTopStepper setValueWraps: NO]; [fTopStepper setIncrement: 2]; [fTopStepper setMinValue: 0]; [fTopStepper setMaxValue: title->inHeight / 4]; [fTopStepper setIntValue: title->topCrop]; [fTopField2 setIntValue: title->topCrop]; [fBottomStepper setValueWraps: NO]; [fBottomStepper setIncrement: 2]; [fBottomStepper setMinValue: 0]; [fBottomStepper setMaxValue: title->inHeight / 4]; [fBottomStepper setIntValue: title->bottomCrop]; [fBottomField2 setIntValue: title->bottomCrop]; [fLeftStepper setValueWraps: NO]; [fLeftStepper setIncrement: 2]; [fLeftStepper setMinValue: 0]; [fLeftStepper setMaxValue: title->inWidth / 4]; [fLeftStepper setIntValue: title->leftCrop]; [fLeftField2 setIntValue: title->leftCrop]; [fRightStepper setValueWraps: NO]; [fRightStepper setIncrement: 2]; [fRightStepper setMinValue: 0]; [fRightStepper setMaxValue: title->inWidth / 4]; [fRightStepper setIntValue: title->rightCrop]; [fRightField2 setIntValue: title->rightCrop]; [fPreviousButton setEnabled: NO]; [fNextButton setEnabled: YES]; [fInfoField setStringValue: [NSString stringWithFormat: _( @"Final size: %dx%d" ), title->outWidth, title->outHeight] ]; /* Resize the panel */ NSSize newSize; /* XXX */ newSize.width = 762 /*fPicturePanelSize.width*/ + title->outWidthMax - 720; newSize.height = 755 /*fPicturePanelSize.height*/ + title->outHeightMax - 576; [fPicturePanel setContentSize: newSize]; [NSApp beginSheet: fPicturePanel modalForWindow: fWindow modalDelegate: nil didEndSelector: nil contextInfo: nil]; [NSApp runModalForWindow: fPicturePanel]; [NSApp endSheet: fPicturePanel]; [fPicturePanel orderOut: self]; } - (IBAction) ClosePanel: (id) sender { [NSApp stopModal]; } - (IBAction) Rip: (id) sender { /* Rip or Cancel ? */ if( [[fRipRipButton title] compare: _( @"Cancel" ) ] == NSOrderedSame ) { [self Cancel: self]; return; } if( [fRipCustomField intValue] < 64 ) { NSBeginCriticalAlertSheet( _( @"Invalid video bitrate" ), _( @"Ooops" ), nil, nil, fWindow, self, nil, nil, nil, _( @"Video bitrate is too low." ) ); return; } if( [fRipCustomField intValue] > 8192 ) { NSBeginCriticalAlertSheet( _( @"Invalid video bitrate" ), _( @"Ooops" ), nil, nil, fWindow, self, nil, nil, nil, _( @"Video bitrate is too high." ) ); return; } if( [fRipLang1PopUp indexOfSelectedItem] == [fRipLang2PopUp indexOfSelectedItem] ) { NSBeginCriticalAlertSheet( _( @"Invalid secondary language" ), _( @"Ooops" ), nil, nil, fWindow, self, nil, nil, nil, _( @"You can't encode the same audio track twice." ) ); return; } if( [[NSFileManager defaultManager] fileExistsAtPath: [fRipFileField2 stringValue]] ) { NSBeginCriticalAlertSheet( _( @"File already exists" ), _( @"No" ), _( @"Yes" ), nil, fWindow, self, @selector( OverwriteAlertDone:returnCode:contextInfo: ), nil, nil, [NSString stringWithFormat: _( @"Do you want to overwrite %@?" ), [fRipFileField2 stringValue]] ); return; } [self _Rip]; } - (void) OverwriteAlertDone: (NSWindow *) sheet returnCode: (int) returnCode contextInfo: (void *) contextInfo { if( returnCode == NSAlertAlternateReturn ) { [self _Rip]; } } - (void) _Rip { /* Get the specified title & audio track(s) */ HBTitle * title = (HBTitle*) HBListItemAt( fTitleList, [fRipTitlePopUp indexOfSelectedItem] ); HBAudio * audio1 = (HBAudio*) HBListItemAt( title->audioList, [fRipLang1PopUp indexOfSelectedItem] ); HBAudio * audio2 = (HBAudio*) HBListItemAt( title->audioList, [fRipLang2PopUp indexOfSelectedItem] ); /* Use user settings */ title->file = strdup( [[fRipFileField2 stringValue] cString] ); title->bitrate = [fRipCustomField intValue]; title->twoPass = ( [fRipTwoPassCheck state] == NSOnState ); int format = [fRipFormatPopUp indexOfSelectedItem]; int codec = [fRipEncoderPopUp indexOfSelectedItem]; title->mux = ( !format ) ? HB_MUX_MP4 : ( ( format == 3 ) ? HB_MUX_OGM : HB_MUX_AVI ); title->codec = ( format == 2 ) ? HB_CODEC_X264 : ( ( !codec ) ? HB_CODEC_FFMPEG : HB_CODEC_XVID ); audio1->outBitrate = [[fRipAudBitPopUp titleOfSelectedItem] intValue]; audio1->codec = ( !format ) ? HB_CODEC_AAC : ( ( format == 3 ) ? HB_CODEC_VORBIS : HB_CODEC_MP3 );; HBListAdd( title->ripAudioList, audio1 ); if( audio2 ) { audio2->outBitrate = [[fRipAudBitPopUp titleOfSelectedItem] intValue]; audio2->codec = ( !format ) ? HB_CODEC_AAC : ( ( format == 3 ) ? HB_CODEC_VORBIS : HB_CODEC_MP3 ); HBListAdd( title->ripAudioList, audio2 ); } /* Disable interface */ [fRipTitlePopUp setEnabled: NO]; [fRipFormatPopUp setEnabled: NO]; [fRipVideoMatrix setEnabled: NO]; [fRipCustomField setEnabled: NO]; [fRipTargetField setEnabled: NO]; [fRipTwoPassCheck setEnabled: NO]; [fRipCropButton setEnabled: NO]; [fRipLang1PopUp setEnabled: NO]; [fRipLang2PopUp setEnabled: NO]; [fRipAudBitPopUp setEnabled: NO]; [fRipFileField2 setEnabled: NO]; [fRipEncoderPopUp setEnabled: NO]; [fRipBrowseButton setEnabled: NO]; [fRipPauseButton setEnabled: YES]; [fRipRipButton setTitle: _( @"Cancel" )]; [fRipProgress setIndeterminate: YES]; [fRipProgress startAnimation: self];; /* Let libhb do the job */ HBStartRip( fHandle, title ); } - (IBAction) Cancel: (id) sender { NSBeginCriticalAlertSheet( _( @"Cancel - Are you sure?" ), _( @"No" ), _( @"Yes" ), nil, fWindow, self, @selector( _Cancel:returnCode:contextInfo: ), nil, nil, _( @"Encoding won't be recoverable." ) ); } - (void) _Cancel: (NSWindow *) sheet returnCode: (int) returnCode contextInfo: (void *) contextInfo { if( returnCode == NSAlertAlternateReturn ) { if( [[fRipPauseButton title] compare: _( @"Resume" ) ] == NSOrderedSame ) { HBResumeRip( fHandle ); } HBStopRip( fHandle ); } } - (IBAction) Pause: (id) sender { if( [[fRipPauseButton title] compare: _( @"Resume" ) ] == NSOrderedSame ) { [self Resume: self]; return; } [fRipPauseButton setTitle: _( @"Resume" )]; HBPauseRip( fHandle ); } - (IBAction) Resume: (id) sender { [fRipPauseButton setTitle: _( @"Pause" )]; HBResumeRip( fHandle ); } - (IBAction) PreviousPicture: (id) sender { fPicture--; if( [fOpenGLCheck state] == NSOnState ) { [fPictureGLView ShowPicture: fPicture animate: HB_ANIMATE_LEFT]; } else { [fPictureGLView ShowPicture: fPicture animate: HB_ANIMATE_NONE]; } [fPreviousButton setEnabled: ( fPicture > 0 )]; [fNextButton setEnabled: YES]; } - (IBAction) NextPicture: (id) sender { fPicture++; if( [fOpenGLCheck state] == NSOnState ) { [fPictureGLView ShowPicture: fPicture animate: HB_ANIMATE_RIGHT]; } else { [fPictureGLView ShowPicture: fPicture animate: HB_ANIMATE_NONE]; } [fPreviousButton setEnabled: YES]; [fNextButton setEnabled: ( fPicture < 9 )]; } - (IBAction) UpdatePicture: (id) sender { HBTitle * title = (HBTitle*) HBListItemAt( fTitleList, [fRipTitlePopUp indexOfSelectedItem] ); title->outWidth = [fWidthStepper intValue]; title->deinterlace = ( [fDeinterlaceCheck state] == NSOnState ); title->topCrop = [fTopStepper intValue]; title->bottomCrop = [fBottomStepper intValue]; title->leftCrop = [fLeftStepper intValue]; title->rightCrop = [fRightStepper intValue]; [fPictureGLView ShowPicture: fPicture animate: HB_ANIMATE_NONE]; [fWidthStepper setIntValue: title->outWidth]; [fTopStepper setIntValue: title->topCrop]; [fBottomStepper setIntValue: title->bottomCrop]; [fLeftStepper setIntValue: title->leftCrop]; [fRightStepper setIntValue: title->rightCrop]; [fWidthField2 setIntValue: [fWidthStepper intValue]]; [fTopField2 setIntValue: [fTopStepper intValue]]; [fBottomField2 setIntValue: [fBottomStepper intValue]]; [fLeftField2 setIntValue: [fLeftStepper intValue]]; [fRightField2 setIntValue: [fRightStepper intValue]]; [fInfoField setStringValue: [NSString stringWithFormat: _( @"Final size: %dx%d" ), title->outWidth, title->outHeight]]; } - (IBAction) AutoCrop: (id) sender { HBTitle * title = (HBTitle*) HBListItemAt( fTitleList, [fRipTitlePopUp indexOfSelectedItem] ); title->topCrop = title->autoTopCrop; title->bottomCrop = title->autoBottomCrop; title->leftCrop = title->autoLeftCrop; title->rightCrop = title->autoRightCrop; [fPictureGLView ShowPicture: fPicture animate: HB_ANIMATE_NONE]; [fWidthStepper setIntValue: title->outWidth]; [fTopStepper setIntValue: title->topCrop]; [fBottomStepper setIntValue: title->bottomCrop]; [fLeftStepper setIntValue: title->leftCrop]; [fRightStepper setIntValue: title->rightCrop]; [fWidthField2 setIntValue: [fWidthStepper intValue]]; [fTopField2 setIntValue: [fTopStepper intValue]]; [fBottomField2 setIntValue: [fBottomStepper intValue]]; [fLeftField2 setIntValue: [fLeftStepper intValue]]; [fRightField2 setIntValue: [fRightStepper intValue]]; [fInfoField setStringValue: [NSString stringWithFormat: _( @"Final size: %dx%d" ), title->outWidth, title->outHeight]]; } - (void) DetectDrives: (NSNotification *) notification { /* Scan DVD drives (stolen from VLC) */ io_object_t next_media; mach_port_t master_port; kern_return_t kern_result; io_iterator_t media_iterator; CFMutableDictionaryRef classes_to_match; kern_result = IOMasterPort( MACH_PORT_NULL, &master_port ); if( kern_result != KERN_SUCCESS ) { return; } classes_to_match = IOServiceMatching( kIODVDMediaClass ); if( classes_to_match == NULL ) { return; } CFDictionarySetValue( classes_to_match, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue ); kern_result = IOServiceGetMatchingServices( master_port, classes_to_match, &media_iterator ); if( kern_result != KERN_SUCCESS ) { return; } NSMutableArray * drivesList; drivesList = [NSMutableArray arrayWithCapacity: 1]; next_media = IOIteratorNext( media_iterator ); if( next_media != NULL ) { char psz_buf[0x32]; size_t dev_path_length; CFTypeRef str_bsd_path; do { str_bsd_path = IORegistryEntryCreateCFProperty( next_media, CFSTR( kIOBSDNameKey ), kCFAllocatorDefault, 0 ); if( str_bsd_path == NULL ) { IOObjectRelease( next_media ); continue; } snprintf( psz_buf, sizeof(psz_buf), "%s%c", _PATH_DEV, 'r' ); dev_path_length = strlen( psz_buf ); if( CFStringGetCString( (CFStringRef) str_bsd_path, (char*)&psz_buf + dev_path_length, sizeof(psz_buf) - dev_path_length, kCFStringEncodingASCII ) ) { [drivesList addObject: [NSString stringWithCString: psz_buf]]; } CFRelease( str_bsd_path ); IOObjectRelease( next_media ); } while( ( next_media = IOIteratorNext( media_iterator ) ) != NULL ); } IOObjectRelease( media_iterator ); [fScDetectedPopUp removeAllItems]; for( unsigned i = 0; i < [drivesList count]; i++ ) { [[fScDetectedPopUp menu] addItemWithTitle: [drivesList objectAtIndex: i] action: nil keyEquivalent: @""]; } [self ScanMatrixChanged: self]; } - (IBAction) ScanMatrixChanged: (id) sender { if( ![fScMatrix selectedRow] ) { [fScDetectedPopUp setEnabled: YES]; [fScFolderField setEnabled: NO]; [fScBrowseButton setEnabled: NO]; [fScOpenButton setEnabled: ( [fScDetectedPopUp selectedItem] != nil )]; } else { [fScDetectedPopUp setEnabled: NO]; [fScFolderField setEnabled: YES]; [fScBrowseButton setEnabled: YES]; [fScOpenButton setEnabled: YES]; } } - (IBAction) TitlePopUpChanged: (id) sender { HBTitle * title = (HBTitle*) HBListItemAt( fTitleList, [fRipTitlePopUp indexOfSelectedItem] ); [fRipLang1PopUp removeAllItems]; [fRipLang2PopUp removeAllItems]; HBAudio * audio; for( int i = 0; i < HBListCount( title->audioList ); i++ ) { audio = (HBAudio*) HBListItemAt( title->audioList, i ); /* We cannot use NSPopUpButton's addItemWithTitle because it checks for duplicate entries */ [[fRipLang1PopUp menu] addItemWithTitle: [NSString stringWithCString: audio->language] action: nil keyEquivalent: @""]; [[fRipLang2PopUp menu] addItemWithTitle: [NSString stringWithCString: audio->language] action: nil keyEquivalent: @""]; } [fRipLang2PopUp addItemWithTitle: _( @"None" )]; [fRipLang2PopUp selectItemWithTitle: _( @"None" )]; [fRipLang2PopUp setEnabled: ( HBListCount( title->audioList ) > 1 )]; [fRipTargetField SetHBTitle: title]; if( [fRipVideoMatrix selectedRow] ) { [fRipTargetField UpdateBitrate]; } } - (IBAction) FormatPopUpChanged: (id) sender { /* Headers size changes depending on the format, so let's recalculate the bitrate if necessary */ if( [fRipVideoMatrix selectedRow] ) { [fRipTargetField UpdateBitrate]; } /* Add/replace to the correct extension */ NSString * string = [fRipFileField2 stringValue]; int format = [fRipFormatPopUp indexOfSelectedItem]; if( [string characterAtIndex: [string length] - 4] == '.' ) { [fRipFileField2 setStringValue: [NSString stringWithFormat: @"%@.%s", [string substringToIndex: [string length] - 4], ( !format ) ? "mp4" : ( ( format == 3 ) ? "ogm" : "avi" )]]; } else { [fRipFileField2 setStringValue: [NSString stringWithFormat: @"%@.%s", string, ( !format ) ? "mp4" : ( ( format == 3 ) ? "ogm" : "avi" )]]; } if( format == 2 ) { /* Can't set X264 bitrate */ [fRipEncoderPopUp setEnabled: NO]; [fRipVideoMatrix setEnabled: NO]; [fRipTwoPassCheck setEnabled: NO]; [fRipAudBitPopUp setEnabled: YES]; } else if( format == 3 ) { [fRipEncoderPopUp setEnabled: YES]; [fRipVideoMatrix setEnabled: YES]; [fRipTwoPassCheck setEnabled: YES]; /* Can't set Vorbis bitrate */ [fRipAudBitPopUp setEnabled: NO]; } else { [fRipEncoderPopUp setEnabled: YES]; [fRipVideoMatrix setEnabled: YES]; [fRipTwoPassCheck setEnabled: YES]; [fRipAudBitPopUp setEnabled: YES]; } [self VideoMatrixChanged: self]; } - (IBAction) AudioPopUpChanged: (id) sender { /* Recalculate the bitrate */ if( [fRipVideoMatrix selectedRow] ) { [fRipTargetField UpdateBitrate]; } } /******************* * libhb callbacks * *******************/ static void _Scanning( void * data, int title, int titleCount ) { HBController * controller = (HBController*) data; controller->fTitle = title; controller->fTitleCount = titleCount; [controller performSelectorOnMainThread: @selector(Scanning:) withObject: nil waitUntilDone: YES]; } - (void) Scanning: (id) sender { [fScProgress stopAnimation: self]; [fScProgress setIndeterminate: NO]; [fScProgress setDoubleValue: 100.0 * fTitle / fTitleCount]; [fScStatusField setStringValue: [NSString stringWithFormat: _( @"Scanning title %d of %d..." ), fTitle, fTitleCount]]; } static void _ScanDone( void * data, HBList * titleList ) { HBController * controller = (HBController*) data; controller->fTitleList = titleList; [controller performSelectorOnMainThread: @selector(ScanDone:) withObject: nil waitUntilDone: YES]; } - (void) ScanDone: (id) sender { if( !fTitleList ) { [fScMatrix setEnabled: YES]; [self ScanMatrixChanged: self]; [fScProgress stopAnimation: self]; [fScProgress setIndeterminate: NO]; [fScOpenButton setEnabled: YES]; [fScStatusField setStringValue: _( @"Invalid volume, try again" ) ]; return; } /* Show a temporary empty view while the window resizing animation */ [fWindow setContentView: fTempView ]; /* Actually resize it */ NSRect newFrame; newFrame = [NSWindow contentRectForFrameRect: [fWindow frame] styleMask: [fWindow styleMask]]; newFrame.origin.y += newFrame.size.height - [fRipView frame].size.height; newFrame.size.height = [fRipView frame].size.height; newFrame.size.width = [fRipView frame].size.width; newFrame = [NSWindow frameRectForContentRect: newFrame styleMask: [fWindow styleMask]]; [fWindow setFrame: newFrame display: YES animate: YES]; /* Show the new GUI */ [fWindow setContentView: fRipView ]; [fRipPauseButton setEnabled: NO]; [fRipTitlePopUp removeAllItems]; HBTitle * title; for( int i = 0; i < HBListCount( fTitleList ); i++ ) { title = (HBTitle*) HBListItemAt( fTitleList, i ); [[fRipTitlePopUp menu] addItemWithTitle: [NSString stringWithFormat: @"%d - %02dh%02dm%02ds", title->index, title->length / 3600, ( title->length % 3600 ) / 60, title->length % 60] action: nil keyEquivalent: @""]; } [self TitlePopUpChanged: self]; } static void _Encoding( void * data, float position, int pass, int passCount, float curFrameRate, float avgFrameRate, int remainingTime ) { HBController * controller = (HBController*) data; controller->fPosition = position; controller->fPass = pass; controller->fPassCount = passCount; controller->fCurFrameRate = curFrameRate; controller->fAvgFrameRate = avgFrameRate; controller->fRemainingTime = remainingTime; [controller performSelectorOnMainThread: @selector(Encoding:) withObject: nil waitUntilDone: YES]; } - (void) Encoding: (id) sender { [fRipStatusField setStringValue: [NSString stringWithFormat: _( @"Encoding: %.2f %% (pass %d of %d)" ), 100.0 * fPosition, fPass, fPassCount]]; [fRipInfoField setStringValue: [NSString stringWithFormat: _( @"Speed: %.2f fps (avg %.2f fps), %02dh%02dm%02ds remaining" ), fCurFrameRate, fAvgFrameRate, fRemainingTime / 3600, ( fRemainingTime / 60 ) % 60, fRemainingTime % 60]]; [fRipProgress setIndeterminate: NO]; [fRipProgress setDoubleValue: 100.0 * fPosition]; } static void _RipDone( void * data, int result ) { HBController * controller = (HBController*) data; controller->fResult = result; [controller performSelectorOnMainThread: @selector(RipDone:) withObject: nil waitUntilDone: YES]; } - (void) RipDone: (id) sender { [fRipTitlePopUp setEnabled: YES]; [fRipFormatPopUp setEnabled: YES]; [fRipVideoMatrix setEnabled: YES]; [fRipTwoPassCheck setEnabled: YES]; [fRipCropButton setEnabled: YES]; [fRipLang1PopUp setEnabled: YES]; [fRipLang2PopUp setEnabled: YES]; [fRipAudBitPopUp setEnabled: YES]; [fRipFileField2 setEnabled: YES]; [fRipBrowseButton setEnabled: YES]; [fRipEncoderPopUp setEnabled: YES]; [fRipPauseButton setEnabled: NO]; [fRipPauseButton setTitle: _( @"Pause" )]; [fRipRipButton setTitle: _( @"Rip" )]; [fRipProgress setIndeterminate: NO]; [fRipProgress setDoubleValue: 0.0]; [self VideoMatrixChanged: self]; switch( fResult ) { case HB_SUCCESS: [fRipStatusField setStringValue: _( @"Rip completed." )]; [fRipInfoField setStringValue: @""]; NSBeep(); [NSApp requestUserAttention: NSInformationalRequest]; [NSApp beginSheet: fDonePanel modalForWindow: fWindow modalDelegate: nil didEndSelector: nil contextInfo: nil]; [NSApp runModalForWindow: fDonePanel]; [NSApp endSheet: fDonePanel]; [fDonePanel orderOut: self]; break; case HB_CANCELED: [fRipStatusField setStringValue: _( @"Canceled." )]; [fRipInfoField setStringValue: @""]; break; case HB_ERROR_A52_SYNC: [fRipStatusField setStringValue: @"Error."]; [fRipInfoField setStringValue: @"Corrupted AC3 data"]; break; case HB_ERROR_AVI_WRITE: [fRipStatusField setStringValue: @"Error."]; [fRipInfoField setStringValue: @"Write error"]; break; case HB_ERROR_DVD_OPEN: [fRipStatusField setStringValue: @"Error."]; [fRipInfoField setStringValue: @"Could not open the DVD"]; break; case HB_ERROR_DVD_READ: [fRipStatusField setStringValue: @"Error."]; [fRipInfoField setStringValue: @"DVD read error"]; break; case HB_ERROR_MP3_INIT: [fRipStatusField setStringValue: @"Error."]; [fRipInfoField setStringValue: @"MP3 encoder initialization failed"]; break; case HB_ERROR_MP3_ENCODE: [fRipStatusField setStringValue: @"Error."]; [fRipInfoField setStringValue: @"MP3 encoder failed"]; break; case HB_ERROR_MPEG4_INIT: [fRipStatusField setStringValue: @"Error."]; [fRipInfoField setStringValue: @"MPEG4 encoder initialization failed"]; break; } } @end