X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=macosx%2FController.m;h=92d31a822057b045c6dd824ba4f0cd29e6d660ad;hb=4b72a63eb61a01275493c4bfb51ba02152d1c5e1;hp=cfc7cad26b9a33cc943226d556d027fa78a96743;hpb=d0f80cff8fe8ae92e347f3fa80ecb2376c61924a;p=handbrake-jp%2Fhandbrake-jp-git.git diff --git a/macosx/Controller.m b/macosx/Controller.m index cfc7cad2..92d31a82 100644 --- a/macosx/Controller.m +++ b/macosx/Controller.m @@ -4,6 +4,7 @@ Homepage: . It may be used under the terms of the GNU General Public License. */ +#include #import "Controller.h" #import "HBOutputPanelController.h" #import "HBPreferencesController.h" @@ -11,6 +12,12 @@ #import "HBPresets.h" #import "HBPreviewController.h" +unsigned int maximumNumberOfAllowedAudioTracks = 24; +NSString *HBContainerChangedNotification = @"HBContainerChangedNotification"; +NSString *keyContainerTag = @"keyContainerTag"; +NSString *HBTitleChangedNotification = @"HBTitleChangedNotification"; +NSString *keyTitleTag = @"keyTitleTag"; + #define DragDropSimplePboardType @"MyCustomOutlineViewPboardType" /* We setup the toolbar values here ShowPreviewIdentifier */ @@ -30,6 +37,8 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It *******************************/ @implementation HBController ++ (unsigned int) maximumNumberOfAllowedAudioTracks { return maximumNumberOfAllowedAudioTracks; } + - (id)init { self = [super init]; @@ -82,7 +91,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It 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:@"CFBundleShortVersionString"]] stringByAppendingString: [NSString stringWithFormat: @" (%@)", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]]]; - [self writeToActivityLog: "%s", [versionStringFull UTF8String]]; + [self writeToActivityLog: "%s", [versionStringFull UTF8String]]; return self; } @@ -117,16 +126,58 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [fSubtitlesTable setDelegate:fSubtitlesDelegate]; [fSubtitlesTable setRowHeight:25.0]; + /* setup the audio controller */ + [fAudioDelegate setHBController: self]; + [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(autoSetM4vExtension:) name: HBMixdownChangedNotification object: nil]; + [fPresetsOutlineView setAutosaveName:@"Presets View"]; [fPresetsOutlineView setAutosaveExpandedItems:YES]; dockIconProgress = 0; - + + /* Init QueueFile .plist */ + [self loadQueueFile]; + /* Run hbInstances to get any info on other instances as well as set the + * pid number for this instance in the case of multi-instance encoding. */ + hbInstanceNum = [self hbInstances]; + + /* If we are a single instance it is safe to clean up the previews if there are any + * left over. This is a bit of a kludge but will prevent a build up of old instance + * live preview cruft. No danger of removing an active preview directory since they + * are created later in HBPreviewController if they don't exist at the moment a live + * preview encode is initiated. */ + if (hbInstanceNum == 1) + { + NSString *PreviewDirectory = [NSString stringWithFormat:@"~/Library/Application Support/HandBrake/Previews"]; + PreviewDirectory = [PreviewDirectory stringByExpandingTildeInPath]; + NSError *error; + NSArray *files = [ [NSFileManager defaultManager] contentsOfDirectoryAtPath: PreviewDirectory error: &error ]; + for( NSString *file in files ) + { + if( file != @"." && file != @".." ) + { + [ [NSFileManager defaultManager] removeItemAtPath: [ PreviewDirectory stringByAppendingPathComponent: file ] error: &error ]; + if( error ) + { + //an error occurred... + [self writeToActivityLog: "Could not remove existing preview at : %s",[file UTF8String] ]; + } + } + } + + } + + + /* Call UpdateUI every 1/2 sec */ + [[NSRunLoop currentRunLoop] addTimer:[NSTimer - scheduledTimerWithTimeInterval:0.5 target:self - selector:@selector(updateUI:) userInfo:nil repeats:YES] - forMode:NSDefaultRunLoopMode]; + scheduledTimerWithTimeInterval:0.5 + target:self + selector:@selector(updateUI:) + userInfo:nil repeats:YES] + forMode:NSDefaultRunLoopMode]; + // Open debug output window now if it was visible when HB was closed if ([[NSUserDefaults standardUserDefaults] boolForKey:@"OutputPanelIsOpen"]) @@ -164,11 +215,11 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It /* We check to see if there is already another instance of hb running. * Note: hbInstances == 1 means we are the only instance of HandBrake.app */ - if ([self hbInstances] > 1) + if (hbInstanceNum > 1) { - alertTitle = [NSString stringWithFormat: - NSLocalizedString(@"There is already an instance of HandBrake running.", @"")]; - NSBeginCriticalAlertSheet( + alertTitle = [NSString stringWithFormat: + NSLocalizedString(@"There is already an instance of HandBrake running.", @"")]; + NSBeginCriticalAlertSheet( alertTitle, NSLocalizedString(@"Reload Queue", nil), nil, @@ -179,31 +230,48 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It } else { - if (fWorkingCount > 0) + if (fWorkingCount > 0 || fPendingCount > 0) { - alertTitle = [NSString stringWithFormat: - NSLocalizedString(@"HandBrake Has Detected %d Previously Encoding Item and %d Pending Item(s) In Your Queue.", @""), - fWorkingCount,fPendingCount]; + if (fWorkingCount > 0) + { + alertTitle = [NSString stringWithFormat: + NSLocalizedString(@"HandBrake Has Detected %d Previously Encoding Item(s) 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)); } else { - alertTitle = [NSString stringWithFormat: - NSLocalizedString(@"HandBrake Has Detected %d Pending Item(s) In Your Queue.", @""), - fPendingCount]; + /* Since we addressed any pending or previously encoding items above, we go ahead and make sure the queue + * is empty of any finished items or cancelled items */ + [self clearQueueAllItems]; + /* We show whichever open source window specified in LaunchSourceBehavior preference key */ + if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"]) + { + [self browseSources:nil]; + } + + if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source (Title Specific)"]) + { + [self browseSources:(id)fOpenSourceTitleMMenu]; + } } - 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 { @@ -218,31 +286,67 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [self browseSources:(id)fOpenSourceTitleMMenu]; } } + currentQueueEncodeNameString = @""; } +#pragma mark - +#pragma mark Multiple Instances + +/* hbInstances checks to see if other instances of HB are running and also sets the pid for this instance for multi-instance queue encoding */ + + /* Note for now since we are in early phases of multi-instance I have put in quite a bit of logging. Can be removed as we see fit. */ - (int) hbInstances { /* check to see if another instance of HandBrake.app is running */ NSArray *runningAppDictionaries = [[NSWorkspace sharedWorkspace] launchedApplications]; - NSDictionary *aDictionary; + NSDictionary *runningAppsDictionary; int hbInstances = 0; - for (aDictionary in runningAppDictionaries) + NSString * thisInstanceAppPath = [[NSBundle mainBundle] bundlePath]; + NSString * runningInstanceAppPath; + int runningInstancePidNum; + [self writeToActivityLog: "hbInstances path to this instance: %s", [thisInstanceAppPath UTF8String]]; + for (runningAppsDictionary in runningAppDictionaries) { - // NSLog(@"Open App: %@", [aDictionary valueForKey:@"NSApplicationName"]); - - if ([[aDictionary valueForKey:@"NSApplicationName"] isEqualToString:@"HandBrake"]) + if ([[runningAppsDictionary valueForKey:@"NSApplicationName"] isEqualToString:@"HandBrake"]) { + /*Report the path to each active instances app path */ + runningInstancePidNum = [[runningAppsDictionary valueForKey:@"NSApplicationProcessIdentifier"] intValue]; + runningInstanceAppPath = [runningAppsDictionary valueForKey:@"NSApplicationPath"]; + [self writeToActivityLog: "hbInstance found instance pidnum:%d at path: %s", runningInstancePidNum, [runningInstanceAppPath UTF8String]]; + /* see if this is us by comparing the app path */ + if ([runningInstanceAppPath isEqualToString: thisInstanceAppPath]) + { + /* If so this is our pidnum */ + [self writeToActivityLog: "hbInstance MATCH FOUND, our pidnum is:%d", runningInstancePidNum]; + /* Get the PID number for this hb instance, used in multi instance encoding */ + pidNum = runningInstancePidNum; + /* Report this pid to the activity log */ + [self writeToActivityLog: "Pid for this instance:%d", pidNum]; + /* Tell fQueueController what our pidNum is */ + [fQueueController setPidNum:pidNum]; + } hbInstances++; - } - } + } + } return hbInstances; } +- (int) getPidnum +{ + return pidNum; +} + +#pragma mark - + - (void) didDimissReloadQueue: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo { + + [self writeToActivityLog: "didDimissReloadQueue number of hb instances:%d", hbInstanceNum]; if (returnCode == NSAlertOtherReturn) { + [self writeToActivityLog: "didDimissReloadQueue NSAlertOtherReturn Chosen"]; [self clearQueueAllItems]; + /* We show whichever open source window specified in LaunchSourceBehavior preference key */ if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"]) { @@ -256,8 +360,10 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It } else { - if ([self hbInstances] == 1) + [self writeToActivityLog: "didDimissReloadQueue First Button Chosen"]; + if (hbInstanceNum == 1) { + [self setQueueEncodingItemsAsPending]; } [self showQueueWindow:NULL]; @@ -266,15 +372,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *) app { - /* if we are in preview full screen mode, we need to go to - * windowed mode and release the display before we terminate. - * We do it here (instead of applicationWillTerminate) so we - * release the displays and can then see the alerts below. - */ - if ([fPictureController previewFullScreenMode] == YES) - { - [fPictureController previewGoWindowed:nil]; - } + hb_state_t s; hb_get_state( fQueueEncodeLibhb, &s ); @@ -313,7 +411,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It - (void)applicationWillTerminate:(NSNotification *)aNotification { - + [currentQueueEncodeNameString release]; [browsedSourceDisplayName release]; [outputPanel release]; [fQueueController release]; @@ -321,15 +419,18 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [fPictureController release]; [fApplicationIcon release]; - hb_close(&fHandle); + hb_close(&fHandle); hb_close(&fQueueEncodeLibhb); + hb_global_close(); + } - (void) awakeFromNib { [fWindow center]; - [fWindow setExcludedFromWindowsMenu:YES]; + [fWindow setExcludedFromWindowsMenu:NO]; + [fAdvancedOptions setView:fAdvancedView]; /* lets setup our presets drawer for drag and drop here */ @@ -344,9 +445,6 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It /* Init UserPresets .plist */ [self loadPresets]; - - /* Init QueueFile .plist */ - [self loadQueueFile]; fRipIndicatorShown = NO; // initially out of view in the nib @@ -382,6 +480,18 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [fSrcAngleLabel setHidden:YES]; [fSrcAnglePopUp setHidden:YES]; + /* Setup the start / stop popup */ + [fEncodeStartStopPopUp removeAllItems]; + [fEncodeStartStopPopUp addItemWithTitle: @"Chapters"]; + [fEncodeStartStopPopUp addItemWithTitle: @"Seconds"]; + [fEncodeStartStopPopUp addItemWithTitle: @"Frames"]; + /* Align the start / stop widgets with the chapter popups */ + [fSrcTimeStartEncodingField setFrameOrigin:[fSrcChapterStartPopUp frame].origin]; + [fSrcTimeEndEncodingField setFrameOrigin:[fSrcChapterEndPopUp frame].origin]; + + [fSrcFrameStartEncodingField setFrameOrigin:[fSrcChapterStartPopUp frame].origin]; + [fSrcFrameEndEncodingField setFrameOrigin:[fSrcChapterEndPopUp frame].origin]; + /* Destination box*/ NSMenuItem *menuItem; [fDstFormatPopUp removeAllItems]; @@ -414,7 +524,6 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"]; - /* Video quality */ [fVidTargetSizeField setIntValue: 700]; [fVidBitrateField setIntValue: 1000]; @@ -452,26 +561,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It /* Set Auto Crop to On at launch */ [fPictureController setAutoCrop:YES]; - - /* Audio bitrate */ - [fAudTrack1BitratePopUp removeAllItems]; - for( int i = 0; i < hb_audio_bitrates_count; i++ ) - { - [fAudTrack1BitratePopUp addItemWithTitle: - [NSString stringWithUTF8String: hb_audio_bitrates[i].string]]; - - } - [fAudTrack1BitratePopUp selectItemAtIndex: hb_audio_bitrates_default]; - - /* Audio samplerate */ - [fAudTrack1RatePopUp removeAllItems]; - for( int i = 0; i < hb_audio_rates_count; i++ ) - { - [fAudTrack1RatePopUp addItemWithTitle: - [NSString stringWithUTF8String: hb_audio_rates[i].string]]; - } - [fAudTrack1RatePopUp selectItemAtIndex: hb_audio_rates_default]; - + /* Bottom */ [fStatusField setStringValue: @""]; @@ -487,8 +577,6 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [self getDefaultPresets:nil]; /* lets initialize the current successful scancount here to 0 */ currentSuccessfulScanCount = 0; - - } - (void) enableUI: (bool) b @@ -500,18 +588,11 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It fDstFormatField, fDstFormatPopUp, fDstFile1Field, fDstFile2Field, fDstBrowseButton, fVidRateField, fVidRatePopUp,fVidEncoderField, fVidEncoderPopUp, fVidQualityField, fPictureSizeField,fPictureCroppingField, fVideoFiltersField,fVidQualityMatrix, fSubField, fSubPopUp, - 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, fQueueStatus,fPresetsAdd,fPresetsDelete,fSrcAngleLabel,fSrcAnglePopUp, fCreateChapterMarkers,fVidTurboPassCheck,fDstMp4LargeFileCheck,fSubForcedCheck,fPresetsOutlineView, - fAudDrcLabel,fDstMp4HttpOptFileCheck,fDstMp4iPodFileCheck,fVidQualityRFField,fVidQualityRFLabel}; + fDstMp4HttpOptFileCheck,fDstMp4iPodFileCheck,fVidQualityRFField,fVidQualityRFLabel, + fEncodeStartStopPopUp,fSrcTimeStartEncodingField,fSrcTimeEndEncodingField,fSrcFrameStartEncodingField, + fSrcFrameEndEncodingField, fLoadChaptersButton, fSaveChaptersButton, fFrameratePfrCheck}; for( unsigned i = 0; i < sizeof( controls ) / sizeof( NSControl * ); i++ ) @@ -530,19 +611,20 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It } - if (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:nil]; /* we also call calculatePictureSizing here to sense check if we already have vfr selected */ [self calculatePictureSizing:nil]; - - } else { + /* Also enable the preview window hud controls */ + [fPictureController enablePreviewHudControls]; + } + else + { [fPresetsOutlineView setEnabled: NO]; - - } + [fPictureController disablePreviewHudControls]; + } [self videoMatrixChanged:nil]; [fAdvancedOptions enableUI:b]; @@ -754,7 +836,56 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It } #undef p + #define p s.param.working + + case HB_STATE_SEARCHING: + { + NSMutableString * string; + NSString * pass_desc; + + /* Update text field */ + pass_desc = @""; + //string = [NSMutableString stringWithFormat: NSLocalizedString( @"Searching for start point: pass %d %@ of %d, %.2f %%", @"" ), p.job_cur, pass_desc, p.job_count, 100.0 * p.progress]; + /* For now, do not announce "pass x of x for the search phase ... */ + string = [NSMutableString stringWithFormat: NSLocalizedString( @"Searching for start point ... : %.2f %%", @"" ), 100.0 * p.progress]; + + if( p.seconds > -1 ) + { + [string appendFormat: + NSLocalizedString( @" (ETA %02dh%02dm%02ds)", @"" ), p.hours, p.minutes, p.seconds]; + } + + [fStatusField setStringValue: string]; + /* Set the status string in fQueueController as well */ + [fQueueController setQueueStatusString: string]; + /* Update slider */ + CGFloat 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 ) + { + NSRect frame = [fWindow frame]; + 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; + + } + + /* Update dock icon */ + /* Note not done yet */ + break; + } + + case HB_STATE_WORKING: { NSMutableString * string; @@ -776,7 +907,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It pass_desc = @""; } - string = [NSMutableString stringWithFormat: NSLocalizedString( @"Encoding: pass %d %@ of %d, %.2f %%", @"" ), p.job_cur, pass_desc, p.job_count, 100.0 * p.progress]; + string = [NSMutableString stringWithFormat: NSLocalizedString( @"Encoding: %@ \nPass %d %@ of %d, %.2f %%", @"" ), currentQueueEncodeNameString, p.job_cur, pass_desc, p.job_count, 100.0 * p.progress]; if( p.seconds > -1 ) { @@ -784,10 +915,9 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It 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]; + [fQueueController setQueueStatusString:string]; + /* Update slider */ CGFloat progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count; [fRipIndicator setIndeterminate: NO]; @@ -808,14 +938,14 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It fRipIndicatorShown = YES; } - + /* Update dock icon */ if( dockIconProgress < 100.0 * progress_total ) { [self UpdateDockIcon: progress_total]; dockIconProgress += 5; } - + break; } #undef p @@ -886,62 +1016,39 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It 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 ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] || + [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"]) { - /* 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"] ) + /* If Play System Alert has been selected in Preferences */ + if( [[NSUserDefaults standardUserDefaults] boolForKey:@"AlertWhenDoneSound"] == YES ) { - /*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]; } - + [self showGrowlDoneNotification:pathOfFinishedEncode]; } + /* Send to MetaX */ + [self sendToMetaX:pathOfFinishedEncode]; + /* since we have successfully completed an encode, we increment the queue counter */ + [self incrementQueueItemDone:currentQueueEncodeIndex]; + } break; } } + /* Since we can use multiple instance off of the same queue file it is imperative that we keep the QueueFileArray updated off of the QueueFile.plist + * so we go ahead and do it in this existing timer as opposed to using a new one */ + + NSMutableArray * tempQueueArray = [[NSMutableArray alloc] initWithContentsOfFile:QueueFile]; + [QueueFileArray setArray:tempQueueArray]; + [tempQueueArray release]; + /* Send Fresh QueueFileArray to fQueueController to update queue window */ + [fQueueController setQueueArray: QueueFileArray]; + [self getQueueStats]; + } /* We use this to write messages to stderr from the macgui which show up in the activity window and log*/ @@ -1129,7 +1236,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It hb_get_state2( fQueueEncodeLibhb, &s ); - if (s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING) + if (s.state == HB_STATE_WORKING || s.state == HB_STATE_SEARCHING || s.state == HB_STATE_MUXING) { if ([ident isEqualToString: StartEncodingIdentifier]) { @@ -1297,34 +1404,79 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It -(void)showGrowlDoneNotification:(NSString *) filePath { /* This end of encode action is called as each encode rolls off of the queue */ + /* Setup the Growl stuff ... */ 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]; - } - + 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 { /* 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]; + NSString *sendToApp = [[NSUserDefaults standardUserDefaults] objectForKey: @"SendCompletedEncodeToApp"]; + if (![sendToApp isEqualToString:@"None"]) + { + [self writeToActivityLog: "trying to send encode to: %s", [sendToApp UTF8String]]; + NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@%@%@", @"tell application \"",sendToApp,@"\" to open (POSIX file \"", filePath, @"\")"]]; + [myScript executeAndReturnError: nil]; + [myScript release]; + } + + } +} + +- (void) queueCompletedAlerts +{ + /* If Play System Alert has been selected in Preferences */ + if( [[NSUserDefaults standardUserDefaults] boolForKey:@"AlertWhenDoneSound"] == YES ) + { + NSBeep(); + } + + /* 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; + 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]; } } + #pragma mark - #pragma mark Get New Source @@ -1484,6 +1636,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It /* make sure we remove any path extension as this can also be an '.mpg' file */ browsedSourceDisplayName = [[path lastPathComponent] retain]; } + applyQueueToScan = NO; [self performScan:path scanTitleNum:0]; } @@ -1516,7 +1669,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It { [NSApp endSheet: fScanSrcTitlePanel]; [fScanSrcTitlePanel orderOut: self]; - + if(sender == fScanSrcTitleOpenButton) { /* We setup the scan status in the main window to indicate a source title scan */ @@ -1526,8 +1679,9 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [fScanIndicator startAnimation: nil]; /* We use the performScan method to actually perform the specified scan passing the path and the title - * to be scanned - */ + * to be scanned + */ + applyQueueToScan = NO; [self performScan:[fScanSrcTitlePathField stringValue] scanTitleNum:[fScanSrcTitleNumField intValue]]; } } @@ -1535,8 +1689,7 @@ 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; BOOL vlcFound = 0; @@ -1551,35 +1704,32 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [fSubtitlesDelegate resetWithTitle:nil]; [fSubtitlesTable reloadData]; + // Notify anyone interested (audio controller) that there's no title + [[NSNotificationCenter defaultCenter] postNotification: + [NSNotification notificationWithName: HBTitleChangedNotification + object: self + userInfo: [NSDictionary dictionaryWithObjectsAndKeys: + [NSData dataWithBytesNoCopy: &fTitle length: sizeof(fTitle) freeWhenDone: NO], keyTitleTag, + nil]]]; + [self enableUI: NO]; if( [detector isVideoDVD] ) { - int hb_arch; -#if defined( __LP64__ ) - /* we are 64 bit */ - hb_arch = 64; -#else - /* we are 32 bit */ - hb_arch = 32; -#endif - - // 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/Contents/MacOS/lib/libdvdcss.2.dylib"; - NSFileManager * fileManager = [NSFileManager defaultManager]; - if ([fileManager fileExistsAtPath:vlcPath] == 0) - { - /*vlc not found in /Applications so we set the bool to cancel scanning to 1 */ + void *dvdcss = dlopen("libdvdcss.2.dylib", RTLD_LAZY); + if (dvdcss == NULL) + { + /*compatible vlc not found, 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 or your VLC is out of date.",@"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"); + status = NSRunAlertPanel(@"HandBrake could not find VLC or your VLC is incompatible (Note: 32 bit vlc is not compatible with 64 bit HandBrake and vice-versa).",@"Please download and install VLC media player if you wish to read encrypted DVDs.", @"Get VLC", @"Cancel Scan", @"Attempt Scan Anyway"); [NSApp requestUserAttention:NSCriticalRequest]; if (status == NSAlertDefaultReturn) @@ -1605,111 +1755,8 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It /* 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"]; vlcFound = 1; + dlclose(dvdcss); } - /* test for architecture of the vlc app */ - NSArray *vlc_architecturesArray = [[NSBundle bundleWithPath:@"/Applications/VLC.app"] executableArchitectures]; - BOOL vlcIntel32bit = NO; - BOOL vlcIntel64bit = NO; - BOOL vlcPPC32bit = NO; - BOOL vlcPPC64bit = NO; - /* check the available architectures for vlc and note accordingly */ - NSEnumerator *enumerator = [vlc_architecturesArray objectEnumerator]; - id tempObject; - while (tempObject = [enumerator nextObject]) - { - - if ([tempObject intValue] == NSBundleExecutableArchitectureI386) - { - vlcIntel32bit = YES; - } - if ([tempObject intValue] == NSBundleExecutableArchitectureX86_64) - { - vlcIntel64bit = YES; - } - if ([tempObject intValue] == NSBundleExecutableArchitecturePPC) - { - vlcPPC32bit = YES; - } - if ([tempObject intValue] == NSBundleExecutableArchitecturePPC64) - { - vlcPPC64bit = YES; - } - - } - /* Write vlc architecture findings to activity window */ - if (vlcIntel32bit) - { - [self writeToActivityLog: " 32-Bit VLC app found for decrypting physical dvd"]; - } - if (vlcIntel64bit) - { - [self writeToActivityLog: " 64-Bit VLC app found for decrypting physical dvd"]; - } - - - - if (vlcFound && hb_arch == 64 && !vlcIntel64bit && cancelScanDecrypt != 1) - { - - /* we are 64 bit */ - - /* Appropriate VLC not found, so cancel */ - cancelScanDecrypt = 1; - [self writeToActivityLog: "This version of HandBrake is 64 bit, 64 bit version of vlc not found, scan cancelled"]; - /*On Screen Notification*/ - int status; - NSBeep(); - status = NSRunAlertPanel(@"This version of HandBrake is 64 bit, VLC found but not 64 bit!",@"", @"Cancel Scan", @"Attempt Scan Anyway", @"Get 64 bit VLC", nil); - [NSApp requestUserAttention:NSCriticalRequest]; - - if (status == NSAlertDefaultReturn) - { - /* User chose to cancel the scan */ - [self writeToActivityLog: "cannot open physical dvd VLC found but not 64 bit, scan cancelled"]; - cancelScanDecrypt = 1; - } - else if (status == NSAlertAlternateReturn) - { - [self writeToActivityLog: "user overrode 64-bit warning trying to open physical dvd without proper decryption"]; - cancelScanDecrypt = 0; - } - else - { - /* 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/vlc/download-macosx.html"]]; - } - - } - else if (vlcFound && hb_arch == 32 && !vlcIntel32bit && cancelScanDecrypt != 1) - { - /* we are 32 bit */ - /* Appropriate VLC not found, so cancel */ - cancelScanDecrypt = 1; - [self writeToActivityLog: "This version of HandBrake is 32 bit, 32 bit version of vlc not found, scan cancelled"]; - /*On Screen Notification*/ - int status; - NSBeep(); - status = NSRunAlertPanel(@"This version of HandBrake is 32 bit, VLC found but not 32 bit!",@"", @"Cancel Scan", @"Attempt Scan Anyway", @"Get 32 bit VLC", nil); - [NSApp requestUserAttention:NSCriticalRequest]; - - if (status == NSAlertDefaultReturn) - { - /* User chose to cancel the scan */ - [self writeToActivityLog: "cannot open physical dvd VLC found but not 32 bit, scan cancelled"]; - cancelScanDecrypt = 1; - } - else if (status == NSAlertAlternateReturn) - { - [self writeToActivityLog: "user overrode 32-bit warning trying to open physical dvd without proper decryption"]; - cancelScanDecrypt = 0; - } - else - { - /* 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/vlc/download-macosx.html"]]; - } - - } } if (cancelScanDecrypt == 0) @@ -1726,11 +1773,14 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It { [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum]; } - /* We use our advance pref to determine how many previews to scan */ + /* We use our advanced pref to determine how many previews to scan */ int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue]; + /* We use our advanced pref to determine the minimum title length to use in seconds*/ + uint64_t min_title_duration_seconds = 90000L * [[[NSUserDefaults standardUserDefaults] objectForKey:@"MinTitleScanSeconds"] intValue]; /* set title to NULL */ - //fTitle = NULL; - hb_scan( fHandle, [path UTF8String], scanTitleNum, hb_num_previews, 1 ); + fTitle = NULL; + [self writeToActivityLog: "Minimum length of title for scan: %d", min_title_duration_seconds]; + hb_scan( fHandle, [path UTF8String], scanTitleNum, hb_num_previews, 1 , min_title_duration_seconds ); [fSrcDVD2Field setStringValue:@"Scanning new source ..."]; } } @@ -1743,10 +1793,8 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It - (IBAction) showNewScan:(id)sender { hb_list_t * list; - 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) - + hb_title_t * title = NULL; + int feature_title=0; // Used to store the main feature title list = hb_get_titles( fHandle ); @@ -1763,14 +1811,36 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It // Notify Subtitles that there's no title [fChapterTitlesDelegate resetWithTitle:nil]; [fChapterTable reloadData]; + + // Notify anyone interested (audio controller) that there's no title + [[NSNotificationCenter defaultCenter] postNotification: + [NSNotification notificationWithName: HBTitleChangedNotification + object: self + userInfo: [NSDictionary dictionaryWithObjectsAndKeys: + [NSData dataWithBytesNoCopy: &fTitle length: sizeof(fTitle) freeWhenDone: NO], keyTitleTag, + nil]]]; } 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 */ + if (applyQueueToScan == YES) + { + /* we are a rescan of an existing queue item and need to apply the queued settings to the scan */ + [self writeToActivityLog: "showNewScan: This is a queued item rescan"]; + + } + else if (applyQueueToScan == NO) + { + [self writeToActivityLog: "showNewScan: This is a new source item scan"]; + } + else + { + [self writeToActivityLog: "showNewScan: cannot grok scan status"]; + } + /* 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]; @@ -1782,12 +1852,20 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It currentSource = [NSString stringWithUTF8String: title->name]; /*Set DVD Name at top of window with the browsedSourceDisplayName grokked right before -performScan */ + if (!browsedSourceDisplayName) + { + browsedSourceDisplayName = @"NoNameDetected"; + } [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*/ + /* If its a queue rescan for edit, get the queue item output path */ + /* if not, its a new source scan. */ /* Check to see if the last destination has been set,use if so, if not, use Desktop */ - if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"]) + if (applyQueueToScan == YES) + { + [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@", [[QueueFileArray objectAtIndex:fqueueEditRescanItemNum] objectForKey:@"DestinationPath"]]]; + } + else if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"]) { [fDstFile2Field setStringValue: [NSString stringWithFormat: @"%@/%@.mp4", [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"],[browsedSourceDisplayName stringByDeletingPathExtension]]]; @@ -1798,11 +1876,10 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It @"%@/Desktop/%@.mp4", NSHomeDirectory(),[browsedSourceDisplayName stringByDeletingPathExtension]]]; } - - if (longuestpri < title->hours*60*60 + title->minutes *60 + title->seconds) + /* See if this is the main feature according to libhb */ + if (title->index == title->job->feature) { - longuestpri=title->hours*60*60 + title->minutes *60 + title->seconds; - indxpri=i; + feature_title = i; } [fSrcTitlePopUp addItemWithTitle: [NSString @@ -1818,8 +1895,8 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It } else { - /* if not then select the longest title (dvd) */ - [fSrcTitlePopUp selectItemAtIndex: indxpri]; + /* if not then select the main feature title */ + [fSrcTitlePopUp selectItemAtIndex: feature_title]; } [self titlePopUpChanged:nil]; @@ -1829,6 +1906,8 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It /* if its the initial successful scan after awakeFromNib */ if (currentSuccessfulScanCount == 1) { + [self encodeStartStopPopUpChanged:nil]; + [self selectDefaultPreset:nil]; // Open preview window now if it was visible when HB was closed @@ -1840,6 +1919,13 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It [self showPicturePanel:nil]; } + if (applyQueueToScan == YES) + { + /* we are a rescan of an existing queue item and need to apply the queued settings to the scan */ + [self writeToActivityLog: "showNewScan: calling applyQueueSettingsToMainWindow"]; + [self applyQueueSettingsToMainWindow:nil]; + + } } @@ -1890,12 +1976,9 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag { if( !flag ) { - [fWindow makeKeyAndOrderFront:nil]; - - return YES; + [fWindow makeKeyAndOrderFront:nil]; } - - return NO; + return YES; } - (NSSize) drawerWillResizeContents:(NSDrawer *) drawer toSize:(NSSize) contentSize { @@ -1912,53 +1995,38 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It /*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 */ + /* We check for the Queue.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]; + /* ONLY clear out encoded items if we are single instance */ + if (hbInstanceNum == 1) + { + [self clearQueueEncodedItems]; + } } - currentQueueEncodeIndex = 0; } - (void)addQueueFileItem { - [QueueFileArray addObject:[self createQueueFileItem]]; - [self saveQueueFileItem]; - + [QueueFileArray addObject:[self createQueueFileItem]]; + [self saveQueueFileItem]; + } - (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]; - } [QueueFileArray removeObjectAtIndex:queueItemToRemove]; [self saveQueueFileItem]; @@ -2002,7 +2070,13 @@ fWorkingCount = 0; if ([[thisQueueDict objectForKey:@"Status"] intValue] == 1) // being encoded { fWorkingCount++; - fEncodingQueueItem = i; + fEncodingQueueItem = i; + /* check to see if we are the instance doing this encoding */ + if ([thisQueueDict objectForKey:@"EncodingPID"] && [[thisQueueDict objectForKey:@"EncodingPID"] intValue] == pidNum) + { + currentQueueEncodeIndex = i; + } + } if ([[thisQueueDict objectForKey:@"Status"] intValue] == 2) // pending { @@ -2019,15 +2093,39 @@ fWorkingCount = 0; NSMutableString * string; if (fPendingCount == 1) { - string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode pending in the queue", @"" ), fPendingCount]; + string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode pending", @"" ), fPendingCount]; } else { - string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode(s) pending in the queue", @"" ), fPendingCount]; + string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode(s) pending", @"" ), fPendingCount]; } [fQueueStatus setStringValue:string]; } +/* Used to get the next pending queue item index and return it if found */ +- (int)getNextPendingQueueIndex +{ + /* initialize nextPendingIndex to -1, this value tells incrementQueueItemDone that there are no pending items in the queue */ + int nextPendingIndex = -1; + BOOL nextPendingFound = NO; + NSEnumerator *enumerator = [QueueFileArray objectEnumerator]; + id tempObject; + int i = 0; + while (tempObject = [enumerator nextObject]) + { + NSDictionary *thisQueueDict = tempObject; + if ([[thisQueueDict objectForKey:@"Status"] intValue] == 2 && nextPendingFound == NO) // pending + { + nextPendingFound = YES; + nextPendingIndex = [QueueFileArray indexOfObject: tempObject]; + [self writeToActivityLog: "getNextPendingQueueIndex next pending encod index is:%d", nextPendingIndex]; + } + i++; + } + return nextPendingIndex; +} + + /* This method will set any item marked as encoding back to pending * currently used right after a queue reload */ @@ -2040,15 +2138,19 @@ fWorkingCount = 0; /* we look here to see if the preset is we move on to the next one */ while ( tempObject = [enumerator nextObject] ) { - /* 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) + /* We want to keep any queue item that is pending or was previously being encoded */ + if ([[tempObject objectForKey:@"Status"] intValue] == 1 || [[tempObject objectForKey:@"Status"] intValue] == 2) { - [tempObject setObject:[NSNumber numberWithInt: 2] forKey:@"Status"]; + /* 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]; } - [tempArray addObject:tempObject]; } [QueueFileArray setArray:tempArray]; @@ -2128,9 +2230,34 @@ fWorkingCount = 0; [queueFileJob setObject:[fSrcDVD2Field stringValue] forKey:@"SourceName"]; [queueFileJob setObject:[NSNumber numberWithInt:title->index] forKey:@"TitleNumber"]; [queueFileJob setObject:[NSNumber numberWithInt:[fSrcAnglePopUp indexOfSelectedItem] + 1] forKey:@"TitleAngle"]; - [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterStartPopUp indexOfSelectedItem] + 1] forKey:@"ChapterStart"]; + /* Determine and set a variable to tell hb what start and stop times to use ... chapters vs seconds */ + if( [fEncodeStartStopPopUp indexOfSelectedItem] == 0 ) + { + [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"fEncodeStartStop"]; + } + else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 1) + { + [queueFileJob setObject:[NSNumber numberWithInt:1] forKey:@"fEncodeStartStop"]; + } + else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 2) + { + [queueFileJob setObject:[NSNumber numberWithInt:2] forKey:@"fEncodeStartStop"]; + } + /* Chapter encode info */ + [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterStartPopUp indexOfSelectedItem] + 1] forKey:@"ChapterStart"]; [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterEndPopUp indexOfSelectedItem] + 1] forKey:@"ChapterEnd"]; + /* Time (pts) encode info */ + [queueFileJob setObject:[NSNumber numberWithInt:[fSrcTimeStartEncodingField intValue]] forKey:@"StartSeconds"]; + [queueFileJob setObject:[NSNumber numberWithInt:[fSrcTimeEndEncodingField intValue] - [fSrcTimeStartEncodingField intValue]] forKey:@"StopSeconds"]; + /* Frame number encode info */ + [queueFileJob setObject:[NSNumber numberWithInt:[fSrcFrameStartEncodingField intValue]] forKey:@"StartFrame"]; + [queueFileJob setObject:[NSNumber numberWithInt:[fSrcFrameEndEncodingField intValue] - [fSrcFrameStartEncodingField intValue]] forKey:@"StopFrame"]; + + + /* The number of seek points equals the number of seconds announced in the title as that is our current granularity */ + int title_duration_seconds = (title->hours * 3600) + (title->minutes * 60) + (title->seconds); + [queueFileJob setObject:[NSNumber numberWithInt:title_duration_seconds] forKey:@"SourceTotalSeconds"]; [queueFileJob setObject:[fDstFile2Field stringValue] forKey:@"DestinationPath"]; @@ -2187,6 +2314,7 @@ fWorkingCount = 0; [queueFileJob setObject:[NSNumber numberWithFloat:[fVidQualityRFField floatValue]] forKey:@"VideoQualitySlider"]; /* Framerate */ [queueFileJob setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"]; + [queueFileJob setObject:[NSNumber numberWithInt:[fFrameratePfrCheck state]] forKey:@"VideoFrameratePFR"]; /* 2 Pass Encoding */ [queueFileJob setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"]; @@ -2200,10 +2328,11 @@ fWorkingCount = 0; [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->anamorphic.mode] forKey:@"PicturePAR"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->modulus] forKey:@"PictureModulus"]; /* if we are custom anamorphic, store the exact storage, par and display dims */ if (fTitle->job->anamorphic.mode == 3) { - [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.modulus] forKey:@"PicturePARModulus"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->modulus] forKey:@"PicturePARModulus"]; [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PicturePARStorageWidth"]; [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PicturePARStorageHeight"]; @@ -2244,46 +2373,7 @@ fWorkingCount = 0; [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController grayscale]] forKey:@"VideoGrayScale"]; /*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"]; - } + [fAudioDelegate prepareAudioForQueueFileJob: queueFileJob]; /* Subtitles*/ NSMutableArray *subtitlesArray = [[NSMutableArray alloc] initWithArray:[fSubtitlesDelegate getSubtitleArray] copyItems:YES]; @@ -2324,41 +2414,7 @@ fWorkingCount = 0; [queueFileJob setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"]; - /*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"]; - } - /* we need to auto relase the queueFileJob and return it */ [queueFileJob autorelease]; return queueFileJob; @@ -2404,54 +2460,61 @@ fWorkingCount = 0; - (void) incrementQueueItemDone:(int) queueItemDoneIndexNum { - int i = currentQueueEncodeIndex; - [[QueueFileArray objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Status"]; + /* Mark the encode just finished as done (status 0)*/ + [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] 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]]; + /* Check to see if there are any more pending items in the queue */ + int newQueueItemIndex = [self getNextPendingQueueIndex]; + /* If we still have more pending items in our queue, lets go to the next one */ + if (newQueueItemIndex >= 0 && newQueueItemIndex < queueItems) + { + /*Set our currentQueueEncodeIndex now to the newly found Pending encode as we own it */ + currentQueueEncodeIndex = newQueueItemIndex; + /* now we mark the queue item as Status = 1 ( being encoded ) so another instance can not come along and try to scan it while we are scanning */ + [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:1] forKey:@"Status"]; + [self writeToActivityLog: "incrementQueueItemDone new pending items found: %d", currentQueueEncodeIndex]; + [self saveQueueFileItem]; + /* now we can go ahead and scan the new pending queue item */ + [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]; + [self writeToActivityLog: "incrementQueueItemDone there are no more pending encodes"]; + /*Since there are no more items to encode, go to queueCompletedAlerts for user specified alerts after queue completed*/ + [self queueCompletedAlerts]; } } /* 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 */ + /* Tell HB to output a new activity log file for this encode */ [outputPanel startEncodeLog:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"]]; + /* We now flag the queue item as being owned by this instance of HB using the PID */ + [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:pidNum] forKey:@"EncodingPID"]; + /* Get the currentQueueEncodeNameString from the queue item to display in the status field */ + currentQueueEncodeNameString = [[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"] lastPathComponent]retain]; + /* We save all of the Queue data here */ + [self saveQueueFileItem]; - /* use a bool to determine whether or not we can decrypt using vlc */ + /* 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; + * settings as this is a queue rescan + */ 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]; - + if( [detector isVideoDVD] ) { // The chosen path was actually on a DVD, so use the raw block @@ -2460,10 +2523,9 @@ fWorkingCount = 0; [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) - { + void *dvdcss = dlopen("libdvdcss.2.dylib", RTLD_LAZY); + if (dvdcss == NULL) + { /*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"]; @@ -2492,6 +2554,7 @@ fWorkingCount = 0; else { /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */ + dlclose(dvdcss); [self writeToActivityLog: "VLC app found for decrypting physical dvd"]; } } @@ -2511,28 +2574,137 @@ fWorkingCount = 0; [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum]; } - [self writeToActivityLog: "performNewQueueScan currentQueueEncodeIndex is: %d", currentQueueEncodeIndex]; - /* We use our advance pref to determine how many previews to scan */ + /* We use our advance pref to determine how many previews to scan */ int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue]; - hb_scan( fQueueEncodeLibhb, [path UTF8String], scanTitleNum, hb_num_previews, 0 ); + hb_scan( fQueueEncodeLibhb, [path UTF8String], scanTitleNum, hb_num_previews, 0 , 0 ); } } -/* 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 +/* 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"]; + } + NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex]; - hb_job_t * job = fTitle->job; + [self writeToActivityLog: "Preset: %s", [[queueToApply objectForKey:@"PresetName"] UTF8String]]; + [self writeToActivityLog: "processNewQueueEncode number of passes expected is: %d", ([[queueToApply objectForKey:@"VideoTwoPass"] intValue] + 1)]; + job->file = [[queueToApply objectForKey:@"DestinationPath"] UTF8String]; + [self prepareJob]; + + /* + * If scanning we need to do some extra setup of the job. + */ + if( job->indepth_scan == 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->x264opts = NULL; + + job->indepth_scan = 1; + + + /* + * Add the pre-scan job + */ + hb_add( fQueueEncodeLibhb, job ); + job->x264opts = x264opts_tmp; + } + + + if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 ) + { + job->indepth_scan = 0; + + + + 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]); + + 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 need to clean up the subtitle tracks after the job(s) have been set */ + int num_subtitle_tracks = hb_list_count(job->list_subtitle); + int ii; + for(ii = 0; ii < num_subtitle_tracks; ii++) + { + hb_subtitle_t * subtitle; + subtitle = (hb_subtitle_t *)hb_list_item(job->list_subtitle, 0); + + + hb_list_rem(job->list_subtitle, subtitle); + free(subtitle); + } + /* We should be all setup so let 'er rip */ + [self doRip]; +} + +#pragma mark - +#pragma mark Queue Item Editing + +/* Rescans the chosen queue item back into the main window */ +- (void)rescanQueueItemToMainWindow:(NSString *) scanPath scanTitleNum: (int) scanTitleNum selectedQueueItem: (int) selectedQueueItem +{ + fqueueEditRescanItemNum = selectedQueueItem; + [self writeToActivityLog: "rescanQueueItemToMainWindow: Re-scanning queue item at index:%d",fqueueEditRescanItemNum]; + applyQueueToScan = YES; + /* Set the browsedSourceDisplayName for showNewScan */ + browsedSourceDisplayName = [[QueueFileArray objectAtIndex:fqueueEditRescanItemNum] objectForKey:@"SourceName"]; + [self performScan:scanPath scanTitleNum:scanTitleNum]; +} + + +/* We use this method after a queue item rescan for edit. + * it largely mirrors -selectPreset in terms of structure. + * Assumes that a queue item has been reloaded into the main window. + */ +- (IBAction)applyQueueSettingsToMainWindow:(id)sender +{ + NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:fqueueEditRescanItemNum]; + hb_job_t * job = fTitle->job; + if (queueToApply) + { + [self writeToActivityLog: "applyQueueSettingsToMainWindow: queue item found"]; + } /* Set title number and chapters */ - /* since the queue only scans a single title, we really don't need to pick a title */ + /* since the queue only scans a single title, its already been selected in showNewScan + so do not try to reset it here. However if we do decide to do full source scans on + a queue edit rescan, we would need it. So leaving in for now but commenting out. */ //[fSrcTitlePopUp selectItemAtIndex: [[queueToApply objectForKey:@"TitleNumber"] intValue] - 1]; [fSrcChapterStartPopUp selectItemAtIndex: [[queueToApply objectForKey:@"ChapterStart"] intValue] - 1]; @@ -2565,10 +2737,34 @@ fWorkingCount = 0; [fVidTargetSizeField setStringValue:[queueToApply objectForKey:@"VideoTargetSize"]]; [fVidBitrateField setStringValue:[queueToApply objectForKey:@"VideoAvgBitrate"]]; - [fVidQualitySlider setFloatValue:[[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]]; + /* Since we are now using RF Values for the slider, we detect if the preset uses an old quality float. + * So, check to see if the quality value is less than 1.0 which should indicate the old ".062" type + * quality preset. Caveat: in the case of x264, where the RF scale starts at 0, it would misinterpret + * a preset that uses 0.0 - 0.99 for RF as an old style preset. Not sure how to get around that one yet, + * though it should be a corner case since it would pretty much be a preset for lossless encoding. */ + if ([[queueToApply objectForKey:@"VideoQualitySlider"] floatValue] < 1.0) + { + /* For the quality slider we need to convert the old percent's to the new rf scales */ + float rf = (([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]); + [fVidQualitySlider setFloatValue:rf]; + + } + else + { + /* Since theora's qp value goes up from left to right, we can just set the slider float value */ + if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA) + { + [fVidQualitySlider setFloatValue:[[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]]; + } + else + { + /* since ffmpeg and x264 use an "inverted" slider (lower qp/rf values indicate a higher quality) we invert the value on the slider */ + [fVidQualitySlider setFloatValue:([fVidQualitySlider maxValue] + [fVidQualitySlider minValue]) - [[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*/ @@ -2588,120 +2784,50 @@ fWorkingCount = 0; [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]]; + /* Now lets add our new tracks to the audio list here */ + [fAudioDelegate addTracksFromQueue: queueToApply]; + /*Subtitles*/ + /* Crashy crashy right now, working on it */ + [fSubtitlesDelegate setNewSubtitles:[queueToApply objectForKey:@"SubtitleList"]]; + [fSubtitlesTable reloadData]; /* Picture Settings */ + + /* 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; + + } + + job->modulus = [[queueToApply objectForKey:@"PictureModulus"] intValue]; + /* 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; + //job->width = fTitle->width; + //job->height = fTitle->height; + [self revertPictureSizeToMax:nil]; } else // source width/height is >= the preset height/width { @@ -2720,53 +2846,108 @@ fWorkingCount = 0; } } job->anamorphic.mode = [[queueToApply objectForKey:@"PicturePAR"] intValue]; + job->modulus = [[queueToApply objectForKey:@"PictureModulus"] intValue]; + /* Filters */ - /* 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) + /* We only allow *either* Decomb or Deinterlace. So check for the PictureDecombDeinterlace key. + * also, older presets may not have this key, in which case we also check to see if that preset had PictureDecomb + * specified, in which case we use decomb and ignore any possible Deinterlace settings as using both was less than + * sane. + */ + [fPictureController setUseDecomb:1]; + [fPictureController setDecomb:0]; + [fPictureController setDeinterlace:0]; + if ([[queueToApply objectForKey:@"PictureDecombDeinterlace"] intValue] == 1 || [[queueToApply objectForKey:@"PictureDecomb"] 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]; - + /* we are using decomb */ + /* Decomb */ + if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] > 0) + { + [fPictureController setDecomb:[[queueToApply objectForKey:@"PictureDecomb"] intValue]]; + + /* if we are using "Custom" in the decomb setting, also set the custom string*/ + if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 1) + { + [fPictureController setDecombCustomString:[queueToApply objectForKey:@"PictureDecombCustom"]]; + } + } } - else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */ + else { - [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; - + /* We are using Deinterlace */ + /* Deinterlace */ + if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] > 0) + { + [fPictureController setUseDecomb:0]; + [fPictureController setDeinterlace:[[queueToApply objectForKey:@"PictureDeinterlace"] intValue]]; + /* if we are using "Custom" in the deinterlace setting, also set the custom string*/ + if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 1) + { + [fPictureController setDeinterlaceCustomString:[queueToApply objectForKey:@"PictureDeinterlaceCustom"]]; + } + } } - /* Filters */ - /* Deinterlace */ - [fPictureController setDeinterlace:[[queueToApply objectForKey:@"PictureDeinterlace"] intValue]]; /* Detelecine */ - [fPictureController setDetelecine:[[queueToApply objectForKey:@"PictureDetelecine"] intValue]]; + if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] > 0) + { + [fPictureController setDetelecine:[[queueToApply objectForKey:@"PictureDetelecine"] intValue]]; + /* if we are using "Custom" in the detelecine setting, also set the custom string*/ + if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1) + { + [fPictureController setDetelecineCustomString:[queueToApply objectForKey:@"PictureDetelecineCustom"]]; + } + } + else + { + [fPictureController setDetelecine:0]; + } + /* Denoise */ - [fPictureController setDenoise:[[queueToApply objectForKey:@"PictureDenoise"] intValue]]; + if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] > 0) + { + [fPictureController setDenoise:[[queueToApply objectForKey:@"PictureDenoise"] intValue]]; + /* if we are using "Custom" in the denoise setting, also set the custom string*/ + if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 1) + { + [fPictureController setDenoiseCustomString:[queueToApply objectForKey:@"PictureDenoiseCustom"]]; + } + } + else + { + [fPictureController setDenoise:0]; + } + /* Deblock */ - [fPictureController setDeblock:[[queueToApply objectForKey:@"PictureDeblock"] intValue]]; - /* Decomb */ - [fPictureController setDecomb:[[queueToApply objectForKey:@"PictureDecomb"] intValue]]; - /* Grayscale */ - [fPictureController setGrayscale:[[queueToApply objectForKey:@"VideoGrayScale"] intValue]]; + if ([[queueToApply 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:[[queueToApply objectForKey:@"PictureDeblock"] intValue]]; + } - [self calculatePictureSizing:nil]; + if ([[queueToApply objectForKey:@"VideoGrayScale"] intValue] == 1) + { + [fPictureController setGrayscale:1]; + } + else + { + [fPictureController setGrayscale:0]; + } + /* we call SetTitle: in fPictureController so we get an instant update in the Picture Settings window */ + [fPictureController SetTitle:fTitle]; + [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"]; + //[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*/ @@ -2792,108 +2973,13 @@ fWorkingCount = 0; /* 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]; + /* Not that source is loaded and settings applied, delete the queue item from the queue */ + [self writeToActivityLog: "applyQueueSettingsToMainWindow: deleting queue item:%d",fqueueEditRescanItemNum]; + [self removeQueueFileItem:fqueueEditRescanItemNum]; } -/* 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"]; - } - - NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex]; - [self writeToActivityLog: "Preset: %s", [[queueToApply objectForKey:@"PresetName"] UTF8String]]; - [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 scanning we need to do some extra setup of the job. - */ - if( job->indepth_scan == 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->x264opts = NULL; - - job->indepth_scan = 1; - - - /* - * Add the pre-scan job - */ - hb_add( fQueueEncodeLibhb, job ); - job->x264opts = x264opts_tmp; - } - - - if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 ) - { - job->indepth_scan = 0; - - - - 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]); - - 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 need to clean up the subtitle tracks after the job(s) have been set */ - int num_subtitle_tracks = hb_list_count(job->list_subtitle); - int ii; - for(ii = 0; ii < num_subtitle_tracks; ii++) - { - hb_subtitle_t * subtitle; - subtitle = (hb_subtitle_t *)hb_list_item(job->list_subtitle, 0); - - - hb_list_rem(job->list_subtitle, subtitle); - free(subtitle); - } - - - /* We should be all setup so let 'er rip */ - [self doRip]; -} - #pragma mark - #pragma mark Live Preview /* Note,this is much like prepareJob, but directly sets the job vars so Picture Preview @@ -2944,7 +3030,14 @@ fWorkingCount = 0; /* 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; + if ([fFrameratePfrCheck state] == 1) + { + job->cfr = 2; + } + else + { + job->cfr = 1; + } } else { @@ -3020,23 +3113,18 @@ bool one_burned = FALSE; [self writeToActivityLog: "Foreign Language Search: %d", 1]; job->indepth_scan = 1; - if (burned == 1 || job->mux != HB_MUX_MP4) + + if (burned != 1) { - if (burned != 1 && job->mux == HB_MUX_MKV) - { - job->select_subtitle_config.dest = PASSTHRUSUB; - } - else - { - job->select_subtitle_config.dest = RENDERSUB; - } - - job->select_subtitle_config.force = force; - job->select_subtitle_config.default_track = def; - + job->select_subtitle_config.dest = PASSTHRUSUB; + } + else + { + job->select_subtitle_config.dest = RENDERSUB; } - + job->select_subtitle_config.force = force; + job->select_subtitle_config.default_track = def; } else { @@ -3082,21 +3170,12 @@ bool one_burned = FALSE; if (subt != NULL) { - [self writeToActivityLog: "Setting Subtitle: %s", subt]; - hb_subtitle_config_t sub_config = subt->config; - if (!burned && job->mux == HB_MUX_MKV && - subt->format == PICTURESUB) + if ( !burned && subt->format == PICTURESUB ) { sub_config.dest = PASSTHRUSUB; } - else if (!burned && job->mux == HB_MUX_MP4 && - subt->format == PICTURESUB) - { - // Skip any non-burned vobsubs when output is mp4 - continue; - } else if ( burned && subt->format == PICTURESUB ) { // Only allow one subtitle to be burned into the video @@ -3120,83 +3199,7 @@ bool one_burned = FALSE; /* 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 ([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); - - } - - 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); - - } + [fAudioDelegate prepareAudioForJob: job]; @@ -3223,6 +3226,7 @@ bool one_burned = FALSE; */ /* Detelecine */ + hb_filter_detelecine.settings = NULL; if ([fPictureController detelecine] == 1) { /* use a custom detelecine string */ @@ -3241,6 +3245,7 @@ bool one_burned = FALSE; { /* Decomb */ /* we add the custom string if present */ + hb_filter_decomb.settings = NULL; if ([fPictureController decomb] == 1) { /* use a custom decomb string */ @@ -3338,10 +3343,46 @@ bool one_burned = FALSE; hb_audio_config_t * audio; /* Title Angle for dvdnav */ job->angle = [[queueToApply objectForKey:@"TitleAngle"] intValue]; - /* Chapter selection */ - job->chapter_start = [[queueToApply objectForKey:@"JobChapterStart"] intValue]; - job->chapter_end = [[queueToApply objectForKey:@"JobChapterEnd"] intValue]; + + if([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 0) + { + /* Chapter selection */ + [self writeToActivityLog: "Start / Stop set to chapters"]; + job->chapter_start = [[queueToApply objectForKey:@"JobChapterStart"] intValue]; + job->chapter_end = [[queueToApply objectForKey:@"JobChapterEnd"] intValue]; + } + else if ([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 1) + { + /* we are pts based start / stop */ + [self writeToActivityLog: "Start / Stop set to seconds ..."]; + + /* Point A to Point B. Time to time in seconds.*/ + /* get the start seconds from the start seconds field */ + int start_seconds = [[queueToApply objectForKey:@"StartSeconds"] intValue]; + job->pts_to_start = start_seconds * 90000LL; + /* Stop seconds is actually the duration of encode, so subtract the end seconds from the start seconds */ + int stop_seconds = [[queueToApply objectForKey:@"StopSeconds"] intValue]; + job->pts_to_stop = stop_seconds * 90000LL; + + } + else if ([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 2) + { + /* we are frame based start / stop */ + [self writeToActivityLog: "Start / Stop set to frames ..."]; + + /* Point A to Point B. Frame to frame */ + /* get the start frame from the start frame field */ + int start_frame = [[queueToApply objectForKey:@"StartFrame"] intValue]; + job->frame_to_start = start_frame; + /* get the frame to stop on from the end frame field */ + int stop_frame = [[queueToApply objectForKey:@"StopFrame"] intValue]; + job->frame_to_stop = stop_frame; + + } + + + /* Format (Muxer) and Video Encoder */ job->mux = [[queueToApply objectForKey:@"JobFileFormatMux"] intValue]; job->vcodec = [[queueToApply objectForKey:@"JobVideoEncoderVcodec"] intValue]; @@ -3440,13 +3481,14 @@ bool one_burned = FALSE; job->keep_ratio = [[queueToApply objectForKey:@"PictureKeepRatio"] intValue]; job->anamorphic.mode = [[queueToApply objectForKey:@"PicturePAR"] intValue]; + job->modulus = [[queueToApply objectForKey:@"PictureModulus"] intValue]; if ([[queueToApply objectForKey:@"PicturePAR"] intValue] == 3) { /* insert our custom values here for capuj */ job->width = [[queueToApply objectForKey:@"PicturePARStorageWidth"] intValue]; job->height = [[queueToApply objectForKey:@"PicturePARStorageHeight"] intValue]; - job->anamorphic.modulus = [[queueToApply objectForKey:@"PicturePARModulus"] intValue]; + job->modulus = [[queueToApply objectForKey:@"PicturePARModulus"] intValue]; job->anamorphic.par_width = [[queueToApply objectForKey:@"PicturePARPixelWidth"] intValue]; job->anamorphic.par_height = [[queueToApply objectForKey:@"PicturePARPixelHeight"] intValue]; @@ -3476,7 +3518,15 @@ bool one_burned = FALSE; /* 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; + + if ([[queueToApply objectForKey:@"VideoFrameratePFR"] intValue] == 1) + { + job->cfr = 2; + } + else + { + job->cfr = 1; + } } else { @@ -3558,22 +3608,18 @@ bool one_burned = FALSE; [self writeToActivityLog: "Foreign Language Search: %d", 1]; job->indepth_scan = 1; - if (burned == 1 || job->mux != HB_MUX_MP4) + + if (burned != 1) { - if (burned != 1 && job->mux == HB_MUX_MKV) - { - job->select_subtitle_config.dest = PASSTHRUSUB; - } - else - { - job->select_subtitle_config.dest = RENDERSUB; - } - - job->select_subtitle_config.force = force; - job->select_subtitle_config.default_track = def; + job->select_subtitle_config.dest = PASSTHRUSUB; + } + else + { + job->select_subtitle_config.dest = RENDERSUB; } - + job->select_subtitle_config.force = force; + job->select_subtitle_config.default_track = def; } else { @@ -3607,9 +3653,7 @@ bool one_burned = FALSE; sub_config.offset = [[tempObject objectForKey:@"subtitleTrackSrtOffset"] intValue]; /* we need to srncpy file name and codeset */ - //sub_config.src_filename = [[tempObject objectForKey:@"subtitleSourceSrtFilePath"] UTF8String]; strncpy(sub_config.src_filename, [[tempObject objectForKey:@"subtitleSourceSrtFilePath"] UTF8String], 128); - //sub_config.src_codeset = [[tempObject objectForKey:@"subtitleTrackSrtCharCode"] UTF8String]; strncpy(sub_config.src_codeset, [[tempObject objectForKey:@"subtitleTrackSrtCharCode"] UTF8String], 40); sub_config.force = 0; @@ -3622,21 +3666,12 @@ bool one_burned = FALSE; if (subt != NULL) { - [self writeToActivityLog: "Setting Subtitle: %s", subt]; - hb_subtitle_config_t sub_config = subt->config; - if (!burned && job->mux == HB_MUX_MKV && - subt->format == PICTURESUB) + if ( !burned && subt->format == PICTURESUB ) { sub_config.dest = PASSTHRUSUB; } - else if (!burned && job->mux == HB_MUX_MP4 && - subt->format == PICTURESUB) - { - // Skip any non-burned vobsubs when output is mp4 - continue; - } else if ( burned && subt->format == PICTURESUB ) { // Only allow one subtitle to be burned into the video @@ -3666,75 +3701,25 @@ bool one_burned = FALSE; 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 ); - - - } + for (unsigned int counter = 0; counter < maximumNumberOfAllowedAudioTracks; counter++) { + NSString *prefix = [NSString stringWithFormat: @"Audio%d", counter + 1]; + if ([[queueToApply objectForKey: [prefix stringByAppendingString: @"Track"]] intValue] > 0) { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [[queueToApply objectForKey: [prefix stringByAppendingString: @"Track"]] intValue] - 1; + /* We go ahead and assign values to our audio->out. */ + audio->out.track = audio->in.track; + audio->out.dynamic_range_compression = [[queueToApply objectForKey: [prefix stringByAppendingString: @"TrackDRCSlider"]] floatValue]; + prefix = [NSString stringWithFormat: @"JobAudio%d", counter + 1]; + audio->out.codec = [[queueToApply objectForKey: [prefix stringByAppendingString: @"Encoder"]] intValue]; + audio->out.mixdown = [[queueToApply objectForKey: [prefix stringByAppendingString: @"Mixdown"]] intValue]; + audio->out.bitrate = [[queueToApply objectForKey: [prefix stringByAppendingString: @"Bitrate"]] intValue]; + audio->out.samplerate = [[queueToApply objectForKey: [prefix stringByAppendingString: @"Samplerate"]] intValue]; + + hb_audio_add( job, audio ); + free(audio); + } + } /* Filters */ job->filters = hb_list_init(); @@ -3743,6 +3728,7 @@ bool one_burned = FALSE; * The order of the filters is critical */ /* Detelecine */ + hb_filter_detelecine.settings = NULL; if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1) { /* use a custom detelecine string */ @@ -3759,6 +3745,7 @@ bool one_burned = FALSE; { /* Decomb */ /* we add the custom string if present */ + hb_filter_decomb.settings = NULL; if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 1) { /* use a custom decomb string */ @@ -3843,7 +3830,7 @@ bool one_burned = FALSE; /* addToQueue: puts up an alert before ultimately calling doAddToQueue -*/ + */ - (IBAction) addToQueue: (id) sender { /* We get the destination directory from the destination field here */ @@ -3893,7 +3880,7 @@ bool one_burned = FALSE; } else if (fileExistsInQueue == YES) { - NSBeginCriticalAlertSheet( NSLocalizedString( @"There is already a queue item for this destination.", @"" ), + NSBeginCriticalAlertSheet( NSLocalizedString( @"There is already a queue item for this destination.", @"" ), NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self, @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ), NULL, NULL, [NSString stringWithFormat: @@ -3945,6 +3932,7 @@ bool one_burned = FALSE; // If there are pending jobs in the queue, then this is a rip the queue if (fPendingCount > 0) { + currentQueueEncodeIndex = [self getNextPendingQueueIndex]; /* here lets start the queue with the first pending item */ [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; @@ -3982,6 +3970,7 @@ bool one_burned = FALSE; } /* go right to processing the new queue encode */ + currentQueueEncodeIndex = [self getNextPendingQueueIndex]; [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; } @@ -4119,23 +4108,29 @@ bool one_burned = FALSE; // 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]]; + /* Check to see if there are any more pending items in the queue */ + int newQueueItemIndex = [self getNextPendingQueueIndex]; + /* If we still have more pending items in our queue, lets go to the next one */ + if (newQueueItemIndex >= 0 && newQueueItemIndex < queueItems) + { + /*Set our currentQueueEncodeIndex now to the newly found Pending encode as we own it */ + currentQueueEncodeIndex = newQueueItemIndex; + /* now we mark the queue item as Status = 1 ( being encoded ) so another instance can not come along and try to scan it while we are scanning */ + [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:1] forKey:@"Status"]; + [self writeToActivityLog: "incrementQueueItemDone new pending items found: %d", currentQueueEncodeIndex]; + [self saveQueueFileItem]; + /* now we can go ahead and scan the new pending queue item */ + [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; + } else { - [self writeToActivityLog: "doCancelCurrentJob the item queue is complete"]; + [self writeToActivityLog: "incrementQueueItemDone there are no more pending encodes"]; } - } - (void) doCancelCurrentJobAndStop @@ -4183,21 +4178,27 @@ bool one_burned = FALSE; hb_title_t * title = (hb_title_t*) hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] ); - /* If we are a stream type, grok the output file name from title->name upon title change */ - if (title->type == HB_STREAM_TYPE) + /* If we are a stream type and a batch scan, grok the output file name from title->name upon title change */ + if (title->type == HB_STREAM_TYPE && hb_list_count( list ) > 1 ) { /* we set the default name according to the new title->name */ [fDstFile2Field setStringValue: [NSString stringWithFormat: @"%@/%@.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent], [NSString stringWithUTF8String: title->name], [[fDstFile2Field stringValue] pathExtension]]]; - /* If we have more than one title and are stream then we have a batch, change the source to read out the parent folder also */ - if ( hb_list_count( list ) > 1 ) - { - [fSrcDVD2Field setStringValue:[NSString stringWithFormat:@"%@/%@", browsedSourceDisplayName,[NSString stringWithUTF8String: title->name]]]; - } + + /* Change the source to read out the parent folder also */ + [fSrcDVD2Field setStringValue:[NSString stringWithFormat:@"%@/%@", browsedSourceDisplayName,[NSString stringWithUTF8String: title->name]]]; } + /* For point a to point b pts encoding, set the start and end fields to 0 and the title duration in seconds respectively */ + int duration = (title->hours * 3600) + (title->minutes * 60) + (title->seconds); + [fSrcTimeStartEncodingField setStringValue: [NSString stringWithFormat: @"%d", 0]]; + [fSrcTimeEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", duration]]; + /* For point a to point b frame encoding, set the start and end fields to 0 and the title duration * announced fps in seconds respectively */ + [fSrcFrameStartEncodingField setStringValue: [NSString stringWithFormat: @"%d", 1]]; + //[fSrcFrameEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", ((title->hours * 3600) + (title->minutes * 60) + (title->seconds)) * 24]]; + [fSrcFrameEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", duration * (title->rate / title->rate_base)]]; /* If Auto Naming is on. We create an output filename of dvd name - title number */ if( [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultAutoNaming"] > 0 && ( hb_list_count( list ) > 1 ) ) @@ -4208,7 +4209,10 @@ bool one_burned = FALSE; title->index, [[fDstFile2Field stringValue] pathExtension]]]; } - + /* Update encode start / stop variables */ + + + /* Update chapter popups */ [fSrcChapterStartPopUp removeAllItems]; [fSrcChapterEndPopUp removeAllItems]; @@ -4273,7 +4277,15 @@ bool one_burned = FALSE; [fChapterTitlesDelegate resetWithTitle:title]; [fChapterTable reloadData]; - /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/ + /* Update audio table */ + [[NSNotificationCenter defaultCenter] postNotification: + [NSNotification notificationWithName: HBTitleChangedNotification + object: self + userInfo: [NSDictionary dictionaryWithObjectsAndKeys: + [NSData dataWithBytesNoCopy: &fTitle length: sizeof(fTitle) freeWhenDone: NO], keyTitleTag, + nil]]]; + + /* 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++) { @@ -4281,25 +4293,7 @@ bool one_burned = FALSE; 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: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]; - [self audioTrackPopUpChanged: fAudLang3PopUp]; - [self audioTrackPopUpChanged: fAudLang4PopUp]; - + [fVidRatePopUp selectItemAtIndex: 0]; /* we run the picture size values through calculatePictureSizing to get all picture setting information*/ @@ -4309,6 +4303,55 @@ bool one_burned = FALSE; [self selectPreset:nil]; } +- (IBAction) encodeStartStopPopUpChanged: (id) sender; +{ + if( [fEncodeStartStopPopUp isEnabled] ) + { + /* We are chapters */ + if( [fEncodeStartStopPopUp indexOfSelectedItem] == 0 ) + { + [fSrcChapterStartPopUp setHidden: NO]; + [fSrcChapterEndPopUp setHidden: NO]; + + [fSrcTimeStartEncodingField setHidden: YES]; + [fSrcTimeEndEncodingField setHidden: YES]; + + [fSrcFrameStartEncodingField setHidden: YES]; + [fSrcFrameEndEncodingField setHidden: YES]; + + [self chapterPopUpChanged:nil]; + } + /* We are time based (seconds) */ + else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 1) + { + [fSrcChapterStartPopUp setHidden: YES]; + [fSrcChapterEndPopUp setHidden: YES]; + + [fSrcTimeStartEncodingField setHidden: NO]; + [fSrcTimeEndEncodingField setHidden: NO]; + + [fSrcFrameStartEncodingField setHidden: YES]; + [fSrcFrameEndEncodingField setHidden: YES]; + + [self startEndSecValueChanged:nil]; + } + /* We are frame based */ + else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 2) + { + [fSrcChapterStartPopUp setHidden: YES]; + [fSrcChapterEndPopUp setHidden: YES]; + + [fSrcTimeStartEncodingField setHidden: YES]; + [fSrcTimeEndEncodingField setHidden: YES]; + + [fSrcFrameStartEncodingField setHidden: NO]; + [fSrcFrameEndEncodingField setHidden: NO]; + + [self startEndFrameValueChanged:nil]; + } + } +} + - (IBAction) chapterPopUpChanged: (id) sender { @@ -4337,7 +4380,7 @@ bool one_burned = FALSE; [fSrcDuration2Field setStringValue: [NSString stringWithFormat: @"%02lld:%02lld:%02lld", duration / 3600, ( duration / 60 ) % 60, duration % 60]]; - + [self calculateBitrate: sender]; if ( [fSrcChapterStartPopUp indexOfSelectedItem] == [fSrcChapterEndPopUp indexOfSelectedItem] ) @@ -4352,6 +4395,33 @@ bool one_burned = FALSE; } } +- (IBAction) startEndSecValueChanged: (id) sender +{ + + int duration = [fSrcTimeEndEncodingField intValue] - [fSrcTimeStartEncodingField intValue]; + [fSrcDuration2Field setStringValue: [NSString stringWithFormat: + @"%02d:%02d:%02d", duration / 3600, ( duration / 60 ) % 60, + duration % 60]]; + + //[self calculateBitrate: sender]; + +} + +- (IBAction) startEndFrameValueChanged: (id) sender +{ + hb_list_t * list = hb_get_titles( fHandle ); + hb_title_t * title = (hb_title_t*) + hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] ); + + int duration = ([fSrcFrameEndEncodingField intValue] - [fSrcFrameStartEncodingField intValue]) / (title->rate / title->rate_base); + [fSrcDuration2Field setStringValue: [NSString stringWithFormat: + @"%02d:%02d:%02d", duration / 3600, ( duration / 60 ) % 60, + duration % 60]]; + + //[self calculateBitrate: sender]; +} + + - (IBAction) formatPopUpChanged: (id) sender { NSString * string = [fDstFile2Field stringValue]; @@ -4413,6 +4483,15 @@ bool one_burned = FALSE; [fSubtitlesDelegate containerChanged:[[fDstFormatPopUp selectedItem] tag]]; [fSubtitlesTable reloadData]; + + /* post a notification for any interested observers to indicate that our video container has changed */ + [[NSNotificationCenter defaultCenter] postNotification: + [NSNotification notificationWithName: HBContainerChangedNotification + object: self + userInfo: [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt: [[fDstFormatPopUp selectedItem] tag]], keyContainerTag, + nil]]]; + /* if we have a previously selected vid encoder tag, then try to select it */ if (selectedVidEncoderTag) { @@ -4423,10 +4502,6 @@ bool one_burned = FALSE; [fVidEncoderPopUp selectItemAtIndex: 0]; } - [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp]; - [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp]; - [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp]; - [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp]; if( format == 0 ) [self autoSetM4vExtension: sender]; @@ -4436,10 +4511,6 @@ bool one_burned = FALSE; if( SuccessfulScan ) { /* Add/replace to the correct extension */ - [self audioTrackPopUpChanged: fAudLang1PopUp]; - [self audioTrackPopUpChanged: fAudLang2PopUp]; - [self audioTrackPopUpChanged: fAudLang3PopUp]; - [self audioTrackPopUpChanged: fAudLang4PopUp]; if( [fVidEncoderPopUp selectedItem] == nil ) { @@ -4465,9 +4536,8 @@ bool one_burned = FALSE; NSString * extension = @"mp4"; - 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 || + BOOL anyCodecAC3 = [fAudioDelegate anyCodecMatches: HB_ACODEC_AC3]; + if (YES == anyCodecAC3 || [fCreateChapterMarkers state] == NSOnState || [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0 ) { @@ -4566,6 +4636,17 @@ the user is using "Custom" settings by determining the sender*/ - (IBAction ) videoFrameRateChanged: (id) sender { + /* Hide and set the PFR Checkbox to OFF if we are set to Same as Source */ + if ([fVidRatePopUp indexOfSelectedItem] == 0) + { + [fFrameratePfrCheck setHidden:YES]; + [fFrameratePfrCheck setState:0]; + } + else + { + [fFrameratePfrCheck setHidden:NO]; + } + /* We call method method to calculatePictureSizing to error check detelecine*/ [self calculatePictureSizing: sender]; @@ -4675,20 +4756,15 @@ the user is using "Custom" settings by determining the sender*/ float sliderRfInverse = ([fVidQualitySlider maxValue] - [fVidQualitySlider floatValue]) + [fVidQualitySlider minValue]; /* If the encoder is theora, use the float, otherwise use the inverse float*/ - float sliderRfToPercent; + //float sliderRfToPercent; if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA) { - [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", [fVidQualitySlider floatValue]]]; - sliderRfToPercent = [fVidQualityRFField floatValue] / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]); + [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", [fVidQualitySlider floatValue]]]; } else { [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", sliderRfInverse]]; - sliderRfToPercent = ( ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) - ([fVidQualityRFField floatValue] - [fVidQualitySlider minValue])) / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]); } - [fVidConstantCell setTitle: [NSString stringWithFormat: - NSLocalizedString( @"Constant quality: %.2f %%", @"" ), 100 * sliderRfToPercent]]; - [self customSettingUsed: sender]; } @@ -4722,82 +4798,7 @@ the user is using "Custom" settings by determining the sender*/ 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); - - } - - 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); - - } + [fAudioDelegate prepareAudioForJob: job]; [fVidBitrateField setIntValue: hb_calc_bitrate( job, [fVidTargetSizeField intValue] )]; } @@ -4838,8 +4839,14 @@ the user is using "Custom" settings by determining the sender*/ fTitle->job->keep_ratio = 0; } - [fPictureSizeField setStringValue: [NSString stringWithFormat:@"Picture Size: %@", [fPictureController getPictureSizeInfoString]]]; - + if (fTitle->job->anamorphic.mode != 1) // we are not strict so show the modulus + { + [fPictureSizeField setStringValue: [NSString stringWithFormat:@"Picture Size: %@, Modulus: %d", [fPictureController getPictureSizeInfoString], fTitle->job->modulus]]; + } + else + { + [fPictureSizeField setStringValue: [NSString stringWithFormat:@"Picture Size: %@", [fPictureController getPictureSizeInfoString]]]; + } NSString *picCropping; /* Set the display field for crop as per boolean */ if (![fPictureController autoCrop]) @@ -4878,972 +4885,89 @@ the user is using "Custom" settings by determining the sender*/ { videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Decomb (%@)",[fPictureController decombCustomString]]]; } - } - else - { - /* Deinterlace */ - if ([fPictureController deinterlace] > 0) - { - fTitle->job->deinterlace = 1; - } - else - { - fTitle->job->deinterlace = 0; - } - - if ([fPictureController deinterlace] == 2) - { - videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Fast)"]; - } - else if ([fPictureController deinterlace] == 3) - { - videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slow)"]; - } - else if ([fPictureController deinterlace] == 4) - { - videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slower)"]; - } - else if ([fPictureController deinterlace] == 1) - { - videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deinterlace (%@)",[fPictureController deinterlaceCustomString]]]; - } - } - - - /* Denoise */ - if ([fPictureController denoise] == 2) - { - videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Weak)"]; - } - else if ([fPictureController denoise] == 3) - { - videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Medium)"]; - } - else if ([fPictureController denoise] == 4) - { - videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Strong)"]; - } - else if ([fPictureController denoise] == 1) - { - videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Denoise (%@)",[fPictureController denoiseCustomString]]]; - } - - /* Deblock */ - if ([fPictureController deblock] > 0) - { - videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deblock (%d)",[fPictureController deblock]]]; - } - - /* Grayscale */ - if ([fPictureController grayscale]) - { - videoFilters = [videoFilters stringByAppendingString:@" - Grayscale"]; - } - [fVideoFiltersField setStringValue: [NSString stringWithFormat:@"Video Filters: %@", videoFilters]]; - - //[fPictureController reloadStillPreview]; -} - - -#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]; - [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: 0.00]; - [self audioDRCSliderChanged: fAudTrack1DrcSlider]; - } - else if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_DCA) - { - [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]; - [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: 0.00]; - [self audioDRCSliderChanged: fAudTrack2DrcSlider]; - } - else if ([[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_DCA) - { - [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: 0.00]; - [self audioDRCSliderChanged: fAudTrack3DrcSlider]; - } - else if ([[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_DCA) - { - [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: 0.00]; - [self audioDRCSliderChanged: fAudTrack4DrcSlider]; - } - else if ([[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_DCA) - { - [fAudTrack4RatePopUp setEnabled: NO]; - [fAudTrack4BitratePopUp setEnabled: NO]; - [fAudTrack4DrcSlider setEnabled: NO]; - [fAudTrack4DrcField setEnabled: NO]; - } - -} - -- (IBAction) addAllAudioTracksToPopUp: (id) sender -{ - - hb_list_t * list = hb_get_titles( fHandle ); - hb_title_t * title = (hb_title_t*) - hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] ); - - hb_audio_config_t * audio; - - [sender removeAllItems]; - [sender addItemWithTitle: NSLocalizedString( @"None", @"" )]; - for( int i = 0; i < hb_list_count( title->list_audio ); i++ ) - { - audio = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, i ); - [[sender menu] addItemWithTitle: - [NSString stringWithUTF8String: audio->lang.description] - action: NULL keyEquivalent: @""]; - } - [sender selectItemAtIndex: 0]; - -} - -- (IBAction) selectAudioTrackInPopUp: (id) sender searchPrefixString: (NSString *) searchPrefixString selectIndexIfNotFound: (int) selectIndexIfNotFound -{ - - /* this method can be used to find a language, or a language-and-source-format combination, by passing in the appropriate string */ - /* 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) - { - - for( int i = 0; i < [sender numberOfItems]; i++ ) - { - /* Try to find the desired search string */ - if ([[[sender itemAtIndex: i] title] hasPrefix:searchPrefixString]) - { - [sender selectItemAtIndex: i]; - return; - } - } - /* couldn't find the string, so select the requested "search string not found" item */ - /* index of 0 means select the "none" item */ - /* index of 1 means select the first audio track */ - [sender selectItemAtIndex: selectIndexIfNotFound]; - } - else - { - /* if no search string is provided, then select the selectIndexIfNotFound item */ - [sender selectItemAtIndex: selectIndexIfNotFound]; - } - -} -- (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 */ - // FAAC - menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""]; - [menuItem setTag: HB_ACODEC_FAAC]; - - // CA_AAC - menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (CoreAudio)" action: NULL keyEquivalent: @""]; - [menuItem setTag: HB_ACODEC_CA_AAC]; - - // AC3 Passthru - menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""]; - [menuItem setTag: HB_ACODEC_AC3]; - break; - - case 1: - /* MKV */ - // FAAC - menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""]; - [menuItem setTag: HB_ACODEC_FAAC]; - // CA_AAC - menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (CoreAudio)" action: NULL keyEquivalent: @""]; - [menuItem setTag: HB_ACODEC_CA_AAC]; - // AC3 Passthru - menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""]; - [menuItem setTag: HB_ACODEC_AC3]; - // DTS Passthru - menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"DTS Passthru" action: NULL keyEquivalent: @""]; - [menuItem setTag: HB_ACODEC_DCA]; - // 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 -{ - /* utility function to call audioTrackPopUpChanged without passing in a mixdown-to-use */ - [self audioTrackPopUpChanged: sender mixdownToUse: 0]; -} - -- (IBAction) audioTrackPopUpChanged: (id) sender mixdownToUse: (int) mixdownToUse -{ - - /* make sure we have a selected title before continuing */ - if (fTitle == NULL) return; - /* 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]; - } - - /* Now lets make the sender the appropriate Audio Track popup from this point on */ - if (sender == fAudTrack1CodecPopUp || sender == fAudTrack1MixPopUp) - { - sender = fAudLang1PopUp; - } - if (sender == fAudTrack2CodecPopUp || sender == fAudTrack2MixPopUp) - { - sender = fAudLang2PopUp; - } - 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; - NSPopUpButton * audiocodecPopUp; - NSPopUpButton * sampleratePopUp; - NSPopUpButton * bitratePopUp; - if (sender == fAudLang1PopUp) - { - mixdownPopUp = fAudTrack1MixPopUp; - audiocodecPopUp = fAudTrack1CodecPopUp; - sampleratePopUp = fAudTrack1RatePopUp; - bitratePopUp = fAudTrack1BitratePopUp; - } - else if (sender == fAudLang2PopUp) - { - 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; - } - - /* 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:nil]; - - if (thisAudioIndex != -1) - { - - /* get the audio */ - 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 */ - /* audioCodecsSupportMono and audioCodecsSupport6Ch are the same for now, - 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->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3) - { - - NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: - [NSString stringWithUTF8String: "AC3 Passthru"] - action: NULL keyEquivalent: @""]; - [menuItem setTag: HB_ACODEC_AC3]; - } - else if (audio->in.codec == HB_ACODEC_DCA && acodec == HB_ACODEC_DCA) - { - NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: - [NSString stringWithUTF8String: "DTS Passthru"] - action: NULL keyEquivalent: @""]; - [menuItem setTag: HB_ACODEC_DCA]; - } - 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 */ - - /* 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->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK; - - /* do we want to add a mono option? */ - if (audioCodecsSupportMono == 1) - { - NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: - [NSString stringWithUTF8String: 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 ((layout == HB_INPUT_CH_LAYOUT_MONO && audioCodecsSupportMono == 0) || layout >= HB_INPUT_CH_LAYOUT_STEREO) - { - NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: - [NSString stringWithUTF8String: 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 (layout == HB_INPUT_CH_LAYOUT_3F1R || layout == HB_INPUT_CH_LAYOUT_3F2R || layout == HB_INPUT_CH_LAYOUT_DOLBY) - { - NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: - [NSString stringWithUTF8String: 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 (layout == HB_INPUT_CH_LAYOUT_3F2R) - { - NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: - [NSString stringWithUTF8String: 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 (audioCodecsSupport6Ch == 1 && layout == HB_INPUT_CH_LAYOUT_3F2R && (audio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE)) - { - NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: - [NSString stringWithUTF8String: 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->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3) - { - NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: - [NSString stringWithUTF8String: 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 a DTS Passthru option ? HB_ACODEC_DCA*/ - if (audio->in.codec == HB_ACODEC_DCA && acodec == HB_ACODEC_DCA) - { - NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: - [NSString stringWithUTF8String: hb_audio_mixdowns[5].human_readable_name] - action: NULL keyEquivalent: @""]; - [menuItem setTag: HB_ACODEC_DCA]; - if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown; - maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].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 */ - /* ultimately this should be a prefs option */ - int useMixdown; - - /* if we passed in a mixdown to use - in order to load a preset - then try and use it */ - if (mixdownToUse > 0) - { - useMixdown = mixdownToUse; - } - else - { - useMixdown = HB_AMIXDOWN_DOLBYPLII; - } - - /* if useMixdown > maxMixdownUsed, then use maxMixdownUsed */ - if (useMixdown > maxMixdownUsed) - { - useMixdown = maxMixdownUsed; - } - - /* if useMixdown < minMixdownUsed, then use minMixdownUsed */ - if (useMixdown < minMixdownUsed) - { - useMixdown = minMixdownUsed; - } - - /* select the (possibly-amended) preferred mixdown */ - [mixdownPopUp selectItemWithTag: useMixdown]; - - } - /* 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]; - } - } - - /* In the case of a source track that is not DTS and the user tries to use DTS 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_DCA && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_DCA) - { - /* 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]; - } -} - -- (IBAction) audioTrackMixdownChanged: (id) sender -{ - - 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; - - switch( acodec ) - { - case HB_ACODEC_FAAC: - /* check if we have a 6ch discrete conversion in either audio track */ - if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH) - { - /* FAAC has a minimum of 192 kbps for 6-channel discrete */ - minbitrate = 192; - /* If either mixdown popup includes 6-channel discrete, then allow up to 448 kbps */ - maxbitrate = 448; - break; - } - else - { - /* FAAC is happy using our min bitrate of 32 kbps for stereo or mono */ - minbitrate = 32; - /* FAAC won't honour anything more than 160 for stereo, so let's not offer it */ - /* note: haven't dealt with mono separately here, FAAC will just use the max it can */ - maxbitrate = 160; - break; - } - - case HB_ACODEC_CA_AAC: - /* check if we have a 6ch discrete conversion in either audio track */ - if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH) - { - minbitrate = 128; - maxbitrate = 768; - break; - } - else - { - minbitrate = 64; - maxbitrate = 320; - break; - } - - 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 ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH) - { - /* Vorbis causes a crash if we use a bitrate below 192 kbps with 6 channel */ - minbitrate = 192; - /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */ - maxbitrate = 384; - break; - } - 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; - } - - default: - /* AC3 passthru disables the bitrate dropdown anyway, so we might as well just use the min and max bitrate */ - minbitrate = 32; - maxbitrate = 384; - - } - - /* 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 && [[mixdownPopUp selectedItem] tag] != HB_ACODEC_DCA) + } + else { - [bitratePopUp removeAllItems]; - - for( int i = 0; i < hb_audio_bitrates_count; i++ ) + /* Deinterlace */ + if ([fPictureController deinterlace] > 0) { - 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 stringWithUTF8String: 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]; - } + fTitle->job->deinterlace = 1; + } + else + { + fTitle->job->deinterlace = 0; } - /* select the default bitrate (but use 384 for 6-ch AAC) */ - if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH) + if ([fPictureController deinterlace] == 2) { - [bitratePopUp selectItemWithTag: 384]; + videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Fast)"]; } - else + else if ([fPictureController deinterlace] == 3) { - [bitratePopUp selectItemWithTag: hb_audio_bitrates[hb_audio_bitrates_default].rate]; + videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slow)"]; } - } - /* 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 stringWithUTF8String: 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]; + else if ([fPictureController deinterlace] == 4) + { + videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slower)"]; + } + else if ([fPictureController deinterlace] == 1) + { + videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deinterlace (%@)",[fPictureController deinterlaceCustomString]]]; + } + } - /* Since AC3 Pass Thru and DTS Pass Thru uses the input bitrate and sample rate, we get the input tracks - * bitrate and display 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 || [[mixdownPopUp selectedItem] tag] == HB_ACODEC_DCA) - { - - /* 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: 0.00]; - [self audioDRCSliderChanged: drcSlider]; - [drcSlider setEnabled: NO]; - [drcField setEnabled: NO]; - } - else - { - [sampleratePopUp setEnabled: YES]; - [bitratePopUp setEnabled: YES]; - [drcSlider setEnabled: YES]; - [drcField setEnabled: YES]; - } -[self calculateBitrate:nil]; -} - -- (IBAction) audioDRCSliderChanged: (id) sender -{ - NSSlider * drcSlider; - NSTextField * drcField; - if (sender == fAudTrack1DrcSlider) - { - drcSlider = fAudTrack1DrcSlider; - drcField = fAudTrack1DrcField; - } - else if (sender == fAudTrack2DrcSlider) - { - drcSlider = fAudTrack2DrcSlider; - drcField = fAudTrack2DrcField; + /* Denoise */ + if ([fPictureController denoise] == 2) + { + videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Weak)"]; } - else if (sender == fAudTrack3DrcSlider) - { - drcSlider = fAudTrack3DrcSlider; - drcField = fAudTrack3DrcField; + else if ([fPictureController denoise] == 3) + { + videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Medium)"]; } - else + else if ([fPictureController denoise] == 4) + { + videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Strong)"]; + } + else if ([fPictureController denoise] == 1) + { + videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Denoise (%@)",[fPictureController denoiseCustomString]]]; + } + + /* Deblock */ + if ([fPictureController deblock] > 0) { - drcSlider = fAudTrack4DrcSlider; - drcField = fAudTrack4DrcField; + videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deblock (%d)",[fPictureController deblock]]]; } - - /* If we are between 0.0 and 1.0 on the slider, snap it to 1.0 */ - if ([drcSlider floatValue] > 0.0 && [drcSlider floatValue] < 1.0) + + /* Grayscale */ + if ([fPictureController grayscale]) { - [drcSlider setFloatValue:1.0]; + videoFilters = [videoFilters stringByAppendingString:@" - Grayscale"]; } + [fVideoFiltersField setStringValue: [NSString stringWithFormat:@"Video Filters: %@", videoFilters]]; - - [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]; + //[fPictureController reloadStillPreview]; } + +#pragma mark - +#pragma mark - Audio and Subtitles + + #pragma mark - +@synthesize hasValidPresetSelected; + +// This causes all audio tracks from the title to be used based on the current preset +- (IBAction) addAllAudioTracks: (id) sender + +{ + [fAudioDelegate addAllTracksFromPreset: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]]; + return; +} + - (IBAction) browseImportSrtFile: (id) sender { @@ -5900,12 +5024,12 @@ the user is using "Custom" settings by determining the sender*/ - (IBAction) openForums: (id) sender { [[NSWorkspace sharedWorkspace] openURL: [NSURL - URLWithString:@"http://handbrake.fr/forum/"]]; + URLWithString:@"http://forum.handbrake.fr/"]]; } - (IBAction) openUserGuide: (id) sender { [[NSWorkspace sharedWorkspace] openURL: [NSURL - URLWithString:@"http://handbrake.fr/trac/wiki/HandBrakeGuide"]]; + URLWithString:@"http://trac.handbrake.fr/wiki/HandBrakeGuide"]]; } /** @@ -6307,6 +5431,7 @@ return YES; if ([fPresetsOutlineView selectedRow] >= 0 && [[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] != 1) { + [self setHasValidPresetSelected: YES]; chosenPreset = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]; [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]]; @@ -6389,7 +5514,9 @@ return YES; { [fVidRatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoFramerate"]]; } - + /* Set PFR */ + [fFrameratePfrCheck setState:[[chosenPreset objectForKey:@"VideoFrameratePFR"] intValue]]; + [self videoFrameRateChanged:nil]; /* 2 Pass Encoding */ [fVidTwoPassCheck setState:[[chosenPreset objectForKey:@"VideoTwoPass"] intValue]]; @@ -6399,293 +5526,7 @@ return YES; [fVidTurboPassCheck setState:[[chosenPreset objectForKey:@"VideoTurboTwoPass"] intValue]]; /*Audio*/ - /* First we check to see if we are using the current audio track layout based on AudioList array */ - if ([chosenPreset objectForKey:@"AudioList"]) - { - - /* pointer to this track's mixdown, codec, sample rate and bitrate NSPopUpButton's */ - NSPopUpButton * trackLangPopUp = nil; - NSPopUpButton * mixdownPopUp = nil; - NSPopUpButton * audiocodecPopUp = nil; - NSPopUpButton * sampleratePopUp = nil; - NSPopUpButton * bitratePopUp = nil; - NSSlider * drcSlider = nil; - - - /* Populate the audio widgets based on the contents of the AudioList array */ - int i = 0; - NSEnumerator *enumerator = [[chosenPreset objectForKey:@"AudioList"] objectEnumerator]; - id tempObject; - while (tempObject = [enumerator nextObject]) - { - i++; - if( i == 1 ) - { - trackLangPopUp = fAudLang1PopUp; - mixdownPopUp = fAudTrack1MixPopUp; - audiocodecPopUp = fAudTrack1CodecPopUp; - sampleratePopUp = fAudTrack1RatePopUp; - bitratePopUp = fAudTrack1BitratePopUp; - drcSlider = fAudTrack1DrcSlider; - } - if( i == 2 ) - { - trackLangPopUp = fAudLang2PopUp; - mixdownPopUp = fAudTrack2MixPopUp; - audiocodecPopUp = fAudTrack2CodecPopUp; - sampleratePopUp = fAudTrack2RatePopUp; - bitratePopUp = fAudTrack2BitratePopUp; - drcSlider = fAudTrack2DrcSlider; - } - if( i == 3 ) - { - trackLangPopUp = fAudLang3PopUp; - mixdownPopUp = fAudTrack3MixPopUp; - audiocodecPopUp = fAudTrack3CodecPopUp; - sampleratePopUp = fAudTrack3RatePopUp; - bitratePopUp = fAudTrack3BitratePopUp; - drcSlider = fAudTrack3DrcSlider; - } - if( i == 4 ) - { - trackLangPopUp = fAudLang4PopUp; - mixdownPopUp = fAudTrack4MixPopUp; - audiocodecPopUp = fAudTrack4CodecPopUp; - sampleratePopUp = fAudTrack4RatePopUp; - bitratePopUp = fAudTrack4BitratePopUp; - drcSlider = fAudTrack4DrcSlider; - } - - - if ([trackLangPopUp indexOfSelectedItem] == 0) - { - [trackLangPopUp selectItemAtIndex: 1]; - } - [self audioTrackPopUpChanged: trackLangPopUp]; - [audiocodecPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioEncoder"]]; - /* check our pref for core audio and use it in place of faac if applicable */ - if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && - [[tempObject objectForKey:@"AudioEncoder"] isEqualToString: @"AAC (faac)"]) - { - [audiocodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"]; - } - - [self audioTrackPopUpChanged: audiocodecPopUp]; - [mixdownPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioMixdown"]]; - /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default - * mixdown*/ - if ([mixdownPopUp selectedItem] == nil) - { - [self audioTrackPopUpChanged: audiocodecPopUp]; - } - [sampleratePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioSamplerate"]]; - /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ - if (![[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"]) - { - [bitratePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioBitrate"]]; - } - [drcSlider setFloatValue:[[tempObject objectForKey:@"AudioTrackDRCSlider"] floatValue]]; - [self audioDRCSliderChanged: drcSlider]; - - - /* If we are any track greater than 1 check to make sure we have a matching source codec is using ac3 passthru or dts passthru, - * if not we will set the track to "None". Track 1 is allowed to mixdown to a suitable DPL2 mix if we cannot passthru */ - - if( i > 1 ) - { - /* Check to see if the preset asks for a passhthru track (AC3 or DTS) and verify there is a matching source track if not, set the track to "None". */ - if (([[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"] || [[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"DTS Passthru"]) && [trackLangPopUp indexOfSelectedItem] != 0) - { - hb_audio_config_t * audio; - /* get the audio source audio codec */ - audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, [trackLangPopUp indexOfSelectedItem] - 1 ); - if (audio != NULL && [[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"] && audio->in.codec != HB_ACODEC_AC3 || - [[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"DTS Passthru"] && audio->in.codec != HB_ACODEC_DCA ) - { - /* We have a preset using ac3 passthru but no ac3 source audio, so set the track to "None" and bail */ - if ([[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"]) - { - [self writeToActivityLog: "Preset calls for AC3 Pass thru ..."]; - } - if ([[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"DTS Passthru"]) - { - [self writeToActivityLog: "Preset calls for DTS Pass thru ..."]; - } - [self writeToActivityLog: "No matching source codec, setting track %d to None", i]; - [trackLangPopUp selectItemAtIndex: 0]; - [self audioTrackPopUpChanged: trackLangPopUp]; - } - } - } - } - - /* We now cleanup any extra audio tracks that may have been previously set if we need to */ - - if (i < 4) - { - [fAudLang4PopUp selectItemAtIndex: 0]; - [self audioTrackPopUpChanged: fAudLang4PopUp]; - - if (i < 3) - { - [fAudLang3PopUp selectItemAtIndex: 0]; - [self audioTrackPopUpChanged: fAudLang3PopUp]; - - if (i < 2) - { - [fAudLang2PopUp selectItemAtIndex: 0]; - [self audioTrackPopUpChanged: fAudLang2PopUp]; - } - } - } - - } - else - { - if ([chosenPreset objectForKey:@"Audio1Track"] > 0) - { - if ([fAudLang1PopUp indexOfSelectedItem] == 0) - { - [fAudLang1PopUp selectItemAtIndex: 1]; - } - [self audioTrackPopUpChanged: fAudLang1PopUp]; - [fAudTrack1CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Encoder"]]; - /* check our pref for core audio and use it in place of faac if applicable */ - if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && - [[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString: @"AAC (faac)"]) - { - [fAudTrack1CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"]; - } - [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"]]; - /* check our pref for core audio and use it in place of faac if applicable */ - if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && - [[chosenPreset objectForKey:@"Audio2Encoder"] isEqualToString: @"AAC (faac)"]) - { - [fAudTrack2CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"]; - } - [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"]]; - /* check our pref for core audio and use it in place of faac if applicable */ - if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && - [[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AAC (faac)"]) - { - [fAudTrack3CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"]; - } - [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"]]; - /* check our pref for core audio and use it in place of faac if applicable */ - if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && - [[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString: @"AAC (faac)"]) - { - [fAudTrack4CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"]; - } - [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 have been previously set if we need to */ - - 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]; - } - } + [fAudioDelegate addTracksFromPreset: chosenPreset]; /*Subtitles*/ [fSubPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Subtitles"]]; @@ -6729,7 +5570,16 @@ return YES; } - + /* Set modulus */ + if ([chosenPreset objectForKey:@"PictureModulus"]) + { + job->modulus = [[chosenPreset objectForKey:@"PictureModulus"] intValue]; + } + else + { + job->modulus = 16; + } + /* 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) { @@ -6766,14 +5616,36 @@ return YES; job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"] intValue]; if (job->keep_ratio == 1) { + int height = fTitle->height; + + if ( job->height && job->height < fTitle->height ) + height = job->height; + hb_fix_aspect( job, HB_KEEP_WIDTH ); - if( job->height > fTitle->height ) + // Make sure the resulting height is less than + // the title height and less than the height + // requested in the preset. + if( job->height > height ) { - job->height = fTitle->height; + job->height = height; hb_fix_aspect( job, HB_KEEP_HEIGHT ); } } job->anamorphic.mode = [[chosenPreset objectForKey:@"PicturePAR"] intValue]; + if ( job->anamorphic.mode > 0 ) + { + int w, h, par_w, par_h; + + job->anamorphic.par_width = fTitle->pixel_aspect_width; + job->anamorphic.par_height = fTitle->pixel_aspect_height; + job->maxWidth = job->width; + job->maxHeight = job->height; + hb_set_anamorphic_size( job, &w, &h, &par_w, &par_h ); + job->maxWidth = 0; + job->maxHeight = 0; + job->width = w; + job->height = h; + } } @@ -6880,6 +5752,9 @@ return YES; [fPictureController SetTitle:fTitle]; [self calculatePictureSizing:nil]; } + else { + [self setHasValidPresetSelected: NO]; + } } @@ -6948,6 +5823,18 @@ return YES; } +- (IBAction) addPresetPicDropdownChanged: (id) sender +{ + if ([fPresetNewPicSettingsPopUp indexOfSelectedItem] == 1) + { + [fPresetNewPicWidthHeightBox setHidden:NO]; + } + else + { + [fPresetNewPicWidthHeightBox setHidden:YES]; + } +} + - (IBAction) showAddPresetPanel: (id) sender { /* Deselect the currently selected Preset if there is one*/ @@ -6956,7 +5843,7 @@ return YES; /* Populate the preset picture settings popup here */ [fPresetNewPicSettingsPopUp removeAllItems]; [fPresetNewPicSettingsPopUp addItemWithTitle:@"None"]; - [fPresetNewPicSettingsPopUp addItemWithTitle:@"Current"]; + [fPresetNewPicSettingsPopUp addItemWithTitle:@"Custom"]; [fPresetNewPicSettingsPopUp addItemWithTitle:@"Source Maximum (post source scan)"]; [fPresetNewPicSettingsPopUp selectItemAtIndex: 0]; /* Uncheck the preset use filters checkbox */ @@ -6966,6 +5853,12 @@ return YES; /* Erase info from the input fields*/ [fPresetNewName setStringValue: @""]; [fPresetNewDesc setStringValue: @""]; + + /* Initialize custom height and width settings to current values */ + + [fPresetNewPicWidth setStringValue: [NSString stringWithFormat:@"%d",fTitle->job->width]]; + [fPresetNewPicHeight setStringValue: [NSString stringWithFormat:@"%d",fTitle->job->height]]; + [self addPresetPicDropdownChanged:nil]; /* Show the panel */ [NSApp beginSheet:fAddPresetPanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; } @@ -7090,6 +5983,7 @@ return YES; { [preset setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"]; } + [preset setObject:[NSNumber numberWithInt:[fFrameratePfrCheck state]] forKey:@"VideoFrameratePFR"]; /* 2 Pass Encoding */ [preset setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"]; @@ -7097,13 +5991,14 @@ return YES; [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:[fPresetNewPicWidth intValue]] forKey:@"PictureWidth"]; + [preset setObject:[NSNumber numberWithInt:[fPresetNewPicHeight intValue]] forKey:@"PictureHeight"]; [preset setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"]; [preset setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"]; + [preset setObject:[NSNumber numberWithInt:fTitle->job->modulus] forKey:@"PictureModulus"]; /* Set crop settings here */ [preset setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"]; @@ -7127,62 +6022,7 @@ return YES; /*Audio*/ NSMutableArray *audioListArray = [[NSMutableArray alloc] init]; - /* we actually call the methods for the nests here */ - if ([fAudLang1PopUp indexOfSelectedItem] > 0) - { - NSMutableDictionary *audioTrack1Array = [[NSMutableDictionary alloc] init]; - [audioTrack1Array setObject:[NSNumber numberWithInt:[fAudLang1PopUp indexOfSelectedItem]] forKey:@"AudioTrack"]; - [audioTrack1Array setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"]; - [audioTrack1Array setObject:[fAudTrack1CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"]; - [audioTrack1Array setObject:[fAudTrack1MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"]; - [audioTrack1Array setObject:[fAudTrack1RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"]; - [audioTrack1Array setObject:[fAudTrack1BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"]; - [audioTrack1Array setObject:[NSNumber numberWithFloat:[fAudTrack1DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"]; - [audioTrack1Array autorelease]; - [audioListArray addObject:audioTrack1Array]; - } - - if ([fAudLang2PopUp indexOfSelectedItem] > 0) - { - NSMutableDictionary *audioTrack2Array = [[NSMutableDictionary alloc] init]; - [audioTrack2Array setObject:[NSNumber numberWithInt:[fAudLang2PopUp indexOfSelectedItem]] forKey:@"AudioTrack"]; - [audioTrack2Array setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"]; - [audioTrack2Array setObject:[fAudTrack2CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"]; - [audioTrack2Array setObject:[fAudTrack2MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"]; - [audioTrack2Array setObject:[fAudTrack2RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"]; - [audioTrack2Array setObject:[fAudTrack2BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"]; - [audioTrack2Array setObject:[NSNumber numberWithFloat:[fAudTrack2DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"]; - [audioTrack2Array autorelease]; - [audioListArray addObject:audioTrack2Array]; - } - - if ([fAudLang3PopUp indexOfSelectedItem] > 0) - { - NSMutableDictionary *audioTrack3Array = [[NSMutableDictionary alloc] init]; - [audioTrack3Array setObject:[NSNumber numberWithInt:[fAudLang3PopUp indexOfSelectedItem]] forKey:@"AudioTrack"]; - [audioTrack3Array setObject:[fAudLang3PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"]; - [audioTrack3Array setObject:[fAudTrack3CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"]; - [audioTrack3Array setObject:[fAudTrack3MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"]; - [audioTrack3Array setObject:[fAudTrack3RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"]; - [audioTrack3Array setObject:[fAudTrack3BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"]; - [audioTrack3Array setObject:[NSNumber numberWithFloat:[fAudTrack3DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"]; - [audioTrack3Array autorelease]; - [audioListArray addObject:audioTrack3Array]; - } - - if ([fAudLang4PopUp indexOfSelectedItem] > 0) - { - NSMutableDictionary *audioTrack4Array = [[NSMutableDictionary alloc] init]; - [audioTrack4Array setObject:[NSNumber numberWithInt:[fAudLang4PopUp indexOfSelectedItem]] forKey:@"AudioTrack"]; - [audioTrack4Array setObject:[fAudLang4PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"]; - [audioTrack4Array setObject:[fAudTrack4CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"]; - [audioTrack4Array setObject:[fAudTrack4MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"]; - [audioTrack4Array setObject:[fAudTrack4RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"]; - [audioTrack4Array setObject:[fAudTrack4BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"]; - [audioTrack4Array setObject:[NSNumber numberWithFloat:[fAudTrack4DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"]; - [audioTrack4Array autorelease]; - [audioListArray addObject:audioTrack4Array]; - } + [fAudioDelegate prepareAudioForPreset: audioListArray]; [preset setObject:[NSMutableArray arrayWithArray: audioListArray] forKey:@"AudioList"]; @@ -7661,6 +6501,154 @@ return YES; } +#pragma mark - +#pragma mark Chapter Files Import / Export + +- (IBAction) browseForChapterFile: (id) sender +{ + /* Open a panel to let the user choose the file */ + NSOpenPanel * panel = [NSOpenPanel openPanel]; + /* We get the current file name and path from the destination field here */ + [panel beginSheetForDirectory: [NSString stringWithFormat:@"%@/", + [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"]] + file: NULL + types: [NSArray arrayWithObjects:@"csv",nil] + modalForWindow: fWindow modalDelegate: self + didEndSelector: @selector( browseForChapterFileDone:returnCode:contextInfo: ) + contextInfo: NULL]; +} + +- (void) browseForChapterFileDone: (NSOpenPanel *) sheet + returnCode: (int) returnCode contextInfo: (void *) contextInfo +{ + NSArray *chaptersArray; /* temp array for chapters */ + NSMutableArray *chaptersMutableArray; /* temp array for chapters */ + NSString *chapterName; /* temp string from file */ + int chapters, i; + + if( returnCode == NSOKButton ) /* if they click OK */ + { + chapterName = [[NSString alloc] initWithContentsOfFile:[sheet filename] encoding:NSUTF8StringEncoding error:NULL]; + chaptersArray = [chapterName componentsSeparatedByString:@"\n"]; + chaptersMutableArray= [chaptersArray mutableCopy]; + chapters = [fChapterTitlesDelegate numberOfRowsInTableView:fChapterTable]; + if ([chaptersMutableArray count] > 0) + { + /* if last item is empty remove it */ + if ([[chaptersMutableArray objectAtIndex:[chaptersArray count]-1] length] == 0) + { + [chaptersMutableArray removeLastObject]; + } + } + /* if chapters in table is not equal to array count */ + if ((unsigned int) chapters != [chaptersMutableArray count]) + { + [sheet close]; + [[NSAlert alertWithMessageText:NSLocalizedString(@"Unable to load chapter file", @"Unable to load chapter file") + defaultButton:NSLocalizedString(@"OK", @"OK") + alternateButton:NULL + otherButton:NULL + informativeTextWithFormat:NSLocalizedString(@"%d chapters expected, %d chapters found in %@", @"%d chapters expected, %d chapters found in %@"), + chapters, [chaptersMutableArray count], [[sheet filename] lastPathComponent]] runModal]; + return; + } + /* otherwise, go ahead and populate table with array */ + for (i=0; i 5) + { + /* avoid a segfault */ + /* Get the Range.location of the first comma in the line and then put everything after that into chapterTitle */ + NSRange firstCommaRange = [[chaptersMutableArray objectAtIndex:i] rangeOfString:@","]; + NSString *chapterTitle = [[chaptersMutableArray objectAtIndex:i] substringFromIndex:firstCommaRange.location + 1]; + /* Since we store our chapterTitle commas as "\," for the cli, we now need to remove the escaping "\" from the title */ + chapterTitle = [chapterTitle stringByReplacingOccurrencesOfString:@"\\," withString:@","]; + [fChapterTitlesDelegate tableView:fChapterTable + setObjectValue:chapterTitle + forTableColumn:fChapterTableNameColumn + row:i]; + } + else + { + [sheet close]; + [[NSAlert alertWithMessageText:NSLocalizedString(@"Unable to load chapter file", @"Unable to load chapter file") + defaultButton:NSLocalizedString(@"OK", @"OK") + alternateButton:NULL + otherButton:NULL + informativeTextWithFormat:NSLocalizedString(@"%@ was not formatted as expected.", @"%@ was not formatted as expected."), [[sheet filename] lastPathComponent]] runModal]; + [fChapterTable reloadData]; + return; + } + } + [fChapterTable reloadData]; + } +} + +- (IBAction) browseForChapterFileSave: (id) sender +{ + NSSavePanel *panel = [NSSavePanel savePanel]; + /* Open a panel to let the user save to a file */ + [panel setAllowedFileTypes:[NSArray arrayWithObjects:@"csv",nil]]; + [panel beginSheetForDirectory: [[fDstFile2Field stringValue] stringByDeletingLastPathComponent] + file: [[[[fDstFile2Field stringValue] lastPathComponent] stringByDeletingPathExtension] + stringByAppendingString:@"-chapters.csv"] + modalForWindow: fWindow + modalDelegate: self + didEndSelector: @selector( browseForChapterFileSaveDone:returnCode:contextInfo: ) + contextInfo: NULL]; +} + +- (void) browseForChapterFileSaveDone: (NSSavePanel *) sheet + returnCode: (int) returnCode contextInfo: (void *) contextInfo +{ + NSString *chapterName; /* pointer for string for later file-writing */ + NSString *chapterTitle; + NSError *saveError = [[NSError alloc] init]; + int chapters, i; /* ints for the number of chapters in the table and the loop */ + + if( returnCode == NSOKButton ) /* if they clicked OK */ + { + chapters = [fChapterTitlesDelegate numberOfRowsInTableView:fChapterTable]; + chapterName = [NSString string]; + for (i=0; i