X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=macosx%2FController.mm;h=5901249e2d80b5fbfc6df100d4b6cac9baeef1aa;hb=da3e9e2a95293ac485cd2b403a22864b3dd92938;hp=ed1a5bab3f58a452ab74a31bcda146e910b4cb2b;hpb=c90e8a202c9560abb9bd7b31238654f592e61441;p=handbrake-jp%2Fhandbrake-jp-git.git diff --git a/macosx/Controller.mm b/macosx/Controller.mm index ed1a5bab..5901249e 100644 --- a/macosx/Controller.mm +++ b/macosx/Controller.mm @@ -1,48 +1,16 @@ /* $Id: Controller.mm,v 1.79 2005/11/04 19:41:32 titer Exp $ This file is part of the HandBrake source code. - Homepage: . + Homepage: . It may be used under the terms of the GNU General Public License. */ -#include "Controller.h" -#include "a52dec/a52.h" +#import "Controller.h" #import "HBOutputPanelController.h" #import "HBPreferencesController.h" -/* Added to integrate scanning into HBController */ -#include -#include -#include -#include "HBDVDDetector.h" -#include "dvdread/dvd_reader.h" -#include "HBPresets.h" - -#define _(a) NSLocalizedString(a,NULL) - -static int FormatSettings[4][10] = - { { HB_MUX_MP4 | HB_VCODEC_FFMPEG | HB_ACODEC_FAAC, - HB_MUX_MP4 | HB_VCODEC_X264 | HB_ACODEC_FAAC, - HB_MUX_MP4 | HB_VCODEC_X264 | HB_ACODEC_FAAC, - HB_MUX_MP4 | HB_VCODEC_X264 | HB_ACODEC_AC3, - 0, - 0 }, - { HB_MUX_MKV | HB_VCODEC_FFMPEG | HB_ACODEC_FAAC, - HB_MUX_MKV | HB_VCODEC_FFMPEG | HB_ACODEC_AC3, - HB_MUX_MKV | HB_VCODEC_FFMPEG | HB_ACODEC_LAME, - HB_MUX_MKV | HB_VCODEC_FFMPEG | HB_ACODEC_VORBIS, - HB_MUX_MKV | HB_VCODEC_X264 | HB_ACODEC_FAAC, - HB_MUX_MKV | HB_VCODEC_X264 | HB_ACODEC_AC3, - HB_MUX_MKV | HB_VCODEC_X264 | HB_ACODEC_LAME, - HB_MUX_MKV | HB_VCODEC_X264 | HB_ACODEC_VORBIS, - 0, - 0 }, - { HB_MUX_AVI | HB_VCODEC_FFMPEG | HB_ACODEC_LAME, - HB_MUX_AVI | HB_VCODEC_FFMPEG | HB_ACODEC_AC3, - HB_MUX_AVI | HB_VCODEC_X264 | HB_ACODEC_LAME, - HB_MUX_AVI | HB_VCODEC_X264 | HB_ACODEC_AC3}, - { HB_MUX_OGM | HB_VCODEC_FFMPEG | HB_ACODEC_VORBIS, - HB_MUX_OGM | HB_VCODEC_FFMPEG | HB_ACODEC_LAME, - 0, - 0 } }; +#import "HBDVDDetector.h" +#import "HBPresets.h" + +#define DragDropSimplePboardType @"MyCustomOutlineViewPboardType" /* We setup the toolbar values here */ static NSString * ToggleDrawerIdentifier = @"Toggle Drawer Item Identifier"; @@ -59,26 +27,31 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It *******************************/ @implementation HBController -- init +- (id)init { self = [super init]; + if( !self ) + { + return nil; + } + [HBPreferencesController registerUserDefaults]; fHandle = NULL; + fQueueEncodeLibhb = NULL; /* Check for check for the app support directory here as - * outputPanel needs it right away, as may other future methods - */ - /* We declare the default NSFileManager into fileManager */ - NSFileManager * fileManager = [NSFileManager defaultManager]; - /* we set the files and support paths here */ - AppSupportDirectory = @"~/Library/Application Support/HandBrake"; - AppSupportDirectory = [AppSupportDirectory stringByExpandingTildeInPath]; - /* We check for the app support directory for handbrake */ - if ([fileManager fileExistsAtPath:AppSupportDirectory] == 0) - { - // If it doesnt exist yet, we create it here - [fileManager createDirectoryAtPath:AppSupportDirectory attributes:nil]; - } - + * outputPanel needs it right away, as may other future methods + */ + NSString *libraryDir = [NSSearchPathForDirectoriesInDomains( NSLibraryDirectory, + NSUserDomainMask, + YES ) objectAtIndex:0]; + AppSupportDirectory = [[libraryDir stringByAppendingPathComponent:@"Application Support"] + stringByAppendingPathComponent:@"HandBrake"]; + if( ![[NSFileManager defaultManager] fileExistsAtPath:AppSupportDirectory] ) + { + [[NSFileManager defaultManager] createDirectoryAtPath:AppSupportDirectory + attributes:nil]; + } + outputPanel = [[HBOutputPanelController alloc] init]; fPictureController = [[PictureController alloc] initWithDelegate:self]; fQueueController = [[HBQueueController alloc] init]; @@ -89,70 +62,134 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It */ fPresetsBuiltin = [[HBPresets alloc] init]; fPreferencesController = [[HBPreferencesController alloc] init]; + /* Lets report the HandBrake version number here to the activity log and text log file */ + NSString *versionStringFull = [[NSString stringWithFormat: @"Handbrake Version: %@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleGetInfoString"]] stringByAppendingString: [NSString stringWithFormat: @" (%@)", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]]]; + [self writeToActivityLog: "%s", [versionStringFull UTF8String]]; + return self; } - (void) applicationDidFinishLaunching: (NSNotification *) notification { - /* Variables from legacy update system, leave but commented out until Sparkle is compeletely vetted */ - //int build; - //char * version; - - // Init libhb - /* Old update method using hb_init, commented out but code left for a few revs til new sparkle updater is vetted */ - //fHandle = hb_init(debugLevel, [[NSUserDefaults standardUserDefaults] boolForKey:@"CheckForUpdates"]); - /* New Init libhb with check for updates libhb style set to "0" so its ignored and lets sparkle take care of it */ - fHandle = hb_init(HB_DEBUG_ALL, 0); + /* Init libhb with check for updates libhb style set to "0" so its ignored and lets sparkle take care of it */ + int loggingLevel = [[[NSUserDefaults standardUserDefaults] objectForKey:@"LoggingLevel"] intValue]; + fHandle = hb_init(loggingLevel, 0); + /* Init a separate instance of libhb for user scanning and setting up jobs */ + fQueueEncodeLibhb = hb_init(loggingLevel, 0); // Set the Growl Delegate - [GrowlApplicationBridge setGrowlDelegate: self]; + [GrowlApplicationBridge setGrowlDelegate: self]; /* Init others controllers */ [fPictureController SetHandle: fHandle]; - [fQueueController setHandle: fHandle]; + [fQueueController setHandle: fQueueEncodeLibhb]; [fQueueController setHBController: self]; - + fChapterTitlesDelegate = [[ChapterTitles alloc] init]; [fChapterTable setDataSource:fChapterTitlesDelegate]; - + [fChapterTable setDelegate:fChapterTitlesDelegate]; + /* Call UpdateUI every 1/2 sec */ - [[NSRunLoop currentRunLoop] addTimer: [NSTimer - scheduledTimerWithTimeInterval: 0.5 target: self - selector: @selector( updateUI: ) userInfo: NULL repeats: YES] - forMode: NSEventTrackingRunLoopMode]; - + [[NSRunLoop currentRunLoop] addTimer:[NSTimer + scheduledTimerWithTimeInterval:0.5 target:self + selector:@selector(updateUI:) userInfo:nil repeats:YES] + forMode:NSEventTrackingRunLoopMode]; + // Open debug output window now if it was visible when HB was closed if ([[NSUserDefaults standardUserDefaults] boolForKey:@"OutputPanelIsOpen"]) [self showDebugOutputPanel:nil]; - + // Open queue window now if it was visible when HB was closed if ([[NSUserDefaults standardUserDefaults] boolForKey:@"QueueWindowIsOpen"]) [self showQueueWindow:nil]; - + [self openMainWindow:nil]; - /* Show Browse Sources Window ASAP */ - [self performSelectorOnMainThread: @selector(browseSources:) - withObject: NULL waitUntilDone: NO]; + /* We have to set the bool to tell hb what to do after a scan + * Initially we set it to NO until we start processing the queue + */ + applyQueueToScan = NO; + + /* Now we re-check the queue array to see if there are + * any remaining encodes to be done in it and ask the + * user if they want to reload the queue */ + if ([QueueFileArray count] > 0) + { + /* run getQueueStats to see whats in the queue file */ + [self getQueueStats]; + /* this results in these values + * fEncodingQueueItem = 0; + * fPendingCount = 0; + * fCompletedCount = 0; + * fCanceledCount = 0; + * fWorkingCount = 0; + */ + + /*On Screen Notification*/ + NSString * alertTitle; + if (fWorkingCount > 0) + { + alertTitle = [NSString stringWithFormat: + NSLocalizedString(@"HandBrake Has Detected %d Previously Encoding Item and %d Pending Item(s) In Your Queue.", @""), + fWorkingCount,fPendingCount]; + } + else + { + alertTitle = [NSString stringWithFormat: + NSLocalizedString(@"HandBrake Has Detected %d Pending Item(s) In Your Queue.", @""), + fPendingCount]; + } + NSBeginCriticalAlertSheet( + alertTitle, + NSLocalizedString(@"Reload Queue", nil), + nil, + NSLocalizedString(@"Empty Queue", nil), + fWindow, self, + nil, @selector(didDimissReloadQueue:returnCode:contextInfo:), nil, + NSLocalizedString(@" Do you want to reload them ?", nil)); + // call didDimissReloadQueue: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo + // right below to either clear the old queue or keep it loaded up. + } + else + { + + /* Show Browse Sources Window ASAP */ + [self performSelectorOnMainThread:@selector(browseSources:) + withObject:nil waitUntilDone:NO]; + } +} + +- (void) didDimissReloadQueue: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo +{ + if (returnCode == NSAlertOtherReturn) + { + [self clearQueueAllItems]; + [self performSelectorOnMainThread:@selector(browseSources:) + withObject:nil waitUntilDone:NO]; + } + else + { + [self setQueueEncodingItemsAsPending]; + [self showQueueWindow:NULL]; + } } - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *) app { + // Warn if encoding a movie hb_state_t s; - hb_get_state( fHandle, &s ); - HBJobGroup * jobGroup = [fQueueController currentJobGroup]; - if ( jobGroup && ( s.state != HB_STATE_IDLE ) ) + hb_get_state( fQueueEncodeLibhb, &s ); + + if ( s.state != HB_STATE_IDLE ) { int result = NSRunCriticalAlertPanel( - NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil), - NSLocalizedString(@"%@ is currently encoding. If you quit HandBrake, your movie will be lost. Do you want to quit anyway?", nil), - NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil, - jobGroup ? [jobGroup name] : @"A movie" ); + NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil), + NSLocalizedString(@"If you quit HandBrake your current encode will be reloaded into your queue at next launch. Do you want to quit anyway?", nil), + NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil, @"A movie" ); if (result == NSAlertDefaultReturn) { - [self doCancelCurrentJob]; return NSTerminateNow; } else @@ -160,12 +197,12 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It } // Warn if items still in the queue - else if ( hb_count( fHandle ) > 0 ) + else if ( fPendingCount > 0 ) { int result = NSRunCriticalAlertPanel( - NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil), - NSLocalizedString(@"One or more encodes are queued for encoding. Do you want to quit anyway?", nil), - NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil); + NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil), + NSLocalizedString(@"There are pending encodes in your queue. Do you want to quit anyway?",nil), + NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil); if ( result == NSAlertDefaultReturn ) return NSTerminateNow; @@ -182,6 +219,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [outputPanel release]; [fQueueController release]; hb_close(&fHandle); + hb_close(&fQueueEncodeLibhb); } @@ -191,15 +229,24 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [fWindow setExcludedFromWindowsMenu:YES]; [fAdvancedOptions setView:fAdvancedView]; + /* lets setup our presets drawer for drag and drop here */ + [fPresetsOutlineView registerForDraggedTypes: [NSArray arrayWithObject:DragDropSimplePboardType] ]; + [fPresetsOutlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES]; + [fPresetsOutlineView setVerticalMotionCanBeginDrag: YES]; + /* Initialize currentScanCount so HB can use it to evaluate successive scans */ currentScanCount = 0; - + + /* Init UserPresets .plist */ [self loadPresets]; - - fRipIndicatorShown = NO; // initially out of view in the nib + + /* Init QueueFile .plist */ + [self loadQueueFile]; + fRipIndicatorShown = NO; // initially out of view in the nib + /* Show/Dont Show Presets drawer upon launch based on user preference DefaultPresetsDrawerShow*/ if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultPresetsDrawerShow"] > 0) @@ -208,46 +255,56 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It } + /* Destination box*/ + NSMenuItem *menuItem; [fDstFormatPopUp removeAllItems]; - [fDstFormatPopUp addItemWithTitle: _( @"MP4 file" )]; - [fDstFormatPopUp addItemWithTitle: _( @"MKV file" )]; - [fDstFormatPopUp addItemWithTitle: _( @"AVI file" )]; - [fDstFormatPopUp addItemWithTitle: _( @"OGM file" )]; + // MP4 file + menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MP4 file" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_MUX_MP4]; + // MKV file + menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MKV file" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_MUX_MKV]; + // AVI file + menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"AVI file" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_MUX_AVI]; + // OGM file + menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"OGM file" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_MUX_OGM]; [fDstFormatPopUp selectItemAtIndex: 0]; - - [self formatPopUpChanged: NULL]; - - /* We enable the create chapters checkbox here since we are .mp4 */ + + [self formatPopUpChanged:nil]; + + /* We enable the create chapters checkbox here since we are .mp4 */ [fCreateChapterMarkers setEnabled: YES]; if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultChapterMarkers"] > 0) { [fCreateChapterMarkers setState: NSOnState]; } - - - - + + + + [fDstFile2Field setStringValue: [NSString stringWithFormat: @"%@/Desktop/Movie.mp4", NSHomeDirectory()]]; - + /* Video encoder */ [fVidEncoderPopUp removeAllItems]; [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"]; [fVidEncoderPopUp addItemWithTitle: @"XviD"]; - - - + + + /* Video quality */ [fVidTargetSizeField setIntValue: 700]; [fVidBitrateField setIntValue: 1000]; - + [fVidQualityMatrix selectCell: fVidBitrateCell]; - [self videoMatrixChanged: NULL]; - + [self videoMatrixChanged:nil]; + /* Video framerate */ [fVidRatePopUp removeAllItems]; - [fVidRatePopUp addItemWithTitle: _( @"Same as source" )]; + [fVidRatePopUp addItemWithTitle: NSLocalizedString( @"Same as source", @"" )]; for( int i = 0; i < hb_video_rates_count; i++ ) { if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.3f",23.976]]) @@ -277,84 +334,69 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [fPictureController setAutoCrop:YES]; /* Audio bitrate */ - [fAudBitratePopUp removeAllItems]; + [fAudTrack1BitratePopUp removeAllItems]; for( int i = 0; i < hb_audio_bitrates_count; i++ ) { - [fAudBitratePopUp addItemWithTitle: + [fAudTrack1BitratePopUp addItemWithTitle: [NSString stringWithCString: hb_audio_bitrates[i].string]]; } - [fAudBitratePopUp selectItemAtIndex: hb_audio_bitrates_default]; + [fAudTrack1BitratePopUp selectItemAtIndex: hb_audio_bitrates_default]; /* Audio samplerate */ - [fAudRatePopUp removeAllItems]; + [fAudTrack1RatePopUp removeAllItems]; for( int i = 0; i < hb_audio_rates_count; i++ ) { - [fAudRatePopUp addItemWithTitle: + [fAudTrack1RatePopUp addItemWithTitle: [NSString stringWithCString: hb_audio_rates[i].string]]; } - [fAudRatePopUp selectItemAtIndex: hb_audio_rates_default]; + [fAudTrack1RatePopUp selectItemAtIndex: hb_audio_rates_default]; /* Bottom */ [fStatusField setStringValue: @""]; - + [self enableUI: NO]; [self setupToolbar]; - - [fPresetsActionButton setMenu:fPresetsActionMenu]; - + /* We disable the Turbo 1st pass checkbox since we are not x264 */ [fVidTurboPassCheck setEnabled: NO]; [fVidTurboPassCheck setState: NSOffState]; - - + + /* lets get our default prefs here */ - [self getDefaultPresets: NULL]; + [self getDefaultPresets:nil]; /* lets initialize the current successful scancount here to 0 */ currentSuccessfulScanCount = 0; - -} -- (void) TranslateStrings -{ - [fSrcTitleField setStringValue: _( @"Title:" )]; - [fSrcChapterField setStringValue: _( @"Chapters:" )]; - [fSrcChapterToField setStringValue: _( @"to" )]; - [fSrcDuration1Field setStringValue: _( @"Duration:" )]; - - [fDstFormatField setStringValue: _( @"Format:" )]; - [fDstCodecsField setStringValue: _( @"Codecs:" )]; - [fDstFile1Field setStringValue: _( @"File:" )]; - [fDstBrowseButton setTitle: _( @"Browse" )]; - [fVidRateField setStringValue: _( @"Framerate (fps):" )]; - [fVidEncoderField setStringValue: _( @"Encoder:" )]; - [fVidQualityField setStringValue: _( @"Quality:" )]; } - - (void) enableUI: (bool) b { NSControl * controls[] = { fSrcTitleField, fSrcTitlePopUp, fSrcChapterField, fSrcChapterStartPopUp, fSrcChapterToField, fSrcChapterEndPopUp, fSrcDuration1Field, fSrcDuration2Field, - fDstFormatField, fDstFormatPopUp, fDstCodecsField, - fDstCodecsPopUp, fDstFile1Field, fDstFile2Field, + fDstFormatField, fDstFormatPopUp, fDstFile1Field, fDstFile2Field, fDstBrowseButton, fVidRateField, fVidRatePopUp, fVidEncoderField, fVidEncoderPopUp, fVidQualityField, fVidQualityMatrix, fVidGrayscaleCheck, fSubField, fSubPopUp, - fAudLang1Field, fAudLang1PopUp, fAudLang2Field, fAudLang2PopUp, - fAudTrack1MixLabel, fAudTrack1MixPopUp, fAudTrack2MixLabel, fAudTrack2MixPopUp, - fAudRateField, fAudRatePopUp, fAudBitrateField, - fAudBitratePopUp, fPictureButton,fQueueStatus,fPicSettingARkeep, - fPicSettingDeinterlace,fPicLabelSettings,fPicLabelSrc,fPicLabelOutp,fPicSettingsSrc,fPicSettingsOutp,fPicSettingsAnamorphic, + fAudSourceLabel, fAudCodecLabel, fAudMixdownLabel, fAudSamplerateLabel, fAudBitrateLabel, + fAudTrack1Label, fAudTrack2Label, fAudTrack3Label, fAudTrack4Label, + fAudLang1PopUp, fAudLang2PopUp, fAudLang3PopUp, fAudLang4PopUp, + fAudTrack1CodecPopUp, fAudTrack2CodecPopUp, fAudTrack3CodecPopUp, fAudTrack4CodecPopUp, + fAudTrack1MixPopUp, fAudTrack2MixPopUp, fAudTrack3MixPopUp, fAudTrack4MixPopUp, + fAudTrack1RatePopUp, fAudTrack2RatePopUp, fAudTrack3RatePopUp, fAudTrack4RatePopUp, + fAudTrack1BitratePopUp, fAudTrack2BitratePopUp, fAudTrack3BitratePopUp, fAudTrack4BitratePopUp, + fAudDrcLabel, fAudTrack1DrcSlider, fAudTrack1DrcField, fAudTrack2DrcSlider, + fAudTrack2DrcField, fAudTrack3DrcSlider, fAudTrack3DrcField, fAudTrack4DrcSlider,fAudTrack4DrcField, + fPictureButton,fQueueStatus,fPicSettingARkeep, fPicSettingDeinterlace,fPicLabelSettings,fPicLabelSrc, + fPicLabelOutp,fPicSettingsSrc,fPicSettingsOutp,fPicSettingsAnamorphic, fPicLabelAr,fPicLabelDeinterlace,fPicSettingPAR,fPicLabelAnamorphic,fPresetsAdd,fPresetsDelete, fCreateChapterMarkers,fVidTurboPassCheck,fDstMp4LargeFileCheck,fPicLabelAutoCrop, fPicSettingAutoCrop,fPicSettingDetelecine,fPicLabelDetelecine,fPicLabelDenoise,fPicSettingDenoise, - fSubForcedCheck,fPicSettingDeblock,fPicLabelDeblock,fPresetsOutlineView,fAudDrcSlider, - fAudDrcField,fAudDrcLabel,fDstMp4HttpOptFileCheck,fAudDrcDescLabel1,fAudDrcDescLabel2,fAudDrcDescLabel3, - fAudDrcDescLabel4,fDstMp4iPodFileCheck}; + fSubForcedCheck,fPicSettingDeblock,fPicLabelDeblock,fPicLabelDecomb,fPicSettingDecomb,fPresetsOutlineView, + fAudDrcLabel,fDstMp4HttpOptFileCheck,fDstMp4iPodFileCheck}; for( unsigned i = 0; i < sizeof( controls ) / sizeof( NSControl * ); i++ ) @@ -372,22 +414,23 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [controls[i] setEnabled: b]; } - + if (b) { /* if we're enabling the interface, check if the audio mixdown controls need to be enabled or not */ /* these will have been enabled by the mass control enablement above anyway, so we're sense-checking it here */ - [self setEnabledStateOfAudioMixdownControls: NULL]; + [self setEnabledStateOfAudioMixdownControls:nil]; /* we also call calculatePictureSizing here to sense check if we already have vfr selected */ - [self calculatePictureSizing: NULL]; - + [self calculatePictureSizing:nil]; + [self shouldEnableHttpMp4CheckBox: nil]; + } else { [fPresetsOutlineView setEnabled: NO]; - + } - [self videoMatrixChanged: NULL]; + [self videoMatrixChanged:nil]; [fAdvancedOptions enableUI:b]; } @@ -478,252 +521,302 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It - (void) updateUI: (NSTimer *) timer { - + + /* Update UI for fHandle (user scanning instance of libhb ) */ + hb_list_t * list; - list = hb_get_titles( fHandle ); + list = hb_get_titles( fHandle ); /* check to see if there has been a new scan done - this bypasses the constraints of HB_STATE_WORKING - not allowing setting a newly scanned source */ + this bypasses the constraints of HB_STATE_WORKING + not allowing setting a newly scanned source */ int checkScanCount = hb_get_scancount( fHandle ); - if (checkScanCount > currentScanCount) + if( checkScanCount > currentScanCount ) { - currentScanCount = checkScanCount; [fScanIndicator setIndeterminate: NO]; [fScanIndicator setDoubleValue: 0.0]; [fScanIndicator setHidden: YES]; - [self showNewScan: NULL]; + [self showNewScan:nil]; } - + hb_state_t s; hb_get_state( fHandle, &s ); - - + switch( s.state ) { case HB_STATE_IDLE: - break; -#define p s.param.scanning + break; +#define p s.param.scanning case HB_STATE_SCANNING: { [fSrcDVD2Field setStringValue: [NSString stringWithFormat: - _( @"Scanning title %d of %d..." ), + NSLocalizedString( @"Scanning title %d of %d...", @"" ), p.title_cur, p.title_count]]; [fScanIndicator setHidden: NO]; [fScanIndicator setDoubleValue: 100.0 * ( p.title_cur - 1 ) / p.title_count]; break; } #undef p - + #define p s.param.scandone case HB_STATE_SCANDONE: { [fScanIndicator setIndeterminate: NO]; [fScanIndicator setDoubleValue: 0.0]; [fScanIndicator setHidden: YES]; - [self showNewScan: NULL]; - [toolbar validateVisibleItems]; + [self writeToActivityLog:"ScanDone state received from fHandle"]; + [self showNewScan:nil]; + [[fWindow toolbar] validateVisibleItems]; + + break; + } +#undef p + +#define p s.param.working + case HB_STATE_WORKING: + { + + break; + } +#undef p + +#define p s.param.muxing + case HB_STATE_MUXING: + { + + break; + } +#undef p + + case HB_STATE_PAUSED: + break; + + case HB_STATE_WORKDONE: + { + break; + } + } + + + /* Update UI for fQueueEncodeLibhb */ + // hb_list_t * list; + // list = hb_get_titles( fQueueEncodeLibhb ); //fQueueEncodeLibhb + /* check to see if there has been a new scan done + this bypasses the constraints of HB_STATE_WORKING + not allowing setting a newly scanned source */ + + checkScanCount = hb_get_scancount( fQueueEncodeLibhb ); + if( checkScanCount > currentScanCount ) + { + currentScanCount = checkScanCount; + [self writeToActivityLog:"currentScanCount received from fQueueEncodeLibhb"]; + } + + //hb_state_t s; + hb_get_state( fQueueEncodeLibhb, &s ); + + switch( s.state ) + { + case HB_STATE_IDLE: + break; +#define p s.param.scanning + case HB_STATE_SCANNING: + { + [fStatusField setStringValue: [NSString stringWithFormat: + NSLocalizedString( @"Queue Scanning title %d of %d...", @"" ), + p.title_cur, p.title_count]]; + + /* Set the status string in fQueueController as well */ + [fQueueController setQueueStatusString: [NSString stringWithFormat: + NSLocalizedString( @"Queue Scanning title %d of %d...", @"" ), + p.title_cur, p.title_count]]; + + [fRipIndicator setHidden: NO]; + [fRipIndicator setDoubleValue: 100.0 * ( p.title_cur - 1 ) / p.title_count]; + break; + } +#undef p + +#define p s.param.scandone + case HB_STATE_SCANDONE: + { + [fRipIndicator setIndeterminate: NO]; + [fRipIndicator setDoubleValue: 0.0]; + + [self writeToActivityLog:"ScanDone state received from fQueueEncodeLibhb"]; + [self processNewQueueEncode]; + [[fWindow toolbar] validateVisibleItems]; + break; } #undef p - + #define p s.param.working case HB_STATE_WORKING: { float progress_total; NSMutableString * string; - /* Currently, p.job_cur and p.job_count get screwed up when adding - jobs during encoding, if they cannot be fixed in libhb, will implement a - nasty but working cocoa solution */ /* Update text field */ - string = [NSMutableString stringWithFormat: _( @"Encoding: task %d of %d, %.2f %%" ), p.job_cur, p.job_count, 100.0 * p.progress]; + string = [NSMutableString stringWithFormat: NSLocalizedString( @"Encoding: pass %d of %d, %.2f %%", @"" ), p.job_cur, p.job_count, 100.0 * p.progress]; if( p.seconds > -1 ) { [string appendFormat: - _( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" ), - p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds]; + NSLocalizedString( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)", @"" ), + p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds]; } + [fStatusField setStringValue: string]; - + /* Set the status string in fQueueController as well */ + [fQueueController setQueueStatusString: string]; /* Update slider */ progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count; [fRipIndicator setIndeterminate: NO]; [fRipIndicator setDoubleValue: 100.0 * progress_total]; - + // If progress bar hasn't been revealed at the bottom of the window, do // that now. This code used to be in doRip. I moved it to here to handle // the case where hb_start is called by HBQueueController and not from // HBController. - if (!fRipIndicatorShown) + if( !fRipIndicatorShown ) { NSRect frame = [fWindow frame]; - if (frame.size.width <= 591) + if( frame.size.width <= 591 ) frame.size.width = 591; frame.size.height += 36; frame.origin.y -= 36; [fWindow setFrame:frame display:YES animate:YES]; fRipIndicatorShown = YES; - /* We check to see if we need to warn the user that the computer will go to sleep - or shut down when encoding is finished */ - [self remindUserOfSleepOrShutdown]; + } - + /* Update dock icon */ [self UpdateDockIcon: progress_total]; - - // Has current job changed? That means the queue has probably changed as - // well so update it - [fQueueController libhbStateChanged: s]; break; } #undef p - + #define p s.param.muxing case HB_STATE_MUXING: { - NSMutableString * string; - /* Update text field */ - string = [NSMutableString stringWithFormat: - _( @"Muxing..." )]; - [fStatusField setStringValue: string]; - + [fStatusField setStringValue: NSLocalizedString( @"Muxing...", @"" )]; + /* Set the status string in fQueueController as well */ + [fQueueController setQueueStatusString: NSLocalizedString( @"Muxing...", @"" )]; /* Update slider */ [fRipIndicator setIndeterminate: YES]; [fRipIndicator startAnimation: nil]; - + /* Update dock icon */ [self UpdateDockIcon: 1.0]; - - // Pass along the info to HBQueueController - [fQueueController libhbStateChanged: s]; - - break; + + break; } #undef p - + case HB_STATE_PAUSED: - [fStatusField setStringValue: _( @"Paused" )]; + [fStatusField setStringValue: NSLocalizedString( @"Paused", @"" )]; + [fQueueController setQueueStatusString: NSLocalizedString( @"Paused", @"" )]; + + break; - // Pass along the info to HBQueueController - [fQueueController libhbStateChanged: s]; - - break; - case HB_STATE_WORKDONE: { // HB_STATE_WORKDONE happpens as a result of libhb finishing all its jobs // or someone calling hb_stop. In the latter case, hb_stop does not clear // out the remaining passes/jobs in the queue. We'll do that here. - + // Delete all remaining jobs of this encode. - hb_job_t * job; - while( ( job = hb_job( fHandle, 0 ) ) && ( !IsFirstPass(job->sequence_id) ) ) - hb_rem( fHandle, job ); - - [fStatusField setStringValue: _( @"Done." )]; + [fStatusField setStringValue: NSLocalizedString( @"Encode Finished.", @"" )]; + /* Set the status string in fQueueController as well */ + [fQueueController setQueueStatusString: NSLocalizedString( @"Encode Finished.", @"" )]; [fRipIndicator setIndeterminate: NO]; [fRipIndicator setDoubleValue: 0.0]; - [toolbar validateVisibleItems]; - + [[fWindow toolbar] validateVisibleItems]; + /* Restore dock icon */ [self UpdateDockIcon: -1.0]; - - if (fRipIndicatorShown) + + if( fRipIndicatorShown ) { NSRect frame = [fWindow frame]; - if (frame.size.width <= 591) + if( frame.size.width <= 591 ) frame.size.width = 591; frame.size.height += -36; frame.origin.y -= -36; [fWindow setFrame:frame display:YES animate:YES]; fRipIndicatorShown = NO; } - - // Pass along the info to HBQueueController - [fQueueController libhbStateChanged: s]; - + /* Since we are done with this encode, tell output to stop writing to the + * individual encode log + */ + [outputPanel endEncodeLog]; /* Check to see if the encode state has not been cancelled - to determine if we should check for encode done notifications */ - if (fEncodeState != 2) { - /* If Growl Notification or Window and Growl has been selected */ - if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] || - [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"]) - { - /*Growl Notification*/ - [self showGrowlDoneNotification: NULL]; - } - /* If Alert Window or Window and Growl has been selected */ - if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window"] || - [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"]) - { - /*On Screen Notification*/ - int status; - NSBeep(); - status = NSRunAlertPanel(@"Put down that cocktail...",@"Your HandBrake encode is done!", @"OK", nil, nil); - [NSApp requestUserAttention:NSCriticalRequest]; - if ( status == NSAlertDefaultReturn ) - { - [self enableUI: YES]; - } - } - else - { - [self enableUI: YES]; - } - /* If sleep has been selected */ - if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"]) - { - /* Sleep */ - NSDictionary* errorDict; - NSAppleEventDescriptor* returnDescriptor = NULL; - NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource: - @"tell application \"Finder\" to sleep"]; - returnDescriptor = [scriptObject executeAndReturnError: &errorDict]; - [scriptObject release]; - [self enableUI: YES]; - } - /* If Shutdown has been selected */ - if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"]) - { - /* Shut Down */ - NSDictionary* errorDict; - NSAppleEventDescriptor* returnDescriptor = NULL; - NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource: - @"tell application \"Finder\" to shut down"]; - returnDescriptor = [scriptObject executeAndReturnError: &errorDict]; - [scriptObject release]; - [self enableUI: YES]; - } - - // MetaX insertion via AppleScript - if([[NSUserDefaults standardUserDefaults] boolForKey: @"sendToMetaX"] == YES) - { - NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@", @"tell application \"MetaX\" to open (POSIX file \"", [fDstFile2Field stringValue], @"\")"]]; - [myScript executeAndReturnError: nil]; - [myScript release]; - } - - - } - else - { - [self enableUI: YES]; - } + to determine if we should check for encode done notifications */ + if( fEncodeState != 2 ) + { + NSString *pathOfFinishedEncode; + /* Get the output file name for the finished encode */ + pathOfFinishedEncode = [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"]; + + /* Both the Growl Alert and Sending to MetaX can be done as encodes roll off the queue */ + /* Growl alert */ + [self showGrowlDoneNotification:pathOfFinishedEncode]; + /* Send to MetaX */ + [self sendToMetaX:pathOfFinishedEncode]; + + /* since we have successfully completed an encode, we increment the queue counter */ + [self incrementQueueItemDone:nil]; + + /* all end of queue actions below need to be done after all queue encodes have finished + * and there are no pending jobs left to process + */ + if (fPendingCount == 0) + { + /* If Alert Window or Window and Growl has been selected */ + if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window"] || + [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"] ) + { + /*On Screen Notification*/ + int status; + NSBeep(); + status = NSRunAlertPanel(@"Put down that cocktail...",@"Your HandBrake queue is done!", @"OK", nil, nil); + [NSApp requestUserAttention:NSCriticalRequest]; + } + + /* If sleep has been selected */ + if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"] ) + { + /* Sleep */ + NSDictionary* errorDict; + NSAppleEventDescriptor* returnDescriptor = nil; + NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource: + @"tell application \"Finder\" to sleep"]; + returnDescriptor = [scriptObject executeAndReturnError: &errorDict]; + [scriptObject release]; + } + /* If Shutdown has been selected */ + if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"] ) + { + /* Shut Down */ + NSDictionary* errorDict; + NSAppleEventDescriptor* returnDescriptor = nil; + NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource: + @"tell application \"Finder\" to shut down"]; + returnDescriptor = [scriptObject executeAndReturnError: &errorDict]; + [scriptObject release]; + } + + } + + + } + break; } } - - /* Lets show the queue status here in the main window */ - int queue_count = [fQueueController pendingCount]; - if( queue_count == 1) - [fQueueStatus setStringValue: _( @"1 encode queued") ]; - else if (queue_count > 1) - [fQueueStatus setStringValue: [NSString stringWithFormat: _( @"%d encodes queued" ), queue_count]]; - else - [fQueueStatus setStringValue: @""]; + } /* We use this to write messages to stderr from the macgui which show up in the activity window and log*/ @@ -750,21 +843,21 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It // ============================================================ - (void) setupToolbar { - toolbar = [[[NSToolbar alloc] initWithIdentifier: @"HandBrake Toolbar"] autorelease]; - + NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: @"HandBrake Toolbar"] autorelease]; + [toolbar setAllowsUserCustomization: YES]; [toolbar setAutosavesConfiguration: YES]; [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel]; - + [toolbar setDelegate: self]; - + [fWindow setToolbar: toolbar]; } - (NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier: (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted { - NSToolbarItem * item = [[NSToolbarItem alloc] initWithItemIdentifier: itemIdent]; - + NSToolbarItem * item = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdent] autorelease]; + if ([itemIdent isEqualToString: ToggleDrawerIdentifier]) { [item setLabel: @"Toggle Presets"]; @@ -832,7 +925,6 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It } else { - [item release]; return nil; } @@ -861,7 +953,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It if (fHandle) { hb_state_t s; - hb_get_state2( fHandle, &s ); + hb_get_state2( fQueueEncodeLibhb, &s ); if (s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING) { @@ -920,7 +1012,9 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It } } - + /* If there are any pending queue items, make sure the start/stop button is active */ + if ([ident isEqualToString: StartEncodingIdentifier] && fPendingCount > 0) + return YES; if ([ident isEqualToString: ShowQueueIdentifier]) return YES; if ([ident isEqualToString: ToggleDrawerIdentifier]) @@ -972,6 +1066,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It return NO; } if (action == @selector(Rip:)) + { if (s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING || s.state == HB_STATE_PAUSED) { if(![[menuItem title] isEqualToString:@"Stop Encoding"]) @@ -987,12 +1082,17 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It else return NO; } - + } + if( action == @selector(setDefaultPreset:) ) + { + return [fPresetsOutlineView selectedRow] != -1; + } + return YES; } #pragma mark - -#pragma mark Growl +#pragma mark Encode Done Actions // register a test notification and make // it enabled by default #define SERVICE_NAME @"Encode Done" @@ -1006,25 +1106,43 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It return registrationDictionary; } --(IBAction)showGrowlDoneNotification:(id)sender +-(void)showGrowlDoneNotification:(NSString *) filePath +{ + /* This end of encode action is called as each encode rolls off of the queue */ + NSString * finishedEncode = filePath; + /* strip off the path to just show the file name */ + finishedEncode = [finishedEncode lastPathComponent]; + if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] || + [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"]) + { + NSString * growlMssg = [NSString stringWithFormat: @"your HandBrake encode %@ is done!",finishedEncode]; + [GrowlApplicationBridge + notifyWithTitle:@"Put down that cocktail..." + description:growlMssg + notificationName:SERVICE_NAME + iconData:nil + priority:0 + isSticky:1 + clickContext:nil]; + } + +} +-(void)sendToMetaX:(NSString *) filePath { - [GrowlApplicationBridge - notifyWithTitle:@"Put down that cocktail..." - description:@"your HandBrake encode is done!" - notificationName:SERVICE_NAME - iconData:nil - priority:0 - isSticky:1 - clickContext:nil]; + /* This end of encode action is called as each encode rolls off of the queue */ + if([[NSUserDefaults standardUserDefaults] boolForKey: @"sendToMetaX"] == YES) + { + NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@", @"tell application \"MetaX\" to open (POSIX file \"", filePath, @"\")"]]; + [myScript executeAndReturnError: nil]; + [myScript release]; + } } - #pragma mark - #pragma mark Get New Source /*Opens the source browse window, called from Open Source widgets */ - (IBAction) browseSources: (id) sender { - [self enableUI: NO]; NSOpenPanel * panel; panel = [NSOpenPanel openPanel]; @@ -1080,28 +1198,28 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It * purposes in the title panel */ /* Full Path */ - [fScanSrcTitlePathField setStringValue: [NSString stringWithFormat:@"%@", scanPath]]; + [fScanSrcTitlePathField setStringValue:scanPath]; NSString *displayTitlescanSourceName; - + if ([[scanPath lastPathComponent] isEqualToString: @"VIDEO_TS"]) { - /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name + /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name we have to use the title->dvd value so we get the proper name of the volume if a physical dvd is the source*/ - displayTitlescanSourceName = [NSString stringWithFormat:[[scanPath stringByDeletingLastPathComponent] lastPathComponent]]; + displayTitlescanSourceName = [[scanPath stringByDeletingLastPathComponent] lastPathComponent]; } else { /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */ - displayTitlescanSourceName = [NSString stringWithFormat:[scanPath lastPathComponent]]; + displayTitlescanSourceName = [scanPath lastPathComponent]; } /* we set the source display name in the title selection dialogue */ - [fSrcDsplyNameTitleScan setStringValue: [NSString stringWithFormat:@"%@", displayTitlescanSourceName]]; + [fSrcDsplyNameTitleScan setStringValue:displayTitlescanSourceName]; /* we set the attempted scans display name for main window to displayTitlescanSourceName*/ browsedSourceDisplayName = [displayTitlescanSourceName retain]; - /* We show the actual sheet where the user specifies the title to be scanned + /* We show the actual sheet where the user specifies the title to be scanned * as we are going to do a title specific scan */ - [self showSourceTitleScanPanel:NULL]; + [self showSourceTitleScanPanel:nil]; } else { @@ -1118,7 +1236,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [self writeToActivityLog:"trying to open eyetv package"]; /* We're looking at an EyeTV package - try to open its enclosed .mpg media file */ - browsedSourceDisplayName = [[NSString stringWithFormat:@"%@",[[path stringByDeletingPathExtension] lastPathComponent]] retain]; + browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain]; NSString *mpgname; int n = [[path stringByAppendingString: @"/"] completePathIntoString: &mpgname caseSensitive: NO @@ -1142,44 +1260,37 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It else if ([[path pathExtension] isEqualToString: @"dvdmedia"]) { /* path IS a package - but dvdmedia packages can be treaded like normal directories */ - browsedSourceDisplayName = [[NSString stringWithFormat:@"%@",[[path stringByDeletingPathExtension] lastPathComponent]] retain]; + browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain]; [self writeToActivityLog:"trying to open dvdmedia package"]; [self performScan:path scanTitleNum:0]; } - else + else { /* The package is not an eyetv package, so we do not call performScan */ [self writeToActivityLog:"unable to open package"]; } } - else // path is not a package, so we treat it as a dvd parent folder or VIDEO_TS folder + else // path is not a package, so we treat it as a dvd parent folder or VIDEO_TS folder { /* path is not a package, so we call perform scan directly on our file */ if ([[path lastPathComponent] isEqualToString: @"VIDEO_TS"]) { [self writeToActivityLog:"trying to open video_ts folder (video_ts folder chosen)"]; /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name*/ - browsedSourceDisplayName = [[NSString stringWithFormat:@"%@",[[path stringByDeletingLastPathComponent] lastPathComponent]] retain]; + browsedSourceDisplayName = [[[path stringByDeletingLastPathComponent] lastPathComponent] retain]; } else { [self writeToActivityLog:"trying to open video_ts folder (parent directory chosen)"]; /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */ - browsedSourceDisplayName = [[NSString stringWithFormat:@"%@",[path lastPathComponent]] retain]; + /* make sure we remove any path extension as this can also be an '.mpg' file */ + browsedSourceDisplayName = [[path lastPathComponent] retain]; } [self performScan:path scanTitleNum:0]; } - - } - - } - else // User clicked Cancel in browse window - { - /* if we have a title loaded up */ - if ([[fSrcDVD2Field stringValue] length] > 0) - { - [self enableUI: YES]; + } + } } @@ -1191,16 +1302,14 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It */ [fScanSrcTitleNumField setStringValue: @"0"]; /* Show the panel */ - [NSApp beginSheet: fScanSrcTitlePanel modalForWindow: fWindow modalDelegate: NULL didEndSelector: NULL contextInfo: NULL]; + [NSApp beginSheet:fScanSrcTitlePanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; } - (IBAction) closeSourceTitleScanPanel: (id) sender { [NSApp endSheet: fScanSrcTitlePanel]; [fScanSrcTitlePanel orderOut: self]; - - - + if(sender == fScanSrcTitleOpenButton) { /* We setup the scan status in the main window to indicate a source title scan */ @@ -1216,34 +1325,84 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It } } - /* Here we actually tell hb_scan to perform the source scan, using the path to source and title number*/ - (void) performScan:(NSString *) scanPath scanTitleNum: (int) scanTitleNum { + /* set the bool applyQueueToScan so that we dont apply a queue setting to the final scan */ + applyQueueToScan = NO; + /* use a bool to determine whether or not we can decrypt using vlc */ + BOOL cancelScanDecrypt = 0; NSString *path = scanPath; HBDVDDetector *detector = [HBDVDDetector detectorForPath:path]; + + // Notify ChapterTitles that there's no title + [fChapterTitlesDelegate resetWithTitle:nil]; + [fChapterTable reloadData]; + + [self enableUI: NO]; + if( [detector isVideoDVD] ) { // The chosen path was actually on a DVD, so use the raw block // device path instead. path = [detector devicePath]; [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]]; + + /* lets check for vlc here to make sure we have a dylib available to use for decrypting */ + NSString *vlcPath = @"/Applications/VLC.app"; + NSFileManager * fileManager = [NSFileManager defaultManager]; + if ([fileManager fileExistsAtPath:vlcPath] == 0) + { + /*vlc not found in /Applications so we set the bool to cancel scanning to 1 */ + cancelScanDecrypt = 1; + [self writeToActivityLog: "VLC app not found for decrypting physical dvd"]; + int status; + status = NSRunAlertPanel(@"HandBrake could not find VLC.",@"Please download and install VLC media player in your /Applications folder if you wish to read encrypted DVDs.", @"Get VLC", @"Cancel Scan", @"Attempt Scan Anyway"); + [NSApp requestUserAttention:NSCriticalRequest]; + + if (status == NSAlertDefaultReturn) + { + /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */ + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/"]]; + } + else if (status == NSAlertAlternateReturn) + { + /* User chose to cancel the scan */ + [self writeToActivityLog: "cannot open physical dvd , scan cancelled"]; + } + else + { + /* User chose to override our warning and scan the physical dvd anyway, at their own peril. on an encrypted dvd this produces massive log files and fails */ + cancelScanDecrypt = 0; + [self writeToActivityLog: "user overrode vlc warning -trying to open physical dvd without decryption"]; + } + + } + else + { + /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */ + [self writeToActivityLog: "VLC app found for decrypting physical dvd"]; + } } - /* If there is no title number passed to scan, we use "0" - * which causes the default behavior of a full source scan - */ - if (!scanTitleNum) - { - scanTitleNum = 0; - } - if (scanTitleNum > 0) + + if (cancelScanDecrypt == 0) { - [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum]; + /* we actually pass the scan off to libhb here */ + /* If there is no title number passed to scan, we use "0" + * which causes the default behavior of a full source scan + */ + if (!scanTitleNum) + { + scanTitleNum = 0; + } + if (scanTitleNum > 0) + { + [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum]; + } + + hb_scan( fHandle, [path UTF8String], scanTitleNum ); + [fSrcDVD2Field setStringValue:@"Scanning new source ..."]; } - [fSrcDVD2Field setStringValue: [NSString stringWithFormat: @"Scanning new source ..."]]; - /* we actually pass the scan off to libhb here */ - hb_scan( fHandle, [path UTF8String], scanTitleNum ); - } - (IBAction) showNewScan:(id)sender @@ -1252,96 +1411,90 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It hb_title_t * title; int indxpri=0; // Used to search the longuest title (default in combobox) int longuestpri=0; // Used to search the longuest title (default in combobox) - - list = hb_get_titles( fHandle ); - - if( !hb_list_count( list ) ) - { - /* We display a message if a valid dvd source was not chosen */ - [fSrcDVD2Field setStringValue: @"No Valid Source Found"]; - SuccessfulScan = NO; - - // Notify ChapterTitles that there's no title - [fChapterTitlesDelegate resetWithTitle:nil]; - [fChapterTable reloadData]; - } - else - { - /* We increment the successful scancount here by one, - which we use at the end of this function to tell the gui - if this is the first successful scan since launch and whether - or not we should set all settings to the defaults */ - - currentSuccessfulScanCount++; + + + list = hb_get_titles( fHandle ); - [toolbar validateVisibleItems]; - - [fSrcTitlePopUp removeAllItems]; - for( int i = 0; i < hb_list_count( list ); i++ ) - { - title = (hb_title_t *) hb_list_item( list, i ); - - currentSource = [NSString stringWithUTF8String: title->name]; + if( !hb_list_count( list ) ) + { + /* We display a message if a valid dvd source was not chosen */ + [fSrcDVD2Field setStringValue: @"No Valid Source Found"]; + SuccessfulScan = NO; - /*Set DVD Name at top of window with the browsedSourceDisplayName grokked right before -performScan */ - [fSrcDVD2Field setStringValue: [NSString stringWithFormat: @"%@",browsedSourceDisplayName]]; - - /* Use the dvd name in the default output field here - May want to add code to remove blank spaces for some dvd names*/ - /* Check to see if the last destination has been set,use if so, if not, use Desktop */ - if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"]) - { - [fDstFile2Field setStringValue: [NSString stringWithFormat: - @"%@/%@.mp4", [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"],browsedSourceDisplayName]]; - } - else - { - [fDstFile2Field setStringValue: [NSString stringWithFormat: - @"%@/Desktop/%@.mp4", NSHomeDirectory(),browsedSourceDisplayName]]; - } - - if (longuestpri < title->hours*60*60 + title->minutes *60 + title->seconds) - { - longuestpri=title->hours*60*60 + title->minutes *60 + title->seconds; - indxpri=i; - } - - [self formatPopUpChanged:NULL]; - - [fSrcTitlePopUp addItemWithTitle: [NSString - stringWithFormat: @"%d - %02dh%02dm%02ds", - title->index, title->hours, title->minutes, - title->seconds]]; - } - - // Select the longuest title - [fSrcTitlePopUp selectItemAtIndex: indxpri]; - [self titlePopUpChanged: NULL]; - - SuccessfulScan = YES; - [self enableUI: YES]; - - /* if its the initial successful scan after awakeFromNib */ - if (currentSuccessfulScanCount == 1) + // Notify ChapterTitles that there's no title + [fChapterTitlesDelegate resetWithTitle:nil]; + [fChapterTable reloadData]; + } + else { - [self selectDefaultPreset: NULL]; - /* if Deinterlace upon launch is specified in the prefs, then set to 1 for "Fast", - if not, then set to 0 for none */ - if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultDeinterlaceOn"] > 0) - { - [fPictureController setDeinterlace:1]; - } - else + /* We increment the successful scancount here by one, + which we use at the end of this function to tell the gui + if this is the first successful scan since launch and whether + or not we should set all settings to the defaults */ + + currentSuccessfulScanCount++; + + [[fWindow toolbar] validateVisibleItems]; + + [fSrcTitlePopUp removeAllItems]; + for( int i = 0; i < hb_list_count( list ); i++ ) { - [fPictureController setDeinterlace:0]; + title = (hb_title_t *) hb_list_item( list, i ); + + currentSource = [NSString stringWithUTF8String: title->name]; + /*Set DVD Name at top of window with the browsedSourceDisplayName grokked right before -performScan */ + [fSrcDVD2Field setStringValue:browsedSourceDisplayName]; + + /* Use the dvd name in the default output field here + May want to add code to remove blank spaces for some dvd names*/ + /* Check to see if the last destination has been set,use if so, if not, use Desktop */ + if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"]) + { + [fDstFile2Field setStringValue: [NSString stringWithFormat: + @"%@/%@.mp4", [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"],[browsedSourceDisplayName stringByDeletingPathExtension]]]; + } + else + { + [fDstFile2Field setStringValue: [NSString stringWithFormat: + @"%@/Desktop/%@.mp4", NSHomeDirectory(),[browsedSourceDisplayName stringByDeletingPathExtension]]]; + } + + + if (longuestpri < title->hours*60*60 + title->minutes *60 + title->seconds) + { + longuestpri=title->hours*60*60 + title->minutes *60 + title->seconds; + indxpri=i; + } + + [fSrcTitlePopUp addItemWithTitle: [NSString + stringWithFormat: @"%d - %02dh%02dm%02ds", + title->index, title->hours, title->minutes, + title->seconds]]; } - /* lets set Denoise to index 0 or "None" since this is the first scan */ - [fPictureController setDenoise:0]; - [fPictureController setInitialPictureFilters]; + // Select the longuest title + [fSrcTitlePopUp selectItemAtIndex: indxpri]; + [self titlePopUpChanged:nil]; + + SuccessfulScan = YES; + [self enableUI: YES]; + + /* if its the initial successful scan after awakeFromNib */ + if (currentSuccessfulScanCount == 1) + { + [self selectDefaultPreset:nil]; + /* initially set deinterlace to 0, will be overridden reset by the default preset anyway */ + //[fPictureController setDeinterlace:0]; + + /* lets set Denoise to index 0 or "None" since this is the first scan */ + //[fPictureController setDenoise:0]; + + [fPictureController setInitialPictureFilters]; + } + + } - - } + } @@ -1393,267 +1546,1326 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It return NO; } + #pragma mark - -#pragma mark Job Handling +#pragma mark Queue File + +- (void) loadQueueFile { + /* We declare the default NSFileManager into fileManager */ + NSFileManager * fileManager = [NSFileManager defaultManager]; + /*We define the location of the user presets file */ + QueueFile = @"~/Library/Application Support/HandBrake/Queue.plist"; + QueueFile = [[QueueFile stringByExpandingTildeInPath]retain]; + /* We check for the presets.plist */ + if ([fileManager fileExistsAtPath:QueueFile] == 0) + { + [fileManager createFileAtPath:QueueFile contents:nil attributes:nil]; + } + QueueFileArray = [[NSMutableArray alloc] initWithContentsOfFile:QueueFile]; + /* lets check to see if there is anything in the queue file .plist */ + if (nil == QueueFileArray) + { + /* if not, then lets initialize an empty array */ + QueueFileArray = [[NSMutableArray alloc] init]; + + /* Initialize our curQueueEncodeIndex to 0 + * so we can use it to track which queue + * item is to be used to track our encodes */ + /* NOTE: this should be changed if and when we + * are able to get the last unfinished encode + * in the case of a crash or shutdown */ + + } + else + { + [self clearQueueEncodedItems]; + } + currentQueueEncodeIndex = 0; +} -- (void) prepareJob +- (void)addQueueFileItem { - hb_list_t * list = hb_get_titles( fHandle ); - hb_title_t * title = (hb_title_t *) hb_list_item( list, - [fSrcTitlePopUp indexOfSelectedItem] ); - hb_job_t * job = title->job; - //int i; + [QueueFileArray addObject:[self createQueueFileItem]]; + [self saveQueueFileItem]; - /* Chapter selection */ - job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1; - job->chapter_end = [fSrcChapterEndPopUp indexOfSelectedItem] + 1; - - /* Format and codecs */ - int format = [fDstFormatPopUp indexOfSelectedItem]; - int codecs = [fDstCodecsPopUp indexOfSelectedItem]; - job->mux = FormatSettings[format][codecs] & HB_MUX_MASK; - job->vcodec = FormatSettings[format][codecs] & HB_VCODEC_MASK; - job->acodec = FormatSettings[format][codecs] & HB_ACODEC_MASK; - /* If mpeg-4, then set mpeg-4 specific options like chapters and > 4gb file sizes */ - if ([fDstFormatPopUp indexOfSelectedItem] == 0) - { - /* We set the largeFileSize (64 bit formatting) variable here to allow for > 4gb files based on the format being - mpeg4 and the checkbox being checked - *Note: this will break compatibility with some target devices like iPod, etc.!!!!*/ - if ([[NSUserDefaults standardUserDefaults] boolForKey:@"AllowLargeFiles"] > 0 && [fDstMp4LargeFileCheck state] == NSOnState) - { - job->largeFileSize = 1; - } - else - { - job->largeFileSize = 0; - } - /* We set http optimized mp4 here */ - if ([fDstMp4HttpOptFileCheck state] == NSOnState) - { - job->mp4_optimize = 1; - } - else - { - job->mp4_optimize = 0; - } +} + +- (void) removeQueueFileItem:(int) queueItemToRemove +{ + + /* Find out if the item we are removing is a cancelled (3) or a finished (0) item*/ + if ([[[QueueFileArray objectAtIndex:queueItemToRemove] objectForKey:@"Status"] intValue] == 3 || [[[QueueFileArray objectAtIndex:queueItemToRemove] objectForKey:@"Status"] intValue] == 0) + { + /* Since we are removing a cancelled or finished item, WE need to decrement the currentQueueEncodeIndex + * by one to keep in sync with the queue array + */ + currentQueueEncodeIndex--; + [self writeToActivityLog: "removeQueueFileItem: Removing a cancelled/finished encode, decrement currentQueueEncodeIndex to %d", currentQueueEncodeIndex]; } - if ([fDstFormatPopUp indexOfSelectedItem] == 0 || [fDstFormatPopUp indexOfSelectedItem] == 3) + [QueueFileArray removeObjectAtIndex:queueItemToRemove]; + [self saveQueueFileItem]; + +} + +- (void)saveQueueFileItem +{ + [QueueFileArray writeToFile:QueueFile atomically:YES]; + [fQueueController setQueueArray: QueueFileArray]; + [self getQueueStats]; +} + +- (void)getQueueStats +{ +/* lets get the stats on the status of the queue array */ + +fEncodingQueueItem = 0; +fPendingCount = 0; +fCompletedCount = 0; +fCanceledCount = 0; +fWorkingCount = 0; + + /* We use a number system to set the encode status of the queue item + * in controller.mm + * 0 == already encoded + * 1 == is being encoded + * 2 == is yet to be encoded + * 3 == cancelled + */ + + int i = 0; + NSEnumerator *enumerator = [QueueFileArray objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) { - /* We set the chapter marker extraction here based on the format being - mpeg4 or mkv and the checkbox being checked */ - if ([fCreateChapterMarkers state] == NSOnState) + NSDictionary *thisQueueDict = tempObject; + if ([[thisQueueDict objectForKey:@"Status"] intValue] == 0) // Completed { - job->chapter_markers = 1; + fCompletedCount++; } - else + if ([[thisQueueDict objectForKey:@"Status"] intValue] == 1) // being encoded { - job->chapter_markers = 0; - } - } - if( ( job->vcodec & HB_VCODEC_FFMPEG ) && - [fVidEncoderPopUp indexOfSelectedItem] > 0 ) - { - job->vcodec = HB_VCODEC_XVID; - } - if( job->vcodec & HB_VCODEC_X264 ) - { - if ([fDstMp4iPodFileCheck state] == NSOnState) - { - job->ipod_atom = 1; + fWorkingCount++; + fEncodingQueueItem = i; } - else + if ([[thisQueueDict objectForKey:@"Status"] intValue] == 2) // pending { - job->ipod_atom = 0; - } - - /* Set this flag to switch from Constant Quantizer(default) to Constant Rate Factor Thanks jbrjake - Currently only used with Constant Quality setting*/ - if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultCrf"] > 0 && [fVidQualityMatrix selectedRow] == 2) - { - job->crf = 1; - } - - /* Below Sends x264 options to the core library if x264 is selected*/ - /* Lets use this as per Nyx, Thanks Nyx!*/ - job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */ - /* Turbo first pass if two pass and Turbo First pass is selected */ - if( [fVidTwoPassCheck state] == NSOnState && [fVidTurboPassCheck state] == NSOnState ) - { - /* pass the "Turbo" string to be appended to the existing x264 opts string into a variable for the first pass */ - NSString *firstPassOptStringTurbo = @":ref=1:subme=1:me=dia:analyse=none:trellis=0:no-fast-pskip=0:8x8dct=0:weightb=0"; - /* append the "Turbo" string variable to the existing opts string. - Note: the "Turbo" string must be appended, not prepended to work properly*/ - NSString *firstPassOptStringCombined = [[fAdvancedOptions optionsString] stringByAppendingString:firstPassOptStringTurbo]; - strcpy(job->x264opts, [firstPassOptStringCombined UTF8String]); + fPendingCount++; } - else - { - strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]); + if ([[thisQueueDict objectForKey:@"Status"] intValue] == 3) // cancelled + { + fCanceledCount++; } - - } + i++; + } - /* Video settings */ - if( [fVidRatePopUp indexOfSelectedItem] > 0 ) + /* Set the queue status field in the main window */ + NSMutableString * string; + if (fPendingCount == 1) { - job->vrate = 27000000; - job->vrate_base = hb_video_rates[[fVidRatePopUp - indexOfSelectedItem]-1].rate; + string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode pending in the queue", @"" ), fPendingCount]; } else { - job->vrate = title->rate; - job->vrate_base = title->rate_base; + string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode(s) pending in the queue", @"" ), fPendingCount]; } + [fQueueStatus setStringValue:string]; +} - switch( [fVidQualityMatrix selectedRow] ) +/* This method will set any item marked as encoding back to pending + * currently used right after a queue reload + */ +- (void) setQueueEncodingItemsAsPending +{ + NSEnumerator *enumerator = [QueueFileArray objectEnumerator]; + id tempObject; + NSMutableArray *tempArray; + tempArray = [NSMutableArray array]; + /* we look here to see if the preset is we move on to the next one */ + while ( tempObject = [enumerator nextObject] ) { - case 0: - /* Target size. - Bitrate should already have been calculated and displayed - in fVidBitrateField, so let's just use it */ - case 1: - job->vquality = -1.0; - job->vbitrate = [fVidBitrateField intValue]; - break; - case 2: - job->vquality = [fVidQualitySlider floatValue]; - job->vbitrate = 0; - break; + /* If the queue item is marked as "encoding" (1) + * then change its status back to pending (2) which effectively + * puts it back into the queue to be encoded + */ + if ([[tempObject objectForKey:@"Status"] intValue] == 1) + { + [tempObject setObject:[NSNumber numberWithInt: 2] forKey:@"Status"]; + } + [tempArray addObject:tempObject]; } + + [QueueFileArray setArray:tempArray]; + [self saveQueueFileItem]; +} - job->grayscale = ( [fVidGrayscaleCheck state] == NSOnState ); - - /* Subtitle settings */ - job->subtitle = [fSubPopUp indexOfSelectedItem] - 2; - - /* Audio tracks and mixdowns */ - /* check for the condition where track 2 has an audio selected, but track 1 does not */ - /* we will use track 2 as track 1 in this scenario */ - if ([fAudLang1PopUp indexOfSelectedItem] > 0) - { - job->audios[0] = [fAudLang1PopUp indexOfSelectedItem] - 1; - job->audios[1] = [fAudLang2PopUp indexOfSelectedItem] - 1; /* will be -1 if "none" is selected */ - job->audios[2] = -1; - job->audio_mixdowns[0] = [[fAudTrack1MixPopUp selectedItem] tag]; - job->audio_mixdowns[1] = [[fAudTrack2MixPopUp selectedItem] tag]; - } - else if ([fAudLang2PopUp indexOfSelectedItem] > 0) - { - job->audios[0] = [fAudLang2PopUp indexOfSelectedItem] - 1; - job->audio_mixdowns[0] = [[fAudTrack2MixPopUp selectedItem] tag]; - job->audios[1] = -1; - } - else - { - job->audios[0] = -1; - } - /* - * Where one or more of the audio tracks has a mixdown of DPLII+AC3 we need to create an extra - * track for each. - */ - if (job->audio_mixdowns[0] == HB_AMIXDOWN_DOLBYPLII_AC3) +/* This method will clear the queue of any encodes that are not still pending + * this includes both successfully completed encodes as well as cancelled encodes */ +- (void) clearQueueEncodedItems +{ + NSEnumerator *enumerator = [QueueFileArray objectEnumerator]; + id tempObject; + NSMutableArray *tempArray; + tempArray = [NSMutableArray array]; + /* we look here to see if the preset is we move on to the next one */ + while ( tempObject = [enumerator nextObject] ) { - /* - * Make space for the AC3 track by moving 1 to 2 + /* If the queue item is either completed (0) or cancelled (3) from the + * last session, then we put it in tempArray to be deleted from QueueFileArray. + * NOTE: this means we retain pending (2) and also an item that is marked as + * still encoding (1). If the queue has an item that is still marked as encoding + * from a previous session, we can conlude that HB was either shutdown, or crashed + * during the encodes so we keep it and tell the user in the "Load Queue Alert" */ - job->audios[2] = job->audios[1]; - job->audio_mixdowns[2] = job->audio_mixdowns[1]; - job->audios[1] = job->audios[0]; - job->audio_mixdowns[0] = HB_AMIXDOWN_DOLBYPLII; - job->audio_mixdowns[1] = HB_AMIXDOWN_AC3; - } - - if (job->audio_mixdowns[1] == HB_AMIXDOWN_DOLBYPLII_AC3) - { - job->audios[2] = job->audios[1]; - job->audio_mixdowns[1] = HB_AMIXDOWN_DOLBYPLII; - job->audio_mixdowns[2] = HB_AMIXDOWN_AC3; - job->audios[3] = -1; + if ([[tempObject objectForKey:@"Status"] intValue] == 0 || [[tempObject objectForKey:@"Status"] intValue] == 3) + { + [tempArray addObject:tempObject]; + } } + + [QueueFileArray removeObjectsInArray:tempArray]; + [self saveQueueFileItem]; +} - if (job->audio_mixdowns[2] == HB_AMIXDOWN_DOLBYPLII_AC3) +/* This method will clear the queue of all encodes. effectively creating an empty queue */ +- (void) clearQueueAllItems +{ + NSEnumerator *enumerator = [QueueFileArray objectEnumerator]; + id tempObject; + NSMutableArray *tempArray; + tempArray = [NSMutableArray array]; + /* we look here to see if the preset is we move on to the next one */ + while ( tempObject = [enumerator nextObject] ) { - job->audios[3] = job->audios[2]; - job->audio_mixdowns[2] = HB_AMIXDOWN_DOLBYPLII; - job->audio_mixdowns[3] = HB_AMIXDOWN_AC3; - job->audios[4] = -1; + [tempArray addObject:tempObject]; } + + [QueueFileArray removeObjectsInArray:tempArray]; + [self saveQueueFileItem]; +} - /* Audio settings */ - job->arate = hb_audio_rates[[fAudRatePopUp - indexOfSelectedItem]].rate; - job->abitrate = [[fAudBitratePopUp selectedItem] tag]; +/* This method will duplicate prepareJob however into the + * queue .plist instead of into the job structure so it can + * be recalled later */ +- (NSDictionary *)createQueueFileItem +{ + NSMutableDictionary *queueFileJob = [[NSMutableDictionary alloc] init]; - /* Dynamic Range Compression */ - job->dynamic_range_compression = [fAudDrcField floatValue]; + hb_list_t * list = hb_get_titles( fHandle ); + hb_title_t * title = (hb_title_t *) hb_list_item( list, + [fSrcTitlePopUp indexOfSelectedItem] ); + hb_job_t * job = title->job; - /* set vfr according to the Picture Window */ - if ([fPictureController vfr]) - { - job->vfr = 1; - } - else - { - job->vfr = 0; - } - /* Filters */ - job->filters = hb_list_init(); - - /* Detelecine */ - if ([fPictureController detelecine]) + + /* We use a number system to set the encode status of the queue item + * 0 == already encoded + * 1 == is being encoded + * 2 == is yet to be encoded + * 3 == cancelled + */ + [queueFileJob setObject:[NSNumber numberWithInt:2] forKey:@"Status"]; + /* Source and Destination Information */ + + [queueFileJob setObject:[NSString stringWithUTF8String: title->dvd] forKey:@"SourcePath"]; + [queueFileJob setObject:[fSrcDVD2Field stringValue] forKey:@"SourceName"]; + [queueFileJob setObject:[NSNumber numberWithInt:title->index] forKey:@"TitleNumber"]; + [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterStartPopUp indexOfSelectedItem] + 1] forKey:@"ChapterStart"]; + + [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterEndPopUp indexOfSelectedItem] + 1] forKey:@"ChapterEnd"]; + + [queueFileJob setObject:[fDstFile2Field stringValue] forKey:@"DestinationPath"]; + + /* Lets get the preset info if there is any */ + [queueFileJob setObject:[fPresetSelectedDisplay stringValue] forKey:@"PresetName"]; + [queueFileJob setObject:[NSNumber numberWithInt:[fPresetsOutlineView selectedRow]] forKey:@"PresetIndexNum"]; + + [queueFileJob setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"]; + /* Chapter Markers fCreateChapterMarkers*/ + [queueFileJob setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"]; + + /* We need to get the list of chapter names to put into an array and store + * in our queue, so they can be reapplied in prepareJob when this queue + * item comes up if Chapter Markers is set to on. + */ + int i; + NSMutableArray *ChapterNamesArray = [[NSMutableArray alloc] init]; + int chaptercount = hb_list_count( fTitle->list_chapter ); + for( i = 0; i < chaptercount; i++ ) + { + hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item( fTitle->list_chapter, i ); + if( chapter != NULL ) + { + [ChapterNamesArray addObject:[NSString stringWithFormat:@"%s",chapter->title]]; + } + } + [queueFileJob setObject:[NSMutableArray arrayWithArray: ChapterNamesArray] forKey:@"ChapterNames"]; + [ChapterNamesArray autorelease]; + + /* Allow Mpeg4 64 bit formatting +4GB file sizes */ + [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"]; + /* Mux mp4 with http optimization */ + [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"]; + /* Add iPod uuid atom */ + [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"]; + + /* Codecs */ + /* Video encoder */ + [queueFileJob setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"]; + /* x264 Option String */ + [queueFileJob setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"]; + + [queueFileJob setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"]; + [queueFileJob setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"]; + [queueFileJob setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"]; + [queueFileJob setObject:[NSNumber numberWithFloat:[fVidQualitySlider floatValue]] forKey:@"VideoQualitySlider"]; + /* Framerate */ + [queueFileJob setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"]; + + /* GrayScale */ + [queueFileJob setObject:[NSNumber numberWithInt:[fVidGrayscaleCheck state]] forKey:@"VideoGrayScale"]; + /* 2 Pass Encoding */ + [queueFileJob setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"]; + /* Turbo 2 pass Encoding fVidTurboPassCheck*/ + [queueFileJob setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"]; + + /* Picture Sizing */ + /* Use Max Picture settings for whatever the dvd is.*/ + [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->pixel_ratio] forKey:@"PicturePAR"]; + NSString * pictureSummary; + pictureSummary = [NSString stringWithFormat:@"Source: %@ Output: %@ Anamorphic: %@", + [fPicSettingsSrc stringValue], + [fPicSettingsOutp stringValue], + [fPicSettingsAnamorphic stringValue]]; + [queueFileJob setObject:pictureSummary forKey:@"PictureSizingSummary"]; + /* Set crop settings here */ + [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"]; + [queueFileJob setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"]; + [queueFileJob setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"]; + [queueFileJob setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"]; + [queueFileJob setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"]; + + /* Picture Filters */ + [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"]; + [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"]; + [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"]; + [queueFileJob setObject:[NSString stringWithFormat:@"%d",[fPictureController deblock]] forKey:@"PictureDeblock"]; + [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"]; + + /*Audio*/ + if ([fAudLang1PopUp indexOfSelectedItem] > 0) + { + [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang1PopUp indexOfSelectedItem]] forKey:@"Audio1Track"]; + [queueFileJob setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"Audio1TrackDescription"]; + [queueFileJob setObject:[fAudTrack1CodecPopUp titleOfSelectedItem] forKey:@"Audio1Encoder"]; + [queueFileJob setObject:[fAudTrack1MixPopUp titleOfSelectedItem] forKey:@"Audio1Mixdown"]; + [queueFileJob setObject:[fAudTrack1RatePopUp titleOfSelectedItem] forKey:@"Audio1Samplerate"]; + [queueFileJob setObject:[fAudTrack1BitratePopUp titleOfSelectedItem] forKey:@"Audio1Bitrate"]; + [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack1DrcSlider floatValue]] forKey:@"Audio1TrackDRCSlider"]; + } + if ([fAudLang2PopUp indexOfSelectedItem] > 0) + { + [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang2PopUp indexOfSelectedItem]] forKey:@"Audio2Track"]; + [queueFileJob setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"Audio2TrackDescription"]; + [queueFileJob setObject:[fAudTrack2CodecPopUp titleOfSelectedItem] forKey:@"Audio2Encoder"]; + [queueFileJob setObject:[fAudTrack2MixPopUp titleOfSelectedItem] forKey:@"Audio2Mixdown"]; + [queueFileJob setObject:[fAudTrack2RatePopUp titleOfSelectedItem] forKey:@"Audio2Samplerate"]; + [queueFileJob setObject:[fAudTrack2BitratePopUp titleOfSelectedItem] forKey:@"Audio2Bitrate"]; + [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack2DrcSlider floatValue]] forKey:@"Audio2TrackDRCSlider"]; + } + if ([fAudLang3PopUp indexOfSelectedItem] > 0) + { + [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang3PopUp indexOfSelectedItem]] forKey:@"Audio3Track"]; + [queueFileJob setObject:[fAudLang3PopUp titleOfSelectedItem] forKey:@"Audio3TrackDescription"]; + [queueFileJob setObject:[fAudTrack3CodecPopUp titleOfSelectedItem] forKey:@"Audio3Encoder"]; + [queueFileJob setObject:[fAudTrack3MixPopUp titleOfSelectedItem] forKey:@"Audio3Mixdown"]; + [queueFileJob setObject:[fAudTrack3RatePopUp titleOfSelectedItem] forKey:@"Audio3Samplerate"]; + [queueFileJob setObject:[fAudTrack3BitratePopUp titleOfSelectedItem] forKey:@"Audio3Bitrate"]; + [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack3DrcSlider floatValue]] forKey:@"Audio3TrackDRCSlider"]; + } + if ([fAudLang4PopUp indexOfSelectedItem] > 0) + { + [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang4PopUp indexOfSelectedItem]] forKey:@"Audio4Track"]; + [queueFileJob setObject:[fAudLang4PopUp titleOfSelectedItem] forKey:@"Audio4TrackDescription"]; + [queueFileJob setObject:[fAudTrack4CodecPopUp titleOfSelectedItem] forKey:@"Audio4Encoder"]; + [queueFileJob setObject:[fAudTrack4MixPopUp titleOfSelectedItem] forKey:@"Audio4Mixdown"]; + [queueFileJob setObject:[fAudTrack4RatePopUp titleOfSelectedItem] forKey:@"Audio4Samplerate"]; + [queueFileJob setObject:[fAudTrack4BitratePopUp titleOfSelectedItem] forKey:@"Audio4Bitrate"]; + [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack4DrcSlider floatValue]] forKey:@"Audio4TrackDRCSlider"]; + } + + /* Subtitles*/ + [queueFileJob setObject:[fSubPopUp titleOfSelectedItem] forKey:@"Subtitles"]; + [queueFileJob setObject:[NSNumber numberWithInt:[fSubPopUp indexOfSelectedItem]] forKey:@"JobSubtitlesIndex"]; + /* Forced Subtitles */ + [queueFileJob setObject:[NSNumber numberWithInt:[fSubForcedCheck state]] forKey:@"SubtitlesForced"]; + + + + /* Now we go ahead and set the "job->values in the plist for passing right to fQueueEncodeLibhb */ + + [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterStartPopUp indexOfSelectedItem] + 1] forKey:@"JobChapterStart"]; + + [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterEndPopUp indexOfSelectedItem] + 1] forKey:@"JobChapterEnd"]; + + + [queueFileJob setObject:[NSNumber numberWithInt:[[fDstFormatPopUp selectedItem] tag]] forKey:@"JobFileFormatMux"]; + + /* Codecs */ + /* Video encoder */ + [queueFileJob setObject:[NSNumber numberWithInt:[[fVidEncoderPopUp selectedItem] tag]] forKey:@"JobVideoEncoderVcodec"]; + + /* Framerate */ + [queueFileJob setObject:[NSNumber numberWithInt:[fVidRatePopUp indexOfSelectedItem]] forKey:@"JobIndexVideoFramerate"]; + [queueFileJob setObject:[NSNumber numberWithInt:title->rate] forKey:@"JobVrate"]; + [queueFileJob setObject:[NSNumber numberWithInt:title->rate_base] forKey:@"JobVrateBase"]; + + /* Picture Sizing */ + /* Use Max Picture settings for whatever the dvd is.*/ + [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->pixel_ratio] forKey:@"PicturePAR"]; + + /* Set crop settings here */ + [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"]; + [queueFileJob setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"]; + [queueFileJob setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"]; + [queueFileJob setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"]; + [queueFileJob setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"]; + + /* Picture Filters */ + [queueFileJob setObject:[fPicSettingDecomb stringValue] forKey:@"JobPictureDecomb"]; + + /*Audio*/ + if ([fAudLang1PopUp indexOfSelectedItem] > 0) + { + //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio1Encoder"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1CodecPopUp selectedItem] tag]] forKey:@"JobAudio1Encoder"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1MixPopUp selectedItem] tag]] forKey:@"JobAudio1Mixdown"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1RatePopUp selectedItem] tag]] forKey:@"JobAudio1Samplerate"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1BitratePopUp selectedItem] tag]] forKey:@"JobAudio1Bitrate"]; + } + if ([fAudLang2PopUp indexOfSelectedItem] > 0) + { + //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio2Encoder"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2CodecPopUp selectedItem] tag]] forKey:@"JobAudio2Encoder"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2MixPopUp selectedItem] tag]] forKey:@"JobAudio2Mixdown"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2RatePopUp selectedItem] tag]] forKey:@"JobAudio2Samplerate"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2BitratePopUp selectedItem] tag]] forKey:@"JobAudio2Bitrate"]; + } + if ([fAudLang3PopUp indexOfSelectedItem] > 0) + { + //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio3Encoder"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3CodecPopUp selectedItem] tag]] forKey:@"JobAudio3Encoder"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3MixPopUp selectedItem] tag]] forKey:@"JobAudio3Mixdown"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3RatePopUp selectedItem] tag]] forKey:@"JobAudio3Samplerate"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3BitratePopUp selectedItem] tag]] forKey:@"JobAudio3Bitrate"]; + } + if ([fAudLang4PopUp indexOfSelectedItem] > 0) + { + //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio4Encoder"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4CodecPopUp selectedItem] tag]] forKey:@"JobAudio4Encoder"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4MixPopUp selectedItem] tag]] forKey:@"JobAudio4Mixdown"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4RatePopUp selectedItem] tag]] forKey:@"JobAudio4Samplerate"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4BitratePopUp selectedItem] tag]] forKey:@"JobAudio4Bitrate"]; + } + /* Subtitles*/ + [queueFileJob setObject:[fSubPopUp titleOfSelectedItem] forKey:@"Subtitles"]; + /* Forced Subtitles */ + [queueFileJob setObject:[NSNumber numberWithInt:[fSubForcedCheck state]] forKey:@"SubtitlesForced"]; + + /* we need to auto relase the queueFileJob and return it */ + [queueFileJob autorelease]; + return queueFileJob; + +} + +/* this is actually called from the queue controller to modify the queue array and return it back to the queue controller */ +- (void)moveObjectsInQueueArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(unsigned)insertIndex +{ + unsigned index = [indexSet lastIndex]; + unsigned aboveInsertIndexCount = 0; + + while (index != NSNotFound) + { + unsigned removeIndex; + + if (index >= insertIndex) + { + removeIndex = index + aboveInsertIndexCount; + aboveInsertIndexCount++; + } + else + { + removeIndex = index; + insertIndex--; + } + + id object = [[QueueFileArray objectAtIndex:removeIndex] retain]; + [QueueFileArray removeObjectAtIndex:removeIndex]; + [QueueFileArray insertObject:object atIndex:insertIndex]; + [object release]; + + index = [indexSet indexLessThanIndex:index]; + } + /* We save all of the Queue data here + * and it also gets sent back to the queue controller*/ + [self saveQueueFileItem]; + +} + + +#pragma mark - +#pragma mark Queue Job Processing + +- (void) incrementQueueItemDone:(int) queueItemDoneIndexNum +{ + int i = currentQueueEncodeIndex; + [[QueueFileArray objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Status"]; + + /* We save all of the Queue data here */ + [self saveQueueFileItem]; + /* We Reload the New Table data for presets */ + //[fPresetsOutlineView reloadData]; + + /* Since we have now marked a queue item as done + * we can go ahead and increment currentQueueEncodeIndex + * so that if there is anything left in the queue we can + * go ahead and move to the next item if we want to */ + currentQueueEncodeIndex++ ; + [self writeToActivityLog: "incrementQueueItemDone currentQueueEncodeIndex is incremented to: %d", currentQueueEncodeIndex]; + int queueItems = [QueueFileArray count]; + /* If we still have more items in our queue, lets go to the next one */ + if (currentQueueEncodeIndex < queueItems) + { + [self writeToActivityLog: "incrementQueueItemDone currentQueueEncodeIndex is incremented to: %d", currentQueueEncodeIndex]; + [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; + } + else + { + [self writeToActivityLog: "incrementQueueItemDone the %d item queue is complete", currentQueueEncodeIndex - 1]; + } +} + +/* Here we actually tell hb_scan to perform the source scan, using the path to source and title number*/ +- (void) performNewQueueScan:(NSString *) scanPath scanTitleNum: (int) scanTitleNum +{ + /* Tell HB to output a new activity log file for this encode */ + [outputPanel startEncodeLog:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"]]; + + + /* use a bool to determine whether or not we can decrypt using vlc */ + BOOL cancelScanDecrypt = 0; + /* set the bool so that showNewScan knows to apply the appropriate queue + * settings as this is a queue rescan + */ + applyQueueToScan = YES; + NSString *path = scanPath; + HBDVDDetector *detector = [HBDVDDetector detectorForPath:path]; + + /*On Screen Notification*/ + //int status; + //status = NSRunAlertPanel(@"HandBrake is now loading up a new queue item...",@"Would You Like to wait until you add another encode?", @"Cancel", @"Okay", nil); + //[NSApp requestUserAttention:NSCriticalRequest]; + + // Notify ChapterTitles that there's no title + [fChapterTitlesDelegate resetWithTitle:nil]; + [fChapterTable reloadData]; + + //[self enableUI: NO]; + + if( [detector isVideoDVD] ) + { + // The chosen path was actually on a DVD, so use the raw block + // device path instead. + path = [detector devicePath]; + [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]]; + + /* lets check for vlc here to make sure we have a dylib available to use for decrypting */ + NSString *vlcPath = @"/Applications/VLC.app"; + NSFileManager * fileManager = [NSFileManager defaultManager]; + if ([fileManager fileExistsAtPath:vlcPath] == 0) + { + /*vlc not found in /Applications so we set the bool to cancel scanning to 1 */ + cancelScanDecrypt = 1; + [self writeToActivityLog: "VLC app not found for decrypting physical dvd"]; + int status; + status = NSRunAlertPanel(@"HandBrake could not find VLC.",@"Please download and install VLC media player in your /Applications folder if you wish to read encrypted DVDs.", @"Get VLC", @"Cancel Scan", @"Attempt Scan Anyway"); + [NSApp requestUserAttention:NSCriticalRequest]; + + if (status == NSAlertDefaultReturn) + { + /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */ + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/"]]; + } + else if (status == NSAlertAlternateReturn) + { + /* User chose to cancel the scan */ + [self writeToActivityLog: "cannot open physical dvd , scan cancelled"]; + } + else + { + /* User chose to override our warning and scan the physical dvd anyway, at their own peril. on an encrypted dvd this produces massive log files and fails */ + cancelScanDecrypt = 0; + [self writeToActivityLog: "user overrode vlc warning -trying to open physical dvd without decryption"]; + } + + } + else + { + /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */ + [self writeToActivityLog: "VLC app found for decrypting physical dvd"]; + } + } + + if (cancelScanDecrypt == 0) + { + /* we actually pass the scan off to libhb here */ + /* If there is no title number passed to scan, we use "0" + * which causes the default behavior of a full source scan + */ + if (!scanTitleNum) + { + scanTitleNum = 0; + } + if (scanTitleNum > 0) + { + [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum]; + } + [self writeToActivityLog: "performNewQueueScan currentQueueEncodeIndex is: %d", currentQueueEncodeIndex]; + hb_scan( fQueueEncodeLibhb, [path UTF8String], scanTitleNum ); + } +} + +/* This method was originally used to load up a new queue item in the gui and + * then start processing it. However we now have modified -prepareJob and use a second + * instance of libhb to do our actual encoding, therefor right now it is not required. + * Nonetheless I want to leave this in here + * because basically its everything we need to be able to actually modify a pending queue + * item in the gui and resave it. At least for now - dynaflash + */ + +- (IBAction)applyQueueSettings:(id)sender +{ + NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex]; + hb_job_t * job = fTitle->job; + + /* Set title number and chapters */ + /* since the queue only scans a single title, we really don't need to pick a title */ + //[fSrcTitlePopUp selectItemAtIndex: [[queueToApply objectForKey:@"TitleNumber"] intValue] - 1]; + + [fSrcChapterStartPopUp selectItemAtIndex: [[queueToApply objectForKey:@"ChapterStart"] intValue] - 1]; + [fSrcChapterEndPopUp selectItemAtIndex: [[queueToApply objectForKey:@"ChapterEnd"] intValue] - 1]; + + /* File Format */ + [fDstFormatPopUp selectItemWithTitle:[queueToApply objectForKey:@"FileFormat"]]; + [self formatPopUpChanged:nil]; + + /* Chapter Markers*/ + [fCreateChapterMarkers setState:[[queueToApply objectForKey:@"ChapterMarkers"] intValue]]; + /* Allow Mpeg4 64 bit formatting +4GB file sizes */ + [fDstMp4LargeFileCheck setState:[[queueToApply objectForKey:@"Mp4LargeFile"] intValue]]; + /* Mux mp4 with http optimization */ + [fDstMp4HttpOptFileCheck setState:[[queueToApply objectForKey:@"Mp4HttpOptimize"] intValue]]; + + /* Video encoder */ + /* We set the advanced opt string here if applicable*/ + [fVidEncoderPopUp selectItemWithTitle:[queueToApply objectForKey:@"VideoEncoder"]]; + [fAdvancedOptions setOptions:[queueToApply objectForKey:@"x264Option"]]; + + /* Lets run through the following functions to get variables set there */ + [self videoEncoderPopUpChanged:nil]; + /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/ + [fDstMp4iPodFileCheck setState:[[queueToApply objectForKey:@"Mp4iPodCompatible"] intValue]]; + [self calculateBitrate:nil]; + + /* Video quality */ + [fVidQualityMatrix selectCellAtRow:[[queueToApply objectForKey:@"VideoQualityType"] intValue] column:0]; + + [fVidTargetSizeField setStringValue:[queueToApply objectForKey:@"VideoTargetSize"]]; + [fVidBitrateField setStringValue:[queueToApply objectForKey:@"VideoAvgBitrate"]]; + [fVidQualitySlider setFloatValue:[[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]]; + + [self videoMatrixChanged:nil]; + + /* Video framerate */ + /* For video preset video framerate, we want to make sure that Same as source does not conflict with the + detected framerate in the fVidRatePopUp so we use index 0*/ + if ([[queueToApply objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"]) + { + [fVidRatePopUp selectItemAtIndex: 0]; + } + else + { + [fVidRatePopUp selectItemWithTitle:[queueToApply objectForKey:@"VideoFramerate"]]; + } + + /* GrayScale */ + [fVidGrayscaleCheck setState:[[queueToApply objectForKey:@"VideoGrayScale"] intValue]]; + + /* 2 Pass Encoding */ + [fVidTwoPassCheck setState:[[queueToApply objectForKey:@"VideoTwoPass"] intValue]]; + [self twoPassCheckboxChanged:nil]; + /* Turbo 1st pass for 2 Pass Encoding */ + [fVidTurboPassCheck setState:[[queueToApply objectForKey:@"VideoTurboTwoPass"] intValue]]; + + /*Audio*/ + if ([queueToApply objectForKey:@"Audio1Track"] > 0) + { + if ([fAudLang1PopUp indexOfSelectedItem] == 0) + { + [fAudLang1PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang1PopUp]; + [fAudTrack1CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Encoder"]]; + [self audioTrackPopUpChanged: fAudTrack1CodecPopUp]; + [fAudTrack1MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Mixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack1MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack1CodecPopUp]; + } + [fAudTrack1RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Samplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[queueToApply objectForKey:@"Audio1Encoder"] isEqualToString:@"AC3 Passthru"]) + { + [fAudTrack1BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Bitrate"]]; + } + [fAudTrack1DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio1TrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack1DrcSlider]; + } + if ([queueToApply objectForKey:@"Audio2Track"] > 0) + { + if ([fAudLang2PopUp indexOfSelectedItem] == 0) + { + [fAudLang2PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang2PopUp]; + [fAudTrack2CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Encoder"]]; + [self audioTrackPopUpChanged: fAudTrack2CodecPopUp]; + [fAudTrack2MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Mixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack2MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack2CodecPopUp]; + } + [fAudTrack2RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Samplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[queueToApply objectForKey:@"Audio2Encoder"] isEqualToString:@"AC3 Passthru"]) + { + [fAudTrack2BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Bitrate"]]; + } + [fAudTrack2DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio2TrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack2DrcSlider]; + } + if ([queueToApply objectForKey:@"Audio3Track"] > 0) + { + if ([fAudLang3PopUp indexOfSelectedItem] == 0) + { + [fAudLang3PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang3PopUp]; + [fAudTrack3CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Encoder"]]; + [self audioTrackPopUpChanged: fAudTrack3CodecPopUp]; + [fAudTrack3MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Mixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack3MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack3CodecPopUp]; + } + [fAudTrack3RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Samplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[queueToApply objectForKey:@"Audio3Encoder"] isEqualToString: @"AC3 Passthru"]) + { + [fAudTrack3BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Bitrate"]]; + } + [fAudTrack3DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio3TrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack3DrcSlider]; + } + if ([queueToApply objectForKey:@"Audio4Track"] > 0) + { + if ([fAudLang4PopUp indexOfSelectedItem] == 0) + { + [fAudLang4PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang4PopUp]; + [fAudTrack4CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Encoder"]]; + [self audioTrackPopUpChanged: fAudTrack4CodecPopUp]; + [fAudTrack4MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Mixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack4MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack4CodecPopUp]; + } + [fAudTrack4RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Samplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString:@"AC3 Passthru"]) + { + [fAudTrack4BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Bitrate"]]; + } + [fAudTrack4DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio4TrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack4DrcSlider]; + } + + + /*Subtitles*/ + [fSubPopUp selectItemWithTitle:[queueToApply objectForKey:@"Subtitles"]]; + /* Forced Subtitles */ + [fSubForcedCheck setState:[[queueToApply objectForKey:@"SubtitlesForced"] intValue]]; + + /* Picture Settings */ + /* we check to make sure the presets width/height does not exceed the sources width/height */ + if (fTitle->width < [[queueToApply objectForKey:@"PictureWidth"] intValue] || fTitle->height < [[queueToApply objectForKey:@"PictureHeight"] intValue]) + { + /* if so, then we use the sources height and width to avoid scaling up */ + job->width = fTitle->width; + job->height = fTitle->height; + } + else // source width/height is >= the preset height/width + { + /* we can go ahead and use the presets values for height and width */ + job->width = [[queueToApply objectForKey:@"PictureWidth"] intValue]; + job->height = [[queueToApply objectForKey:@"PictureHeight"] intValue]; + } + job->keep_ratio = [[queueToApply objectForKey:@"PictureKeepRatio"] intValue]; + if (job->keep_ratio == 1) + { + hb_fix_aspect( job, HB_KEEP_WIDTH ); + if( job->height > fTitle->height ) + { + job->height = fTitle->height; + hb_fix_aspect( job, HB_KEEP_HEIGHT ); + } + } + job->pixel_ratio = [[queueToApply objectForKey:@"PicturePAR"] intValue]; + + + /* If Cropping is set to custom, then recall all four crop values from + when the preset was created and apply them */ + if ([[queueToApply objectForKey:@"PictureAutoCrop"] intValue] == 0) + { + [fPictureController setAutoCrop:NO]; + + /* Here we use the custom crop values saved at the time the preset was saved */ + job->crop[0] = [[queueToApply objectForKey:@"PictureTopCrop"] intValue]; + job->crop[1] = [[queueToApply objectForKey:@"PictureBottomCrop"] intValue]; + job->crop[2] = [[queueToApply objectForKey:@"PictureLeftCrop"] intValue]; + job->crop[3] = [[queueToApply objectForKey:@"PictureRightCrop"] intValue]; + + } + else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */ + { + [fPictureController setAutoCrop:YES]; + /* Here we use the auto crop values determined right after scan */ + job->crop[0] = AutoCropTop; + job->crop[1] = AutoCropBottom; + job->crop[2] = AutoCropLeft; + job->crop[3] = AutoCropRight; + + } + + /* Filters */ + /* Deinterlace */ + [fPictureController setDeinterlace:[[queueToApply objectForKey:@"PictureDeinterlace"] intValue]]; + + /* Detelecine */ + [fPictureController setDetelecine:[[queueToApply objectForKey:@"PictureDetelecine"] intValue]]; + /* Denoise */ + [fPictureController setDenoise:[[queueToApply objectForKey:@"PictureDenoise"] intValue]]; + /* Deblock */ + [fPictureController setDeblock:[[queueToApply objectForKey:@"PictureDeblock"] intValue]]; + /* Decomb */ + [fPictureController setDecomb:[[queueToApply objectForKey:@"PictureDecomb"] intValue]]; + + [self calculatePictureSizing:nil]; + + + /* somehow we need to figure out a way to tie the queue item to a preset if it used one */ + //[queueFileJob setObject:[fPresetSelectedDisplay stringValue] forKey:@"PresetName"]; + // [queueFileJob setObject:[NSNumber numberWithInt:[fPresetsOutlineView selectedRow]] forKey:@"PresetIndexNum"]; + if ([queueToApply objectForKey:@"PresetIndexNum"]) // This item used a preset so insert that info + { + /* Deselect the currently selected Preset if there is one*/ + //[fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[[queueToApply objectForKey:@"PresetIndexNum"] intValue]] byExtendingSelection:NO]; + //[self selectPreset:nil]; + + //[fPresetsOutlineView selectRow:[[queueToApply objectForKey:@"PresetIndexNum"] intValue]]; + /* Change UI to show "Custom" settings are being used */ + //[fPresetSelectedDisplay setStringValue: [[queueToApply objectForKey:@"PresetName"] stringValue]]; + + curUserPresetChosenNum = nil; + } + else + { + /* Deselect the currently selected Preset if there is one*/ + [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]]; + /* Change UI to show "Custom" settings are being used */ + [fPresetSelectedDisplay setStringValue: @"Custom"]; + + //curUserPresetChosenNum = nil; + } + + /* We need to set this bool back to NO, in case the user wants to do a scan */ + //applyQueueToScan = NO; + + /* so now we go ahead and process the new settings */ + [self processNewQueueEncode]; +} + + + +/* This assumes that we have re-scanned and loaded up a new queue item to send to libhb as fQueueEncodeLibhb */ +- (void) processNewQueueEncode +{ + hb_list_t * list = hb_get_titles( fQueueEncodeLibhb ); + hb_title_t * title = (hb_title_t *) hb_list_item( list,0 ); // is always zero since now its a single title scan + hb_job_t * job = title->job; + + if( !hb_list_count( list ) ) + { + [self writeToActivityLog: "processNewQueueEncode WARNING nothing found in the title list"]; + } + else + { + [self writeToActivityLog: "processNewQueueEncode title list is: %d", hb_list_count( list )]; + } + + NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex]; + //[self writeToActivityLog: "processNewQueueEncode currentQueueEncodeIndex is: %d", currentQueueEncodeIndex]; + [self writeToActivityLog: "processNewQueueEncode number of passes expected is: %d", ([[queueToApply objectForKey:@"VideoTwoPass"] intValue] + 1)]; + job->file = [[queueToApply objectForKey:@"DestinationPath"] UTF8String]; + //[self writeToActivityLog: "processNewQueueEncode sending to prepareJob"]; + [self prepareJob]; + if( [[queueToApply objectForKey:@"SubtitlesForced"] intValue] == 1 ) + job->subtitle_force = 1; + else + job->subtitle_force = 0; + + /* + * subtitle of -1 is a scan + */ + if( job->subtitle == -1 ) + { + char *x264opts_tmp; + + /* + * When subtitle scan is enabled do a fast pre-scan job + * which will determine which subtitles to enable, if any. + */ + job->pass = -1; + x264opts_tmp = job->x264opts; + job->subtitle = -1; + + job->x264opts = NULL; + + job->indepth_scan = 1; + + job->select_subtitle = (hb_subtitle_t**)malloc(sizeof(hb_subtitle_t*)); + *(job->select_subtitle) = NULL; + + /* + * Add the pre-scan job + */ + hb_add( fQueueEncodeLibhb, job ); + job->x264opts = x264opts_tmp; + } + else + job->select_subtitle = NULL; + + /* No subtitle were selected, so reset the subtitle to -1 (which before + * this point meant we were scanning + */ + if( job->subtitle == -2 ) + job->subtitle = -1; + + if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 ) + { + hb_subtitle_t **subtitle_tmp = job->select_subtitle; + job->indepth_scan = 0; + + /* + * Do not autoselect subtitles on the first pass of a two pass + */ + job->select_subtitle = NULL; + + job->pass = 1; + + hb_add( fQueueEncodeLibhb, job ); + + job->pass = 2; + + job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */ + strcpy(job->x264opts, [[queueToApply objectForKey:@"x264Option"] UTF8String]); + + job->select_subtitle = subtitle_tmp; + + hb_add( fQueueEncodeLibhb, job ); + + } + else + { + job->indepth_scan = 0; + job->pass = 0; + + hb_add( fQueueEncodeLibhb, job ); + } + + NSString *destinationDirectory = [[queueToApply objectForKey:@"DestinationPath"] stringByDeletingLastPathComponent]; + [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"]; + /* Lets mark our new encode as 1 or "Encoding" */ + [queueToApply setObject:[NSNumber numberWithInt:1] forKey:@"Status"]; + [self saveQueueFileItem]; + /* We should be all setup so let 'er rip */ + [self doRip]; +} + + +#pragma mark - +#pragma mark Job Handling + + +- (void) prepareJob +{ + + NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex]; + hb_list_t * list = hb_get_titles( fQueueEncodeLibhb ); + hb_title_t * title = (hb_title_t *) hb_list_item( list,0 ); // is always zero since now its a single title scan + hb_job_t * job = title->job; + hb_audio_config_t * audio; + /* Chapter selection */ + job->chapter_start = [[queueToApply objectForKey:@"JobChapterStart"] intValue]; + job->chapter_end = [[queueToApply objectForKey:@"JobChapterEnd"] intValue]; + + /* Format (Muxer) and Video Encoder */ + job->mux = [[queueToApply objectForKey:@"JobFileFormatMux"] intValue]; + job->vcodec = [[queueToApply objectForKey:@"JobVideoEncoderVcodec"] intValue]; + + + /* If mpeg-4, then set mpeg-4 specific options like chapters and > 4gb file sizes */ + //if( [fDstFormatPopUp indexOfSelectedItem] == 0 ) + //{ + /* We set the largeFileSize (64 bit formatting) variable here to allow for > 4gb files based on the format being + mpeg4 and the checkbox being checked + *Note: this will break compatibility with some target devices like iPod, etc.!!!!*/ + if( [[queueToApply objectForKey:@"Mp4LargeFile"] intValue] == 1) + { + job->largeFileSize = 1; + } + else + { + job->largeFileSize = 0; + } + /* We set http optimized mp4 here */ + if( [[queueToApply objectForKey:@"Mp4HttpOptimize"] intValue] == 1 ) + { + job->mp4_optimize = 1; + } + else + { + job->mp4_optimize = 0; + } + + //} + + /* We set the chapter marker extraction here based on the format being + mpeg4 or mkv and the checkbox being checked */ + if ([[queueToApply objectForKey:@"ChapterMarkers"] intValue] == 1) + { + job->chapter_markers = 1; + + /* now lets get our saved chapter names out the array in the queue file + * and insert them back into the title chapter list. We have it here, + * because unless we are inserting chapter markers there is no need to + * spend the overhead of iterating through the chapter names array imo + * Also, note that if for some reason we don't apply chapter names, the + * chapters just come out 001, 002, etc. etc. + */ + + NSMutableArray *ChapterNamesArray = [queueToApply objectForKey:@"ChapterNames"]; + int i = 0; + NSEnumerator *enumerator = [ChapterNamesArray objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) + { + hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i ); + if( chapter != NULL ) + { + strncpy( chapter->title, [tempObject UTF8String], 1023); + chapter->title[1023] = '\0'; + } + i++; + } + } + else + { + job->chapter_markers = 0; + } + + + + + + if( job->vcodec & HB_VCODEC_X264 ) + { + if ([[queueToApply objectForKey:@"Mp4iPodCompatible"] intValue] == 1) + { + job->ipod_atom = 1; + } + else + { + job->ipod_atom = 0; + } + + /* Set this flag to switch from Constant Quantizer(default) to Constant Rate Factor Thanks jbrjake + Currently only used with Constant Quality setting*/ + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultCrf"] > 0 && [[queueToApply objectForKey:@"VideoQualityType"] intValue] == 2) + { + job->crf = 1; + } + /* Below Sends x264 options to the core library if x264 is selected*/ + /* Lets use this as per Nyx, Thanks Nyx!*/ + job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */ + /* Turbo first pass if two pass and Turbo First pass is selected */ + if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 && [[queueToApply objectForKey:@"VideoTurboTwoPass"] intValue] == 1 ) + { + /* pass the "Turbo" string to be appended to the existing x264 opts string into a variable for the first pass */ + NSString *firstPassOptStringTurbo = @":ref=1:subme=1:me=dia:analyse=none:trellis=0:no-fast-pskip=0:8x8dct=0:weightb=0"; + /* append the "Turbo" string variable to the existing opts string. + Note: the "Turbo" string must be appended, not prepended to work properly*/ + NSString *firstPassOptStringCombined = [[queueToApply objectForKey:@"x264Option"] stringByAppendingString:firstPassOptStringTurbo]; + strcpy(job->x264opts, [firstPassOptStringCombined UTF8String]); + } + else + { + strcpy(job->x264opts, [[queueToApply objectForKey:@"x264Option"] UTF8String]); + } + + } + + + /* Picture Size Settings */ + job->width = [[queueToApply objectForKey:@"PictureWidth"] intValue]; + job->height = [[queueToApply objectForKey:@"PictureHeight"] intValue]; + + job->keep_ratio = [[queueToApply objectForKey:@"PictureKeepRatio"] intValue]; + job->pixel_ratio = [[queueToApply objectForKey:@"PicturePAR"] intValue]; + + + /* Here we use the crop values saved at the time the preset was saved */ + job->crop[0] = [[queueToApply objectForKey:@"PictureTopCrop"] intValue]; + job->crop[1] = [[queueToApply objectForKey:@"PictureBottomCrop"] intValue]; + job->crop[2] = [[queueToApply objectForKey:@"PictureLeftCrop"] intValue]; + job->crop[3] = [[queueToApply objectForKey:@"PictureRightCrop"] intValue]; + + /* Video settings */ + /* Framerate */ + + /* Set vfr to 0 as it's only on if using same as source in the framerate popup + * and detelecine is on, so we handle that in the logic below + */ + job->vfr = 0; + if( [[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue] > 0 ) + { + /* a specific framerate has been chosen */ + job->vrate = 27000000; + job->vrate_base = hb_video_rates[[[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue]-1].rate; + /* We are not same as source so we set job->cfr to 1 + * to enable constant frame rate since user has specified + * a specific framerate*/ + job->cfr = 1; + } + else + { + /* We are same as source (variable) */ + job->vrate = [[queueToApply objectForKey:@"JobVrate"] intValue]; + job->vrate_base = [[queueToApply objectForKey:@"JobVrateBase"] intValue]; + /* We are same as source so we set job->cfr to 0 + * to enable true same as source framerate */ + job->cfr = 0; + /* If we are same as source and we have detelecine on, we need to turn on + * job->vfr + */ + if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1) + { + job->vfr = 1; + } + } + if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] != 2 ) + { + /* Target size. + Bitrate should already have been calculated and displayed + in fVidBitrateField, so let's just use it same as abr*/ + job->vquality = -1.0; + job->vbitrate = [[queueToApply objectForKey:@"VideoAvgBitrate"] intValue]; + } + if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] == 2 ) + { + job->vquality = [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]; + job->vbitrate = 0; + + } + + job->grayscale = [[queueToApply objectForKey:@"VideoGrayScale"] intValue]; + /* Subtitle settings */ + job->subtitle = [[queueToApply objectForKey:@"JobSubtitlesIndex"] intValue] - 2; + + /* Audio tracks and mixdowns */ + /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/ + int audiotrack_count = hb_list_count(job->list_audio); + for( int i = 0; i < audiotrack_count;i++) + { + hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 ); + hb_list_rem(job->list_audio, temp_audio); + } + /* Now lets add our new tracks to the audio list here */ + if ([[queueToApply objectForKey:@"Audio1Track"] intValue] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [[queueToApply objectForKey:@"Audio1Track"] intValue] - 1; + /* We go ahead and assign values to our audio->out. */ + audio->out.track = [[queueToApply objectForKey:@"Audio1Track"] intValue] - 1; + audio->out.codec = [[queueToApply objectForKey:@"JobAudio1Encoder"] intValue]; + audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio1Mixdown"] intValue]; + audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio1Bitrate"] intValue]; + audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio1Samplerate"] intValue]; + audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio1TrackDRCSlider"] floatValue]; + + hb_audio_add( job, audio ); + free(audio); + } + if ([[queueToApply objectForKey:@"Audio2Track"] intValue] > 0) + { + + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [[queueToApply objectForKey:@"Audio2Track"] intValue] - 1; + [self writeToActivityLog: "prepareJob audiotrack 2 is: %d", audio->in.track]; + /* We go ahead and assign values to our audio->out. */ + audio->out.track = [[queueToApply objectForKey:@"Audio2Track"] intValue] - 1; + audio->out.codec = [[queueToApply objectForKey:@"JobAudio2Encoder"] intValue]; + audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio2Mixdown"] intValue]; + audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio2Bitrate"] intValue]; + audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio2Samplerate"] intValue]; + audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio2TrackDRCSlider"] floatValue]; + + hb_audio_add( job, audio ); + free(audio); + } + + if ([[queueToApply objectForKey:@"Audio3Track"] intValue] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [[queueToApply objectForKey:@"Audio3Track"] intValue] - 1; + /* We go ahead and assign values to our audio->out. */ + audio->out.track = [[queueToApply objectForKey:@"Audio3Track"] intValue] - 1; + audio->out.codec = [[queueToApply objectForKey:@"JobAudio3Encoder"] intValue]; + audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio3Mixdown"] intValue]; + audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio3Bitrate"] intValue]; + audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio3Samplerate"] intValue]; + audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio3TrackDRCSlider"] floatValue]; + + hb_audio_add( job, audio ); + free(audio); + } + + if ([[queueToApply objectForKey:@"Audio4Track"] intValue] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [[queueToApply objectForKey:@"Audio4Track"] intValue] - 1; + /* We go ahead and assign values to our audio->out. */ + audio->out.track = [[queueToApply objectForKey:@"Audio4Track"] intValue] - 1; + audio->out.codec = [[queueToApply objectForKey:@"JobAudio4Encoder"] intValue]; + audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio4Mixdown"] intValue]; + audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio4Bitrate"] intValue]; + audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio4Samplerate"] intValue]; + audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio4TrackDRCSlider"] floatValue]; + + hb_audio_add( job, audio ); + free(audio); + } + + /* Filters */ + job->filters = hb_list_init(); + + /* Now lets call the filters if applicable. + * The order of the filters is critical + */ + /* Detelecine */ + if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1) { hb_list_add( job->filters, &hb_filter_detelecine ); } - + + /* Decomb */ + if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 1) + { + /* Run old deinterlacer fd by default */ + hb_filter_decomb.settings = (char *) [[queueToApply objectForKey:@"JobPictureDecomb"] UTF8String]; + hb_list_add( job->filters, &hb_filter_decomb ); + } + /* Deinterlace */ - if ([fPictureController deinterlace] == 1) + if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 1) { /* Run old deinterlacer fd by default */ hb_filter_deinterlace.settings = "-1"; hb_list_add( job->filters, &hb_filter_deinterlace ); } - else if ([fPictureController deinterlace] == 2) + else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 2) { /* Yadif mode 0 (without spatial deinterlacing.) */ hb_filter_deinterlace.settings = "2"; hb_list_add( job->filters, &hb_filter_deinterlace ); } - else if ([fPictureController deinterlace] == 3) + else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 3) { /* Yadif (with spatial deinterlacing) */ hb_filter_deinterlace.settings = "0"; hb_list_add( job->filters, &hb_filter_deinterlace ); } - /* Denoise */ - - if ([fPictureController denoise] == 1) // Weak in popup + /* Denoise */ + if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 1) // Weak in popup { hb_filter_denoise.settings = "2:1:2:3"; hb_list_add( job->filters, &hb_filter_denoise ); } - else if ([fPictureController denoise] == 2) // Medium in popup + else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 2) // Medium in popup { hb_filter_denoise.settings = "3:2:2:3"; hb_list_add( job->filters, &hb_filter_denoise ); } - else if ([fPictureController denoise] == 3) // Strong in popup + else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 3) // Strong in popup { hb_filter_denoise.settings = "7:7:5:5"; hb_list_add( job->filters, &hb_filter_denoise ); } /* Deblock (uses pp7 default) */ - if ([fPictureController deblock]) + /* NOTE: even though there is a valid deblock setting of 0 for the filter, for + * the macgui's purposes a value of 0 actually means to not even use the filter + * current hb_filter_deblock.settings valid ranges are from 5 - 15 + */ + if ([[queueToApply objectForKey:@"PictureDeblock"] intValue] != 0) { + hb_filter_deblock.settings = (char *) [[queueToApply objectForKey:@"PictureDeblock"] UTF8String]; hb_list_add( job->filters, &hb_filter_deblock ); } - +[self writeToActivityLog: "prepareJob exiting"]; } @@ -1675,28 +2887,14 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It if( [[NSFileManager defaultManager] fileExistsAtPath: [fDstFile2Field stringValue]] ) { - NSBeginCriticalAlertSheet( _( @"File already exists" ), - _( @"Cancel" ), _( @"Overwrite" ), NULL, fWindow, self, - @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ), - NULL, NULL, [NSString stringWithFormat: - _( @"Do you want to overwrite %@?" ), - [fDstFile2Field stringValue]] ); - // overwriteAddToQueueAlertDone: will be called when the alert is dismissed. - } - - // Warn if another pending job in the queue has the same destination path - else if ( ([fQueueController pendingJobGroupWithDestinationPath:[fDstFile2Field stringValue]] != nil) - || ([[[fQueueController currentJobGroup] destinationPath] isEqualToString: [fDstFile2Field stringValue]]) ) - { - NSBeginCriticalAlertSheet( _( @"Another queued encode has specified the same destination." ), - _( @"Cancel" ), _( @"Overwrite" ), NULL, fWindow, self, + NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists", @"" ), + NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self, @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ), NULL, NULL, [NSString stringWithFormat: - _( @"Do you want to overwrite %@?" ), + NSLocalizedString( @"Do you want to overwrite %@?", @"" ), [fDstFile2Field stringValue]] ); // overwriteAddToQueueAlertDone: will be called when the alert is dismissed. } - else { [self doAddToQueue]; @@ -1715,174 +2913,76 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It - (void) doAddToQueue { - hb_list_t * list = hb_get_titles( fHandle ); - hb_title_t * title = (hb_title_t *) hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] ); - hb_job_t * job = title->job; - - // Create a Queue Controller job group. Each job that we submit to libhb will also - // get added to the job group so that the queue can track the jobs. - HBJobGroup * jobGroup = [HBJobGroup jobGroup]; - // The job group can maintain meta data that libhb can not... - [jobGroup setPresetName: [fPresetSelectedDisplay stringValue]]; - - // Job groups require that each job within the group be assigned a unique id so - // that the queue can xref between itself and the private jobs that libhb - // maintains. The ID is composed a group id number and a "sequence" number. libhb - // does not use this id. - static int jobGroupID = 0; - jobGroupID++; - - // A sequence number, starting at zero, is used to identifiy to each pass. This is - // used by the queue UI to determine if a pass if the first pass of an encode. - int sequenceNum = -1; - - [self prepareJob]; - - /* Destination file */ - job->file = [[fDstFile2Field stringValue] UTF8String]; - - if( [fSubForcedCheck state] == NSOnState ) - job->subtitle_force = 1; - else - job->subtitle_force = 0; - - /* - * subtitle of -1 is a scan - */ - if( job->subtitle == -1 ) - { - char *x264opts_tmp; - - /* - * When subtitle scan is enabled do a fast pre-scan job - * which will determine which subtitles to enable, if any. - */ - job->pass = -1; - x264opts_tmp = job->x264opts; - job->subtitle = -1; - - job->x264opts = NULL; - - job->indepth_scan = 1; - - job->select_subtitle = (hb_subtitle_t**)malloc(sizeof(hb_subtitle_t*)); - *(job->select_subtitle) = NULL; - - /* - * Add the pre-scan job - */ - job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum); - hb_add( fHandle, job ); - [jobGroup addJob:[HBJob jobWithLibhbJob:job]]; // add this pass to the job group - - job->x264opts = x264opts_tmp; - } - else - job->select_subtitle = NULL; - - /* No subtitle were selected, so reset the subtitle to -1 (which before - * this point meant we were scanning - */ - if( job->subtitle == -2 ) - job->subtitle = -1; - - if( [fVidTwoPassCheck state] == NSOnState ) - { - hb_subtitle_t **subtitle_tmp = job->select_subtitle; - job->indepth_scan = 0; - - /* - * Do not autoselect subtitles on the first pass of a two pass - */ - job->select_subtitle = NULL; - - job->pass = 1; - job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum); - hb_add( fHandle, job ); - [jobGroup addJob:[HBJob jobWithLibhbJob:job]]; // add this pass to the job group - - job->pass = 2; - job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum); - - job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */ - strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]); + [self addQueueFileItem ]; +} - job->select_subtitle = subtitle_tmp; - hb_add( fHandle, job ); - [jobGroup addJob:[HBJob jobWithLibhbJob:job]]; // add this pass to the job group - } - else - { - job->indepth_scan = 0; - job->pass = 0; - job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum); - hb_add( fHandle, job ); - [jobGroup addJob:[HBJob jobWithLibhbJob:job]]; // add this pass to the job group - } - - NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent]; - [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"]; - - // Let the queue controller know about the job group - [fQueueController addJobGroup:jobGroup]; -} /* Rip: puts up an alert before ultimately calling doRip */ - (IBAction) Rip: (id) sender { + [self writeToActivityLog: "Rip: Pending queue count is %d", fPendingCount]; /* Rip or Cancel ? */ hb_state_t s; - hb_get_state2( fHandle, &s ); - + hb_get_state2( fQueueEncodeLibhb, &s ); + if(s.state == HB_STATE_WORKING || s.state == HB_STATE_PAUSED) { [self Cancel: sender]; return; } - // If there are jobs in the queue, then this is a rip the queue + /* We check to see if we need to warn the user that the computer will go to sleep + or shut down when encoding is finished */ + [self remindUserOfSleepOrShutdown]; - if (hb_count( fHandle ) > 0) + // If there are pending jobs in the queue, then this is a rip the queue + if (fPendingCount > 0) { - [self doRip]; + /* here lets start the queue with the first pending item */ + [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; + return; } - + // Before adding jobs to the queue, check for a valid destination. - + NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent]; if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) { NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil); return; } - + /* We check for duplicate name here */ if( [[NSFileManager defaultManager] fileExistsAtPath:[fDstFile2Field stringValue]] ) { - NSBeginCriticalAlertSheet( _( @"File already exists" ), - _( @"Cancel" ), _( @"Overwrite" ), NULL, fWindow, self, - @selector( overWriteAlertDone:returnCode:contextInfo: ), - NULL, NULL, [NSString stringWithFormat: - _( @"Do you want to overwrite %@?" ), - [fDstFile2Field stringValue]] ); - + NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists", @"" ), + NSLocalizedString( @"Cancel", "" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self, + @selector( overWriteAlertDone:returnCode:contextInfo: ), + NULL, NULL, [NSString stringWithFormat: + NSLocalizedString( @"Do you want to overwrite %@?", @"" ), + [fDstFile2Field stringValue]] ); + // overWriteAlertDone: will be called when the alert is dismissed. It will call doRip. } else { - /* if there are no jobs in the queue, then add this one to the queue and rip - otherwise, just rip the queue */ - if( hb_count( fHandle ) == 0) + /* if there are no pending jobs in the queue, then add this one to the queue and rip + otherwise, just rip the queue */ + if(fPendingCount == 0) { - [self doAddToQueue]; + [self writeToActivityLog: "Rip: No pending jobs, so sending this one to doAddToQueue"]; + [self doAddToQueue]; } - + NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent]; [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"]; - [self doRip]; + /* go right to processing the new queue encode */ + [self writeToActivityLog: "Rip: Going right to performNewQueueScan"]; + [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; + } } @@ -1896,14 +2996,15 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It { /* if there are no jobs in the queue, then add this one to the queue and rip otherwise, just rip the queue */ - if( hb_count( fHandle ) == 0 ) + if( fPendingCount == 0 ) { [self doAddToQueue]; } NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent]; [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"]; - [self doRip]; + [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; + } } @@ -1916,11 +3017,11 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It NSBeep(); reminduser = NSRunAlertPanel(@"The computer will sleep after encoding is done.",@"You have selected to sleep the computer after encoding. To turn off sleeping, go to the HandBrake preferences.", @"OK", @"Preferences...", nil); [NSApp requestUserAttention:NSCriticalRequest]; - if ( reminduser == NSAlertAlternateReturn ) + if ( reminduser == NSAlertAlternateReturn ) { - [self showPreferencesWindow:NULL]; + [self showPreferencesWindow:nil]; } - } + } else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"]) { /*Warn that computer will shut down after encoding*/ @@ -1928,9 +3029,9 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It NSBeep(); reminduser = NSRunAlertPanel(@"The computer will shut down after encoding is done.",@"You have selected to shut down the computer after encoding. To turn off shut down, go to the HandBrake preferences.", @"OK", @"Preferences...", nil); [NSApp requestUserAttention:NSCriticalRequest]; - if ( reminduser == NSAlertAlternateReturn ) + if ( reminduser == NSAlertAlternateReturn ) { - [self showPreferencesWindow:NULL]; + [self showPreferencesWindow:nil]; } } @@ -1940,24 +3041,12 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It - (void) doRip { /* Let libhb do the job */ - hb_start( fHandle ); - /*set the fEncodeState State */ + hb_start( fQueueEncodeLibhb ); + /*set the fEncodeState State */ fEncodeState = 1; } - - -//------------------------------------------------------------------------------------ -// Removes all jobs from the queue. Does not cancel the current processing job. -//------------------------------------------------------------------------------------ -- (void) doDeleteQueuedJobs -{ - hb_job_t * job; - while( ( job = hb_job( fHandle, 0 ) ) ) - hb_rem( fHandle, job ); -} - //------------------------------------------------------------------------------------ // Cancels and deletes the current job and stops libhb from processing the remaining // encodes. @@ -1970,10 +3059,32 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It // remaining passes of the job and then start the queue back up if there are any // remaining jobs. - [fQueueController libhbWillStop]; - hb_stop( fHandle ); + + hb_stop( fQueueEncodeLibhb ); fEncodeState = 2; // don't alert at end of processing since this was a cancel + // now that we've stopped the currently encoding job, lets mark it as cancelled + [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"]; + // and as always, save it in the queue .plist... + /* We save all of the Queue data here */ + [self saveQueueFileItem]; + // so now lets move to + currentQueueEncodeIndex++ ; + // ... and see if there are more items left in our queue + int queueItems = [QueueFileArray count]; + /* If we still have more items in our queue, lets go to the next one */ + if (currentQueueEncodeIndex < queueItems) + { + [self writeToActivityLog: "doCancelCurrentJob currentQueueEncodeIndex is incremented to: %d", currentQueueEncodeIndex]; + [self writeToActivityLog: "doCancelCurrentJob moving to the next job"]; + + [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; + } + else + { + [self writeToActivityLog: "doCancelCurrentJob the item queue is complete"]; + } + } //------------------------------------------------------------------------------------ @@ -1983,14 +3094,11 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It //------------------------------------------------------------------------------------ - (IBAction)Cancel: (id)sender { - if (!fHandle) return; - - HBJobGroup * jobGroup = [fQueueController currentJobGroup]; - if (!jobGroup) return; - - NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop encoding %@?", nil), - [jobGroup name]]; + if (!fQueueController) return; + hb_pause( fQueueEncodeLibhb ); + NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"You are currently encoding. What would you like to do ?", nil)]; + // Which window to attach the sheet to? NSWindow * docWindow; if ([sender respondsToSelector: @selector(window)]) @@ -2000,38 +3108,55 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It NSBeginCriticalAlertSheet( alertTitle, - NSLocalizedString(@"Keep Encoding", nil), - nil, - NSLocalizedString(@"Stop Encoding", nil), + NSLocalizedString(@"Continue Encoding", nil), + NSLocalizedString(@"Cancel Current and Stop", nil), + NSLocalizedString(@"Cancel Current and Continue", nil), docWindow, self, - nil, @selector(didDimissCancelCurrentJob:returnCode:contextInfo:), nil, - NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil)); + nil, @selector(didDimissCancel:returnCode:contextInfo:), nil, + NSLocalizedString(@"Your encode will be cancelled if you don't continue encoding.", nil)); // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed } -- (void) didDimissCancelCurrentJob: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo +- (void) didDimissCancel: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo { - if (returnCode == NSAlertOtherReturn) + hb_resume( fQueueEncodeLibhb ); + if (returnCode == NSAlertOtherReturn) + { [self doCancelCurrentJob]; // <- this also stops libhb + } + if (returnCode == NSAlertAlternateReturn) + { + [self doCancelCurrentJobAndStop]; + } } - - - - +- (void) doCancelCurrentJobAndStop +{ + hb_stop( fQueueEncodeLibhb ); + fEncodeState = 2; // don't alert at end of processing since this was a cancel + + // now that we've stopped the currently encoding job, lets mark it as cancelled + [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"]; + // and as always, save it in the queue .plist... + /* We save all of the Queue data here */ + [self saveQueueFileItem]; + // so now lets move to + currentQueueEncodeIndex++ ; + [self writeToActivityLog: "cancelling current job and stopping the queue"]; +} - (IBAction) Pause: (id) sender { hb_state_t s; - hb_get_state2( fHandle, &s ); + hb_get_state2( fQueueEncodeLibhb, &s ); if( s.state == HB_STATE_PAUSED ) { - hb_resume( fHandle ); + hb_resume( fQueueEncodeLibhb ); } else { - hb_pause( fHandle ); + hb_pause( fQueueEncodeLibhb ); } } @@ -2043,15 +3168,14 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It hb_list_t * list = hb_get_titles( fHandle ); hb_title_t * title = (hb_title_t*) hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] ); - - + /* If Auto Naming is on. We create an output filename of dvd name - title number */ - if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultAutoNaming"] > 0) + if( [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultAutoNaming"] > 0 && ( hb_list_count( list ) > 1 ) ) { [fDstFile2Field setStringValue: [NSString stringWithFormat: @"%@/%@-%d.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent], - browsedSourceDisplayName, - title->index, + [browsedSourceDisplayName stringByDeletingPathExtension], + title->index, [[fDstFile2Field stringValue] pathExtension]]]; } @@ -2065,24 +3189,16 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [fSrcChapterEndPopUp addItemWithTitle: [NSString stringWithFormat: @"%d", i + 1]]; } + [fSrcChapterStartPopUp selectItemAtIndex: 0]; [fSrcChapterEndPopUp selectItemAtIndex: hb_list_count( title->list_chapter ) - 1]; - [self chapterPopUpChanged: NULL]; + [self chapterPopUpChanged:nil]; -/* Start Get and set the initial pic size for display */ + /* Start Get and set the initial pic size for display */ hb_job_t * job = title->job; - fTitle = title; + fTitle = title; - /* Pixel Ratio Setting */ - if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PixelRatio"]) - { - job->pixel_ratio = 1 ; - } - else - { - job->pixel_ratio = 0 ; - } /*Set Source Size Field Here */ [fPicSettingsSrc setStringValue: [NSString stringWithFormat: @"%d x %d", fTitle->width, fTitle->height]]; @@ -2118,81 +3234,52 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It subtitle->lang] action: NULL keyEquivalent: @""]; } [fSubPopUp selectItemAtIndex: 0]; - - [self subtitleSelectionChanged: NULL]; - + + [self subtitleSelectionChanged:nil]; + /* Update chapter table */ [fChapterTitlesDelegate resetWithTitle:title]; [fChapterTable reloadData]; + /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/ + int audiotrack_count = hb_list_count(job->list_audio); + for( int i = 0; i < audiotrack_count;i++) + { + hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 ); + hb_list_rem(job->list_audio, temp_audio); + } + /* Update audio popups */ [self addAllAudioTracksToPopUp: fAudLang1PopUp]; [self addAllAudioTracksToPopUp: fAudLang2PopUp]; + [self addAllAudioTracksToPopUp: fAudLang3PopUp]; + [self addAllAudioTracksToPopUp: fAudLang4PopUp]; /* search for the first instance of our prefs default language for track 1, and set track 2 to "none" */ NSString * audioSearchPrefix = [[NSUserDefaults standardUserDefaults] stringForKey:@"DefaultLanguage"]; [self selectAudioTrackInPopUp: fAudLang1PopUp searchPrefixString: audioSearchPrefix selectIndexIfNotFound: 1]; - [self selectAudioTrackInPopUp: fAudLang2PopUp searchPrefixString: NULL selectIndexIfNotFound: 0]; - + [self selectAudioTrackInPopUp:fAudLang2PopUp searchPrefixString:nil selectIndexIfNotFound:0]; + [self selectAudioTrackInPopUp:fAudLang3PopUp searchPrefixString:nil selectIndexIfNotFound:0]; + [self selectAudioTrackInPopUp:fAudLang4PopUp searchPrefixString:nil selectIndexIfNotFound:0]; + /* changing the title may have changed the audio channels on offer, */ /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */ [self audioTrackPopUpChanged: fAudLang1PopUp]; [self audioTrackPopUpChanged: fAudLang2PopUp]; - - /* We repopulate the Video Framerate popup and show the detected framerate along with "Same as Source"*/ - [fVidRatePopUp removeAllItems]; - if (fTitle->rate_base == 1126125) // 23.976 NTSC Film - { - [fVidRatePopUp addItemWithTitle: @"Same as source (23.976)"]; - } - else if (fTitle->rate_base == 1080000) // 25 PAL Film/Video - { - [fVidRatePopUp addItemWithTitle: @"Same as source (25)"]; - } - else if (fTitle->rate_base == 900900) // 29.97 NTSC Video - { - [fVidRatePopUp addItemWithTitle: @"Same as source (29.97)"]; - } - else - { - /* if none of the common dvd source framerates is detected, just use "Same as source" */ - [fVidRatePopUp addItemWithTitle: @"Same as source"]; - } - for( int i = 0; i < hb_video_rates_count; i++ ) - { - if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.3f",23.976]]) - { - [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@", - [NSString stringWithCString: hb_video_rates[i].string], @" (NTSC Film)"]]; - } - else if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%d",25]]) - { - [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@", - [NSString stringWithCString: hb_video_rates[i].string], @" (PAL Film/Video)"]]; - } - else if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.2f",29.97]]) - { - [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@", - [NSString stringWithCString: hb_video_rates[i].string], @" (NTSC Video)"]]; - } - else - { - [fVidRatePopUp addItemWithTitle: - [NSString stringWithCString: hb_video_rates[i].string]]; - } - } + [self audioTrackPopUpChanged: fAudLang3PopUp]; + [self audioTrackPopUpChanged: fAudLang4PopUp]; + [fVidRatePopUp selectItemAtIndex: 0]; - + /* we run the picture size values through calculatePictureSizing to get all picture setting information*/ - [self calculatePictureSizing: NULL]; - + [self calculatePictureSizing:nil]; + /* lets call tableViewSelected to make sure that any preset we have selected is enforced after a title change */ - [self selectPreset:NULL]; - + [self selectPreset:nil]; } - (IBAction) chapterPopUpChanged: (id) sender { - + /* If start chapter popup is greater than end chapter popup, we set the end chapter popup to the same as start chapter popup */ if ([fSrcChapterStartPopUp indexOfSelectedItem] > [fSrcChapterEndPopUp indexOfSelectedItem]) @@ -2225,7 +3312,6 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It - (IBAction) formatPopUpChanged: (id) sender { NSString * string = [fDstFile2Field stringValue]; - NSString * selectedCodecs = [fDstCodecsPopUp titleOfSelectedItem]; int format = [fDstFormatPopUp indexOfSelectedItem]; char * ext = NULL; /* Initially set the large file (64 bit formatting) output checkbox to hidden */ @@ -2233,8 +3319,16 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [fDstMp4HttpOptFileCheck setHidden: YES]; [fDstMp4iPodFileCheck setHidden: YES]; - /* Update the codecs popup */ - [fDstCodecsPopUp removeAllItems]; + /* Update the Video Codec PopUp */ + /* Note: we now store the video encoder int values from common.c in the tags of each popup for easy retrieval later */ + [fVidEncoderPopUp removeAllItems]; + NSMenuItem *menuItem; + /* These video encoders are available to all of our current muxers, so lets list them once here */ + menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"MPEG-4 (FFmpeg)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_VCODEC_FFMPEG]; + + menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"MPEG-4 (XviD)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_VCODEC_XVID]; switch( format ) { case 0: @@ -2247,148 +3341,156 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It { ext = "mp4"; } - - [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / AAC Audio" )]; - [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / AAC Audio" )]; - /* We add a new codecs entry which will allow the new aac/ ac3 hybrid */ - [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / AAC + AC3 Audio" )]; - [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / AC3 Audio" )]; - /* We enable the create chapters checkbox here since we are .mp4*/ - /* We show the mp4 option checkboxes here since we are mp4 */ + /* Add additional video encoders here */ + menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_VCODEC_X264]; + /* We show the mp4 option checkboxes here since we are mp4 */ [fCreateChapterMarkers setEnabled: YES]; [fDstMp4LargeFileCheck setHidden: NO]; [fDstMp4HttpOptFileCheck setHidden: NO]; [fDstMp4iPodFileCheck setHidden: NO]; break; - case 1: + case 1: ext = "mkv"; - [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / AAC Audio" )]; - [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / AC-3 Audio" )]; - [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / MP3 Audio" )]; - [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / Vorbis Audio" )]; - - [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / AAC Audio" )]; - [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / AC-3 Audio" )]; - [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / MP3 Audio" )]; - [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / Vorbis Audio" )]; + /* Add additional video encoders here */ + menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_VCODEC_X264]; + menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"VP3 (Theora)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_VCODEC_THEORA]; /* We enable the create chapters checkbox here */ [fCreateChapterMarkers setEnabled: YES]; break; - case 2: + case 2: ext = "avi"; - [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / MP3 Audio" )]; - [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / AC-3 Audio" )]; - [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / MP3 Audio" )]; - [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / AC-3 Audio" )]; - /* We disable the create chapters checkbox here and make sure it is unchecked*/ + /* Add additional video encoders here */ + menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_VCODEC_X264]; + /* We disable the create chapters checkbox here and make sure it is unchecked*/ [fCreateChapterMarkers setEnabled: NO]; [fCreateChapterMarkers setState: NSOffState]; break; - case 3: + case 3: ext = "ogm"; - [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / Vorbis Audio" )]; - [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / MP3 Audio" )]; + /* Add additional video encoders here */ + menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"VP3 (Theora)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_VCODEC_THEORA]; /* We disable the create chapters checkbox here and make sure it is unchecked*/ [fCreateChapterMarkers setEnabled: NO]; [fCreateChapterMarkers setState: NSOffState]; break; } - - if ( SuccessfulScan ) { - [fDstCodecsPopUp selectItemWithTitle:selectedCodecs]; - + [fVidEncoderPopUp selectItemAtIndex: 0]; + + [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp]; + [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp]; + [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp]; + [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp]; + + if( format == 0 ) + [self autoSetM4vExtension: sender]; + else + [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%s", [string stringByDeletingPathExtension], ext]]; + + if( SuccessfulScan ) + { /* Add/replace to the correct extension */ - if( [string characterAtIndex: [string length] - 4] == '.' ) - { - [fDstFile2Field setStringValue: [NSString stringWithFormat: - @"%@.%s", [string substringToIndex: [string length] - 4], - ext]]; - } - else - { - [fDstFile2Field setStringValue: [NSString stringWithFormat: - @"%@.%s", string, ext]]; - } - - if ( [fDstCodecsPopUp selectedItem] == NULL ) + [self audioTrackPopUpChanged: fAudLang1PopUp]; + [self audioTrackPopUpChanged: fAudLang2PopUp]; + [self audioTrackPopUpChanged: fAudLang3PopUp]; + [self audioTrackPopUpChanged: fAudLang4PopUp]; + + if( [fVidEncoderPopUp selectedItem] == nil ) { - [fDstCodecsPopUp selectItemAtIndex:0]; - [self codecsPopUpChanged: NULL]; - + + [fVidEncoderPopUp selectItemAtIndex:0]; + [self videoEncoderPopUpChanged:nil]; + /* changing the format may mean that we can / can't offer mono or 6ch, */ /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */ - [self audioTrackPopUpChanged: fAudLang1PopUp]; - [self audioTrackPopUpChanged: fAudLang2PopUp]; + /* We call the method to properly enable/disable turbo 2 pass */ [self twoPassCheckboxChanged: sender]; /* We call method method to change UI to reflect whether a preset is used or not*/ } } - - /* Lets check to see if we want to auto set the .m4v extension for mp4 */ - [self autoSetM4vExtension: sender]; - [self customSettingUsed: sender]; + [self customSettingUsed: sender]; } -- (IBAction) codecsPopUpChanged: (id) sender +- (IBAction) autoSetM4vExtension: (id) sender { - int format = [fDstFormatPopUp indexOfSelectedItem]; - int codecs = [fDstCodecsPopUp indexOfSelectedItem]; - - [fAdvancedOptions setHidden:YES]; + if ( [fDstFormatPopUp indexOfSelectedItem] ) + return; - /* Update the encoder popup*/ - if( ( FormatSettings[format][codecs] & HB_VCODEC_X264 ) ) - { - /* MPEG-4 -> H.264 */ - [fVidEncoderPopUp removeAllItems]; - [fVidEncoderPopUp addItemWithTitle: @"x264"]; - [fVidEncoderPopUp selectItemAtIndex: 0]; - [fAdvancedOptions setHidden:NO]; - [self autoSetM4vExtension: sender]; - } - - else if( ( FormatSettings[format][codecs] & HB_VCODEC_FFMPEG ) ) - { - /* H.264 -> MPEG-4 */ - [fVidEncoderPopUp removeAllItems]; - [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"]; - [fVidEncoderPopUp addItemWithTitle: @"XviD"]; - [fVidEncoderPopUp selectItemAtIndex: 0]; - - } + NSString * extension = @"mp4"; - if( FormatSettings[format][codecs] & HB_ACODEC_AC3 ) + if( [[fAudTrack1CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack2CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 || + [[fAudTrack3CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 || + [[fAudTrack4CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 || + [fCreateChapterMarkers state] == NSOnState || + [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0 ) { - /* AC-3 pass-through: disable samplerate and bitrate */ - [fAudRatePopUp setEnabled: NO]; - [fAudBitratePopUp setEnabled: NO]; + extension = @"m4v"; } + + if( [extension isEqualTo: [[fDstFile2Field stringValue] pathExtension]] ) + return; else - { - [fAudRatePopUp setEnabled: YES]; - [fAudBitratePopUp setEnabled: YES]; - } - /* changing the codecs on offer may mean that we can / can't offer mono or 6ch, */ - /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */ - [self audioTrackPopUpChanged: fAudLang1PopUp]; - [self audioTrackPopUpChanged: fAudLang2PopUp]; - [self encoderPopUpChanged: sender]; + [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%@", + [[fDstFile2Field stringValue] stringByDeletingPathExtension], extension]]; +} + +- (void) shouldEnableHttpMp4CheckBox: (id) sender +{ + if( [[fAudTrack1CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack2CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 || + [[fAudTrack3CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 || + [[fAudTrack4CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ) + [fDstMp4HttpOptFileCheck setEnabled: NO]; + else + [fDstMp4HttpOptFileCheck setEnabled: YES]; +} + +/* Method to determine if we should change the UI +To reflect whether or not a Preset is being used or if +the user is using "Custom" settings by determining the sender*/ +- (IBAction) customSettingUsed: (id) sender +{ + if ([sender stringValue]) + { + /* Deselect the currently selected Preset if there is one*/ + [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]]; + /* Change UI to show "Custom" settings are being used */ + [fPresetSelectedDisplay setStringValue: @"Custom"]; + curUserPresetChosenNum = nil; + } +[self calculateBitrate:nil]; } -- (IBAction) encoderPopUpChanged: (id) sender + +#pragma mark - +#pragma mark - Video + +- (IBAction) videoEncoderPopUpChanged: (id) sender { hb_job_t * job = fTitle->job; + int videoEncoder = [[fVidEncoderPopUp selectedItem] tag]; + + [fAdvancedOptions setHidden:YES]; + /* If we are using x264 then show the x264 advanced panel*/ + if (videoEncoder == HB_VCODEC_X264) + { + [fAdvancedOptions setHidden:NO]; + [self autoSetM4vExtension: sender]; + } /* We need to set loose anamorphic as available depending on whether or not the ffmpeg encoder is being used as it borks up loose anamorphic . For convenience lets use the titleOfSelected index. Probably should revisit whether or not we want to use the index itself but this is easier */ - if ([fVidEncoderPopUp titleOfSelectedItem] == @"FFmpeg") + if (videoEncoder == HB_VCODEC_FFMPEG) { if (job->pixel_ratio == 2) { @@ -2413,46 +3515,10 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It } - /* if MP4 format and [fDstCodecsPopUp indexOfSelectedItem] > 1 we know that the audio is going to be - * either aac + ac3 passthru, or just ac3 passthru so we need to make sure the output file extension is m4v - * otherwise Quicktime will not play it at all */ -- (IBAction) autoSetM4vExtension: (id) sender -{ - if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [fDstCodecsPopUp indexOfSelectedItem] > 1) - { - NSString *newpath = [[[fDstFile2Field stringValue] stringByDeletingPathExtension] stringByAppendingPathExtension: @"m4v"]; - [fDstFile2Field setStringValue: [NSString stringWithFormat: - @"%@", newpath]]; - } -} -/* Method to determine if we should change the UI -To reflect whether or not a Preset is being used or if -the user is using "Custom" settings by determining the sender*/ -- (IBAction) customSettingUsed: (id) sender -{ - if ([sender stringValue] != NULL) - { - /* Deselect the currently selected Preset if there is one*/ - [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]]; - [[fPresetsActionMenu itemAtIndex:0] setEnabled: NO]; - /* Change UI to show "Custom" settings are being used */ - [fPresetSelectedDisplay setStringValue: @"Custom"]; - - curUserPresetChosenNum = nil; - } - -} - - -#pragma mark - -#pragma mark - Video - - (IBAction) twoPassCheckboxChanged: (id) sender { /* check to see if x264 is chosen */ - int format = [fDstFormatPopUp indexOfSelectedItem]; - int codecs = [fDstCodecsPopUp indexOfSelectedItem]; - if( ( FormatSettings[format][codecs] & HB_VCODEC_X264 ) ) + if([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264) { if( [fVidTwoPassCheck state] == NSOnState) { @@ -2527,14 +3593,14 @@ the user is using "Custom" settings by determining the sender*/ - (IBAction) qualitySliderChanged: (id) sender { [fVidConstantCell setTitle: [NSString stringWithFormat: - _( @"Constant quality: %.0f %%" ), 100.0 * + NSLocalizedString( @"Constant quality: %.0f %%", @"" ), 100.0 * [fVidQualitySlider floatValue]]]; [self customSettingUsed: sender]; } - (void) controlTextDidChange: (NSNotification *) notification { - [self calculateBitrate: NULL]; + [self calculateBitrate:nil]; } - (IBAction) calculateBitrate: (id) sender @@ -2548,11 +3614,98 @@ the user is using "Custom" settings by determining the sender*/ hb_title_t * title = (hb_title_t *) hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] ); hb_job_t * job = title->job; + hb_audio_config_t * audio; + /* For hb_calc_bitrate in addition to the Target Size in MB out of the + * Target Size Field, we also need the job info for the Muxer, the Chapters + * as well as all of the audio track info. + * This used to be accomplished by simply calling prepareJob here, however + * since the resilient queue sets the queue array values instead of the job + * values directly, we duplicate the old prepareJob code here for the variables + * needed + */ + job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1; + job->chapter_end = [fSrcChapterEndPopUp indexOfSelectedItem] + 1; + job->mux = [[fDstFormatPopUp selectedItem] tag]; + + /* Audio goes here */ + int audiotrack_count = hb_list_count(job->list_audio); + for( int i = 0; i < audiotrack_count;i++) + { + hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 ); + hb_list_rem(job->list_audio, temp_audio); + } + /* Now we need our audio info here for each track if applicable */ + if ([fAudLang1PopUp indexOfSelectedItem] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [fAudLang1PopUp indexOfSelectedItem] - 1; + /* We go ahead and assign values to our audio->out. */ + audio->out.track = [fAudLang1PopUp indexOfSelectedItem] - 1; + audio->out.codec = [[fAudTrack1CodecPopUp selectedItem] tag]; + audio->out.mixdown = [[fAudTrack1MixPopUp selectedItem] tag]; + audio->out.bitrate = [[fAudTrack1BitratePopUp selectedItem] tag]; + audio->out.samplerate = [[fAudTrack1RatePopUp selectedItem] tag]; + audio->out.dynamic_range_compression = [fAudTrack1DrcField floatValue]; + + hb_audio_add( job, audio ); + free(audio); + } + if ([fAudLang2PopUp indexOfSelectedItem] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [fAudLang2PopUp indexOfSelectedItem] - 1; + /* We go ahead and assign values to our audio->out. */ + audio->out.track = [fAudLang2PopUp indexOfSelectedItem] - 1; + audio->out.codec = [[fAudTrack2CodecPopUp selectedItem] tag]; + audio->out.mixdown = [[fAudTrack2MixPopUp selectedItem] tag]; + audio->out.bitrate = [[fAudTrack2BitratePopUp selectedItem] tag]; + audio->out.samplerate = [[fAudTrack2RatePopUp selectedItem] tag]; + audio->out.dynamic_range_compression = [fAudTrack2DrcField floatValue]; + + hb_audio_add( job, audio ); + free(audio); + + } + + if ([fAudLang3PopUp indexOfSelectedItem] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [fAudLang3PopUp indexOfSelectedItem] - 1; + /* We go ahead and assign values to our audio->out. */ + audio->out.track = [fAudLang3PopUp indexOfSelectedItem] - 1; + audio->out.codec = [[fAudTrack3CodecPopUp selectedItem] tag]; + audio->out.mixdown = [[fAudTrack3MixPopUp selectedItem] tag]; + audio->out.bitrate = [[fAudTrack3BitratePopUp selectedItem] tag]; + audio->out.samplerate = [[fAudTrack3RatePopUp selectedItem] tag]; + audio->out.dynamic_range_compression = [fAudTrack3DrcField floatValue]; + + hb_audio_add( job, audio ); + free(audio); + + } - [self prepareJob]; - - [fVidBitrateField setIntValue: hb_calc_bitrate( job, - [fVidTargetSizeField intValue] )]; + if ([fAudLang4PopUp indexOfSelectedItem] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [fAudLang4PopUp indexOfSelectedItem] - 1; + /* We go ahead and assign values to our audio->out. */ + audio->out.track = [fAudLang4PopUp indexOfSelectedItem] - 1; + audio->out.codec = [[fAudTrack4CodecPopUp selectedItem] tag]; + audio->out.mixdown = [[fAudTrack4MixPopUp selectedItem] tag]; + audio->out.bitrate = [[fAudTrack4BitratePopUp selectedItem] tag]; + audio->out.samplerate = [[fAudTrack4RatePopUp selectedItem] tag]; + audio->out.dynamic_range_compression = [fAudTrack4DrcField floatValue]; + + hb_audio_add( job, audio ); + free(audio); + + } + +[fVidBitrateField setIntValue: hb_calc_bitrate( job, [fVidTargetSizeField intValue] )]; } #pragma mark - @@ -2565,18 +3718,9 @@ the user is using "Custom" settings by determining the sender*/ - (IBAction) revertPictureSizeToMax: (id) sender { hb_job_t * job = fTitle->job; - /* We use the output picture width and height - as calculated from libhb right after title is set - in TitlePopUpChanged */ - job->width = PicOrigOutputWidth; - job->height = PicOrigOutputHeight; - [fPictureController setAutoCrop:YES]; - /* Here we use the auto crop values determined right after scan */ - job->crop[0] = AutoCropTop; - job->crop[1] = AutoCropBottom; - job->crop[2] = AutoCropLeft; - job->crop[3] = AutoCropRight; - + /* Here we apply the max source storage width and height */ + job->width = fTitle->width-fTitle->job->crop[2]-fTitle->job->crop[3]; + job->height = fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1]; [self calculatePictureSizing: sender]; /* We call method to change UI to reflect whether a preset is used or not*/ @@ -2588,7 +3732,7 @@ the user is using "Custom" settings by determining the sender*/ */ - (void)pictureSettingsDidChange { - [self calculatePictureSizing: NULL]; + [self calculatePictureSizing:nil]; } /* Get and Display Current Pic Settings in main window */ @@ -2614,18 +3758,18 @@ the user is using "Custom" settings by determining the sender*/ hb_set_anamorphic_size(job, &output_width, &output_height, &output_par_width, &output_par_height); int display_width; display_width = output_width * output_par_width / output_par_height; - + [fPicSettingsOutp setStringValue: [NSString stringWithFormat:@"%d x %d", output_width, output_height]]; [fPicSettingsAnamorphic setStringValue: [NSString stringWithFormat:@"%d x %d Loose", display_width, output_height]]; - + fTitle->job->keep_ratio = 0; } else { - [fPicSettingsAnamorphic setStringValue: [NSString stringWithFormat:@"Off"]]; + [fPicSettingsAnamorphic setStringValue:@"Off"]; } - - /* Set ON/Off values for the deinterlace/keep aspect ratio according to boolean */ + + /* Set ON/Off values for the deinterlace/keep aspect ratio according to boolean */ if (fTitle->job->keep_ratio > 0) { [fPicSettingARkeep setStringValue: @"On"]; @@ -2634,6 +3778,7 @@ the user is using "Custom" settings by determining the sender*/ { [fPicSettingARkeep setStringValue: @"Off"]; } + /* Detelecine */ if ([fPictureController detelecine]) { [fPicSettingDetelecine setStringValue: @"Yes"]; @@ -2642,16 +3787,22 @@ the user is using "Custom" settings by determining the sender*/ [fPicSettingDetelecine setStringValue: @"No"]; } - /* VFR (Variable Frame Rate) */ - if ([fPictureController vfr]) { - /* We change the string of the fps popup to warn that vfr is on Framerate (FPS): */ - [fVidRateField setStringValue: @"Framerate (VFR On):"]; - - } - else { - /* make sure the label for framerate is set to its default */ - [fVidRateField setStringValue: @"Framerate (FPS):"]; + /* Decomb */ + if ([fPictureController decomb] == 0) + { + [fPicSettingDecomb setStringValue: @"Off"]; + } + else if ([fPictureController decomb] == 1) + { + [fPicSettingDecomb setStringValue: @"1:2:6:9:80:16:16"]; + } + else if ([fPictureController decomb] == 2) + { + [fPicSettingDecomb setStringValue:[[NSUserDefaults standardUserDefaults] stringForKey:@"DecombCustomString"]]; } + + /* VFR (Variable Frame Rate) */ + /* Deinterlace */ if ([fPictureController deinterlace] == 0) @@ -2670,7 +3821,8 @@ the user is using "Custom" settings by determining the sender*/ { [fPicSettingDeinterlace setStringValue: @"Slower"]; } - /* Denoise */ + + /* Denoise */ if ([fPictureController denoise] == 0) { [fPicSettingDenoise setStringValue: @"Off"]; @@ -2689,11 +3841,13 @@ the user is using "Custom" settings by determining the sender*/ } /* Deblock */ - if ([fPictureController deblock]) { - [fPicSettingDeblock setStringValue: @"Yes"]; + if ([fPictureController deblock] == 0) + { + [fPicSettingDeblock setStringValue: @"Off"]; } - else { - [fPicSettingDeblock setStringValue: @"No"]; + else + { + [fPicSettingDeblock setStringValue: [NSString stringWithFormat:@"%d",[fPictureController deblock]]]; } if (fTitle->job->pixel_ratio > 0) @@ -2704,7 +3858,8 @@ the user is using "Custom" settings by determining the sender*/ { [fPicSettingPAR setStringValue: @"Off"]; } - /* Set the display field for crop as per boolean */ + + /* Set the display field for crop as per boolean */ if (![fPictureController autoCrop]) { [fPicSettingAutoCrop setStringValue: @"Custom"]; @@ -2717,23 +3872,185 @@ the user is using "Custom" settings by determining the sender*/ } - -#pragma mark - -#pragma mark - Audio and Subtitles - + +#pragma mark - +#pragma mark - Audio and Subtitles +- (IBAction) audioCodecsPopUpChanged: (id) sender +{ + + NSPopUpButton * audiotrackPopUp; + NSPopUpButton * sampleratePopUp; + NSPopUpButton * bitratePopUp; + NSPopUpButton * audiocodecPopUp; + if (sender == fAudTrack1CodecPopUp) + { + audiotrackPopUp = fAudLang1PopUp; + audiocodecPopUp = fAudTrack1CodecPopUp; + sampleratePopUp = fAudTrack1RatePopUp; + bitratePopUp = fAudTrack1BitratePopUp; + } + else if (sender == fAudTrack2CodecPopUp) + { + audiotrackPopUp = fAudLang2PopUp; + audiocodecPopUp = fAudTrack2CodecPopUp; + sampleratePopUp = fAudTrack2RatePopUp; + bitratePopUp = fAudTrack2BitratePopUp; + } + else if (sender == fAudTrack3CodecPopUp) + { + audiotrackPopUp = fAudLang3PopUp; + audiocodecPopUp = fAudTrack3CodecPopUp; + sampleratePopUp = fAudTrack3RatePopUp; + bitratePopUp = fAudTrack3BitratePopUp; + } + else + { + audiotrackPopUp = fAudLang4PopUp; + audiocodecPopUp = fAudTrack4CodecPopUp; + sampleratePopUp = fAudTrack4RatePopUp; + bitratePopUp = fAudTrack4BitratePopUp; + } + + /* changing the codecs on offer may mean that we can / can't offer mono or 6ch, */ + /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */ + [self audioTrackPopUpChanged: audiotrackPopUp]; + +} + - (IBAction) setEnabledStateOfAudioMixdownControls: (id) sender { - + /* We will be setting the enabled/disabled state of each tracks audio controls based on + * the settings of the source audio for that track. We leave the samplerate and bitrate + * to audiotrackMixdownChanged + */ + + /* We will first verify that a lower track number has been selected before enabling each track + * for example, make sure a track is selected for track 1 before enabling track 2, etc. + */ + if ([fAudLang1PopUp indexOfSelectedItem] == 0) + { + [fAudLang2PopUp setEnabled: NO]; + [fAudLang2PopUp selectItemAtIndex: 0]; + } + else + { + [fAudLang2PopUp setEnabled: YES]; + } + + if ([fAudLang2PopUp indexOfSelectedItem] == 0) + { + [fAudLang3PopUp setEnabled: NO]; + [fAudLang3PopUp selectItemAtIndex: 0]; + } + else + { + [fAudLang3PopUp setEnabled: YES]; + } + if ([fAudLang3PopUp indexOfSelectedItem] == 0) + { + [fAudLang4PopUp setEnabled: NO]; + [fAudLang4PopUp selectItemAtIndex: 0]; + } + else + { + [fAudLang4PopUp setEnabled: YES]; + } /* enable/disable the mixdown text and popupbutton for audio track 1 */ + [fAudTrack1CodecPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES]; [fAudTrack1MixPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES]; - [fAudTrack1MixLabel setTextColor: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? - [NSColor disabledControlTextColor] : [NSColor controlTextColor]]; - + [fAudTrack1RatePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack1BitratePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack1DrcSlider setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack1DrcField setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES]; + if ([fAudLang1PopUp indexOfSelectedItem] == 0) + { + [fAudTrack1CodecPopUp removeAllItems]; + [fAudTrack1MixPopUp removeAllItems]; + [fAudTrack1RatePopUp removeAllItems]; + [fAudTrack1BitratePopUp removeAllItems]; + [fAudTrack1DrcSlider setFloatValue: 1.00]; + [self audioDRCSliderChanged: fAudTrack1DrcSlider]; + } + else if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_AC3) + { + [fAudTrack1RatePopUp setEnabled: NO]; + [fAudTrack1BitratePopUp setEnabled: NO]; + [fAudTrack1DrcSlider setEnabled: NO]; + [fAudTrack1DrcField setEnabled: NO]; + } + /* enable/disable the mixdown text and popupbutton for audio track 2 */ + [fAudTrack2CodecPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES]; [fAudTrack2MixPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES]; - [fAudTrack2MixLabel setTextColor: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? - [NSColor disabledControlTextColor] : [NSColor controlTextColor]]; - + [fAudTrack2RatePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack2BitratePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack2DrcSlider setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack2DrcField setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES]; + if ([fAudLang2PopUp indexOfSelectedItem] == 0) + { + [fAudTrack2CodecPopUp removeAllItems]; + [fAudTrack2MixPopUp removeAllItems]; + [fAudTrack2RatePopUp removeAllItems]; + [fAudTrack2BitratePopUp removeAllItems]; + [fAudTrack2DrcSlider setFloatValue: 1.00]; + [self audioDRCSliderChanged: fAudTrack2DrcSlider]; + } + else if ([[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_AC3) + { + [fAudTrack2RatePopUp setEnabled: NO]; + [fAudTrack2BitratePopUp setEnabled: NO]; + [fAudTrack2DrcSlider setEnabled: NO]; + [fAudTrack2DrcField setEnabled: NO]; + } + + /* enable/disable the mixdown text and popupbutton for audio track 3 */ + [fAudTrack3CodecPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack3MixPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack3RatePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack3BitratePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack3DrcSlider setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack3DrcField setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES]; + if ([fAudLang3PopUp indexOfSelectedItem] == 0) + { + [fAudTrack3CodecPopUp removeAllItems]; + [fAudTrack3MixPopUp removeAllItems]; + [fAudTrack3RatePopUp removeAllItems]; + [fAudTrack3BitratePopUp removeAllItems]; + [fAudTrack3DrcSlider setFloatValue: 1.00]; + [self audioDRCSliderChanged: fAudTrack3DrcSlider]; + } + else if ([[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_AC3) + { + [fAudTrack3RatePopUp setEnabled: NO]; + [fAudTrack3BitratePopUp setEnabled: NO]; + [fAudTrack3DrcSlider setEnabled: NO]; + [fAudTrack3DrcField setEnabled: NO]; + } + + /* enable/disable the mixdown text and popupbutton for audio track 4 */ + [fAudTrack4CodecPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack4MixPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack4RatePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack4BitratePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack4DrcSlider setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack4DrcField setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES]; + if ([fAudLang4PopUp indexOfSelectedItem] == 0) + { + [fAudTrack4CodecPopUp removeAllItems]; + [fAudTrack4MixPopUp removeAllItems]; + [fAudTrack4RatePopUp removeAllItems]; + [fAudTrack4BitratePopUp removeAllItems]; + [fAudTrack4DrcSlider setFloatValue: 1.00]; + [self audioDRCSliderChanged: fAudTrack4DrcSlider]; + } + else if ([[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_AC3) + { + [fAudTrack4RatePopUp setEnabled: NO]; + [fAudTrack4BitratePopUp setEnabled: NO]; + [fAudTrack4DrcSlider setEnabled: NO]; + [fAudTrack4DrcField setEnabled: NO]; + } + } - (IBAction) addAllAudioTracksToPopUp: (id) sender @@ -2743,15 +4060,15 @@ the user is using "Custom" settings by determining the sender*/ hb_title_t * title = (hb_title_t*) hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] ); - hb_audio_t * audio; + hb_audio_config_t * audio; [sender removeAllItems]; - [sender addItemWithTitle: _( @"None" )]; + [sender addItemWithTitle: NSLocalizedString( @"None", @"" )]; for( int i = 0; i < hb_list_count( title->list_audio ); i++ ) { - audio = (hb_audio_t *) hb_list_item( title->list_audio, i ); + audio = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, i ); [[sender menu] addItemWithTitle: - [NSString stringWithCString: audio->lang] + [NSString stringWithCString: audio->lang.description] action: NULL keyEquivalent: @""]; } [sender selectItemAtIndex: 0]; @@ -2765,8 +4082,8 @@ the user is using "Custom" settings by determining the sender*/ /* e.g. to find the first French track, pass in an NSString * of "Francais" */ /* e.g. to find the first English 5.1 AC3 track, pass in an NSString * of "English (AC3) (5.1 ch)" */ /* if no matching track is found, then selectIndexIfNotFound is used to choose which track to select instead */ - - if (searchPrefixString != NULL) + + if (searchPrefixString) { for( int i = 0; i < [sender numberOfItems]; i++ ) @@ -2790,6 +4107,97 @@ the user is using "Custom" settings by determining the sender*/ } } +- (IBAction) audioAddAudioTrackCodecs: (id)sender +{ + int format = [fDstFormatPopUp indexOfSelectedItem]; + + /* setup pointers to the appropriate popups for the correct track */ + NSPopUpButton * audiocodecPopUp; + NSPopUpButton * audiotrackPopUp; + if (sender == fAudTrack1CodecPopUp) + { + audiotrackPopUp = fAudLang1PopUp; + audiocodecPopUp = fAudTrack1CodecPopUp; + } + else if (sender == fAudTrack2CodecPopUp) + { + audiotrackPopUp = fAudLang2PopUp; + audiocodecPopUp = fAudTrack2CodecPopUp; + } + else if (sender == fAudTrack3CodecPopUp) + { + audiotrackPopUp = fAudLang3PopUp; + audiocodecPopUp = fAudTrack3CodecPopUp; + } + else + { + audiotrackPopUp = fAudLang4PopUp; + audiocodecPopUp = fAudTrack4CodecPopUp; + } + + [audiocodecPopUp removeAllItems]; + /* Make sure "None" isnt selected in the source track */ + if ([audiotrackPopUp indexOfSelectedItem] > 0) + { + [audiocodecPopUp setEnabled:YES]; + NSMenuItem *menuItem; + /* We setup our appropriate popups for codecs and put the int value in the popup tag for easy retrieval */ + switch( format ) + { + case 0: + /* MP4 */ + // AAC + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_FAAC]; + + // AC3 Passthru + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_AC3]; + break; + + case 1: + /* MKV */ + // AAC + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_FAAC]; + // AC3 Passthru + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_AC3]; + // MP3 + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_LAME]; + // Vorbis + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"Vorbis (vorbis)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_VORBIS]; + break; + + case 2: + /* AVI */ + // MP3 + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_LAME]; + // AC3 Passthru + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_AC3]; + break; + + case 3: + /* OGM */ + // Vorbis + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"Vorbis (vorbis)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_VORBIS]; + // MP3 + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_LAME]; + break; + } + [audiocodecPopUp selectItemAtIndex:0]; + } + else + { + [audiocodecPopUp setEnabled:NO]; + } +} - (IBAction) audioTrackPopUpChanged: (id) sender { @@ -2799,184 +4207,209 @@ the user is using "Custom" settings by determining the sender*/ - (IBAction) audioTrackPopUpChanged: (id) sender mixdownToUse: (int) mixdownToUse { - + /* make sure we have a selected title before continuing */ if (fTitle == NULL) return; - - /* find out if audio track 1 or 2 was changed - this is passed to us in the tag of the sender */ - /* the sender will have been either fAudLang1PopUp (tag = 0) or fAudLang2PopUp (tag = 1) */ - int thisAudio = [sender tag]; - - /* get the index of the selected audio */ - int thisAudioIndex = [sender indexOfSelectedItem] - 1; - -#if 0 - /* Handbrake can't currently cope with ripping the same source track twice */ - /* So, if this audio is also selected in the other audio track popup, set that popup's selection to "none" */ - /* get a reference to the two audio track popups */ - NSPopUpButton * thisAudioPopUp = (thisAudio == 1 ? fAudLang2PopUp : fAudLang1PopUp); - NSPopUpButton * otherAudioPopUp = (thisAudio == 1 ? fAudLang1PopUp : fAudLang2PopUp); - /* if the same track is selected in the other audio popup, then select "none" in that popup */ - /* unless, of course, both are selected as "none!" */ - if ([thisAudioPopUp indexOfSelectedItem] != 0 && [thisAudioPopUp indexOfSelectedItem] == [otherAudioPopUp indexOfSelectedItem]) { - [otherAudioPopUp selectItemAtIndex: 0]; - [self audioTrackPopUpChanged: otherAudioPopUp]; + /* if the sender is the lanaguage popup and there is nothing in the codec popup, lets call + * audioAddAudioTrackCodecs on the codec popup to populate it properly before moving on + */ + if (sender == fAudLang1PopUp && [[fAudTrack1CodecPopUp menu] numberOfItems] == 0) + { + [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp]; + } + if (sender == fAudLang2PopUp && [[fAudTrack2CodecPopUp menu] numberOfItems] == 0) + { + [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp]; + } + if (sender == fAudLang3PopUp && [[fAudTrack3CodecPopUp menu] numberOfItems] == 0) + { + [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp]; + } + if (sender == fAudLang4PopUp && [[fAudTrack4CodecPopUp menu] numberOfItems] == 0) + { + [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp]; } -#endif - - /* pointer for the hb_audio_s struct we will use later on */ - hb_audio_t * audio; - - /* find out what the currently-selected output audio codec is */ - int format = [fDstFormatPopUp indexOfSelectedItem]; - int codecs = [fDstCodecsPopUp indexOfSelectedItem]; - int acodec = FormatSettings[format][codecs] & HB_ACODEC_MASK; - /*HACK: Lets setup a convenience variable to decide whether or not to allow aac hybrid (aac + ac3 passthru )*/ - bool mp4AacAc3; - if (format == 0 && codecs == 2) // if mp4 and aac + ac3 + /* Now lets make the sender the appropriate Audio Track popup from this point on */ + if (sender == fAudTrack1CodecPopUp || sender == fAudTrack1MixPopUp) { - mp4AacAc3 = 1; + sender = fAudLang1PopUp; } - else + if (sender == fAudTrack2CodecPopUp || sender == fAudTrack2MixPopUp) { - mp4AacAc3 = 0; + sender = fAudLang2PopUp; } - - /* pointer to this track's mixdown NSPopUpButton */ - NSTextField * mixdownTextField; + if (sender == fAudTrack3CodecPopUp || sender == fAudTrack3MixPopUp) + { + sender = fAudLang3PopUp; + } + if (sender == fAudTrack4CodecPopUp || sender == fAudTrack4MixPopUp) + { + sender = fAudLang4PopUp; + } + + /* pointer to this track's mixdown, codec, sample rate and bitrate NSPopUpButton's */ NSPopUpButton * mixdownPopUp; - - /* find our mixdown NSTextField and NSPopUpButton */ - if (thisAudio == 0) + NSPopUpButton * audiocodecPopUp; + NSPopUpButton * sampleratePopUp; + NSPopUpButton * bitratePopUp; + if (sender == fAudLang1PopUp) { - mixdownTextField = fAudTrack1MixLabel; mixdownPopUp = fAudTrack1MixPopUp; + audiocodecPopUp = fAudTrack1CodecPopUp; + sampleratePopUp = fAudTrack1RatePopUp; + bitratePopUp = fAudTrack1BitratePopUp; } - else + else if (sender == fAudLang2PopUp) { - mixdownTextField = fAudTrack2MixLabel; mixdownPopUp = fAudTrack2MixPopUp; + audiocodecPopUp = fAudTrack2CodecPopUp; + sampleratePopUp = fAudTrack2RatePopUp; + bitratePopUp = fAudTrack2BitratePopUp; + } + else if (sender == fAudLang3PopUp) + { + mixdownPopUp = fAudTrack3MixPopUp; + audiocodecPopUp = fAudTrack3CodecPopUp; + sampleratePopUp = fAudTrack3RatePopUp; + bitratePopUp = fAudTrack3BitratePopUp; + } + else + { + mixdownPopUp = fAudTrack4MixPopUp; + audiocodecPopUp = fAudTrack4CodecPopUp; + sampleratePopUp = fAudTrack4RatePopUp; + bitratePopUp = fAudTrack4BitratePopUp; } - /* delete the previous audio mixdown options */ - [mixdownPopUp removeAllItems]; + /* get the index of the selected audio Track*/ + int thisAudioIndex = [sender indexOfSelectedItem] - 1; + + /* pointer for the hb_audio_s struct we will use later on */ + hb_audio_config_t * audio; + int acodec; /* check if the audio mixdown controls need their enabled state changing */ - [self setEnabledStateOfAudioMixdownControls: NULL]; + [self setEnabledStateOfAudioMixdownControls:nil]; if (thisAudioIndex != -1) { /* get the audio */ - audio = (hb_audio_t *) hb_list_item( fTitle->list_audio, thisAudioIndex ); + audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, thisAudioIndex );// Should "fTitle" be title and be setup ? + + /* actually manipulate the proper mixdowns here */ + /* delete the previous audio mixdown options */ + [mixdownPopUp removeAllItems]; + + acodec = [[audiocodecPopUp selectedItem] tag]; + if (audio != NULL) { /* find out if our selected output audio codec supports mono and / or 6ch */ /* we also check for an input codec of AC3 or DCA, - as they are the only libraries able to do the mixdown to mono / conversion to 6-ch */ + as they are the only libraries able to do the mixdown to mono / conversion to 6-ch */ /* audioCodecsSupportMono and audioCodecsSupport6Ch are the same for now, - but this may change in the future, so they are separated for flexibility */ - int audioCodecsSupportMono = ((audio->codec == HB_ACODEC_AC3 || - audio->codec == HB_ACODEC_DCA) && acodec == HB_ACODEC_FAAC); - int audioCodecsSupport6Ch = ((audio->codec == HB_ACODEC_AC3 || - audio->codec == HB_ACODEC_DCA) && (acodec == HB_ACODEC_FAAC || - acodec == HB_ACODEC_VORBIS)); - + but this may change in the future, so they are separated for flexibility */ + int audioCodecsSupportMono = + (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) && + (acodec != HB_ACODEC_LAME); + int audioCodecsSupport6Ch = + (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) && + (acodec != HB_ACODEC_LAME); + /* check for AC-3 passthru */ - if (audio->codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3) + if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3) { - - [[mixdownPopUp menu] addItemWithTitle: - [NSString stringWithCString: "AC3 Passthru"] - action: NULL keyEquivalent: @""]; + + NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: + [NSString stringWithCString: "AC3 Passthru"] + action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_AC3]; } else { - + /* add the appropriate audio mixdown menuitems to the popupbutton */ /* in each case, we set the new menuitem's tag to be the amixdown value for that mixdown, - so that we can reference the mixdown later */ - + so that we can reference the mixdown later */ + /* keep a track of the min and max mixdowns we used, so we can select the best match later */ int minMixdownUsed = 0; int maxMixdownUsed = 0; /* get the input channel layout without any lfe channels */ - int layout = audio->input_channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK; - + int layout = audio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK; + /* do we want to add a mono option? */ - if (!mp4AacAc3 && audioCodecsSupportMono == 1) { + if (audioCodecsSupportMono == 1) + { NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: - [NSString stringWithCString: hb_audio_mixdowns[0].human_readable_name] - action: NULL keyEquivalent: @""]; + [NSString stringWithCString: hb_audio_mixdowns[0].human_readable_name] + action: NULL keyEquivalent: @""]; [menuItem setTag: hb_audio_mixdowns[0].amixdown]; if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[0].amixdown; maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[0].amixdown); } - + /* do we want to add a stereo option? */ /* offer stereo if we have a mono source and non-mono-supporting codecs, as otherwise we won't have a mixdown at all */ /* also offer stereo if we have a stereo-or-better source */ - if (((!mp4AacAc3 || audio->codec == HB_ACODEC_MPGA || audio->codec == HB_ACODEC_LPCM || audio->codec == HB_ACODEC_DCA) && ((layout == HB_INPUT_CH_LAYOUT_MONO && audioCodecsSupportMono == 0) || layout >= HB_INPUT_CH_LAYOUT_STEREO))) { + if ((layout == HB_INPUT_CH_LAYOUT_MONO && audioCodecsSupportMono == 0) || layout >= HB_INPUT_CH_LAYOUT_STEREO) + { NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: - [NSString stringWithCString: hb_audio_mixdowns[1].human_readable_name] - action: NULL keyEquivalent: @""]; + [NSString stringWithCString: hb_audio_mixdowns[1].human_readable_name] + action: NULL keyEquivalent: @""]; [menuItem setTag: hb_audio_mixdowns[1].amixdown]; if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[1].amixdown; maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[1].amixdown); } - + /* do we want to add a dolby surround (DPL1) option? */ - if (!mp4AacAc3 && (layout == HB_INPUT_CH_LAYOUT_3F1R || layout == HB_INPUT_CH_LAYOUT_3F2R || layout == HB_INPUT_CH_LAYOUT_DOLBY)) { + if (layout == HB_INPUT_CH_LAYOUT_3F1R || layout == HB_INPUT_CH_LAYOUT_3F2R || layout == HB_INPUT_CH_LAYOUT_DOLBY) + { NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: - [NSString stringWithCString: hb_audio_mixdowns[2].human_readable_name] - action: NULL keyEquivalent: @""]; + [NSString stringWithCString: hb_audio_mixdowns[2].human_readable_name] + action: NULL keyEquivalent: @""]; [menuItem setTag: hb_audio_mixdowns[2].amixdown]; if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[2].amixdown; maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[2].amixdown); } - + /* do we want to add a dolby pro logic 2 (DPL2) option? */ - if ((!mp4AacAc3 || audio->codec == HB_ACODEC_DCA) && layout == HB_INPUT_CH_LAYOUT_3F2R) { + if (layout == HB_INPUT_CH_LAYOUT_3F2R) + { NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: - [NSString stringWithCString: hb_audio_mixdowns[3].human_readable_name] - action: NULL keyEquivalent: @""]; + [NSString stringWithCString: hb_audio_mixdowns[3].human_readable_name] + action: NULL keyEquivalent: @""]; [menuItem setTag: hb_audio_mixdowns[3].amixdown]; if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[3].amixdown; maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[3].amixdown); } - + /* do we want to add a 6-channel discrete option? */ - if (!mp4AacAc3 && (audioCodecsSupport6Ch == 1 && layout == HB_INPUT_CH_LAYOUT_3F2R && (audio->input_channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE))) { + if (audioCodecsSupport6Ch == 1 && layout == HB_INPUT_CH_LAYOUT_3F2R && (audio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE)) + { NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: - [NSString stringWithCString: hb_audio_mixdowns[4].human_readable_name] - action: NULL keyEquivalent: @""]; + [NSString stringWithCString: hb_audio_mixdowns[4].human_readable_name] + action: NULL keyEquivalent: @""]; [menuItem setTag: hb_audio_mixdowns[4].amixdown]; if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[4].amixdown; maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[4].amixdown); } - + /* do we want to add an AC-3 passthrough option? */ - if (audio->codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3) { + if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3) + { NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: - [NSString stringWithCString: hb_audio_mixdowns[5].human_readable_name] - action: NULL keyEquivalent: @""]; - [menuItem setTag: hb_audio_mixdowns[5].amixdown]; + [NSString stringWithCString: hb_audio_mixdowns[5].human_readable_name] + action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_AC3]; if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown; maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown); } - - /* do we want to add the DPLII+AC3 passthrough option? */ - if (mp4AacAc3 && audio->codec == HB_ACODEC_AC3) { - NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: - [NSString stringWithCString: hb_audio_mixdowns[6].human_readable_name] - action: NULL keyEquivalent: @""]; - [menuItem setTag: hb_audio_mixdowns[6].amixdown]; - if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[6].amixdown; - maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[6].amixdown); - } + /* auto-select the best mixdown based on our saved mixdown preference */ /* for now, this is hard-coded to a "best" mixdown of HB_AMIXDOWN_DOLBYPLII */ @@ -2994,34 +4427,104 @@ the user is using "Custom" settings by determining the sender*/ } /* if useMixdown > maxMixdownUsed, then use maxMixdownUsed */ - if (useMixdown > maxMixdownUsed) useMixdown = maxMixdownUsed; - + if (useMixdown > maxMixdownUsed) + { + useMixdown = maxMixdownUsed; + } + /* if useMixdown < minMixdownUsed, then use minMixdownUsed */ - if (useMixdown < minMixdownUsed) useMixdown = minMixdownUsed; - + if (useMixdown < minMixdownUsed) + { + useMixdown = minMixdownUsed; + } + /* select the (possibly-amended) preferred mixdown */ [mixdownPopUp selectItemWithTag: useMixdown]; - - /* lets call the audioTrackMixdownChanged method here to determine appropriate bitrates, etc. */ - [self audioTrackMixdownChanged: NULL]; - } + } + /* In the case of a source track that is not AC3 and the user tries to use AC3 Passthru (which does not work) + * we force the Audio Codec choice back to a workable codec. We use MP3 for avi and aac for all + * other containers. + */ + if (audio->in.codec != HB_ACODEC_AC3 && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_AC3) + { + /* If we are using the avi container, we select MP3 as there is no aac available*/ + if ([[fDstFormatPopUp selectedItem] tag] == HB_MUX_AVI) + { + [audiocodecPopUp selectItemWithTag: HB_ACODEC_LAME]; + } + else + { + [audiocodecPopUp selectItemWithTag: HB_ACODEC_FAAC]; + } + } + /* Setup our samplerate and bitrate popups we will need based on mixdown */ + [self audioTrackMixdownChanged: mixdownPopUp]; } - + + } + if( [fDstFormatPopUp indexOfSelectedItem] == 0 ) + { + [self autoSetM4vExtension: sender]; + [self shouldEnableHttpMp4CheckBox: sender]; } - - /* see if the new audio track choice will change the bitrate we need */ - [self calculateBitrate: sender]; - } + - (IBAction) audioTrackMixdownChanged: (id) sender { - - /* find out what the currently-selected output audio codec is */ - int format = [fDstFormatPopUp indexOfSelectedItem]; - int codecs = [fDstCodecsPopUp indexOfSelectedItem]; - int acodec = FormatSettings[format][codecs] & HB_ACODEC_MASK; + int acodec; + /* setup pointers to all of the other audio track controls + * we will need later + */ + NSPopUpButton * mixdownPopUp; + NSPopUpButton * sampleratePopUp; + NSPopUpButton * bitratePopUp; + NSPopUpButton * audiocodecPopUp; + NSPopUpButton * audiotrackPopUp; + NSSlider * drcSlider; + NSTextField * drcField; + if (sender == fAudTrack1MixPopUp) + { + audiotrackPopUp = fAudLang1PopUp; + audiocodecPopUp = fAudTrack1CodecPopUp; + mixdownPopUp = fAudTrack1MixPopUp; + sampleratePopUp = fAudTrack1RatePopUp; + bitratePopUp = fAudTrack1BitratePopUp; + drcSlider = fAudTrack1DrcSlider; + drcField = fAudTrack1DrcField; + } + else if (sender == fAudTrack2MixPopUp) + { + audiotrackPopUp = fAudLang2PopUp; + audiocodecPopUp = fAudTrack2CodecPopUp; + mixdownPopUp = fAudTrack2MixPopUp; + sampleratePopUp = fAudTrack2RatePopUp; + bitratePopUp = fAudTrack2BitratePopUp; + drcSlider = fAudTrack2DrcSlider; + drcField = fAudTrack2DrcField; + } + else if (sender == fAudTrack3MixPopUp) + { + audiotrackPopUp = fAudLang3PopUp; + audiocodecPopUp = fAudTrack3CodecPopUp; + mixdownPopUp = fAudTrack3MixPopUp; + sampleratePopUp = fAudTrack3RatePopUp; + bitratePopUp = fAudTrack3BitratePopUp; + drcSlider = fAudTrack3DrcSlider; + drcField = fAudTrack3DrcField; + } + else + { + audiotrackPopUp = fAudLang4PopUp; + audiocodecPopUp = fAudTrack4CodecPopUp; + mixdownPopUp = fAudTrack4MixPopUp; + sampleratePopUp = fAudTrack4RatePopUp; + bitratePopUp = fAudTrack4BitratePopUp; + drcSlider = fAudTrack4DrcSlider; + drcField = fAudTrack4DrcField; + } + acodec = [[audiocodecPopUp selectedItem] tag]; /* storage variable for the min and max bitrate allowed for this codec */ int minbitrate; int maxbitrate; @@ -3030,10 +4533,7 @@ the user is using "Custom" settings by determining the sender*/ { case HB_ACODEC_FAAC: /* check if we have a 6ch discrete conversion in either audio track */ - if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH || - [[fAudTrack2MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH || - [[fAudTrack1MixPopUp selectedItem] tag] == HB_AMIXDOWN_AC3 || - [[fAudTrack2MixPopUp selectedItem] tag] == HB_AMIXDOWN_AC3) + if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH) { /* FAAC is happy using our min bitrate of 32 kbps, even for 6ch */ minbitrate = 32; @@ -3050,16 +4550,16 @@ the user is using "Custom" settings by determining the sender*/ maxbitrate = 160; break; } - - case HB_ACODEC_LAME: + + case HB_ACODEC_LAME: /* Lame is happy using our min bitrate of 32 kbps */ minbitrate = 32; /* Lame won't encode if the bitrate is higher than 320 kbps */ maxbitrate = 320; break; - - case HB_ACODEC_VORBIS: - if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH || [[fAudTrack2MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH) + + case HB_ACODEC_VORBIS: + if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH) { /* Vorbis causes a crash if we use a bitrate below 192 kbps with 6 channel */ minbitrate = 192; @@ -3069,54 +4569,137 @@ the user is using "Custom" settings by determining the sender*/ } else { - /* Vorbis causes a crash if we use a bitrate below 48 kbps */ - minbitrate = 48; - /* Vorbis can cope with 384 kbps quite happily, even for stereo */ - maxbitrate = 384; - break; + /* Vorbis causes a crash if we use a bitrate below 48 kbps */ + minbitrate = 48; + /* Vorbis can cope with 384 kbps quite happily, even for stereo */ + maxbitrate = 384; + break; } - - default: + + default: /* AC3 passthru disables the bitrate dropdown anyway, so we might as well just use the min and max bitrate */ minbitrate = 32; maxbitrate = 384; - + } - - [fAudBitratePopUp removeAllItems]; - - for( int i = 0; i < hb_audio_bitrates_count; i++ ) + + /* make sure we have a selected title before continuing */ + if (fTitle == NULL) return; + /* get the audio so we can find out what input rates are*/ + hb_audio_config_t * audio; + audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, [audiotrackPopUp indexOfSelectedItem] - 1 ); + int inputbitrate = audio->in.bitrate / 1000; + int inputsamplerate = audio->in.samplerate; + + if ([[mixdownPopUp selectedItem] tag] != HB_ACODEC_AC3) { - if (hb_audio_bitrates[i].rate >= minbitrate && hb_audio_bitrates[i].rate <= maxbitrate) + [bitratePopUp removeAllItems]; + + for( int i = 0; i < hb_audio_bitrates_count; i++ ) + { + if (hb_audio_bitrates[i].rate >= minbitrate && hb_audio_bitrates[i].rate <= maxbitrate) + { + /* add a new menuitem for this bitrate */ + NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle: + [NSString stringWithCString: hb_audio_bitrates[i].string] + action: NULL keyEquivalent: @""]; + /* set its tag to be the actual bitrate as an integer, so we can retrieve it later */ + [menuItem setTag: hb_audio_bitrates[i].rate]; + } + } + + /* select the default bitrate (but use 384 for 6-ch AAC) */ + if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH) + { + [bitratePopUp selectItemWithTag: 384]; + } + else { - /* add a new menuitem for this bitrate */ - NSMenuItem *menuItem = [[fAudBitratePopUp menu] addItemWithTitle: - [NSString stringWithCString: hb_audio_bitrates[i].string] - action: NULL keyEquivalent: @""]; - /* set its tag to be the actual bitrate as an integer, so we can retrieve it later */ - [menuItem setTag: hb_audio_bitrates[i].rate]; + [bitratePopUp selectItemWithTag: hb_audio_bitrates[hb_audio_bitrates_default].rate]; } } - - /* select the default bitrate (but use 384 for 6-ch AAC) */ - if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH || - [[fAudTrack2MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH || - [[fAudTrack1MixPopUp selectedItem] tag] == HB_AMIXDOWN_AC3 || - [[fAudTrack2MixPopUp selectedItem] tag] == HB_AMIXDOWN_AC3) + /* populate and set the sample rate popup */ + /* Audio samplerate */ + [sampleratePopUp removeAllItems]; + /* we create a same as source selection (Auto) so that we can choose to use the input sample rate */ + NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle: @"Auto" action: NULL keyEquivalent: @""]; + [menuItem setTag: inputsamplerate]; + + for( int i = 0; i < hb_audio_rates_count; i++ ) + { + NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle: + [NSString stringWithCString: hb_audio_rates[i].string] + action: NULL keyEquivalent: @""]; + [menuItem setTag: hb_audio_rates[i].rate]; + } + /* We use the input sample rate as the default sample rate as downsampling just makes audio worse + * and there is no compelling reason to use anything else as default, though the users default + * preset will likely override any setting chosen here. + */ + [sampleratePopUp selectItemWithTag: inputsamplerate]; + + + /* Since AC3 Pass Thru uses the input ac3 bitrate and sample rate, we get the input tracks + * bitrate and dispay it in the bitrate popup even though libhb happily ignores any bitrate input from + * the gui. We do this for better user feedback in the audio tab as well as the queue for the most part + */ + if ([[mixdownPopUp selectedItem] tag] == HB_ACODEC_AC3) { - [fAudBitratePopUp selectItemWithTag: 384]; + + /* lets also set the bitrate popup to the input bitrate as thats what passthru will use */ + [bitratePopUp removeAllItems]; + NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle: + [NSString stringWithFormat:@"%d", inputbitrate] + action: NULL keyEquivalent: @""]; + [menuItem setTag: inputbitrate]; + /* For ac3 passthru we disable the sample rate and bitrate popups as well as the drc slider*/ + [bitratePopUp setEnabled: NO]; + [sampleratePopUp setEnabled: NO]; + + [drcSlider setFloatValue: 1.00]; + [self audioDRCSliderChanged: drcSlider]; + [drcSlider setEnabled: NO]; + [drcField setEnabled: NO]; } else { - [fAudBitratePopUp selectItemWithTag: hb_audio_bitrates[hb_audio_bitrates_default].rate]; + [sampleratePopUp setEnabled: YES]; + [bitratePopUp setEnabled: YES]; + [drcSlider setEnabled: YES]; + [drcField setEnabled: YES]; } - +[self calculateBitrate:nil]; } - (IBAction) audioDRCSliderChanged: (id) sender { - [fAudDrcField setStringValue: [NSString stringWithFormat: @"%.2f", [fAudDrcSlider floatValue]]]; - [self customSettingUsed: sender]; + NSSlider * drcSlider; + NSTextField * drcField; + if (sender == fAudTrack1DrcSlider) + { + drcSlider = fAudTrack1DrcSlider; + drcField = fAudTrack1DrcField; + } + else if (sender == fAudTrack2DrcSlider) + { + drcSlider = fAudTrack2DrcSlider; + drcField = fAudTrack2DrcField; + } + else if (sender == fAudTrack3DrcSlider) + { + drcSlider = fAudTrack3DrcSlider; + drcField = fAudTrack3DrcField; + } + else + { + drcSlider = fAudTrack4DrcSlider; + drcField = fAudTrack4DrcField; + } + [drcField setStringValue: [NSString stringWithFormat: @"%.2f", [drcSlider floatValue]]]; + /* For now, do not call this until we have an intelligent way to determine audio track selections + * compared to presets + */ + //[self customSettingUsed: sender]; } - (IBAction) subtitleSelectionChanged: (id) sender @@ -3201,39 +4784,116 @@ the user is using "Custom" settings by determining the sender*/ [fPictureController showPanelInWindow:fWindow forTitle:title]; } -#pragma mark - -#pragma mark Preset Outline View Methods -#pragma mark - Required -/* These are required by the NSOutlineView Datasource Delegate */ +#pragma mark - +#pragma mark Preset Outline View Methods +#pragma mark - Required +/* These are required by the NSOutlineView Datasource Delegate */ + + +/* used to specify the number of levels to show for each item */ +- (int)outlineView:(NSOutlineView *)fPresetsOutlineView numberOfChildrenOfItem:(id)item +{ + /* currently use no levels to test outline view viability */ + if (item == nil) // for an outline view the root level of the hierarchy is always nil + { + return [UserPresets count]; + } + else + { + /* we need to return the count of the array in ChildrenArray for this folder */ + NSArray *children = nil; + children = [item objectForKey:@"ChildrenArray"]; + if ([children count] > 0) + { + return [children count]; + } + else + { + return 0; + } + } +} + /* We use this to deterimine children of an item */ -- (id)outlineView:(NSOutlineView *)fPresetsOutlineView child:(NSInteger)index ofItem:(id)item +- (id)outlineView:(NSOutlineView *)fPresetsOutlineView child:(int)index ofItem:(id)item { -if (item == nil) - return [UserPresets objectAtIndex:index]; + + /* we need to return the count of the array in ChildrenArray for this folder */ + NSArray *children = nil; + if (item == nil) + { + children = UserPresets; + } + else + { + if ([item objectForKey:@"ChildrenArray"]) + { + children = [item objectForKey:@"ChildrenArray"]; + } + } + if ((children == nil) || ([children count] <= index)) + { + return nil; + } + else + { + return [children objectAtIndex:index]; + } + // We are only one level deep, so we can't be asked about children - NSAssert (NO, @"Presets View outlineView:child:ofItem: currently can't handle nested items."); - return nil; + //NSAssert (NO, @"Presets View outlineView:child:ofItem: currently can't handle nested items."); + //return nil; } + /* We use this to determine if an item should be expandable */ - (BOOL)outlineView:(NSOutlineView *)fPresetsOutlineView isItemExpandable:(id)item { - - /* For now, we maintain one level, so set to no - * when nested, we set to yes for any preset "folders" - */ - return NO; - -} -/* used to specify the number of levels to show for each item */ -- (int)outlineView:(NSOutlineView *)fPresetsOutlineView numberOfChildrenOfItem:(id)item -{ - /* currently use no levels to test outline view viability */ + + /* we need to return the count of the array in ChildrenArray for this folder */ + NSArray *children= nil; if (item == nil) - return [UserPresets count]; + { + children = UserPresets; + } + else + { + if ([item objectForKey:@"ChildrenArray"]) + { + children = [item objectForKey:@"ChildrenArray"]; + } + } + + /* To deterimine if an item should show a disclosure triangle + * we could do it by the children count as so: + * if ([children count] < 1) + * However, lets leave the triangle show even if there are no + * children to help indicate a folder, just like folder in the + * finder can show a disclosure triangle even when empty + */ + + /* We need to determine if the item is a folder */ + if ([[item objectForKey:@"Folder"] intValue] == 1) + { + return YES; + } else - return 0; + { + return NO; + } + +} + +- (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item +{ + // Our outline view has no levels, but we can still expand every item. Doing so + // just makes the row taller. See heightOfRowByItem below. +//return ![(HBQueueOutlineView*)outlineView isDragging]; + +return YES; } + + /* Used to tell the outline view which information is to be displayed per item */ - (id)outlineView:(NSOutlineView *)fPresetsOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { @@ -3245,7 +4905,8 @@ if (item == nil) } else { - return @"something"; + //return @""; + return nil; } } @@ -3255,7 +4916,6 @@ if (item == nil) { if ([[tableColumn identifier] isEqualToString:@"PresetName"]) { - NSDictionary *userPresetDict = item; NSFont *txtFont; NSColor *fontColor; NSColor *shadowColor; @@ -3264,12 +4924,12 @@ if (item == nil) if ([fPresetsOutlineView selectedRow] == [fPresetsOutlineView rowForItem:item]) { - fontColor = [NSColor whiteColor]; + fontColor = [NSColor blackColor]; shadowColor = [NSColor colorWithDeviceRed:(127.0/255.0) green:(140.0/255.0) blue:(160.0/255.0) alpha:1.0]; } else { - if ([[userPresetDict objectForKey:@"Type"] intValue] == 0) + if ([[item objectForKey:@"Type"] intValue] == 0) { fontColor = [NSColor blueColor]; } @@ -3277,15 +4937,21 @@ if (item == nil) { fontColor = [NSColor blackColor]; } - shadowColor = nil; + /* check to see if its a folder */ + //if ([[item objectForKey:@"Folder"] intValue] == 1) + //{ + //fontColor = [NSColor greenColor]; + //} + + } /* We use Bold Text for the HB Default */ - if ([[userPresetDict objectForKey:@"Default"] intValue] == 1)// 1 is HB default + if ([[item objectForKey:@"Default"] intValue] == 1)// 1 is HB default { txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]]; } /* We use Bold Text for the User Specified Default */ - if ([[userPresetDict objectForKey:@"Default"] intValue] == 2)// 2 is User default + if ([[item objectForKey:@"Default"] intValue] == 2)// 2 is User default { txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]]; } @@ -3298,7 +4964,7 @@ if (item == nil) } /* We use this to edit the name field in the outline view */ -- (void)outlineView:(NSOutlineView *)fPresetsOutlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item +- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { if ([[tableColumn identifier] isEqualToString:@"PresetName"]) { @@ -3306,17 +4972,10 @@ if (item == nil) theRecord = item; [theRecord setObject:object forKey:@"PresetName"]; - /* We Sort the Presets By Factory or Custom */ - NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type" - ascending:YES] autorelease]; - /* We Sort the Presets Alphabetically by name */ - NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName" - ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease]; - NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil]; - NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors]; - [UserPresets setArray:sortedArray]; - /* We Reload the New Table data for presets */ - //[fPresetsOutlineView reloadData]; + + [self sortPresets]; + + [fPresetsOutlineView reloadData]; /* We save all of the preset data here */ [self savePreset]; } @@ -3329,9 +4988,9 @@ if (item == nil) /* initialize the tooltip contents variable */ NSString *loc_tip; /* if there is a description for the preset, we show it in the tooltip */ - if ([item valueForKey:@"PresetDescription"]) + if ([item objectForKey:@"PresetDescription"]) { - loc_tip = [NSString stringWithFormat: @"%@",[item valueForKey:@"PresetDescription"]]; + loc_tip = [item objectForKey:@"PresetDescription"]; return (loc_tip); } else @@ -3342,28 +5001,140 @@ if (item == nil) //} } +#pragma mark - +#pragma mark Preset Outline View Methods (dragging related) + + +- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard +{ + // Dragging is only allowed for custom presets. + //[[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Default"] intValue] != 1 + if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Type"] intValue] == 0) // 0 is built in preset + { + return NO; + } + // Don't retain since this is just holding temporaral drag information, and it is + //only used during a drag! We could put this in the pboard actually. + fDraggedNodes = items; + // Provide data for our custom type, and simple NSStrings. + [pboard declareTypes:[NSArray arrayWithObjects: DragDropSimplePboardType, nil] owner:self]; + + // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!. + [pboard setData:[NSData data] forType:DragDropSimplePboardType]; + + return YES; +} + +- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id )info proposedItem:(id)item proposedChildIndex:(int)index +{ + + // Don't allow dropping ONTO an item since they can't really contain any children. + + BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex; + if (isOnDropTypeProposal) + return NSDragOperationNone; + + // Don't allow dropping INTO an item since they can't really contain any children as of yet. + if (item != nil) + { + index = [fPresetsOutlineView rowForItem: item] + 1; + item = nil; + } + + // Don't allow dropping into the Built In Presets. + if (index < presetCurrentBuiltInCount) + { + return NSDragOperationNone; + index = MAX (index, presetCurrentBuiltInCount); + } + + [outlineView setDropItem:item dropChildIndex:index]; + return NSDragOperationGeneric; +} + + + +- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id )info item:(id)item childIndex:(int)index +{ + /* first, lets see if we are dropping into a folder */ + if ([[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] && [[[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] intValue] == 1) // if its a folder + { + NSMutableArray *childrenArray = [[NSMutableArray alloc] init]; + childrenArray = [[fPresetsOutlineView itemAtRow:index] objectForKey:@"ChildrenArray"]; + [childrenArray addObject:item]; + [[fPresetsOutlineView itemAtRow:index] setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"]; + [childrenArray autorelease]; + } + else // We are not, so we just move the preset into the existing array + { + NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet]; + id obj; + NSEnumerator *enumerator = [fDraggedNodes objectEnumerator]; + while (obj = [enumerator nextObject]) + { + [moveItems addIndex:[UserPresets indexOfObject:obj]]; + } + // Successful drop, lets rearrange the view and save it all + [self moveObjectsInPresetsArray:UserPresets fromIndexes:moveItems toIndex: index]; + } + [fPresetsOutlineView reloadData]; + [self savePreset]; + return YES; +} + +- (void)moveObjectsInPresetsArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(unsigned)insertIndex +{ + unsigned index = [indexSet lastIndex]; + unsigned aboveInsertIndexCount = 0; + + while (index != NSNotFound) + { + unsigned removeIndex; + + if (index >= insertIndex) + { + removeIndex = index + aboveInsertIndexCount; + aboveInsertIndexCount++; + } + else + { + removeIndex = index; + insertIndex--; + } + + id object = [[array objectAtIndex:removeIndex] retain]; + [array removeObjectAtIndex:removeIndex]; + [array insertObject:object atIndex:insertIndex]; + [object release]; + + index = [indexSet indexLessThanIndex:index]; + } +} + + #pragma mark - Functional Preset NSOutlineView Methods - (IBAction)selectPreset:(id)sender { - if ([fPresetsOutlineView selectedRow] >= 0) + if ([fPresetsOutlineView selectedRow] >= 0 && [[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] != 1) { chosenPreset = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]; - /* we set the preset display field in main window here */ - [fPresetSelectedDisplay setStringValue: [NSString stringWithFormat: @"%@",[chosenPreset valueForKey:@"PresetName"]]]; + [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]]; + if ([[chosenPreset objectForKey:@"Default"] intValue] == 1) { - [fPresetSelectedDisplay setStringValue: [NSString stringWithFormat: @"%@ (Default)",[chosenPreset valueForKey:@"PresetName"]]]; + [fPresetSelectedDisplay setStringValue:[NSString stringWithFormat:@"%@ (Default)", [chosenPreset objectForKey:@"PresetName"]]]; } else { - [fPresetSelectedDisplay setStringValue: [NSString stringWithFormat: @"%@",[chosenPreset valueForKey:@"PresetName"]]]; + [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]]; } + /* File Format */ - [fDstFormatPopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"FileFormat"]]]; - [self formatPopUpChanged: NULL]; + [fDstFormatPopUp selectItemWithTitle:[chosenPreset objectForKey:@"FileFormat"]]; + [self formatPopUpChanged:nil]; /* Chapter Markers*/ [fCreateChapterMarkers setState:[[chosenPreset objectForKey:@"ChapterMarkers"] intValue]]; @@ -3371,61 +5142,67 @@ if (item == nil) [fDstMp4LargeFileCheck setState:[[chosenPreset objectForKey:@"Mp4LargeFile"] intValue]]; /* Mux mp4 with http optimization */ [fDstMp4HttpOptFileCheck setState:[[chosenPreset objectForKey:@"Mp4HttpOptimize"] intValue]]; - /* Set the state of ipod compatible with Mp4iPodCompatible */ - [fDstMp4iPodFileCheck setState:[[chosenPreset objectForKey:@"Mp4iPodCompatible"] intValue]]; - /* Codecs */ - [fDstCodecsPopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"FileCodecs"]]]; - [self codecsPopUpChanged: NULL]; /* Video encoder */ /* We set the advanced opt string here if applicable*/ - [fAdvancedOptions setOptions: [NSString stringWithFormat:[chosenPreset valueForKey:@"x264Option"]]]; + [fAdvancedOptions setOptions:[chosenPreset objectForKey:@"x264Option"]]; /* We use a conditional to account for the new x264 encoder dropdown as well as presets made using legacy x264 settings*/ - if ([[NSString stringWithFormat:[chosenPreset valueForKey:@"VideoEncoder"]] isEqualToString: @"x264 (h.264 Main)"] || [[NSString stringWithFormat:[chosenPreset valueForKey:@"VideoEncoder"]] isEqualToString: @"x264 (h.264 iPod)"]) + if ([[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"x264 (h.264 Main)"] || + [[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"x264 (h.264 iPod)"] || + [[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"x264"]) { - [fVidEncoderPopUp selectItemWithTitle: [NSString stringWithFormat:@"x264"]]; + [fVidEncoderPopUp selectItemWithTitle:@"H.264 (x264)"]; /* special case for legacy preset to check the new fDstMp4HttpOptFileCheck checkbox to set the ipod atom */ - if ([[NSString stringWithFormat:[chosenPreset valueForKey:@"VideoEncoder"]] isEqualToString: @"x264 (h.264 iPod)"]) + if ([[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"x264 (h.264 iPod)"]) { [fDstMp4iPodFileCheck setState:NSOnState]; /* We also need to add "level=30:" to the advanced opts string to set the correct level for the iPod when encountering a legacy preset as it used to be handled separately from the opt string*/ - [fAdvancedOptions setOptions: [NSString stringWithFormat:[@"level=30:" stringByAppendingString:[fAdvancedOptions optionsString]]]]; + [fAdvancedOptions setOptions:[@"level=30:" stringByAppendingString:[fAdvancedOptions optionsString]]]; } else { [fDstMp4iPodFileCheck setState:NSOffState]; } } + else if ([[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"FFmpeg"]) + { + [fVidEncoderPopUp selectItemWithTitle:@"MPEG-4 (FFmpeg)"]; + } + else if ([[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"XviD"]) + { + [fVidEncoderPopUp selectItemWithTitle:@"MPEG-4 (XviD)"]; + } else { - [fVidEncoderPopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"VideoEncoder"]]]; + [fVidEncoderPopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoEncoder"]]; } /* Lets run through the following functions to get variables set there */ - [self encoderPopUpChanged: NULL]; - - [self calculateBitrate: NULL]; + [self videoEncoderPopUpChanged:nil]; + /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/ + [fDstMp4iPodFileCheck setState:[[chosenPreset objectForKey:@"Mp4iPodCompatible"] intValue]]; + [self calculateBitrate:nil]; /* Video quality */ [fVidQualityMatrix selectCellAtRow:[[chosenPreset objectForKey:@"VideoQualityType"] intValue] column:0]; - [fVidTargetSizeField setStringValue: [NSString stringWithFormat:[chosenPreset valueForKey:@"VideoTargetSize"]]]; - [fVidBitrateField setStringValue: [NSString stringWithFormat:[chosenPreset valueForKey:@"VideoAvgBitrate"]]]; - [fVidQualitySlider setFloatValue: [[chosenPreset valueForKey:@"VideoQualitySlider"] floatValue]]; + [fVidTargetSizeField setStringValue:[chosenPreset objectForKey:@"VideoTargetSize"]]; + [fVidBitrateField setStringValue:[chosenPreset objectForKey:@"VideoAvgBitrate"]]; + [fVidQualitySlider setFloatValue:[[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]]; - [self videoMatrixChanged: NULL]; + [self videoMatrixChanged:nil]; /* Video framerate */ /* For video preset video framerate, we want to make sure that Same as source does not conflict with the detected framerate in the fVidRatePopUp so we use index 0*/ - if ([[NSString stringWithFormat:[chosenPreset valueForKey:@"VideoFramerate"]] isEqualToString: @"Same as source"]) + if ([[chosenPreset objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"]) { [fVidRatePopUp selectItemAtIndex: 0]; } else { - [fVidRatePopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"VideoFramerate"]]]; + [fVidRatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoFramerate"]]; } /* GrayScale */ @@ -3433,24 +5210,316 @@ if (item == nil) /* 2 Pass Encoding */ [fVidTwoPassCheck setState:[[chosenPreset objectForKey:@"VideoTwoPass"] intValue]]; - [self twoPassCheckboxChanged: NULL]; + [self twoPassCheckboxChanged:nil]; /* Turbo 1st pass for 2 Pass Encoding */ [fVidTurboPassCheck setState:[[chosenPreset objectForKey:@"VideoTurboTwoPass"] intValue]]; /*Audio*/ + if ([chosenPreset objectForKey:@"FileCodecs"]) + { + /* We need to handle the audio codec popup by determining what was chosen from the deprecated Codecs PopUp for past presets*/ + if ([[chosenPreset objectForKey:@"FileCodecs"] isEqualToString: @"AVC/H.264 Video / AAC + AC3 Audio"]) + { + /* We need to address setting languages etc. here in the new multi track audio panel */ + /* Track One set here */ + /*for track one though a track should be selected but lets check here anyway and use track one if its not.*/ + if ([fAudLang1PopUp indexOfSelectedItem] == 0) + { + [fAudLang1PopUp selectItemAtIndex: 1]; + [self audioTrackPopUpChanged: fAudLang1PopUp]; + } + [fAudTrack1CodecPopUp selectItemWithTitle: @"AAC (faac)"]; + [self audioTrackPopUpChanged: fAudTrack1CodecPopUp]; + /* Track Two, set source same as track one */ + [fAudLang2PopUp selectItemAtIndex: [fAudLang1PopUp indexOfSelectedItem]]; + [self audioTrackPopUpChanged: fAudLang2PopUp]; + [fAudTrack2CodecPopUp selectItemWithTitle: @"AC3 Passthru"]; + [self audioTrackPopUpChanged: fAudTrack2CodecPopUp]; + } + else if ([[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"MPEG-4 Video / AAC Audio"] || + [[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"AVC/H.264 Video / AAC Audio"]) + { + if ([fAudLang1PopUp indexOfSelectedItem] > 0) + { + [fAudTrack1CodecPopUp selectItemWithTitle: @"AAC (faac)"]; + [self audioTrackPopUpChanged: fAudTrack1CodecPopUp]; + } + if ([fAudLang2PopUp indexOfSelectedItem] > 0) + { + [fAudTrack2CodecPopUp selectItemWithTitle: @"AAC (faac)"]; + [self audioTrackPopUpChanged: fAudTrack2CodecPopUp]; + } + if ([fAudLang3PopUp indexOfSelectedItem] > 0) + { + [fAudTrack3CodecPopUp selectItemWithTitle: @"AAC (faac)"]; + [self audioTrackPopUpChanged: fAudTrack3CodecPopUp]; + } + if ([fAudLang4PopUp indexOfSelectedItem] > 0) + { + [fAudTrack4CodecPopUp selectItemWithTitle: @"AAC (faac)"]; + [self audioTrackPopUpChanged: fAudTrack4CodecPopUp]; + } + } + else if ([[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"MPEG-4 Video / AC-3 Audio"] || + [[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"AVC/H.264 Video / AC-3 Audio"]) + { + if ([fAudLang1PopUp indexOfSelectedItem] > 0) + { + [fAudTrack1CodecPopUp selectItemWithTitle: @"AC3 Passthru"]; + [self audioTrackPopUpChanged: fAudTrack1CodecPopUp]; + } + if ([fAudLang2PopUp indexOfSelectedItem] > 0) + { + [fAudTrack2CodecPopUp selectItemWithTitle: @"AC3 Passthru"]; + [self audioTrackPopUpChanged: fAudTrack2CodecPopUp]; + } + if ([fAudLang3PopUp indexOfSelectedItem] > 0) + { + [fAudTrack3CodecPopUp selectItemWithTitle: @"AC3 Passthru"]; + [self audioTrackPopUpChanged: fAudTrack3CodecPopUp]; + } + if ([fAudLang4PopUp indexOfSelectedItem] > 0) + { + [fAudTrack4CodecPopUp selectItemWithTitle: @"AC3 Passthru"]; + [self audioTrackPopUpChanged: fAudTrack4CodecPopUp]; + } + } + else if ([[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"MPEG-4 Video / MP3 Audio"] || + [[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"AVC/H.264 Video / MP3 Audio"]) + { + if ([fAudLang1PopUp indexOfSelectedItem] > 0) + { + [fAudTrack1CodecPopUp selectItemWithTitle: @"MP3 (lame)"]; + [self audioTrackPopUpChanged: fAudTrack1CodecPopUp]; + } + if ([fAudLang2PopUp indexOfSelectedItem] > 0) + { + [fAudTrack2CodecPopUp selectItemWithTitle: @"MP3 (lame)"]; + [self audioTrackPopUpChanged: fAudTrack2CodecPopUp]; + } + if ([fAudLang3PopUp indexOfSelectedItem] > 0) + { + [fAudTrack3CodecPopUp selectItemWithTitle: @"MP3 (lame)"]; + [self audioTrackPopUpChanged: fAudTrack3CodecPopUp]; + } + if ([fAudLang4PopUp indexOfSelectedItem] > 0) + { + [fAudTrack4CodecPopUp selectItemWithTitle: @"MP3 (lame)"]; + [self audioTrackPopUpChanged: fAudTrack4CodecPopUp]; + } + } + else if ([[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"MPEG-4 Video / Vorbis Audio"]) + { + if ([fAudLang1PopUp indexOfSelectedItem] > 0) + { + [fAudTrack1CodecPopUp selectItemWithTitle: @"Vorbis (vorbis)"]; + [self audioTrackPopUpChanged: fAudTrack1CodecPopUp]; + } + if ([fAudLang2PopUp indexOfSelectedItem] > 0) + { + [fAudTrack2CodecPopUp selectItemWithTitle: @"Vorbis (vorbis)"]; + [self audioTrackPopUpChanged: fAudTrack2CodecPopUp]; + } + if ([fAudLang3PopUp indexOfSelectedItem] > 0) + { + [fAudTrack3CodecPopUp selectItemWithTitle: @"Vorbis (vorbis)"]; + [self audioTrackPopUpChanged: fAudTrack3CodecPopUp]; + } + if ([fAudLang4PopUp indexOfSelectedItem] > 0) + { + [fAudTrack4CodecPopUp selectItemWithTitle: @"Vorbis (vorbis)"]; + [self audioTrackPopUpChanged: fAudTrack4CodecPopUp]; + } + } + /* We detect here if we have the old audio sample rate and if so we apply samplerate and bitrate to the existing four tracks if chosen + * UNLESS the CodecPopUp is AC3 in which case the preset values are ignored in favor of rates set in audioTrackMixdownChanged*/ + if ([chosenPreset objectForKey:@"AudioSampleRate"]) + { + if ([fAudLang1PopUp indexOfSelectedItem] > 0 && [fAudTrack1CodecPopUp titleOfSelectedItem] != @"AC3 Passthru") + { + [fAudTrack1RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioSampleRate"]]; + [fAudTrack1BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioBitRate"]]; + } + if ([fAudLang2PopUp indexOfSelectedItem] > 0 && [fAudTrack2CodecPopUp titleOfSelectedItem] != @"AC3 Passthru") + { + [fAudTrack2RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioSampleRate"]]; + [fAudTrack2BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioBitRate"]]; + } + if ([fAudLang3PopUp indexOfSelectedItem] > 0 && [fAudTrack3CodecPopUp titleOfSelectedItem] != @"AC3 Passthru") + { + [fAudTrack3RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioSampleRate"]]; + [fAudTrack3BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioBitRate"]]; + } + if ([fAudLang4PopUp indexOfSelectedItem] > 0 && [fAudTrack4CodecPopUp titleOfSelectedItem] != @"AC3 Passthru") + { + [fAudTrack4RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioSampleRate"]]; + [fAudTrack4BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioBitRate"]]; + } + } + /* We detect here if we have the old DRC Slider and if so we apply it to the existing four tracks if chosen */ + if ([chosenPreset objectForKey:@"AudioDRCSlider"]) + { + if ([fAudLang1PopUp indexOfSelectedItem] > 0) + { + [fAudTrack1DrcSlider setFloatValue:[[chosenPreset objectForKey:@"AudioDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack1DrcSlider]; + } + if ([fAudLang2PopUp indexOfSelectedItem] > 0) + { + [fAudTrack2DrcSlider setFloatValue:[[chosenPreset objectForKey:@"AudioDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack2DrcSlider]; + } + if ([fAudLang3PopUp indexOfSelectedItem] > 0) + { + [fAudTrack3DrcSlider setFloatValue:[[chosenPreset objectForKey:@"AudioDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack3DrcSlider]; + } + if ([fAudLang4PopUp indexOfSelectedItem] > 0) + { + [fAudTrack4DrcSlider setFloatValue:[[chosenPreset objectForKey:@"AudioDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack4DrcSlider]; + } + } + } + else // since there was no codecs key in the preset we know we can use new multi-audio track presets + { + if ([chosenPreset objectForKey:@"Audio1Track"] > 0) + { + if ([fAudLang1PopUp indexOfSelectedItem] == 0) + { + [fAudLang1PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang1PopUp]; + [fAudTrack1CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Encoder"]]; + [self audioTrackPopUpChanged: fAudTrack1CodecPopUp]; + [fAudTrack1MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Mixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack1MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack1CodecPopUp]; + } + [fAudTrack1RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Samplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString:@"AC3 Passthru"]) + { + [fAudTrack1BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Bitrate"]]; + } + [fAudTrack1DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio1TrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack1DrcSlider]; + } + if ([chosenPreset objectForKey:@"Audio2Track"] > 0) + { + if ([fAudLang2PopUp indexOfSelectedItem] == 0) + { + [fAudLang2PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang2PopUp]; + [fAudTrack2CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Encoder"]]; + [self audioTrackPopUpChanged: fAudTrack2CodecPopUp]; + [fAudTrack2MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Mixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack2MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack2CodecPopUp]; + } + [fAudTrack2RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Samplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[chosenPreset objectForKey:@"Audio2Encoder"] isEqualToString:@"AC3 Passthru"]) + { + [fAudTrack2BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Bitrate"]]; + } + [fAudTrack2DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio2TrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack2DrcSlider]; + } + if ([chosenPreset objectForKey:@"Audio3Track"] > 0) + { + if ([fAudLang3PopUp indexOfSelectedItem] == 0) + { + [fAudLang3PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang3PopUp]; + [fAudTrack3CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Encoder"]]; + [self audioTrackPopUpChanged: fAudTrack3CodecPopUp]; + [fAudTrack3MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Mixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack3MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack3CodecPopUp]; + } + [fAudTrack3RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Samplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AC3 Passthru"]) + { + [fAudTrack3BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Bitrate"]]; + } + [fAudTrack3DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio3TrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack3DrcSlider]; + } + if ([chosenPreset objectForKey:@"Audio4Track"] > 0) + { + if ([fAudLang4PopUp indexOfSelectedItem] == 0) + { + [fAudLang4PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang4PopUp]; + [fAudTrack4CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Encoder"]]; + [self audioTrackPopUpChanged: fAudTrack4CodecPopUp]; + [fAudTrack4MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Mixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack4MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack4CodecPopUp]; + } + [fAudTrack4RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Samplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString:@"AC3 Passthru"]) + { + [fAudTrack4BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Bitrate"]]; + } + [fAudTrack4DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio4TrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack4DrcSlider]; + } + + + } + + /* We now cleanup any extra audio tracks that may be previously set if we need to, we do it here so we don't have to + * duplicate any code for legacy presets.*/ + /* First we handle the legacy Codecs crazy AVC/H.264 Video / AAC + AC3 Audio atv hybrid */ + if ([chosenPreset objectForKey:@"FileCodecs"] && [[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"AVC/H.264 Video / AAC + AC3 Audio"]) + { + [fAudLang3PopUp selectItemAtIndex: 0]; + [self audioTrackPopUpChanged: fAudLang3PopUp]; + [fAudLang4PopUp selectItemAtIndex: 0]; + [self audioTrackPopUpChanged: fAudLang4PopUp]; + } + else + { + if (![chosenPreset objectForKey:@"Audio2Track"] || [chosenPreset objectForKey:@"Audio2Track"] == 0) + { + [fAudLang2PopUp selectItemAtIndex: 0]; + [self audioTrackPopUpChanged: fAudLang2PopUp]; + } + if (![chosenPreset objectForKey:@"Audio3Track"] || [chosenPreset objectForKey:@"Audio3Track"] > 0) + { + [fAudLang3PopUp selectItemAtIndex: 0]; + [self audioTrackPopUpChanged: fAudLang3PopUp]; + } + if (![chosenPreset objectForKey:@"Audio4Track"] || [chosenPreset objectForKey:@"Audio4Track"] > 0) + { + [fAudLang4PopUp selectItemAtIndex: 0]; + [self audioTrackPopUpChanged: fAudLang4PopUp]; + } + } - /* Audio Sample Rate*/ - [fAudRatePopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"AudioSampleRate"]]]; - /* Audio Bitrate Rate*/ - [fAudBitratePopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"AudioBitRate"]]]; /*Subtitles*/ - [fSubPopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"Subtitles"]]]; + [fSubPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Subtitles"]]; /* Forced Subtitles */ [fSubForcedCheck setState:[[chosenPreset objectForKey:@"SubtitlesForced"] intValue]]; - - /* Dynamic Range Control Slider */ - [fAudDrcSlider setFloatValue: [[chosenPreset valueForKey:@"AudioDRCSlider"] floatValue]]; - [self audioDRCSliderChanged: NULL]; /* Picture Settings */ /* Note: objectForKey:@"UsesPictureSettings" now refers to picture size, this encompasses: @@ -3465,11 +5534,37 @@ if (item == nil) if ([[chosenPreset objectForKey:@"UsesPictureSettings"] intValue] > 0) { hb_job_t * job = fTitle->job; + + /* If Cropping is set to custom, then recall all four crop values from + when the preset was created and apply them */ + if ([[chosenPreset objectForKey:@"PictureAutoCrop"] intValue] == 0) + { + [fPictureController setAutoCrop:NO]; + + /* Here we use the custom crop values saved at the time the preset was saved */ + job->crop[0] = [[chosenPreset objectForKey:@"PictureTopCrop"] intValue]; + job->crop[1] = [[chosenPreset objectForKey:@"PictureBottomCrop"] intValue]; + job->crop[2] = [[chosenPreset objectForKey:@"PictureLeftCrop"] intValue]; + job->crop[3] = [[chosenPreset objectForKey:@"PictureRightCrop"] intValue]; + + } + else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */ + { + [fPictureController setAutoCrop:YES]; + /* Here we use the auto crop values determined right after scan */ + job->crop[0] = AutoCropTop; + job->crop[1] = AutoCropBottom; + job->crop[2] = AutoCropLeft; + job->crop[3] = AutoCropRight; + + } + + /* Check to see if the objectForKey:@"UsesPictureSettings is 2 which is "Use Max for the source */ if ([[chosenPreset objectForKey:@"UsesPictureSettings"] intValue] == 2 || [[chosenPreset objectForKey:@"UsesMaxPictureSettings"] intValue] == 1) { /* Use Max Picture settings for whatever the dvd is.*/ - [self revertPictureSizeToMax: NULL]; + [self revertPictureSizeToMax:nil]; job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"] intValue]; if (job->keep_ratio == 1) { @@ -3510,30 +5605,7 @@ if (item == nil) job->pixel_ratio = [[chosenPreset objectForKey:@"PicturePAR"] intValue]; - /* If Cropping is set to custom, then recall all four crop values from - when the preset was created and apply them */ - if ([[chosenPreset objectForKey:@"PictureAutoCrop"] intValue] == 0) - { - [fPictureController setAutoCrop:NO]; - - /* Here we use the custom crop values saved at the time the preset was saved */ - job->crop[0] = [[chosenPreset objectForKey:@"PictureTopCrop"] intValue]; - job->crop[1] = [[chosenPreset objectForKey:@"PictureBottomCrop"] intValue]; - job->crop[2] = [[chosenPreset objectForKey:@"PictureLeftCrop"] intValue]; - job->crop[3] = [[chosenPreset objectForKey:@"PictureRightCrop"] intValue]; - - } - else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */ - { - [fPictureController setAutoCrop:YES]; - /* Here we use the auto crop values determined right after scan */ - job->crop[0] = AutoCropTop; - job->crop[1] = AutoCropBottom; - job->crop[2] = AutoCropLeft; - job->crop[3] = AutoCropRight; - - } - /* If the preset has no objectForKey:@"UsesPictureFilters", then we know it is a legacy preset + /* If the preset has no objectForKey:@"UsesPictureFilters", then we know it is a legacy preset * and handle the filters here as before. * NOTE: This should be removed when the update presets code is done as we can be assured that legacy * presets are updated to work properly with new keys. @@ -3545,14 +5617,14 @@ if (item == nil) if ([chosenPreset objectForKey:@"PictureDeinterlace"]) { /* We check to see if the preset used the past fourth "Slowest" deinterlaceing and set that to "Slower - * since we no longer have a fourth "Slowest" deinterlacing due to the mcdeint bug */ + * since we no longer have a fourth "Slowest" deinterlacing due to the mcdeint bug */ if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] == 4) { [fPictureController setDeinterlace:3]; } else { - + [fPictureController setDeinterlace:[[chosenPreset objectForKey:@"PictureDeinterlace"] intValue]]; } } @@ -3563,12 +5635,11 @@ if (item == nil) /* VFR */ if ([[chosenPreset objectForKey:@"VFR"] intValue] == 1) { - [fPictureController setVFR:[[chosenPreset objectForKey:@"VFR"] intValue]]; - } - else - { - [fPictureController setVFR:0]; + // We make sure that framerate is set to Same as source variable + // detelecine will take care of itself right below + //[fPictureController setVFR:[[chosenPreset objectForKey:@"VFR"] intValue]]; } + /* Detelecine */ if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 1) { @@ -3590,13 +5661,16 @@ if (item == nil) /* Deblock */ if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1) { - [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]]; + /* since we used to use 1 to turn on deblock, we now use a 5 in our sliding scale */ + [fPictureController setDeblock:5]; } else { [fPictureController setDeblock:0]; - } - [self calculatePictureSizing: NULL]; + + } + + [self calculatePictureSizing:nil]; } } @@ -3627,15 +5701,7 @@ if (item == nil) { [fPictureController setDeinterlace:0]; } - /* VFR */ - if ([[chosenPreset objectForKey:@"VFR"] intValue] == 1) - { - [fPictureController setVFR:[[chosenPreset objectForKey:@"VFR"] intValue]]; - } - else - { - [fPictureController setVFR:0]; - } + /* Detelecine */ if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 1) { @@ -3657,15 +5723,27 @@ if (item == nil) /* Deblock */ if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1) { + /* if its a one, then its the old on/off deblock, set on to 5*/ + [fPictureController setDeblock:5]; + } + else + { + /* use the settings intValue */ [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]]; } + /* Decomb */ + /* Even though we currently allow for a custom setting for decomb, ultimately it will only have Off and + * Default so we just pay attention to anything greater than 0 as 1 (Default). 0 is Off. */ + if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0) + { + [fPictureController setDecomb:1]; + } else { - [fPictureController setDeblock:0]; - } + [fPictureController setDecomb:0]; + } } - [self calculatePictureSizing: NULL]; - [[fPresetsActionMenu itemAtIndex:0] setEnabled: YES]; + [self calculatePictureSizing:nil]; } } @@ -3680,16 +5758,16 @@ if (item == nil) UserPresetsFile = @"~/Library/Application Support/HandBrake/UserPresets.plist"; UserPresetsFile = [[UserPresetsFile stringByExpandingTildeInPath]retain]; /* We check for the presets.plist */ - if ([fileManager fileExistsAtPath:UserPresetsFile] == 0) + if ([fileManager fileExistsAtPath:UserPresetsFile] == 0) { [fileManager createFileAtPath:UserPresetsFile contents:nil attributes:nil]; } - + UserPresets = [[NSMutableArray alloc] initWithContentsOfFile:UserPresetsFile]; - if (nil == UserPresets) + if (nil == UserPresets) { UserPresets = [[NSMutableArray alloc] init]; - [self addFactoryPresets:NULL]; + [self addFactoryPresets:nil]; } [fPresetsOutlineView reloadData]; } @@ -3708,11 +5786,13 @@ if (item == nil) [fPresetNewPicSettingsPopUp selectItemAtIndex: 0]; /* Uncheck the preset use filters checkbox */ [fPresetNewPicFiltersCheck setState:NSOffState]; + // fPresetNewFolderCheck + [fPresetNewFolderCheck setState:NSOffState]; /* Erase info from the input fields*/ [fPresetNewName setStringValue: @""]; [fPresetNewDesc setStringValue: @""]; /* Show the panel */ - [NSApp beginSheet: fAddPresetPanel modalForWindow: fWindow modalDelegate: NULL didEndSelector: NULL contextInfo: NULL]; + [NSApp beginSheet:fAddPresetPanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; } - (IBAction) closeAddPresetPanel: (id) sender @@ -3730,29 +5810,40 @@ if (item == nil) /* Here we create a custom user preset */ [UserPresets addObject:[self createPreset]]; [self addPreset]; - - [self closeAddPresetPanel:NULL]; + + [self closeAddPresetPanel:nil]; } } - (void)addPreset { + /* We Reload the New Table data for presets */ + [fPresetsOutlineView reloadData]; + /* We save all of the preset data here */ + [self savePreset]; +} + +- (void)sortPresets +{ + + /* We Sort the Presets By Factory or Custom */ NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type" ascending:YES] autorelease]; - /* We Sort the Presets Alphabetically by name */ - NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName" + /* We Sort the Presets Alphabetically by name We do not use this now as we have drag and drop*/ + /* + NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName" ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease]; - NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil]; + //NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil]; + + */ + /* Since we can drag and drop our custom presets, lets just sort by type and not name */ + NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,nil]; NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors]; [UserPresets setArray:sortedArray]; - - /* We Reload the New Table data for presets */ - [fPresetsOutlineView reloadData]; - /* We save all of the preset data here */ - [self savePreset]; + } - (IBAction)insertPreset:(id)sender @@ -3768,133 +5859,196 @@ if (item == nil) NSMutableDictionary *preset = [[NSMutableDictionary alloc] init]; /* Get the New Preset Name from the field in the AddPresetPanel */ [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"]; + /* Set whether or not this is to be a folder fPresetNewFolderCheck*/ + [preset setObject:[NSNumber numberWithBool:[fPresetNewFolderCheck state]] forKey:@"Folder"]; /*Set whether or not this is a user preset or factory 0 is factory, 1 is user*/ [preset setObject:[NSNumber numberWithInt:1] forKey:@"Type"]; /*Set whether or not this is default, at creation set to 0*/ [preset setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; - /*Get the whether or not to apply pic Size and Cropping (includes Anamorphic)*/ - [preset setObject:[NSNumber numberWithInt:[fPresetNewPicSettingsPopUp indexOfSelectedItem]] forKey:@"UsesPictureSettings"]; - /* Get whether or not to use the current Picture Filter settings for the preset */ - [preset setObject:[NSNumber numberWithInt:[fPresetNewPicFiltersCheck state]] forKey:@"UsesPictureFilters"]; - - /* Get New Preset Description from the field in the AddPresetPanel*/ - [preset setObject:[fPresetNewDesc stringValue] forKey:@"PresetDescription"]; - /* File Format */ - [preset setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"]; - /* Chapter Markers fCreateChapterMarkers*/ - [preset setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"]; - /* Allow Mpeg4 64 bit formatting +4GB file sizes */ - [preset setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"]; - /* Mux mp4 with http optimization */ - [preset setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"]; - /* Add iPod uuid atom */ - [preset setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"]; - - /* Codecs */ - [preset setObject:[fDstCodecsPopUp titleOfSelectedItem] forKey:@"FileCodecs"]; - /* Video encoder */ - [preset setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"]; - /* x264 Option String */ - [preset setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"]; - - [preset setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"]; - [preset setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"]; - [preset setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"]; - [preset setObject:[NSNumber numberWithFloat:[fVidQualitySlider floatValue]] forKey:@"VideoQualitySlider"]; - - /* Video framerate */ - if ([fVidRatePopUp indexOfSelectedItem] == 0) // Same as source is selected - { - [preset setObject:[NSString stringWithFormat: @"Same as source"] forKey:@"VideoFramerate"]; + if ([fPresetNewFolderCheck state] == YES) + { + /* initialize and set an empty array for children here since we are a new folder */ + NSMutableArray *childrenArray = [[NSMutableArray alloc] init]; + [preset setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"]; + [childrenArray autorelease]; } - else // we can record the actual titleOfSelectedItem + else // we are not creating a preset folder, so we go ahead with the rest of the preset info { - [preset setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"]; + /*Get the whether or not to apply pic Size and Cropping (includes Anamorphic)*/ + [preset setObject:[NSNumber numberWithInt:[fPresetNewPicSettingsPopUp indexOfSelectedItem]] forKey:@"UsesPictureSettings"]; + /* Get whether or not to use the current Picture Filter settings for the preset */ + [preset setObject:[NSNumber numberWithInt:[fPresetNewPicFiltersCheck state]] forKey:@"UsesPictureFilters"]; + + /* Get New Preset Description from the field in the AddPresetPanel*/ + [preset setObject:[fPresetNewDesc stringValue] forKey:@"PresetDescription"]; + /* File Format */ + [preset setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"]; + /* Chapter Markers fCreateChapterMarkers*/ + [preset setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"]; + /* Allow Mpeg4 64 bit formatting +4GB file sizes */ + [preset setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"]; + /* Mux mp4 with http optimization */ + [preset setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"]; + /* Add iPod uuid atom */ + [preset setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"]; + + /* Codecs */ + /* Video encoder */ + [preset setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"]; + /* x264 Option String */ + [preset setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"]; + + [preset setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"]; + [preset setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"]; + [preset setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"]; + [preset setObject:[NSNumber numberWithFloat:[fVidQualitySlider floatValue]] forKey:@"VideoQualitySlider"]; + + /* Video framerate */ + if ([fVidRatePopUp indexOfSelectedItem] == 0) // Same as source is selected + { + [preset setObject:@"Same as source" forKey:@"VideoFramerate"]; + } + else // we can record the actual titleOfSelectedItem + { + [preset setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"]; + } + /* GrayScale */ + [preset setObject:[NSNumber numberWithInt:[fVidGrayscaleCheck state]] forKey:@"VideoGrayScale"]; + /* 2 Pass Encoding */ + [preset setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"]; + /* Turbo 2 pass Encoding fVidTurboPassCheck*/ + [preset setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"]; + /*Picture Settings*/ + hb_job_t * job = fTitle->job; + /* Picture Sizing */ + /* Use Max Picture settings for whatever the dvd is.*/ + [preset setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"]; + [preset setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"]; + [preset setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"]; + [preset setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"]; + [preset setObject:[NSNumber numberWithInt:fTitle->job->pixel_ratio] forKey:@"PicturePAR"]; + + /* Set crop settings here */ + [preset setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"]; + [preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"]; + [preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"]; + [preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"]; + [preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"]; + + /* Picture Filters */ + [preset setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"]; + [preset setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"]; + //[preset setObject:[NSNumber numberWithInt:[fPictureController vfr]] forKey:@"VFR"]; + [preset setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"]; + [preset setObject:[NSNumber numberWithInt:[fPictureController deblock]] forKey:@"PictureDeblock"]; + [preset setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"]; + + + /*Audio*/ + if ([fAudLang1PopUp indexOfSelectedItem] > 0) + { + [preset setObject:[NSNumber numberWithInt:[fAudLang1PopUp indexOfSelectedItem]] forKey:@"Audio1Track"]; + [preset setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"Audio1TrackDescription"]; + [preset setObject:[fAudTrack1CodecPopUp titleOfSelectedItem] forKey:@"Audio1Encoder"]; + [preset setObject:[fAudTrack1MixPopUp titleOfSelectedItem] forKey:@"Audio1Mixdown"]; + [preset setObject:[fAudTrack1RatePopUp titleOfSelectedItem] forKey:@"Audio1Samplerate"]; + [preset setObject:[fAudTrack1BitratePopUp titleOfSelectedItem] forKey:@"Audio1Bitrate"]; + [preset setObject:[NSNumber numberWithFloat:[fAudTrack1DrcSlider floatValue]] forKey:@"Audio1TrackDRCSlider"]; + } + if ([fAudLang2PopUp indexOfSelectedItem] > 0) + { + [preset setObject:[NSNumber numberWithInt:[fAudLang2PopUp indexOfSelectedItem]] forKey:@"Audio2Track"]; + [preset setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"Audio2TrackDescription"]; + [preset setObject:[fAudTrack2CodecPopUp titleOfSelectedItem] forKey:@"Audio2Encoder"]; + [preset setObject:[fAudTrack2MixPopUp titleOfSelectedItem] forKey:@"Audio2Mixdown"]; + [preset setObject:[fAudTrack2RatePopUp titleOfSelectedItem] forKey:@"Audio2Samplerate"]; + [preset setObject:[fAudTrack2BitratePopUp titleOfSelectedItem] forKey:@"Audio2Bitrate"]; + [preset setObject:[NSNumber numberWithFloat:[fAudTrack2DrcSlider floatValue]] forKey:@"Audio2TrackDRCSlider"]; + } + if ([fAudLang3PopUp indexOfSelectedItem] > 0) + { + [preset setObject:[NSNumber numberWithInt:[fAudLang3PopUp indexOfSelectedItem]] forKey:@"Audio3Track"]; + [preset setObject:[fAudLang3PopUp titleOfSelectedItem] forKey:@"Audio3TrackDescription"]; + [preset setObject:[fAudTrack3CodecPopUp titleOfSelectedItem] forKey:@"Audio3Encoder"]; + [preset setObject:[fAudTrack3MixPopUp titleOfSelectedItem] forKey:@"Audio3Mixdown"]; + [preset setObject:[fAudTrack3RatePopUp titleOfSelectedItem] forKey:@"Audio3Samplerate"]; + [preset setObject:[fAudTrack3BitratePopUp titleOfSelectedItem] forKey:@"Audio3Bitrate"]; + [preset setObject:[NSNumber numberWithFloat:[fAudTrack3DrcSlider floatValue]] forKey:@"Audio3TrackDRCSlider"]; + } + if ([fAudLang4PopUp indexOfSelectedItem] > 0) + { + [preset setObject:[NSNumber numberWithInt:[fAudLang4PopUp indexOfSelectedItem]] forKey:@"Audio4Track"]; + [preset setObject:[fAudLang4PopUp titleOfSelectedItem] forKey:@"Audio4TrackDescription"]; + [preset setObject:[fAudTrack4CodecPopUp titleOfSelectedItem] forKey:@"Audio4Encoder"]; + [preset setObject:[fAudTrack4MixPopUp titleOfSelectedItem] forKey:@"Audio4Mixdown"]; + [preset setObject:[fAudTrack4RatePopUp titleOfSelectedItem] forKey:@"Audio4Samplerate"]; + [preset setObject:[fAudTrack4BitratePopUp titleOfSelectedItem] forKey:@"Audio4Bitrate"]; + [preset setObject:[NSNumber numberWithFloat:[fAudTrack4DrcSlider floatValue]] forKey:@"Audio4TrackDRCSlider"]; + } + + /* Subtitles*/ + [preset setObject:[fSubPopUp titleOfSelectedItem] forKey:@"Subtitles"]; + /* Forced Subtitles */ + [preset setObject:[NSNumber numberWithInt:[fSubForcedCheck state]] forKey:@"SubtitlesForced"]; } - /* GrayScale */ - [preset setObject:[NSNumber numberWithInt:[fVidGrayscaleCheck state]] forKey:@"VideoGrayScale"]; - /* 2 Pass Encoding */ - [preset setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"]; - /* Turbo 2 pass Encoding fVidTurboPassCheck*/ - [preset setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"]; - /*Picture Settings*/ - hb_job_t * job = fTitle->job; - /* Picture Sizing */ - /* Use Max Picture settings for whatever the dvd is.*/ - [preset setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"]; - [preset setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"]; - [preset setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"]; - [preset setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"]; - [preset setObject:[NSNumber numberWithInt:fTitle->job->pixel_ratio] forKey:@"PicturePAR"]; - - /* Set crop settings here */ - [preset setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"]; - [preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"]; - [preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"]; - [preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"]; - [preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"]; - - /* Picture Filters */ - [preset setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"]; - [preset setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"]; - [preset setObject:[NSNumber numberWithInt:[fPictureController vfr]] forKey:@"VFR"]; - [preset setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"]; - [preset setObject:[NSNumber numberWithInt:[fPictureController deblock]] forKey:@"PictureDeblock"]; - - - - /*Audio*/ - /* Audio Sample Rate*/ - [preset setObject:[fAudRatePopUp titleOfSelectedItem] forKey:@"AudioSampleRate"]; - /* Audio Bitrate Rate*/ - [preset setObject:[fAudBitratePopUp titleOfSelectedItem] forKey:@"AudioBitRate"]; - /* Subtitles*/ - [preset setObject:[fSubPopUp titleOfSelectedItem] forKey:@"Subtitles"]; - /* Forced Subtitles */ - [preset setObject:[NSNumber numberWithInt:[fSubForcedCheck state]] forKey:@"SubtitlesForced"]; - /* Dynamic Range Control Slider */ - [preset setObject:[NSNumber numberWithFloat:[fAudDrcSlider floatValue]] forKey:@"AudioDRCSlider"]; - - [preset autorelease]; return preset; - + } - (void)savePreset { [UserPresets writeToFile:UserPresetsFile atomically:YES]; /* We get the default preset in case it changed */ - [self getDefaultPresets: NULL]; + [self getDefaultPresets:nil]; } - (IBAction)deletePreset:(id)sender { - int status; - NSEnumerator *enumerator; - NSNumber *index; - NSMutableArray *tempArray; - id tempObject; + if ( [fPresetsOutlineView numberOfSelectedRows] == 0 ) + { return; + } /* Alert user before deleting preset */ - /* Comment out for now, tie to user pref eventually */ - - //NSBeep(); + int status; status = NSRunAlertPanel(@"Warning!", @"Are you sure that you want to delete the selected preset?", @"OK", @"Cancel", nil); - if ( status == NSAlertDefaultReturn ) { - enumerator = [fPresetsOutlineView selectedRowEnumerator]; + if ( status == NSAlertDefaultReturn ) + { + int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]]; + NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]; + NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod]; + + NSEnumerator *enumerator; + NSMutableArray *presetsArrayToMod; + NSMutableArray *tempArray; + id tempObject; + /* If we are a root level preset, we are modding the UserPresets array */ + if (presetToModLevel == 0) + { + presetsArrayToMod = UserPresets; + } + else // We have a parent preset, so we modify the chidren array object for key + { + presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"]; + } + + enumerator = [presetsArrayToMod objectEnumerator]; tempArray = [NSMutableArray array]; - while ( (index = [enumerator nextObject]) ) { - tempObject = [UserPresets objectAtIndex:[index intValue]]; - [tempArray addObject:tempObject]; + while (tempObject = [enumerator nextObject]) + { + NSDictionary *thisPresetDict = tempObject; + if (thisPresetDict == presetToMod) + { + [tempArray addObject:tempObject]; + } } - [UserPresets removeObjectsInArray:tempArray]; + [presetsArrayToMod removeObjectsInArray:tempArray]; [fPresetsOutlineView reloadData]; [self savePreset]; } @@ -3905,67 +6059,224 @@ if (item == nil) - (IBAction)getDefaultPresets:(id)sender { - int i = 0; + presetHbDefault = nil; + presetUserDefault = nil; + presetUserDefaultParent = nil; + presetUserDefaultParentParent = nil; + NSMutableDictionary *presetHbDefaultParent = nil; + NSMutableDictionary *presetHbDefaultParentParent = nil; + + int i = 0; + BOOL userDefaultFound = NO; + presetCurrentBuiltInCount = 0; + /* First we iterate through the root UserPresets array to check for defaults */ NSEnumerator *enumerator = [UserPresets objectEnumerator]; id tempObject; while (tempObject = [enumerator nextObject]) { - NSDictionary *thisPresetDict = tempObject; + NSMutableDictionary *thisPresetDict = tempObject; if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default { - presetHbDefault = i; + presetHbDefault = thisPresetDict; } if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default { - presetUserDefault = i; + presetUserDefault = thisPresetDict; + userDefaultFound = YES; + } + if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset + { + presetCurrentBuiltInCount++; // <--increment the current number of built in presets } i++; + + /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */ + if ([thisPresetDict objectForKey:@"ChildrenArray"]) + { + NSMutableDictionary *thisPresetDictParent = thisPresetDict; + NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) + { + NSMutableDictionary *thisPresetDict = tempObject; + if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default + { + presetHbDefault = thisPresetDict; + presetHbDefaultParent = thisPresetDictParent; + } + if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default + { + presetUserDefault = thisPresetDict; + presetUserDefaultParent = thisPresetDictParent; + userDefaultFound = YES; + } + + /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */ + if ([thisPresetDict objectForKey:@"ChildrenArray"]) + { + NSMutableDictionary *thisPresetDictParentParent = thisPresetDict; + NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) + { + NSMutableDictionary *thisPresetDict = tempObject; + if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default + { + presetHbDefault = thisPresetDict; + presetHbDefaultParent = thisPresetDictParent; + presetHbDefaultParentParent = thisPresetDictParentParent; + } + if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default + { + presetUserDefault = thisPresetDict; + presetUserDefaultParent = thisPresetDictParent; + presetUserDefaultParentParent = thisPresetDictParentParent; + userDefaultFound = YES; + } + + } + } + } + } + } + /* check to see if a user specified preset was found, if not then assign the parents for + * the presetHbDefault so that we can open the parents for the nested presets + */ + if (userDefaultFound == NO) + { + presetUserDefaultParent = presetHbDefaultParent; + presetUserDefaultParentParent = presetHbDefaultParentParent; + } } - (IBAction)setDefaultPreset:(id)sender { +/* We need to determine if the item is a folder */ + if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] == 1) + { + return; + } + int i = 0; NSEnumerator *enumerator = [UserPresets objectEnumerator]; id tempObject; /* First make sure the old user specified default preset is removed */ - while (tempObject = [enumerator nextObject]) + while (tempObject = [enumerator nextObject]) { - /* make sure we are not removing the default HB preset */ - if ([[[UserPresets objectAtIndex:i] objectForKey:@"Default"] intValue] != 1) // 1 is HB default + NSMutableDictionary *thisPresetDict = tempObject; + if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0 { - [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; + [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; } - i++; - } - /* Second, go ahead and set the appropriate user specfied preset */ - /* we get the chosen preset from the UserPresets array */ - if ([[[UserPresets objectAtIndex:[fPresetsOutlineView selectedRow]] objectForKey:@"Default"] intValue] != 1) // 1 is HB default - { - [[UserPresets objectAtIndex:[fPresetsOutlineView selectedRow]] setObject:[NSNumber numberWithInt:2] forKey:@"Default"]; + + /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */ + if ([thisPresetDict objectForKey:@"ChildrenArray"]) + { + NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator]; + id tempObject; + int ii = 0; + while (tempObject = [enumerator nextObject]) + { + NSMutableDictionary *thisPresetDict1 = tempObject; + if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0 + { + [[[thisPresetDict objectForKey:@"ChildrenArray"] objectAtIndex:ii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; + } + /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */ + if ([thisPresetDict1 objectForKey:@"ChildrenArray"]) + { + NSEnumerator *enumerator = [[thisPresetDict1 objectForKey:@"ChildrenArray"] objectEnumerator]; + id tempObject; + int iii = 0; + while (tempObject = [enumerator nextObject]) + { + if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0 + { + [[[thisPresetDict1 objectForKey:@"ChildrenArray"] objectAtIndex:iii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; + } + iii++; + } + } + ii++; + } + + } + i++; } - /*FIX ME: I think we now need to use the items not rows in NSOutlineView */ - presetUserDefault = [fPresetsOutlineView selectedRow]; - - /* We save all of the preset data here */ + + + int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]]; + NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]; + NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod]; + + + NSMutableArray *presetsArrayToMod; + NSMutableArray *tempArray; + + /* If we are a root level preset, we are modding the UserPresets array */ + if (presetToModLevel == 0) + { + presetsArrayToMod = UserPresets; + } + else // We have a parent preset, so we modify the chidren array object for key + { + presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"]; + } + + enumerator = [presetsArrayToMod objectEnumerator]; + tempArray = [NSMutableArray array]; + int iiii = 0; + while (tempObject = [enumerator nextObject]) + { + NSDictionary *thisPresetDict = tempObject; + if (thisPresetDict == presetToMod) + { + if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 2 + { + [[presetsArrayToMod objectAtIndex:iiii] setObject:[NSNumber numberWithInt:2] forKey:@"Default"]; + } + } + iiii++; + } + + + /* We save all of the preset data here */ [self savePreset]; - /* We Reload the New Table data for presets */ + /* We Reload the New Table data for presets */ [fPresetsOutlineView reloadData]; } - (IBAction)selectDefaultPreset:(id)sender { - /* if there is a user specified default, we use it */ + NSMutableDictionary *presetToMod; + /* if there is a user specified default, we use it */ if (presetUserDefault) { - [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:presetUserDefault] byExtendingSelection:NO]; - [self selectPreset:NULL]; - } + presetToMod = presetUserDefault; + } else if (presetHbDefault) //else we use the built in default presetHbDefault { - [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:presetHbDefault] byExtendingSelection:NO]; - [self selectPreset:NULL]; + presetToMod = presetHbDefault; } + else + { + return; + } + + if (presetUserDefaultParent != nil) + { + [fPresetsOutlineView expandItem:presetUserDefaultParent]; + + } + if (presetUserDefaultParentParent != nil) + { + [fPresetsOutlineView expandItem:presetUserDefaultParentParent]; + + } + + [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[fPresetsOutlineView rowForItem: presetToMod]] byExtendingSelection:NO]; + [self selectPreset:nil]; } @@ -4011,15 +6322,47 @@ if (item == nil) /* Then we generate new built in presets programmatically with fPresetsBuiltin * which is all setup in HBPresets.h and HBPresets.m*/ [fPresetsBuiltin generateBuiltinPresets:UserPresets]; - + [self sortPresets]; [self addPreset]; + } +@end + +/******************************* + * Subclass of the HBPresetsOutlineView * + *******************************/ + +@implementation HBPresetsOutlineView +- (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent*)dragEvent offset:(NSPointPointer)dragImageOffset +{ + fIsDragging = YES; + + // By default, NSTableView only drags an image of the first column. Change this to + // drag an image of the queue's icon and PresetName columns. + NSArray * cols = [NSArray arrayWithObjects: [self tableColumnWithIdentifier:@"PresetName"], nil]; + return [super dragImageForRowsWithIndexes:dragRows tableColumns:cols event:dragEvent offset:dragImageOffset]; +} + + +- (void) mouseDown:(NSEvent *)theEvent +{ + [super mouseDown:theEvent]; + fIsDragging = NO; +} + +- (BOOL) isDragging; +{ + return fIsDragging; +} @end + + +