1 /* $Id: Controller.mm,v 1.79 2005/11/04 19:41:32 titer Exp $
3 This file is part of the HandBrake source code.
4 Homepage: <http://handbrake.fr/>.
5 It may be used under the terms of the GNU General Public License. */
8 #import "HBOutputPanelController.h"
9 #import "HBPreferencesController.h"
10 #import "HBDVDDetector.h"
12 #import "HBPreviewController.h"
14 #define DragDropSimplePboardType @"MyCustomOutlineViewPboardType"
16 /* We setup the toolbar values here ShowPreviewIdentifier */
17 static NSString * ToggleDrawerIdentifier = @"Toggle Drawer Item Identifier";
18 static NSString * StartEncodingIdentifier = @"Start Encoding Item Identifier";
19 static NSString * PauseEncodingIdentifier = @"Pause Encoding Item Identifier";
20 static NSString * ShowQueueIdentifier = @"Show Queue Item Identifier";
21 static NSString * AddToQueueIdentifier = @"Add to Queue Item Identifier";
22 static NSString * ShowPictureIdentifier = @"Show Picture Window Item Identifier";
23 static NSString * ShowPreviewIdentifier = @"Show Preview Window Item Identifier";
24 static NSString * ShowActivityIdentifier = @"Debug Output Item Identifier";
25 static NSString * ChooseSourceIdentifier = @"Choose Source Item Identifier";
28 /*******************************
29 * HBController implementation *
30 *******************************/
31 @implementation HBController
41 /* replace bundled app icon with one which is 32/64-bit savvy */
42 #if defined( __LP64__ )
43 fApplicationIcon = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForImageResource:@"HandBrake-64.icns"]];
45 fApplicationIcon = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForImageResource:@"HandBrake.icns"]];
47 if( fApplicationIcon != nil )
48 [NSApp setApplicationIconImage:fApplicationIcon];
50 [HBPreferencesController registerUserDefaults];
52 fQueueEncodeLibhb = NULL;
53 /* Check for check for the app support directory here as
54 * outputPanel needs it right away, as may other future methods
56 NSString *libraryDir = [NSSearchPathForDirectoriesInDomains( NSLibraryDirectory,
58 YES ) objectAtIndex:0];
59 AppSupportDirectory = [[libraryDir stringByAppendingPathComponent:@"Application Support"]
60 stringByAppendingPathComponent:@"HandBrake"];
61 if( ![[NSFileManager defaultManager] fileExistsAtPath:AppSupportDirectory] )
63 [[NSFileManager defaultManager] createDirectoryAtPath:AppSupportDirectory
66 /* Check for and create the App Support Preview directory if necessary */
67 NSString *PreviewDirectory = [AppSupportDirectory stringByAppendingPathComponent:@"Previews"];
68 if( ![[NSFileManager defaultManager] fileExistsAtPath:PreviewDirectory] )
70 [[NSFileManager defaultManager] createDirectoryAtPath:PreviewDirectory
73 outputPanel = [[HBOutputPanelController alloc] init];
74 fPictureController = [[PictureController alloc] init];
75 fQueueController = [[HBQueueController alloc] init];
76 fAdvancedOptions = [[HBAdvancedController alloc] init];
77 /* we init the HBPresets class which currently is only used
78 * for updating built in presets, may move more functionality
81 fPresetsBuiltin = [[HBPresets alloc] init];
82 fPreferencesController = [[HBPreferencesController alloc] init];
83 /* Lets report the HandBrake version number here to the activity log and text log file */
84 NSString *versionStringFull = [[NSString stringWithFormat: @"Handbrake Version: %@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]] stringByAppendingString: [NSString stringWithFormat: @" (%@)", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]]];
85 [self writeToActivityLog: "%s", [versionStringFull UTF8String]];
91 - (void) applicationDidFinishLaunching: (NSNotification *) notification
93 /* Init libhb with check for updates libhb style set to "0" so its ignored and lets sparkle take care of it */
94 int loggingLevel = [[[NSUserDefaults standardUserDefaults] objectForKey:@"LoggingLevel"] intValue];
95 fHandle = hb_init(loggingLevel, 0);
96 /* Optional dvd nav UseDvdNav*/
97 hb_dvd_set_dvdnav([[[NSUserDefaults standardUserDefaults] objectForKey:@"UseDvdNav"] boolValue]);
98 /* Init a separate instance of libhb for user scanning and setting up jobs */
99 fQueueEncodeLibhb = hb_init(loggingLevel, 0);
101 // Set the Growl Delegate
102 [GrowlApplicationBridge setGrowlDelegate: self];
103 /* Init others controllers */
104 [fPictureController SetHandle: fHandle];
105 [fPictureController setHBController: self];
107 [fQueueController setHandle: fQueueEncodeLibhb];
108 [fQueueController setHBController: self];
110 fChapterTitlesDelegate = [[ChapterTitles alloc] init];
111 [fChapterTable setDataSource:fChapterTitlesDelegate];
112 [fChapterTable setDelegate:fChapterTitlesDelegate];
114 /* setup the subtitles delegate and connections to table */
115 fSubtitlesDelegate = [[HBSubtitles alloc] init];
116 [fSubtitlesTable setDataSource:fSubtitlesDelegate];
117 [fSubtitlesTable setDelegate:fSubtitlesDelegate];
118 [fSubtitlesTable setRowHeight:25.0];
120 [fPresetsOutlineView setAutosaveName:@"Presets View"];
121 [fPresetsOutlineView setAutosaveExpandedItems:YES];
123 dockIconProgress = 0;
125 /* Call UpdateUI every 1/2 sec */
126 [[NSRunLoop currentRunLoop] addTimer:[NSTimer
127 scheduledTimerWithTimeInterval:0.5 target:self
128 selector:@selector(updateUI:) userInfo:nil repeats:YES]
129 forMode:NSDefaultRunLoopMode];
131 // Open debug output window now if it was visible when HB was closed
132 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"OutputPanelIsOpen"])
133 [self showDebugOutputPanel:nil];
135 // Open queue window now if it was visible when HB was closed
136 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"QueueWindowIsOpen"])
137 [self showQueueWindow:nil];
139 [self openMainWindow:nil];
141 /* We have to set the bool to tell hb what to do after a scan
142 * Initially we set it to NO until we start processing the queue
144 applyQueueToScan = NO;
146 /* Now we re-check the queue array to see if there are
147 * any remaining encodes to be done in it and ask the
148 * user if they want to reload the queue */
149 if ([QueueFileArray count] > 0)
151 /* run getQueueStats to see whats in the queue file */
152 [self getQueueStats];
153 /* this results in these values
154 * fEncodingQueueItem = 0;
156 * fCompletedCount = 0;
157 * fCanceledCount = 0;
161 /*On Screen Notification*/
162 NSString * alertTitle;
164 /* We check to see if there is already another instance of hb running.
165 * Note: hbInstances == 1 means we are the only instance of HandBrake.app
167 if ([self hbInstances] > 1)
169 alertTitle = [NSString stringWithFormat:
170 NSLocalizedString(@"There is already an instance of HandBrake running.", @"")];
171 NSBeginCriticalAlertSheet(
173 NSLocalizedString(@"Reload Queue", nil),
177 nil, @selector(didDimissReloadQueue:returnCode:contextInfo:), nil,
178 NSLocalizedString(@" HandBrake will now load up the existing queue.", nil));
182 if (fWorkingCount > 0)
184 alertTitle = [NSString stringWithFormat:
185 NSLocalizedString(@"HandBrake Has Detected %d Previously Encoding Item and %d Pending Item(s) In Your Queue.", @""),
186 fWorkingCount,fPendingCount];
190 alertTitle = [NSString stringWithFormat:
191 NSLocalizedString(@"HandBrake Has Detected %d Pending Item(s) In Your Queue.", @""),
195 NSBeginCriticalAlertSheet(
197 NSLocalizedString(@"Reload Queue", nil),
199 NSLocalizedString(@"Empty Queue", nil),
201 nil, @selector(didDimissReloadQueue:returnCode:contextInfo:), nil,
202 NSLocalizedString(@" Do you want to reload them ?", nil));
208 /* We show whichever open source window specified in LaunchSourceBehavior preference key */
209 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"])
211 [self browseSources:nil];
214 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source (Title Specific)"])
216 [self browseSources:(id)fOpenSourceTitleMMenu];
223 /* check to see if another instance of HandBrake.app is running */
224 NSArray *runningAppDictionaries = [[NSWorkspace sharedWorkspace] launchedApplications];
225 NSDictionary *aDictionary;
227 for (aDictionary in runningAppDictionaries)
229 if ([[aDictionary valueForKey:@"NSApplicationName"] isEqualToString:@"HandBrake"])
237 - (void) didDimissReloadQueue: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
239 if (returnCode == NSAlertOtherReturn)
241 [self clearQueueAllItems];
242 /* We show whichever open source window specified in LaunchSourceBehavior preference key */
243 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"])
245 [self browseSources:nil];
248 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source (Title Specific)"])
250 [self browseSources:(id)fOpenSourceTitleMMenu];
255 if ([self hbInstances] == 1)
257 [self setQueueEncodingItemsAsPending];
259 [self showQueueWindow:NULL];
263 - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *) app
268 hb_get_state( fQueueEncodeLibhb, &s );
270 if ( s.state != HB_STATE_IDLE )
272 int result = NSRunCriticalAlertPanel(
273 NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),
274 NSLocalizedString(@"If you quit HandBrake your current encode will be reloaded into your queue at next launch. Do you want to quit anyway?", nil),
275 NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil, @"A movie" );
277 if (result == NSAlertDefaultReturn)
279 return NSTerminateNow;
282 return NSTerminateCancel;
285 // Warn if items still in the queue
286 else if ( fPendingCount > 0 )
288 int result = NSRunCriticalAlertPanel(
289 NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),
290 NSLocalizedString(@"There are pending encodes in your queue. Do you want to quit anyway?",nil),
291 NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil);
293 if ( result == NSAlertDefaultReturn )
294 return NSTerminateNow;
296 return NSTerminateCancel;
299 return NSTerminateNow;
302 - (void)applicationWillTerminate:(NSNotification *)aNotification
305 [browsedSourceDisplayName release];
306 [outputPanel release];
307 [fQueueController release];
308 [fPreviewController release];
309 [fPictureController release];
310 [fApplicationIcon release];
313 hb_close(&fQueueEncodeLibhb);
317 - (void) awakeFromNib
320 [fWindow setExcludedFromWindowsMenu:NO];
322 [fAdvancedOptions setView:fAdvancedView];
324 /* lets setup our presets drawer for drag and drop here */
325 [fPresetsOutlineView registerForDraggedTypes: [NSArray arrayWithObject:DragDropSimplePboardType] ];
326 [fPresetsOutlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
327 [fPresetsOutlineView setVerticalMotionCanBeginDrag: YES];
329 /* Initialize currentScanCount so HB can use it to
330 evaluate successive scans */
331 currentScanCount = 0;
334 /* Init UserPresets .plist */
337 /* Init QueueFile .plist */
338 [self loadQueueFile];
340 fRipIndicatorShown = NO; // initially out of view in the nib
342 /* For 64 bit builds, the threaded animation in the progress
343 * indicators conflicts with the animation in the advanced tab
344 * for reasons not completely clear. jbrjake found a note in the
345 * 10.5 dev notes regarding this possiblility. It was also noted
346 * that unless specified, setUsesThreadedAnimation defaults to true.
347 * So, at least for now we set the indicator animation to NO for
348 * both the scan and regular progress indicators for both 32 and 64 bit
349 * as it test out fine on both and there is no reason our progress indicators
350 * should require their own thread.
353 [fScanIndicator setUsesThreadedAnimation:NO];
354 [fRipIndicator setUsesThreadedAnimation:NO];
358 /* Show/Dont Show Presets drawer upon launch based
359 on user preference DefaultPresetsDrawerShow*/
360 if( [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultPresetsDrawerShow"] > 0 )
362 [fPresetDrawer setDelegate:self];
363 NSSize drawerSize = NSSizeFromString( [[NSUserDefaults standardUserDefaults]
364 stringForKey:@"Drawer Size"] );
365 if( drawerSize.width )
366 [fPresetDrawer setContentSize: drawerSize];
367 [fPresetDrawer open];
370 /* Initially set the dvd angle widgets to hidden (dvdnav only) */
371 [fSrcAngleLabel setHidden:YES];
372 [fSrcAnglePopUp setHidden:YES];
374 /* Setup the start / stop popup */
375 [fEncodeStartStopPopUp removeAllItems];
376 [fEncodeStartStopPopUp addItemWithTitle: @"Chapters"];
377 [fEncodeStartStopPopUp addItemWithTitle: @"Seconds"];
378 [fEncodeStartStopPopUp addItemWithTitle: @"Frames"];
379 /* Align the start / stop widgets with the chapter popups */
380 [fSrcTimeStartEncodingField setFrameOrigin:[fSrcChapterStartPopUp frame].origin];
381 [fSrcTimeEndEncodingField setFrameOrigin:[fSrcChapterEndPopUp frame].origin];
383 [fSrcFrameStartEncodingField setFrameOrigin:[fSrcChapterStartPopUp frame].origin];
384 [fSrcFrameEndEncodingField setFrameOrigin:[fSrcChapterEndPopUp frame].origin];
387 NSMenuItem *menuItem;
388 [fDstFormatPopUp removeAllItems];
390 menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MP4 file" action: NULL keyEquivalent: @""];
391 [menuItem setTag: HB_MUX_MP4];
393 menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MKV file" action: NULL keyEquivalent: @""];
394 [menuItem setTag: HB_MUX_MKV];
396 [fDstFormatPopUp selectItemAtIndex: 0];
398 [self formatPopUpChanged:nil];
400 /* We enable the create chapters checkbox here since we are .mp4 */
401 [fCreateChapterMarkers setEnabled: YES];
402 if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultChapterMarkers"] > 0)
404 [fCreateChapterMarkers setState: NSOnState];
410 [fDstFile2Field setStringValue: [NSString stringWithFormat:
411 @"%@/Desktop/Movie.mp4", NSHomeDirectory()]];
414 [fVidEncoderPopUp removeAllItems];
415 [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"];
419 [fVidTargetSizeField setIntValue: 700];
420 [fVidBitrateField setIntValue: 1000];
422 [fVidQualityMatrix selectCell: fVidBitrateCell];
423 [self videoMatrixChanged:nil];
425 /* Video framerate */
426 [fVidRatePopUp removeAllItems];
427 [fVidRatePopUp addItemWithTitle: NSLocalizedString( @"Same as source", @"" )];
428 for( int i = 0; i < hb_video_rates_count; i++ )
430 if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.3f",23.976]])
432 [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
433 [NSString stringWithUTF8String: hb_video_rates[i].string], @" (NTSC Film)"]];
435 else if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%d",25]])
437 [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
438 [NSString stringWithUTF8String: hb_video_rates[i].string], @" (PAL Film/Video)"]];
440 else if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.2f",29.97]])
442 [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
443 [NSString stringWithUTF8String: hb_video_rates[i].string], @" (NTSC Video)"]];
447 [fVidRatePopUp addItemWithTitle:
448 [NSString stringWithUTF8String: hb_video_rates[i].string]];
451 [fVidRatePopUp selectItemAtIndex: 0];
453 /* Set Auto Crop to On at launch */
454 [fPictureController setAutoCrop:YES];
457 [fAudTrack1BitratePopUp removeAllItems];
458 for( int i = 0; i < hb_audio_bitrates_count; i++ )
460 [fAudTrack1BitratePopUp addItemWithTitle:
461 [NSString stringWithUTF8String: hb_audio_bitrates[i].string]];
464 [fAudTrack1BitratePopUp selectItemAtIndex: hb_audio_bitrates_default];
466 /* Audio samplerate */
467 [fAudTrack1RatePopUp removeAllItems];
468 for( int i = 0; i < hb_audio_rates_count; i++ )
470 [fAudTrack1RatePopUp addItemWithTitle:
471 [NSString stringWithUTF8String: hb_audio_rates[i].string]];
473 [fAudTrack1RatePopUp selectItemAtIndex: hb_audio_rates_default];
476 [fStatusField setStringValue: @""];
481 /* We disable the Turbo 1st pass checkbox since we are not x264 */
482 [fVidTurboPassCheck setEnabled: NO];
483 [fVidTurboPassCheck setState: NSOffState];
486 /* lets get our default prefs here */
487 [self getDefaultPresets:nil];
488 /* lets initialize the current successful scancount here to 0 */
489 currentSuccessfulScanCount = 0;
494 - (void) enableUI: (bool) b
496 NSControl * controls[] =
497 { fSrcTitleField, fSrcTitlePopUp,
498 fSrcChapterField, fSrcChapterStartPopUp, fSrcChapterToField,
499 fSrcChapterEndPopUp, fSrcDuration1Field, fSrcDuration2Field,
500 fDstFormatField, fDstFormatPopUp, fDstFile1Field, fDstFile2Field,
501 fDstBrowseButton, fVidRateField, fVidRatePopUp,fVidEncoderField, fVidEncoderPopUp, fVidQualityField,
502 fPictureSizeField,fPictureCroppingField, fVideoFiltersField,fVidQualityMatrix, fSubField, fSubPopUp,
503 fAudSourceLabel, fAudCodecLabel, fAudMixdownLabel, fAudSamplerateLabel, fAudBitrateLabel,
504 fAudTrack1Label, fAudTrack2Label, fAudTrack3Label, fAudTrack4Label,
505 fAudLang1PopUp, fAudLang2PopUp, fAudLang3PopUp, fAudLang4PopUp,
506 fAudTrack1CodecPopUp, fAudTrack2CodecPopUp, fAudTrack3CodecPopUp, fAudTrack4CodecPopUp,
507 fAudTrack1MixPopUp, fAudTrack2MixPopUp, fAudTrack3MixPopUp, fAudTrack4MixPopUp,
508 fAudTrack1RatePopUp, fAudTrack2RatePopUp, fAudTrack3RatePopUp, fAudTrack4RatePopUp,
509 fAudTrack1BitratePopUp, fAudTrack2BitratePopUp, fAudTrack3BitratePopUp, fAudTrack4BitratePopUp,
510 fAudDrcLabel, fAudTrack1DrcSlider, fAudTrack1DrcField, fAudTrack2DrcSlider,
511 fAudTrack2DrcField, fAudTrack3DrcSlider, fAudTrack3DrcField, fAudTrack4DrcSlider,fAudTrack4DrcField,
512 fQueueStatus,fPresetsAdd,fPresetsDelete,fSrcAngleLabel,fSrcAnglePopUp,
513 fCreateChapterMarkers,fVidTurboPassCheck,fDstMp4LargeFileCheck,fSubForcedCheck,fPresetsOutlineView,
514 fAudDrcLabel,fDstMp4HttpOptFileCheck,fDstMp4iPodFileCheck,fVidQualityRFField,fVidQualityRFLabel,
515 fEncodeStartStopPopUp,fSrcTimeStartEncodingField,fSrcTimeEndEncodingField,fSrcFrameStartEncodingField,fSrcFrameEndEncodingField};
518 i < sizeof( controls ) / sizeof( NSControl * ); i++ )
520 if( [[controls[i] className] isEqualToString: @"NSTextField"] )
522 NSTextField * tf = (NSTextField *) controls[i];
523 if( ![tf isBezeled] )
525 [tf setTextColor: b ? [NSColor controlTextColor] :
526 [NSColor disabledControlTextColor]];
530 [controls[i] setEnabled: b];
536 /* if we're enabling the interface, check if the audio mixdown controls need to be enabled or not */
537 /* these will have been enabled by the mass control enablement above anyway, so we're sense-checking it here */
538 [self setEnabledStateOfAudioMixdownControls:nil];
539 /* we also call calculatePictureSizing here to sense check if we already have vfr selected */
540 [self calculatePictureSizing:nil];
544 [fPresetsOutlineView setEnabled: NO];
548 [self videoMatrixChanged:nil];
549 [fAdvancedOptions enableUI:b];
553 /***********************************************************************
555 ***********************************************************************
556 * Shows a progression bar on the dock icon, filled according to
557 * 'progress' (0.0 <= progress <= 1.0).
558 * Called with progress < 0.0 or progress > 1.0, restores the original
560 **********************************************************************/
561 - (void) UpdateDockIcon: (float) progress
564 NSBitmapImageRep * bmp;
566 uint32_t black = htonl( 0x000000FF );
567 uint32_t red = htonl( 0xFF0000FF );
568 uint32_t white = htonl( 0xFFFFFFFF );
569 int row_start, row_end;
572 if( progress < 0.0 || progress > 1.0 )
574 [NSApp setApplicationIconImage: fApplicationIcon];
578 /* Get it in a raw bitmap form */
579 tiff = [fApplicationIcon TIFFRepresentationUsingCompression:
580 NSTIFFCompressionNone factor: 1.0];
581 bmp = [NSBitmapImageRep imageRepWithData: tiff];
583 /* Draw the progression bar */
584 /* It's pretty simple (ugly?) now, but I'm no designer */
586 row_start = 3 * (int) [bmp size].height / 4;
587 row_end = 7 * (int) [bmp size].height / 8;
589 for( i = row_start; i < row_start + 2; i++ )
591 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
592 for( j = 0; j < (int) [bmp size].width; j++ )
597 for( i = row_start + 2; i < row_end - 2; i++ )
599 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
602 for( j = 2; j < (int) [bmp size].width - 2; j++ )
604 if( j < 2 + (int) ( ( [bmp size].width - 4.0 ) * progress ) )
616 for( i = row_end - 2; i < row_end; i++ )
618 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
619 for( j = 0; j < (int) [bmp size].width; j++ )
625 /* Now update the dock icon */
626 tiff = [bmp TIFFRepresentationUsingCompression:
627 NSTIFFCompressionNone factor: 1.0];
628 NSImage* icon = [[NSImage alloc] initWithData: tiff];
629 [NSApp setApplicationIconImage: icon];
633 - (void) updateUI: (NSTimer *) timer
636 /* Update UI for fHandle (user scanning instance of libhb ) */
639 list = hb_get_titles( fHandle );
640 /* check to see if there has been a new scan done
641 this bypasses the constraints of HB_STATE_WORKING
642 not allowing setting a newly scanned source */
643 int checkScanCount = hb_get_scancount( fHandle );
644 if( checkScanCount > currentScanCount )
646 currentScanCount = checkScanCount;
647 [fScanIndicator setIndeterminate: NO];
648 [fScanIndicator setDoubleValue: 0.0];
649 [fScanIndicator setHidden: YES];
650 [self showNewScan:nil];
654 hb_get_state( fHandle, &s );
660 #define p s.param.scanning
661 case HB_STATE_SCANNING:
663 [fSrcDVD2Field setStringValue: [NSString stringWithFormat:
664 NSLocalizedString( @"Scanning title %d of %d...", @"" ),
665 p.title_cur, p.title_count]];
666 [fScanIndicator setHidden: NO];
667 [fScanIndicator setDoubleValue: 100.0 * ((double)( p.title_cur - 1 ) / p.title_count)];
672 #define p s.param.scandone
673 case HB_STATE_SCANDONE:
675 [fScanIndicator setIndeterminate: NO];
676 [fScanIndicator setDoubleValue: 0.0];
677 [fScanIndicator setHidden: YES];
678 [self writeToActivityLog:"ScanDone state received from fHandle"];
679 [self showNewScan:nil];
680 [[fWindow toolbar] validateVisibleItems];
686 #define p s.param.working
687 case HB_STATE_WORKING:
694 #define p s.param.muxing
695 case HB_STATE_MUXING:
702 case HB_STATE_PAUSED:
705 case HB_STATE_WORKDONE:
712 /* Update UI for fQueueEncodeLibhb */
714 // list = hb_get_titles( fQueueEncodeLibhb ); //fQueueEncodeLibhb
715 /* check to see if there has been a new scan done
716 this bypasses the constraints of HB_STATE_WORKING
717 not allowing setting a newly scanned source */
719 checkScanCount = hb_get_scancount( fQueueEncodeLibhb );
720 if( checkScanCount > currentScanCount )
722 currentScanCount = checkScanCount;
726 hb_get_state( fQueueEncodeLibhb, &s );
732 #define p s.param.scanning
733 case HB_STATE_SCANNING:
735 [fStatusField setStringValue: [NSString stringWithFormat:
736 NSLocalizedString( @"Queue Scanning title %d of %d...", @"" ),
737 p.title_cur, p.title_count]];
739 /* Set the status string in fQueueController as well */
740 [fQueueController setQueueStatusString: [NSString stringWithFormat:
741 NSLocalizedString( @"Queue Scanning title %d of %d...", @"" ),
742 p.title_cur, p.title_count]];
747 #define p s.param.scandone
748 case HB_STATE_SCANDONE:
750 [self writeToActivityLog:"ScanDone state received from fQueueEncodeLibhb"];
751 [self processNewQueueEncode];
752 [[fWindow toolbar] validateVisibleItems];
759 #define p s.param.working
761 case HB_STATE_SEARCHING:
763 NSMutableString * string;
764 NSString * pass_desc;
766 /* Update text field */
768 //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];
769 /* For now, do not announce "pass x of x for the search phase ... */
770 string = [NSMutableString stringWithFormat: NSLocalizedString( @"Searching for start point ... : %.2f %%", @"" ), 100.0 * p.progress];
774 [string appendFormat:
775 NSLocalizedString( @" (ETA %02dh%02dm%02ds)", @"" ), p.hours, p.minutes, p.seconds];
778 [fStatusField setStringValue: string];
779 /* Set the status string in fQueueController as well */
780 [fQueueController setQueueStatusString: string];
782 CGFloat progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
783 [fRipIndicator setIndeterminate: NO];
784 [fRipIndicator setDoubleValue:100.0 * progress_total];
786 // If progress bar hasn't been revealed at the bottom of the window, do
787 // that now. This code used to be in doRip. I moved it to here to handle
788 // the case where hb_start is called by HBQueueController and not from
790 if( !fRipIndicatorShown )
792 NSRect frame = [fWindow frame];
793 if( frame.size.width <= 591 )
794 frame.size.width = 591;
795 frame.size.height += 36;
796 frame.origin.y -= 36;
797 [fWindow setFrame:frame display:YES animate:YES];
798 fRipIndicatorShown = YES;
802 /* Update dock icon */
803 /* Note not done yet */
808 case HB_STATE_WORKING:
810 NSMutableString * string;
811 NSString * pass_desc;
812 /* Update text field */
813 if (p.job_cur == 1 && p.job_count > 1)
815 if ([[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SubtitleList"] && [[[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex]objectForKey:@"SubtitleList"] objectAtIndex:0] objectForKey:@"subtitleSourceTrackNum"] intValue] == 1)
817 pass_desc = @"(subtitle scan)";
829 string = [NSMutableString stringWithFormat: NSLocalizedString( @"Encoding: pass %d %@ of %d, %.2f %%", @"" ), p.job_cur, pass_desc, p.job_count, 100.0 * p.progress];
833 [string appendFormat:
834 NSLocalizedString( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)", @"" ),
835 p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];
838 [fStatusField setStringValue: string];
839 /* Set the status string in fQueueController as well */
840 [fQueueController setQueueStatusString: string];
842 CGFloat progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
843 [fRipIndicator setIndeterminate: NO];
844 [fRipIndicator setDoubleValue:100.0 * progress_total];
846 // If progress bar hasn't been revealed at the bottom of the window, do
847 // that now. This code used to be in doRip. I moved it to here to handle
848 // the case where hb_start is called by HBQueueController and not from
850 if( !fRipIndicatorShown )
852 NSRect frame = [fWindow frame];
853 if( frame.size.width <= 591 )
854 frame.size.width = 591;
855 frame.size.height += 36;
856 frame.origin.y -= 36;
857 [fWindow setFrame:frame display:YES animate:YES];
858 fRipIndicatorShown = YES;
862 /* Update dock icon */
863 if( dockIconProgress < 100.0 * progress_total )
865 [self UpdateDockIcon: progress_total];
866 dockIconProgress += 5;
873 #define p s.param.muxing
874 case HB_STATE_MUXING:
876 /* Update text field */
877 [fStatusField setStringValue: NSLocalizedString( @"Muxing...", @"" )];
878 /* Set the status string in fQueueController as well */
879 [fQueueController setQueueStatusString: NSLocalizedString( @"Muxing...", @"" )];
881 [fRipIndicator setIndeterminate: YES];
882 [fRipIndicator startAnimation: nil];
884 /* Update dock icon */
885 [self UpdateDockIcon: 1.0];
891 case HB_STATE_PAUSED:
892 [fStatusField setStringValue: NSLocalizedString( @"Paused", @"" )];
893 [fQueueController setQueueStatusString: NSLocalizedString( @"Paused", @"" )];
897 case HB_STATE_WORKDONE:
899 // HB_STATE_WORKDONE happpens as a result of libhb finishing all its jobs
900 // or someone calling hb_stop. In the latter case, hb_stop does not clear
901 // out the remaining passes/jobs in the queue. We'll do that here.
903 // Delete all remaining jobs of this encode.
904 [fStatusField setStringValue: NSLocalizedString( @"Encode Finished.", @"" )];
905 /* Set the status string in fQueueController as well */
906 [fQueueController setQueueStatusString: NSLocalizedString( @"Encode Finished.", @"" )];
907 [fRipIndicator setIndeterminate: NO];
908 [fRipIndicator stopAnimation: nil];
909 [fRipIndicator setDoubleValue: 0.0];
910 [[fWindow toolbar] validateVisibleItems];
912 /* Restore dock icon */
913 [self UpdateDockIcon: -1.0];
914 dockIconProgress = 0;
916 if( fRipIndicatorShown )
918 NSRect frame = [fWindow frame];
919 if( frame.size.width <= 591 )
920 frame.size.width = 591;
921 frame.size.height += -36;
922 frame.origin.y -= -36;
923 [fWindow setFrame:frame display:YES animate:YES];
924 fRipIndicatorShown = NO;
926 /* Since we are done with this encode, tell output to stop writing to the
927 * individual encode log
929 [outputPanel endEncodeLog];
930 /* Check to see if the encode state has not been cancelled
931 to determine if we should check for encode done notifications */
932 if( fEncodeState != 2 )
934 NSString *pathOfFinishedEncode;
935 /* Get the output file name for the finished encode */
936 pathOfFinishedEncode = [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"];
938 /* Both the Growl Alert and Sending to MetaX can be done as encodes roll off the queue */
940 [self showGrowlDoneNotification:pathOfFinishedEncode];
942 [self sendToMetaX:pathOfFinishedEncode];
944 /* since we have successfully completed an encode, we increment the queue counter */
945 [self incrementQueueItemDone:nil];
947 /* all end of queue actions below need to be done after all queue encodes have finished
948 * and there are no pending jobs left to process
950 if (fPendingCount == 0)
952 /* If Alert Window or Window and Growl has been selected */
953 if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window"] ||
954 [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"] )
956 /*On Screen Notification*/
959 status = NSRunAlertPanel(@"Put down that cocktail...",@"Your HandBrake queue is done!", @"OK", nil, nil);
960 [NSApp requestUserAttention:NSCriticalRequest];
963 /* If sleep has been selected */
964 if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"] )
967 NSDictionary* errorDict;
968 NSAppleEventDescriptor* returnDescriptor = nil;
969 NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:
970 @"tell application \"Finder\" to sleep"];
971 returnDescriptor = [scriptObject executeAndReturnError: &errorDict];
972 [scriptObject release];
974 /* If Shutdown has been selected */
975 if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"] )
978 NSDictionary* errorDict;
979 NSAppleEventDescriptor* returnDescriptor = nil;
980 NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:
981 @"tell application \"Finder\" to shut down"];
982 returnDescriptor = [scriptObject executeAndReturnError: &errorDict];
983 [scriptObject release];
997 /* We use this to write messages to stderr from the macgui which show up in the activity window and log*/
998 - (void) writeToActivityLog:(const char *) format, ...
1001 va_start(args, format);
1005 vsnprintf( str, 1024, format, args );
1007 time_t _now = time( NULL );
1008 struct tm * now = localtime( &_now );
1009 fprintf(stderr, "[%02d:%02d:%02d] macgui: %s\n", now->tm_hour, now->tm_min, now->tm_sec, str );
1015 #pragma mark Toolbar
1016 // ============================================================
1017 // NSToolbar Related Methods
1018 // ============================================================
1020 - (void) setupToolbar {
1021 NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: @"HandBrake Toolbar"] autorelease];
1023 [toolbar setAllowsUserCustomization: YES];
1024 [toolbar setAutosavesConfiguration: YES];
1025 [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
1027 [toolbar setDelegate: self];
1029 [fWindow setToolbar: toolbar];
1032 - (NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier:
1033 (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted {
1034 NSToolbarItem * item = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdent] autorelease];
1036 if ([itemIdent isEqualToString: ToggleDrawerIdentifier])
1038 [item setLabel: @"Toggle Presets"];
1039 [item setPaletteLabel: @"Toggler Presets"];
1040 [item setToolTip: @"Open/Close Preset Drawer"];
1041 [item setImage: [NSImage imageNamed: @"Drawer"]];
1042 [item setTarget: self];
1043 [item setAction: @selector(toggleDrawer:)];
1044 [item setAutovalidates: NO];
1046 else if ([itemIdent isEqualToString: StartEncodingIdentifier])
1048 [item setLabel: @"Start"];
1049 [item setPaletteLabel: @"Start Encoding"];
1050 [item setToolTip: @"Start Encoding"];
1051 [item setImage: [NSImage imageNamed: @"Play"]];
1052 [item setTarget: self];
1053 [item setAction: @selector(Rip:)];
1055 else if ([itemIdent isEqualToString: ShowQueueIdentifier])
1057 [item setLabel: @"Show Queue"];
1058 [item setPaletteLabel: @"Show Queue"];
1059 [item setToolTip: @"Show Queue"];
1060 [item setImage: [NSImage imageNamed: @"Queue"]];
1061 [item setTarget: self];
1062 [item setAction: @selector(showQueueWindow:)];
1063 [item setAutovalidates: NO];
1065 else if ([itemIdent isEqualToString: AddToQueueIdentifier])
1067 [item setLabel: @"Add to Queue"];
1068 [item setPaletteLabel: @"Add to Queue"];
1069 [item setToolTip: @"Add to Queue"];
1070 [item setImage: [NSImage imageNamed: @"AddToQueue"]];
1071 [item setTarget: self];
1072 [item setAction: @selector(addToQueue:)];
1074 else if ([itemIdent isEqualToString: PauseEncodingIdentifier])
1076 [item setLabel: @"Pause"];
1077 [item setPaletteLabel: @"Pause Encoding"];
1078 [item setToolTip: @"Pause Encoding"];
1079 [item setImage: [NSImage imageNamed: @"Pause"]];
1080 [item setTarget: self];
1081 [item setAction: @selector(Pause:)];
1083 else if ([itemIdent isEqualToString: ShowPictureIdentifier])
1085 [item setLabel: @"Picture Settings"];
1086 [item setPaletteLabel: @"Show Picture Settings"];
1087 [item setToolTip: @"Show Picture Settings"];
1088 [item setImage: [NSImage imageNamed: @"pref-picture"]];
1089 [item setTarget: self];
1090 [item setAction: @selector(showPicturePanel:)];
1092 else if ([itemIdent isEqualToString: ShowPreviewIdentifier])
1094 [item setLabel: @"Preview Window"];
1095 [item setPaletteLabel: @"Show Preview"];
1096 [item setToolTip: @"Show Preview"];
1097 //[item setImage: [NSImage imageNamed: @"pref-picture"]];
1098 [item setImage: [NSImage imageNamed: @"Brushed_Window"]];
1099 [item setTarget: self];
1100 [item setAction: @selector(showPreviewWindow:)];
1102 else if ([itemIdent isEqualToString: ShowActivityIdentifier])
1104 [item setLabel: @"Activity Window"];
1105 [item setPaletteLabel: @"Show Activity Window"];
1106 [item setToolTip: @"Show Activity Window"];
1107 [item setImage: [NSImage imageNamed: @"ActivityWindow"]];
1108 [item setTarget: self];
1109 [item setAction: @selector(showDebugOutputPanel:)];
1110 [item setAutovalidates: NO];
1112 else if ([itemIdent isEqualToString: ChooseSourceIdentifier])
1114 [item setLabel: @"Source"];
1115 [item setPaletteLabel: @"Source"];
1116 [item setToolTip: @"Choose Video Source"];
1117 [item setImage: [NSImage imageNamed: @"Source"]];
1118 [item setTarget: self];
1119 [item setAction: @selector(browseSources:)];
1129 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
1131 return [NSArray arrayWithObjects: ChooseSourceIdentifier, NSToolbarSeparatorItemIdentifier, StartEncodingIdentifier,
1132 PauseEncodingIdentifier, AddToQueueIdentifier, ShowQueueIdentifier, NSToolbarFlexibleSpaceItemIdentifier,
1133 NSToolbarSpaceItemIdentifier, ShowPictureIdentifier, ShowPreviewIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier, nil];
1136 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
1138 return [NSArray arrayWithObjects: StartEncodingIdentifier, PauseEncodingIdentifier, AddToQueueIdentifier,
1139 ChooseSourceIdentifier, ShowQueueIdentifier, ShowPictureIdentifier, ShowPreviewIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier,
1140 NSToolbarCustomizeToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier,
1141 NSToolbarSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier, nil];
1144 - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem
1146 NSString * ident = [toolbarItem itemIdentifier];
1152 hb_get_state( fHandle, &s );
1153 if (s.state == HB_STATE_SCANNING)
1156 if ([ident isEqualToString: ChooseSourceIdentifier])
1158 [toolbarItem setImage: [NSImage imageNamed: @"Stop"]];
1159 [toolbarItem setLabel: @"Cancel Scan"];
1160 [toolbarItem setPaletteLabel: @"Cancel Scanning"];
1161 [toolbarItem setToolTip: @"Cancel Scanning Source"];
1165 if ([ident isEqualToString: StartEncodingIdentifier] || [ident isEqualToString: AddToQueueIdentifier])
1170 if ([ident isEqualToString: ChooseSourceIdentifier])
1172 [toolbarItem setImage: [NSImage imageNamed: @"Source"]];
1173 [toolbarItem setLabel: @"Source"];
1174 [toolbarItem setPaletteLabel: @"Source"];
1175 [toolbarItem setToolTip: @"Choose Video Source"];
1180 hb_get_state2( fQueueEncodeLibhb, &s );
1182 if (s.state == HB_STATE_WORKING || s.state == HB_STATE_SEARCHING || s.state == HB_STATE_MUXING)
1184 if ([ident isEqualToString: StartEncodingIdentifier])
1186 [toolbarItem setImage: [NSImage imageNamed: @"Stop"]];
1187 [toolbarItem setLabel: @"Stop"];
1188 [toolbarItem setPaletteLabel: @"Stop"];
1189 [toolbarItem setToolTip: @"Stop Encoding"];
1192 if ([ident isEqualToString: PauseEncodingIdentifier])
1194 [toolbarItem setImage: [NSImage imageNamed: @"Pause"]];
1195 [toolbarItem setLabel: @"Pause"];
1196 [toolbarItem setPaletteLabel: @"Pause Encoding"];
1197 [toolbarItem setToolTip: @"Pause Encoding"];
1202 if ([ident isEqualToString: AddToQueueIdentifier])
1204 if ([ident isEqualToString: ShowPictureIdentifier])
1206 if ([ident isEqualToString: ShowPreviewIdentifier])
1210 else if (s.state == HB_STATE_PAUSED)
1212 if ([ident isEqualToString: PauseEncodingIdentifier])
1214 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
1215 [toolbarItem setLabel: @"Resume"];
1216 [toolbarItem setPaletteLabel: @"Resume Encoding"];
1217 [toolbarItem setToolTip: @"Resume Encoding"];
1220 if ([ident isEqualToString: StartEncodingIdentifier])
1222 if ([ident isEqualToString: AddToQueueIdentifier])
1224 if ([ident isEqualToString: ShowPictureIdentifier])
1226 if ([ident isEqualToString: ShowPreviewIdentifier])
1229 else if (s.state == HB_STATE_SCANNING)
1231 else if (s.state == HB_STATE_WORKDONE || s.state == HB_STATE_SCANDONE || SuccessfulScan)
1233 if ([ident isEqualToString: StartEncodingIdentifier])
1235 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
1236 if (hb_count(fHandle) > 0)
1237 [toolbarItem setLabel: @"Start Queue"];
1239 [toolbarItem setLabel: @"Start"];
1240 [toolbarItem setPaletteLabel: @"Start Encoding"];
1241 [toolbarItem setToolTip: @"Start Encoding"];
1244 if ([ident isEqualToString: AddToQueueIdentifier])
1246 if ([ident isEqualToString: ShowPictureIdentifier])
1248 if ([ident isEqualToString: ShowPreviewIdentifier])
1253 /* If there are any pending queue items, make sure the start/stop button is active */
1254 if ([ident isEqualToString: StartEncodingIdentifier] && fPendingCount > 0)
1256 if ([ident isEqualToString: ShowQueueIdentifier])
1258 if ([ident isEqualToString: ToggleDrawerIdentifier])
1260 if ([ident isEqualToString: ChooseSourceIdentifier])
1262 if ([ident isEqualToString: ShowActivityIdentifier])
1268 - (BOOL) validateMenuItem: (NSMenuItem *) menuItem
1270 SEL action = [menuItem action];
1273 hb_get_state2( fHandle, &s );
1277 if (action == @selector(addToQueue:) || action == @selector(showPicturePanel:) || action == @selector(showAddPresetPanel:))
1278 return SuccessfulScan && [fWindow attachedSheet] == nil;
1280 if (action == @selector(browseSources:))
1282 if (s.state == HB_STATE_SCANNING)
1285 return [fWindow attachedSheet] == nil;
1287 if (action == @selector(selectDefaultPreset:))
1288 return [fPresetsOutlineView selectedRow] >= 0 && [fWindow attachedSheet] == nil;
1289 if (action == @selector(Pause:))
1291 if (s.state == HB_STATE_WORKING)
1293 if(![[menuItem title] isEqualToString:@"Pause Encoding"])
1294 [menuItem setTitle:@"Pause Encoding"];
1297 else if (s.state == HB_STATE_PAUSED)
1299 if(![[menuItem title] isEqualToString:@"Resume Encoding"])
1300 [menuItem setTitle:@"Resume Encoding"];
1306 if (action == @selector(Rip:))
1308 if (s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING || s.state == HB_STATE_PAUSED)
1310 if(![[menuItem title] isEqualToString:@"Stop Encoding"])
1311 [menuItem setTitle:@"Stop Encoding"];
1314 else if (SuccessfulScan)
1316 if(![[menuItem title] isEqualToString:@"Start Encoding"])
1317 [menuItem setTitle:@"Start Encoding"];
1318 return [fWindow attachedSheet] == nil;
1324 if( action == @selector(setDefaultPreset:) )
1326 return [fPresetsOutlineView selectedRow] != -1;
1333 #pragma mark Encode Done Actions
1334 // register a test notification and make
1335 // it enabled by default
1336 #define SERVICE_NAME @"Encode Done"
1337 - (NSDictionary *)registrationDictionaryForGrowl
1339 NSDictionary *registrationDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
1340 [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_ALL,
1341 [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_DEFAULT,
1344 return registrationDictionary;
1347 -(void)showGrowlDoneNotification:(NSString *) filePath
1349 /* This end of encode action is called as each encode rolls off of the queue */
1350 NSString * finishedEncode = filePath;
1351 /* strip off the path to just show the file name */
1352 finishedEncode = [finishedEncode lastPathComponent];
1353 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] ||
1354 [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])
1356 NSString * growlMssg = [NSString stringWithFormat: @"your HandBrake encode %@ is done!",finishedEncode];
1357 [GrowlApplicationBridge
1358 notifyWithTitle:@"Put down that cocktail..."
1359 description:growlMssg
1360 notificationName:SERVICE_NAME
1368 -(void)sendToMetaX:(NSString *) filePath
1370 /* This end of encode action is called as each encode rolls off of the queue */
1371 if([[NSUserDefaults standardUserDefaults] boolForKey: @"sendToMetaX"] == YES)
1373 NSString *sendToApp = [[NSUserDefaults standardUserDefaults] objectForKey: @"SendCompletedEncodeToApp"];
1374 if (![sendToApp isEqualToString:@"None"])
1376 [self writeToActivityLog: "trying to send encode to: %s", [sendToApp UTF8String]];
1377 NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@%@%@", @"tell application \"",sendToApp,@"\" to open (POSIX file \"", filePath, @"\")"]];
1378 [myScript executeAndReturnError: nil];
1385 #pragma mark Get New Source
1387 /*Opens the source browse window, called from Open Source widgets */
1388 - (IBAction) browseSources: (id) sender
1392 hb_get_state( fHandle, &s );
1393 if (s.state == HB_STATE_SCANNING)
1395 [self cancelScanning:nil];
1400 NSOpenPanel * panel;
1402 panel = [NSOpenPanel openPanel];
1403 [panel setAllowsMultipleSelection: NO];
1404 [panel setCanChooseFiles: YES];
1405 [panel setCanChooseDirectories: YES ];
1406 NSString * sourceDirectory;
1407 if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"])
1409 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"];
1413 sourceDirectory = @"~/Desktop";
1414 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
1416 /* we open up the browse sources sheet here and call for browseSourcesDone after the sheet is closed
1417 * to evaluate whether we want to specify a title, we pass the sender in the contextInfo variable
1419 [panel beginSheetForDirectory: sourceDirectory file: nil types: nil
1420 modalForWindow: fWindow modalDelegate: self
1421 didEndSelector: @selector( browseSourcesDone:returnCode:contextInfo: )
1422 contextInfo: sender];
1425 - (void) browseSourcesDone: (NSOpenPanel *) sheet
1426 returnCode: (int) returnCode contextInfo: (void *) contextInfo
1428 /* we convert the sender content of contextInfo back into a variable called sender
1429 * mostly just for consistency for evaluation later
1431 id sender = (id)contextInfo;
1432 /* User selected a file to open */
1433 if( returnCode == NSOKButton )
1435 /* Free display name allocated previously by this code */
1436 [browsedSourceDisplayName release];
1438 NSString *scanPath = [[sheet filenames] objectAtIndex: 0];
1439 /* we set the last searched source directory in the prefs here */
1440 NSString *sourceDirectory = [scanPath stringByDeletingLastPathComponent];
1441 [[NSUserDefaults standardUserDefaults] setObject:sourceDirectory forKey:@"LastSourceDirectory"];
1442 /* we order out sheet, which is the browse window as we need to open
1443 * the title selection sheet right away
1445 [sheet orderOut: self];
1447 if (sender == fOpenSourceTitleMMenu || [[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
1449 /* We put the chosen source path in the source display text field for the
1450 * source title selection sheet in which the user specifies the specific title to be
1451 * scanned as well as the short source name in fSrcDsplyNameTitleScan just for display
1452 * purposes in the title panel
1455 [fScanSrcTitlePathField setStringValue:scanPath];
1456 NSString *displayTitlescanSourceName;
1458 if ([[scanPath lastPathComponent] isEqualToString: @"VIDEO_TS"])
1460 /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name
1461 we have to use the title->path value so we get the proper name of the volume if a physical dvd is the source*/
1462 displayTitlescanSourceName = [[scanPath stringByDeletingLastPathComponent] lastPathComponent];
1466 /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */
1467 displayTitlescanSourceName = [scanPath lastPathComponent];
1469 /* we set the source display name in the title selection dialogue */
1470 [fSrcDsplyNameTitleScan setStringValue:displayTitlescanSourceName];
1471 /* we set the attempted scans display name for main window to displayTitlescanSourceName*/
1472 browsedSourceDisplayName = [displayTitlescanSourceName retain];
1473 /* We show the actual sheet where the user specifies the title to be scanned
1474 * as we are going to do a title specific scan
1476 [self showSourceTitleScanPanel:nil];
1480 /* We are just doing a standard full source scan, so we specify "0" to libhb */
1481 NSString *path = [[sheet filenames] objectAtIndex: 0];
1483 /* We check to see if the chosen file at path is a package */
1484 if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:path])
1486 [self writeToActivityLog: "trying to open a package at: %s", [path UTF8String]];
1487 /* We check to see if this is an .eyetv package */
1488 if ([[path pathExtension] isEqualToString: @"eyetv"])
1490 [self writeToActivityLog:"trying to open eyetv package"];
1491 /* We're looking at an EyeTV package - try to open its enclosed
1493 browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain];
1495 int n = [[path stringByAppendingString: @"/"]
1496 completePathIntoString: &mpgname caseSensitive: YES
1497 matchesIntoArray: nil
1498 filterTypes: [NSArray arrayWithObject: @"mpg"]];
1501 /* Found an mpeg inside the eyetv package, make it our scan path
1502 and call performScan on the enclosed mpeg */
1504 [self writeToActivityLog:"found mpeg in eyetv package"];
1505 [self performScan:path scanTitleNum:0];
1509 /* We did not find an mpeg file in our package, so we do not call performScan */
1510 [self writeToActivityLog:"no valid mpeg in eyetv package"];
1513 /* We check to see if this is a .dvdmedia package */
1514 else if ([[path pathExtension] isEqualToString: @"dvdmedia"])
1516 /* path IS a package - but dvdmedia packages can be treaded like normal directories */
1517 browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain];
1518 [self writeToActivityLog:"trying to open dvdmedia package"];
1519 [self performScan:path scanTitleNum:0];
1523 /* The package is not an eyetv package, so we do not call performScan */
1524 [self writeToActivityLog:"unable to open package"];
1527 else // path is not a package, so we treat it as a dvd parent folder or VIDEO_TS folder
1529 /* path is not a package, so we call perform scan directly on our file */
1530 if ([[path lastPathComponent] isEqualToString: @"VIDEO_TS"])
1532 [self writeToActivityLog:"trying to open video_ts folder (video_ts folder chosen)"];
1533 /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name*/
1534 browsedSourceDisplayName = [[[path stringByDeletingLastPathComponent] lastPathComponent] retain];
1538 [self writeToActivityLog:"trying to open video_ts folder (parent directory chosen)"];
1539 /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */
1540 /* make sure we remove any path extension as this can also be an '.mpg' file */
1541 browsedSourceDisplayName = [[path lastPathComponent] retain];
1543 applyQueueToScan = NO;
1544 [self performScan:path scanTitleNum:0];
1552 - (IBAction)showAboutPanel:(id)sender
1554 NSMutableDictionary* d = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
1555 fApplicationIcon, @"ApplicationIcon",
1557 [NSApp orderFrontStandardAboutPanelWithOptions:d];
1561 /* Here we open the title selection sheet where we can specify an exact title to be scanned */
1562 - (IBAction) showSourceTitleScanPanel: (id) sender
1564 /* We default the title number to be scanned to "0" which results in a full source scan, unless the
1567 [fScanSrcTitleNumField setStringValue: @"0"];
1568 /* Show the panel */
1569 [NSApp beginSheet:fScanSrcTitlePanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
1572 - (IBAction) closeSourceTitleScanPanel: (id) sender
1574 [NSApp endSheet: fScanSrcTitlePanel];
1575 [fScanSrcTitlePanel orderOut: self];
1577 if(sender == fScanSrcTitleOpenButton)
1579 /* We setup the scan status in the main window to indicate a source title scan */
1580 [fSrcDVD2Field setStringValue: @"Opening a new source title ..."];
1581 [fScanIndicator setHidden: NO];
1582 [fScanIndicator setIndeterminate: YES];
1583 [fScanIndicator startAnimation: nil];
1585 /* We use the performScan method to actually perform the specified scan passing the path and the title
1588 applyQueueToScan = NO;
1589 [self performScan:[fScanSrcTitlePathField stringValue] scanTitleNum:[fScanSrcTitleNumField intValue]];
1593 /* Here we actually tell hb_scan to perform the source scan, using the path to source and title number*/
1594 - (void) performScan:(NSString *) scanPath scanTitleNum: (int) scanTitleNum
1597 /* use a bool to determine whether or not we can decrypt using vlc */
1598 BOOL cancelScanDecrypt = 0;
1600 NSString *path = scanPath;
1601 HBDVDDetector *detector = [HBDVDDetector detectorForPath:path];
1603 // Notify ChapterTitles that there's no title
1604 [fChapterTitlesDelegate resetWithTitle:nil];
1605 [fChapterTable reloadData];
1607 // Notify Subtitles that there's no title
1608 [fSubtitlesDelegate resetWithTitle:nil];
1609 [fSubtitlesTable reloadData];
1611 [self enableUI: NO];
1613 if( [detector isVideoDVD] )
1616 #if defined( __LP64__ )
1625 // The chosen path was actually on a DVD, so use the raw block
1626 // device path instead.
1627 path = [detector devicePath];
1628 [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]];
1630 /* lets check for vlc here to make sure we have a dylib available to use for decrypting */
1631 NSString *vlcPath = @"/Applications/VLC.app/Contents/MacOS/lib/libdvdcss.2.dylib";
1632 NSFileManager * fileManager = [NSFileManager defaultManager];
1633 if ([fileManager fileExistsAtPath:vlcPath] == 0)
1635 /*vlc not found in /Applications so we set the bool to cancel scanning to 1 */
1636 cancelScanDecrypt = 1;
1637 [self writeToActivityLog: "VLC app not found for decrypting physical dvd"];
1639 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");
1640 [NSApp requestUserAttention:NSCriticalRequest];
1642 if (status == NSAlertDefaultReturn)
1644 /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
1645 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/vlc/download-macosx.html"]];
1647 else if (status == NSAlertAlternateReturn)
1649 /* User chose to cancel the scan */
1650 [self writeToActivityLog: "cannot open physical dvd , scan cancelled"];
1654 /* User chose to override our warning and scan the physical dvd anyway, at their own peril. on an encrypted dvd this produces massive log files and fails */
1655 cancelScanDecrypt = 0;
1656 [self writeToActivityLog: "user overrode vlc warning -trying to open physical dvd without decryption"];
1662 /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */
1663 [self writeToActivityLog: "VLC app found for decrypting physical dvd"];
1666 /* test for architecture of the vlc app */
1667 NSArray *vlc_architecturesArray = [[NSBundle bundleWithPath:@"/Applications/VLC.app"] executableArchitectures];
1668 BOOL vlcIntel32bit = NO;
1669 BOOL vlcIntel64bit = NO;
1670 BOOL vlcPPC32bit = NO;
1671 BOOL vlcPPC64bit = NO;
1672 /* check the available architectures for vlc and note accordingly */
1673 NSEnumerator *enumerator = [vlc_architecturesArray objectEnumerator];
1675 while (tempObject = [enumerator nextObject])
1678 if ([tempObject intValue] == NSBundleExecutableArchitectureI386)
1680 vlcIntel32bit = YES;
1682 if ([tempObject intValue] == NSBundleExecutableArchitectureX86_64)
1684 vlcIntel64bit = YES;
1686 if ([tempObject intValue] == NSBundleExecutableArchitecturePPC)
1690 if ([tempObject intValue] == NSBundleExecutableArchitecturePPC64)
1696 /* Write vlc architecture findings to activity window */
1699 [self writeToActivityLog: " 32-Bit VLC app found for decrypting physical dvd"];
1703 [self writeToActivityLog: " 64-Bit VLC app found for decrypting physical dvd"];
1708 if (vlcFound && hb_arch == 64 && !vlcIntel64bit && cancelScanDecrypt != 1)
1713 /* Appropriate VLC not found, so cancel */
1714 cancelScanDecrypt = 1;
1715 [self writeToActivityLog: "This version of HandBrake is 64 bit, 64 bit version of vlc not found, scan cancelled"];
1716 /*On Screen Notification*/
1719 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);
1720 [NSApp requestUserAttention:NSCriticalRequest];
1722 if (status == NSAlertDefaultReturn)
1724 /* User chose to cancel the scan */
1725 [self writeToActivityLog: "cannot open physical dvd VLC found but not 64 bit, scan cancelled"];
1726 cancelScanDecrypt = 1;
1728 else if (status == NSAlertAlternateReturn)
1730 [self writeToActivityLog: "user overrode 64-bit warning trying to open physical dvd without proper decryption"];
1731 cancelScanDecrypt = 0;
1735 /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
1736 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/vlc/download-macosx.html"]];
1740 else if (vlcFound && hb_arch == 32 && !vlcIntel32bit && cancelScanDecrypt != 1)
1743 /* Appropriate VLC not found, so cancel */
1744 cancelScanDecrypt = 1;
1745 [self writeToActivityLog: "This version of HandBrake is 32 bit, 32 bit version of vlc not found, scan cancelled"];
1746 /*On Screen Notification*/
1749 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);
1750 [NSApp requestUserAttention:NSCriticalRequest];
1752 if (status == NSAlertDefaultReturn)
1754 /* User chose to cancel the scan */
1755 [self writeToActivityLog: "cannot open physical dvd VLC found but not 32 bit, scan cancelled"];
1756 cancelScanDecrypt = 1;
1758 else if (status == NSAlertAlternateReturn)
1760 [self writeToActivityLog: "user overrode 32-bit warning trying to open physical dvd without proper decryption"];
1761 cancelScanDecrypt = 0;
1765 /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
1766 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/vlc/download-macosx.html"]];
1772 if (cancelScanDecrypt == 0)
1774 /* we actually pass the scan off to libhb here */
1775 /* If there is no title number passed to scan, we use "0"
1776 * which causes the default behavior of a full source scan
1782 if (scanTitleNum > 0)
1784 [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum];
1786 /* We use our advance pref to determine how many previews to scan */
1787 int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue];
1788 /* set title to NULL */
1790 hb_scan( fHandle, [path UTF8String], scanTitleNum, hb_num_previews, 1 );
1791 [fSrcDVD2Field setStringValue:@"Scanning new source ..."];
1795 - (IBAction) cancelScanning:(id)sender
1797 hb_scan_stop(fHandle);
1800 - (IBAction) showNewScan:(id)sender
1804 int indxpri=0; // Used to search the longuest title (default in combobox)
1805 int longuestpri=0; // Used to search the longuest title (default in combobox)
1808 list = hb_get_titles( fHandle );
1810 if( !hb_list_count( list ) )
1812 /* We display a message if a valid dvd source was not chosen */
1813 [fSrcDVD2Field setStringValue: @"No Valid Source Found"];
1814 SuccessfulScan = NO;
1816 // Notify ChapterTitles that there's no title
1817 [fSubtitlesDelegate resetWithTitle:nil];
1818 [fSubtitlesTable reloadData];
1820 // Notify Subtitles that there's no title
1821 [fChapterTitlesDelegate resetWithTitle:nil];
1822 [fChapterTable reloadData];
1826 if (applyQueueToScan == YES)
1828 /* we are a rescan of an existing queue item and need to apply the queued settings to the scan */
1829 [self writeToActivityLog: "showNewScan: This is a queued item rescan"];
1832 else if (applyQueueToScan == NO)
1834 [self writeToActivityLog: "showNewScan: This is a new source item scan"];
1838 [self writeToActivityLog: "showNewScan: cannot grok scan status"];
1841 /* We increment the successful scancount here by one,
1842 which we use at the end of this function to tell the gui
1843 if this is the first successful scan since launch and whether
1844 or not we should set all settings to the defaults */
1845 currentSuccessfulScanCount++;
1847 [[fWindow toolbar] validateVisibleItems];
1849 [fSrcTitlePopUp removeAllItems];
1850 for( int i = 0; i < hb_list_count( list ); i++ )
1852 title = (hb_title_t *) hb_list_item( list, i );
1854 currentSource = [NSString stringWithUTF8String: title->name];
1855 /*Set DVD Name at top of window with the browsedSourceDisplayName grokked right before -performScan */
1856 if (!browsedSourceDisplayName)
1858 browsedSourceDisplayName = @"NoNameDetected";
1860 [fSrcDVD2Field setStringValue:browsedSourceDisplayName];
1862 /* If its a queue rescan for edit, get the queue item output path */
1863 /* if not, its a new source scan. */
1864 /* Check to see if the last destination has been set,use if so, if not, use Desktop */
1865 if (applyQueueToScan == YES)
1867 [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@", [[QueueFileArray objectAtIndex:fqueueEditRescanItemNum] objectForKey:@"DestinationPath"]]];
1869 else if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"])
1871 [fDstFile2Field setStringValue: [NSString stringWithFormat:
1872 @"%@/%@.mp4", [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"],[browsedSourceDisplayName stringByDeletingPathExtension]]];
1876 [fDstFile2Field setStringValue: [NSString stringWithFormat:
1877 @"%@/Desktop/%@.mp4", NSHomeDirectory(),[browsedSourceDisplayName stringByDeletingPathExtension]]];
1881 if (longuestpri < title->hours*60*60 + title->minutes *60 + title->seconds)
1883 longuestpri=title->hours*60*60 + title->minutes *60 + title->seconds;
1887 [fSrcTitlePopUp addItemWithTitle: [NSString
1888 stringWithFormat: @"%s %d - %02dh%02dm%02ds",
1889 title->name,title->index, title->hours, title->minutes,
1893 /* if we are a stream, select the first title */
1894 if (title->type == HB_STREAM_TYPE)
1896 [fSrcTitlePopUp selectItemAtIndex: 0];
1900 /* if not then select the longest title (dvd) */
1901 [fSrcTitlePopUp selectItemAtIndex: indxpri];
1903 [self titlePopUpChanged:nil];
1905 SuccessfulScan = YES;
1906 [self enableUI: YES];
1908 /* if its the initial successful scan after awakeFromNib */
1909 if (currentSuccessfulScanCount == 1)
1911 [self encodeStartStopPopUpChanged:nil];
1913 [self selectDefaultPreset:nil];
1915 // Open preview window now if it was visible when HB was closed
1916 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PreviewWindowIsOpen"])
1917 [self showPreviewWindow:nil];
1919 // Open picture sizing window now if it was visible when HB was closed
1920 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PictureSizeWindowIsOpen"])
1921 [self showPicturePanel:nil];
1924 if (applyQueueToScan == YES)
1926 /* we are a rescan of an existing queue item and need to apply the queued settings to the scan */
1927 [self writeToActivityLog: "showNewScan: calling applyQueueSettingsToMainWindow"];
1928 [self applyQueueSettingsToMainWindow:nil];
1939 #pragma mark New Output Destination
1941 - (IBAction) browseFile: (id) sender
1943 /* Open a panel to let the user choose and update the text field */
1944 NSSavePanel * panel = [NSSavePanel savePanel];
1945 /* We get the current file name and path from the destination field here */
1946 [panel beginSheetForDirectory: [[fDstFile2Field stringValue] stringByDeletingLastPathComponent] file: [[fDstFile2Field stringValue] lastPathComponent]
1947 modalForWindow: fWindow modalDelegate: self
1948 didEndSelector: @selector( browseFileDone:returnCode:contextInfo: )
1952 - (void) browseFileDone: (NSSavePanel *) sheet
1953 returnCode: (int) returnCode contextInfo: (void *) contextInfo
1955 if( returnCode == NSOKButton )
1957 [fDstFile2Field setStringValue: [sheet filename]];
1958 /* Save this path to the prefs so that on next browse destination window it opens there */
1959 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1960 [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
1966 #pragma mark Main Window Control
1968 - (IBAction) openMainWindow: (id) sender
1970 [fWindow makeKeyAndOrderFront:nil];
1973 - (BOOL) windowShouldClose: (id) sender
1978 - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
1981 [fWindow makeKeyAndOrderFront:nil];
1989 - (NSSize) drawerWillResizeContents:(NSDrawer *) drawer toSize:(NSSize) contentSize {
1990 [[NSUserDefaults standardUserDefaults] setObject:NSStringFromSize( contentSize ) forKey:@"Drawer Size"];
1995 #pragma mark Queue File
1997 - (void) loadQueueFile {
1998 /* We declare the default NSFileManager into fileManager */
1999 NSFileManager * fileManager = [NSFileManager defaultManager];
2000 /*We define the location of the user presets file */
2001 QueueFile = @"~/Library/Application Support/HandBrake/Queue.plist";
2002 QueueFile = [[QueueFile stringByExpandingTildeInPath]retain];
2003 /* We check for the presets.plist */
2004 if ([fileManager fileExistsAtPath:QueueFile] == 0)
2006 [fileManager createFileAtPath:QueueFile contents:nil attributes:nil];
2009 QueueFileArray = [[NSMutableArray alloc] initWithContentsOfFile:QueueFile];
2010 /* lets check to see if there is anything in the queue file .plist */
2011 if (nil == QueueFileArray)
2013 /* if not, then lets initialize an empty array */
2014 QueueFileArray = [[NSMutableArray alloc] init];
2016 /* Initialize our curQueueEncodeIndex to 0
2017 * so we can use it to track which queue
2018 * item is to be used to track our encodes */
2019 /* NOTE: this should be changed if and when we
2020 * are able to get the last unfinished encode
2021 * in the case of a crash or shutdown */
2026 [self clearQueueEncodedItems];
2028 currentQueueEncodeIndex = 0;
2031 - (void)addQueueFileItem
2033 [QueueFileArray addObject:[self createQueueFileItem]];
2034 [self saveQueueFileItem];
2038 - (void) removeQueueFileItem:(int) queueItemToRemove
2041 /* Find out if the item we are removing is a cancelled (3) or a finished (0) item*/
2042 if ([[[QueueFileArray objectAtIndex:queueItemToRemove] objectForKey:@"Status"] intValue] == 3 || [[[QueueFileArray objectAtIndex:queueItemToRemove] objectForKey:@"Status"] intValue] == 0)
2044 /* Since we are removing a cancelled or finished item, WE need to decrement the currentQueueEncodeIndex
2045 * by one to keep in sync with the queue array
2047 currentQueueEncodeIndex--;
2048 [self writeToActivityLog: "removeQueueFileItem: Removing a cancelled/finished encode, decrement currentQueueEncodeIndex to %d", currentQueueEncodeIndex];
2050 [QueueFileArray removeObjectAtIndex:queueItemToRemove];
2051 [self saveQueueFileItem];
2055 - (void)saveQueueFileItem
2057 [QueueFileArray writeToFile:QueueFile atomically:YES];
2058 [fQueueController setQueueArray: QueueFileArray];
2059 [self getQueueStats];
2062 - (void)getQueueStats
2064 /* lets get the stats on the status of the queue array */
2066 fEncodingQueueItem = 0;
2068 fCompletedCount = 0;
2072 /* We use a number system to set the encode status of the queue item
2074 * 0 == already encoded
2075 * 1 == is being encoded
2076 * 2 == is yet to be encoded
2081 NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2083 while (tempObject = [enumerator nextObject])
2085 NSDictionary *thisQueueDict = tempObject;
2086 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 0) // Completed
2090 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 1) // being encoded
2093 fEncodingQueueItem = i;
2095 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 2) // pending
2099 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 3) // cancelled
2106 /* Set the queue status field in the main window */
2107 NSMutableString * string;
2108 if (fPendingCount == 1)
2110 string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode pending in the queue", @"" ), fPendingCount];
2114 string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode(s) pending in the queue", @"" ), fPendingCount];
2116 [fQueueStatus setStringValue:string];
2119 /* This method will set any item marked as encoding back to pending
2120 * currently used right after a queue reload
2122 - (void) setQueueEncodingItemsAsPending
2124 NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2126 NSMutableArray *tempArray;
2127 tempArray = [NSMutableArray array];
2128 /* we look here to see if the preset is we move on to the next one */
2129 while ( tempObject = [enumerator nextObject] )
2131 /* If the queue item is marked as "encoding" (1)
2132 * then change its status back to pending (2) which effectively
2133 * puts it back into the queue to be encoded
2135 if ([[tempObject objectForKey:@"Status"] intValue] == 1)
2137 [tempObject setObject:[NSNumber numberWithInt: 2] forKey:@"Status"];
2139 [tempArray addObject:tempObject];
2142 [QueueFileArray setArray:tempArray];
2143 [self saveQueueFileItem];
2147 /* This method will clear the queue of any encodes that are not still pending
2148 * this includes both successfully completed encodes as well as cancelled encodes */
2149 - (void) clearQueueEncodedItems
2151 NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2153 NSMutableArray *tempArray;
2154 tempArray = [NSMutableArray array];
2155 /* we look here to see if the preset is we move on to the next one */
2156 while ( tempObject = [enumerator nextObject] )
2158 /* If the queue item is either completed (0) or cancelled (3) from the
2159 * last session, then we put it in tempArray to be deleted from QueueFileArray.
2160 * NOTE: this means we retain pending (2) and also an item that is marked as
2161 * still encoding (1). If the queue has an item that is still marked as encoding
2162 * from a previous session, we can conlude that HB was either shutdown, or crashed
2163 * during the encodes so we keep it and tell the user in the "Load Queue Alert"
2165 if ([[tempObject objectForKey:@"Status"] intValue] == 0 || [[tempObject objectForKey:@"Status"] intValue] == 3)
2167 [tempArray addObject:tempObject];
2171 [QueueFileArray removeObjectsInArray:tempArray];
2172 [self saveQueueFileItem];
2175 /* This method will clear the queue of all encodes. effectively creating an empty queue */
2176 - (void) clearQueueAllItems
2178 NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2180 NSMutableArray *tempArray;
2181 tempArray = [NSMutableArray array];
2182 /* we look here to see if the preset is we move on to the next one */
2183 while ( tempObject = [enumerator nextObject] )
2185 [tempArray addObject:tempObject];
2188 [QueueFileArray removeObjectsInArray:tempArray];
2189 [self saveQueueFileItem];
2192 /* This method will duplicate prepareJob however into the
2193 * queue .plist instead of into the job structure so it can
2194 * be recalled later */
2195 - (NSDictionary *)createQueueFileItem
2197 NSMutableDictionary *queueFileJob = [[NSMutableDictionary alloc] init];
2199 hb_list_t * list = hb_get_titles( fHandle );
2200 hb_title_t * title = (hb_title_t *) hb_list_item( list,
2201 [fSrcTitlePopUp indexOfSelectedItem] );
2202 hb_job_t * job = title->job;
2206 /* We use a number system to set the encode status of the queue item
2207 * 0 == already encoded
2208 * 1 == is being encoded
2209 * 2 == is yet to be encoded
2212 [queueFileJob setObject:[NSNumber numberWithInt:2] forKey:@"Status"];
2213 /* Source and Destination Information */
2215 [queueFileJob setObject:[NSString stringWithUTF8String: title->path] forKey:@"SourcePath"];
2216 [queueFileJob setObject:[fSrcDVD2Field stringValue] forKey:@"SourceName"];
2217 [queueFileJob setObject:[NSNumber numberWithInt:title->index] forKey:@"TitleNumber"];
2218 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcAnglePopUp indexOfSelectedItem] + 1] forKey:@"TitleAngle"];
2220 /* Determine and set a variable to tell hb what start and stop times to use ... chapters vs seconds */
2221 if( [fEncodeStartStopPopUp indexOfSelectedItem] == 0 )
2223 [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"fEncodeStartStop"];
2225 else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 1)
2227 [queueFileJob setObject:[NSNumber numberWithInt:1] forKey:@"fEncodeStartStop"];
2229 else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 2)
2231 [queueFileJob setObject:[NSNumber numberWithInt:2] forKey:@"fEncodeStartStop"];
2233 /* Chapter encode info */
2234 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterStartPopUp indexOfSelectedItem] + 1] forKey:@"ChapterStart"];
2235 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterEndPopUp indexOfSelectedItem] + 1] forKey:@"ChapterEnd"];
2236 /* Time (pts) encode info */
2237 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcTimeStartEncodingField intValue]] forKey:@"StartSeconds"];
2238 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcTimeEndEncodingField intValue] - [fSrcTimeStartEncodingField intValue]] forKey:@"StopSeconds"];
2239 /* Frame number encode info */
2240 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcFrameStartEncodingField intValue]] forKey:@"StartFrame"];
2241 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcFrameEndEncodingField intValue] - [fSrcFrameStartEncodingField intValue]] forKey:@"StopFrame"];
2244 /* The number of seek points equals the number of seconds announced in the title as that is our current granularity */
2245 int title_duration_seconds = (title->hours * 3600) + (title->minutes * 60) + (title->seconds);
2246 [queueFileJob setObject:[NSNumber numberWithInt:title_duration_seconds] forKey:@"SourceTotalSeconds"];
2248 [queueFileJob setObject:[fDstFile2Field stringValue] forKey:@"DestinationPath"];
2250 /* Lets get the preset info if there is any */
2251 [queueFileJob setObject:[fPresetSelectedDisplay stringValue] forKey:@"PresetName"];
2252 [queueFileJob setObject:[NSNumber numberWithInt:[fPresetsOutlineView selectedRow]] forKey:@"PresetIndexNum"];
2254 [queueFileJob setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"];
2255 /* Chapter Markers*/
2256 /* If we have only one chapter or a title without chapters, set chapter markers to off */
2257 if ([fSrcChapterStartPopUp indexOfSelectedItem] == [fSrcChapterEndPopUp indexOfSelectedItem])
2259 [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"ChapterMarkers"];
2263 [queueFileJob setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"];
2266 /* We need to get the list of chapter names to put into an array and store
2267 * in our queue, so they can be reapplied in prepareJob when this queue
2268 * item comes up if Chapter Markers is set to on.
2271 NSMutableArray *ChapterNamesArray = [[NSMutableArray alloc] init];
2272 int chaptercount = hb_list_count( fTitle->list_chapter );
2273 for( i = 0; i < chaptercount; i++ )
2275 hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item( fTitle->list_chapter, i );
2276 if( chapter != NULL )
2278 [ChapterNamesArray addObject:[NSString stringWithCString:chapter->title encoding:NSUTF8StringEncoding]];
2281 [queueFileJob setObject:[NSMutableArray arrayWithArray: ChapterNamesArray] forKey:@"ChapterNames"];
2282 [ChapterNamesArray autorelease];
2284 /* Allow Mpeg4 64 bit formatting +4GB file sizes */
2285 [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"];
2286 /* Mux mp4 with http optimization */
2287 [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"];
2288 /* Add iPod uuid atom */
2289 [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"];
2293 [queueFileJob setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"];
2294 /* x264 Option String */
2295 [queueFileJob setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"];
2297 [queueFileJob setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"];
2298 [queueFileJob setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
2299 [queueFileJob setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"];
2300 [queueFileJob setObject:[NSNumber numberWithFloat:[fVidQualityRFField floatValue]] forKey:@"VideoQualitySlider"];
2302 [queueFileJob setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"];
2304 /* 2 Pass Encoding */
2305 [queueFileJob setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"];
2306 /* Turbo 2 pass Encoding fVidTurboPassCheck*/
2307 [queueFileJob setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"];
2309 /* Picture Sizing */
2310 /* Use Max Picture settings for whatever the dvd is.*/
2311 [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
2312 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
2313 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
2314 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
2315 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"];
2316 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->modulus] forKey:@"PictureModulus"];
2317 /* if we are custom anamorphic, store the exact storage, par and display dims */
2318 if (fTitle->job->anamorphic.mode == 3)
2320 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->modulus] forKey:@"PicturePARModulus"];
2322 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PicturePARStorageWidth"];
2323 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PicturePARStorageHeight"];
2325 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.par_width] forKey:@"PicturePARPixelWidth"];
2326 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.par_height] forKey:@"PicturePARPixelHeight"];
2328 [queueFileJob setObject:[NSNumber numberWithFloat:fTitle->job->anamorphic.dar_width] forKey:@"PicturePARDisplayWidth"];
2329 [queueFileJob setObject:[NSNumber numberWithFloat:fTitle->job->anamorphic.dar_height] forKey:@"PicturePARDisplayHeight"];
2332 NSString * pictureSummary;
2333 pictureSummary = [fPictureSizeField stringValue];
2334 [queueFileJob setObject:pictureSummary forKey:@"PictureSizingSummary"];
2335 /* Set crop settings here */
2336 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
2337 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
2338 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
2339 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
2340 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
2342 /* Picture Filters */
2343 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"];
2344 [queueFileJob setObject:[fPictureController detelecineCustomString] forKey:@"PictureDetelecineCustom"];
2346 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController useDecomb]] forKey:@"PictureDecombDeinterlace"];
2347 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"];
2348 [queueFileJob setObject:[fPictureController decombCustomString] forKey:@"PictureDecombCustom"];
2350 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"];
2351 [queueFileJob setObject:[fPictureController deinterlaceCustomString] forKey:@"PictureDeinterlaceCustom"];
2353 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"];
2354 [queueFileJob setObject:[fPictureController denoiseCustomString] forKey:@"PictureDenoiseCustom"];
2356 [queueFileJob setObject:[NSString stringWithFormat:@"%d",[fPictureController deblock]] forKey:@"PictureDeblock"];
2358 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController grayscale]] forKey:@"VideoGrayScale"];
2361 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
2363 [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang1PopUp indexOfSelectedItem]] forKey:@"Audio1Track"];
2364 [queueFileJob setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"Audio1TrackDescription"];
2365 [queueFileJob setObject:[fAudTrack1CodecPopUp titleOfSelectedItem] forKey:@"Audio1Encoder"];
2366 [queueFileJob setObject:[fAudTrack1MixPopUp titleOfSelectedItem] forKey:@"Audio1Mixdown"];
2367 [queueFileJob setObject:[fAudTrack1RatePopUp titleOfSelectedItem] forKey:@"Audio1Samplerate"];
2368 [queueFileJob setObject:[fAudTrack1BitratePopUp titleOfSelectedItem] forKey:@"Audio1Bitrate"];
2369 [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack1DrcSlider floatValue]] forKey:@"Audio1TrackDRCSlider"];
2371 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
2373 [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang2PopUp indexOfSelectedItem]] forKey:@"Audio2Track"];
2374 [queueFileJob setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"Audio2TrackDescription"];
2375 [queueFileJob setObject:[fAudTrack2CodecPopUp titleOfSelectedItem] forKey:@"Audio2Encoder"];
2376 [queueFileJob setObject:[fAudTrack2MixPopUp titleOfSelectedItem] forKey:@"Audio2Mixdown"];
2377 [queueFileJob setObject:[fAudTrack2RatePopUp titleOfSelectedItem] forKey:@"Audio2Samplerate"];
2378 [queueFileJob setObject:[fAudTrack2BitratePopUp titleOfSelectedItem] forKey:@"Audio2Bitrate"];
2379 [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack2DrcSlider floatValue]] forKey:@"Audio2TrackDRCSlider"];
2381 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
2383 [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang3PopUp indexOfSelectedItem]] forKey:@"Audio3Track"];
2384 [queueFileJob setObject:[fAudLang3PopUp titleOfSelectedItem] forKey:@"Audio3TrackDescription"];
2385 [queueFileJob setObject:[fAudTrack3CodecPopUp titleOfSelectedItem] forKey:@"Audio3Encoder"];
2386 [queueFileJob setObject:[fAudTrack3MixPopUp titleOfSelectedItem] forKey:@"Audio3Mixdown"];
2387 [queueFileJob setObject:[fAudTrack3RatePopUp titleOfSelectedItem] forKey:@"Audio3Samplerate"];
2388 [queueFileJob setObject:[fAudTrack3BitratePopUp titleOfSelectedItem] forKey:@"Audio3Bitrate"];
2389 [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack3DrcSlider floatValue]] forKey:@"Audio3TrackDRCSlider"];
2391 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
2393 [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang4PopUp indexOfSelectedItem]] forKey:@"Audio4Track"];
2394 [queueFileJob setObject:[fAudLang4PopUp titleOfSelectedItem] forKey:@"Audio4TrackDescription"];
2395 [queueFileJob setObject:[fAudTrack4CodecPopUp titleOfSelectedItem] forKey:@"Audio4Encoder"];
2396 [queueFileJob setObject:[fAudTrack4MixPopUp titleOfSelectedItem] forKey:@"Audio4Mixdown"];
2397 [queueFileJob setObject:[fAudTrack4RatePopUp titleOfSelectedItem] forKey:@"Audio4Samplerate"];
2398 [queueFileJob setObject:[fAudTrack4BitratePopUp titleOfSelectedItem] forKey:@"Audio4Bitrate"];
2399 [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack4DrcSlider floatValue]] forKey:@"Audio4TrackDRCSlider"];
2403 NSMutableArray *subtitlesArray = [[NSMutableArray alloc] initWithArray:[fSubtitlesDelegate getSubtitleArray] copyItems:YES];
2404 [queueFileJob setObject:[NSArray arrayWithArray: subtitlesArray] forKey:@"SubtitleList"];
2405 [subtitlesArray autorelease];
2407 /* Now we go ahead and set the "job->values in the plist for passing right to fQueueEncodeLibhb */
2409 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterStartPopUp indexOfSelectedItem] + 1] forKey:@"JobChapterStart"];
2411 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterEndPopUp indexOfSelectedItem] + 1] forKey:@"JobChapterEnd"];
2414 [queueFileJob setObject:[NSNumber numberWithInt:[[fDstFormatPopUp selectedItem] tag]] forKey:@"JobFileFormatMux"];
2418 [queueFileJob setObject:[NSNumber numberWithInt:[[fVidEncoderPopUp selectedItem] tag]] forKey:@"JobVideoEncoderVcodec"];
2421 [queueFileJob setObject:[NSNumber numberWithInt:[fVidRatePopUp indexOfSelectedItem]] forKey:@"JobIndexVideoFramerate"];
2422 [queueFileJob setObject:[NSNumber numberWithInt:title->rate] forKey:@"JobVrate"];
2423 [queueFileJob setObject:[NSNumber numberWithInt:title->rate_base] forKey:@"JobVrateBase"];
2425 /* Picture Sizing */
2426 /* Use Max Picture settings for whatever the dvd is.*/
2427 [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
2428 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
2429 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
2430 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
2431 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"];
2433 /* Set crop settings here */
2434 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
2435 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
2436 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
2437 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
2438 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
2442 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
2444 //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio1Encoder"];
2445 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1CodecPopUp selectedItem] tag]] forKey:@"JobAudio1Encoder"];
2446 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1MixPopUp selectedItem] tag]] forKey:@"JobAudio1Mixdown"];
2447 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1RatePopUp selectedItem] tag]] forKey:@"JobAudio1Samplerate"];
2448 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1BitratePopUp selectedItem] tag]] forKey:@"JobAudio1Bitrate"];
2450 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
2452 //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio2Encoder"];
2453 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2CodecPopUp selectedItem] tag]] forKey:@"JobAudio2Encoder"];
2454 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2MixPopUp selectedItem] tag]] forKey:@"JobAudio2Mixdown"];
2455 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2RatePopUp selectedItem] tag]] forKey:@"JobAudio2Samplerate"];
2456 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2BitratePopUp selectedItem] tag]] forKey:@"JobAudio2Bitrate"];
2458 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
2460 //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio3Encoder"];
2461 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3CodecPopUp selectedItem] tag]] forKey:@"JobAudio3Encoder"];
2462 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3MixPopUp selectedItem] tag]] forKey:@"JobAudio3Mixdown"];
2463 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3RatePopUp selectedItem] tag]] forKey:@"JobAudio3Samplerate"];
2464 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3BitratePopUp selectedItem] tag]] forKey:@"JobAudio3Bitrate"];
2466 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
2468 //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio4Encoder"];
2469 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4CodecPopUp selectedItem] tag]] forKey:@"JobAudio4Encoder"];
2470 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4MixPopUp selectedItem] tag]] forKey:@"JobAudio4Mixdown"];
2471 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4RatePopUp selectedItem] tag]] forKey:@"JobAudio4Samplerate"];
2472 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4BitratePopUp selectedItem] tag]] forKey:@"JobAudio4Bitrate"];
2476 /* we need to auto relase the queueFileJob and return it */
2477 [queueFileJob autorelease];
2478 return queueFileJob;
2482 /* this is actually called from the queue controller to modify the queue array and return it back to the queue controller */
2483 - (void)moveObjectsInQueueArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
2485 NSUInteger index = [indexSet lastIndex];
2486 NSUInteger aboveInsertIndexCount = 0;
2489 NSUInteger removeIndex;
2491 if (index >= insertIndex)
2493 removeIndex = index + aboveInsertIndexCount;
2494 aboveInsertIndexCount++;
2498 removeIndex = index;
2502 id object = [[QueueFileArray objectAtIndex:removeIndex] retain];
2503 [QueueFileArray removeObjectAtIndex:removeIndex];
2504 [QueueFileArray insertObject:object atIndex:insertIndex];
2507 index = [indexSet indexLessThanIndex:index];
2509 /* We save all of the Queue data here
2510 * and it also gets sent back to the queue controller*/
2511 [self saveQueueFileItem];
2517 #pragma mark Queue Job Processing
2519 - (void) incrementQueueItemDone:(int) queueItemDoneIndexNum
2521 int i = currentQueueEncodeIndex;
2522 [[QueueFileArray objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Status"];
2524 /* We save all of the Queue data here */
2525 [self saveQueueFileItem];
2526 /* We Reload the New Table data for presets */
2527 //[fPresetsOutlineView reloadData];
2529 /* Since we have now marked a queue item as done
2530 * we can go ahead and increment currentQueueEncodeIndex
2531 * so that if there is anything left in the queue we can
2532 * go ahead and move to the next item if we want to */
2533 currentQueueEncodeIndex++ ;
2534 [self writeToActivityLog: "incrementQueueItemDone currentQueueEncodeIndex is incremented to: %d", currentQueueEncodeIndex];
2535 int queueItems = [QueueFileArray count];
2536 /* If we still have more items in our queue, lets go to the next one */
2537 if (currentQueueEncodeIndex < queueItems)
2539 [self writeToActivityLog: "incrementQueueItemDone currentQueueEncodeIndex is incremented to: %d", currentQueueEncodeIndex];
2540 [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
2544 [self writeToActivityLog: "incrementQueueItemDone the %d item queue is complete", currentQueueEncodeIndex - 1];
2548 /* Here we actually tell hb_scan to perform the source scan, using the path to source and title number*/
2549 - (void) performNewQueueScan:(NSString *) scanPath scanTitleNum: (int) scanTitleNum
2551 /* Tell HB to output a new activity log file for this encode */
2552 [outputPanel startEncodeLog:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"]];
2555 /* use a bool to determine whether or not we can decrypt using vlc */
2556 BOOL cancelScanDecrypt = 0;
2557 /* set the bool so that showNewScan knows to apply the appropriate queue
2558 * settings as this is a queue rescan
2560 //applyQueueToScan = YES;
2561 NSString *path = scanPath;
2562 HBDVDDetector *detector = [HBDVDDetector detectorForPath:path];
2564 if( [detector isVideoDVD] )
2566 // The chosen path was actually on a DVD, so use the raw block
2567 // device path instead.
2568 path = [detector devicePath];
2569 [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]];
2571 /* lets check for vlc here to make sure we have a dylib available to use for decrypting */
2572 NSString *vlcPath = @"/Applications/VLC.app";
2573 NSFileManager * fileManager = [NSFileManager defaultManager];
2574 if ([fileManager fileExistsAtPath:vlcPath] == 0)
2576 /*vlc not found in /Applications so we set the bool to cancel scanning to 1 */
2577 cancelScanDecrypt = 1;
2578 [self writeToActivityLog: "VLC app not found for decrypting physical dvd"];
2580 status = NSRunAlertPanel(@"HandBrake could not find VLC.",@"Please download and install VLC media player in your /Applications folder if you wish to read encrypted DVDs.", @"Get VLC", @"Cancel Scan", @"Attempt Scan Anyway");
2581 [NSApp requestUserAttention:NSCriticalRequest];
2583 if (status == NSAlertDefaultReturn)
2585 /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
2586 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/"]];
2588 else if (status == NSAlertAlternateReturn)
2590 /* User chose to cancel the scan */
2591 [self writeToActivityLog: "cannot open physical dvd , scan cancelled"];
2595 /* User chose to override our warning and scan the physical dvd anyway, at their own peril. on an encrypted dvd this produces massive log files and fails */
2596 cancelScanDecrypt = 0;
2597 [self writeToActivityLog: "user overrode vlc warning -trying to open physical dvd without decryption"];
2603 /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */
2604 [self writeToActivityLog: "VLC app found for decrypting physical dvd"];
2608 if (cancelScanDecrypt == 0)
2610 /* we actually pass the scan off to libhb here */
2611 /* If there is no title number passed to scan, we use "0"
2612 * which causes the default behavior of a full source scan
2618 if (scanTitleNum > 0)
2620 [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum];
2623 [self writeToActivityLog: "performNewQueueScan currentQueueEncodeIndex is: %d", currentQueueEncodeIndex];
2624 /* We use our advance pref to determine how many previews to scan */
2625 int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue];
2626 hb_scan( fQueueEncodeLibhb, [path UTF8String], scanTitleNum, hb_num_previews, 0 );
2630 /* This assumes that we have re-scanned and loaded up a new queue item to send to libhb as fQueueEncodeLibhb */
2631 - (void) processNewQueueEncode
2633 hb_list_t * list = hb_get_titles( fQueueEncodeLibhb );
2634 hb_title_t * title = (hb_title_t *) hb_list_item( list,0 ); // is always zero since now its a single title scan
2635 hb_job_t * job = title->job;
2637 if( !hb_list_count( list ) )
2639 [self writeToActivityLog: "processNewQueueEncode WARNING nothing found in the title list"];
2642 NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex];
2643 [self writeToActivityLog: "Preset: %s", [[queueToApply objectForKey:@"PresetName"] UTF8String]];
2644 [self writeToActivityLog: "processNewQueueEncode number of passes expected is: %d", ([[queueToApply objectForKey:@"VideoTwoPass"] intValue] + 1)];
2645 job->file = [[queueToApply objectForKey:@"DestinationPath"] UTF8String];
2646 //[self writeToActivityLog: "processNewQueueEncode sending to prepareJob"];
2650 * If scanning we need to do some extra setup of the job.
2652 if( job->indepth_scan == 1 )
2657 * When subtitle scan is enabled do a fast pre-scan job
2658 * which will determine which subtitles to enable, if any.
2661 x264opts_tmp = job->x264opts;
2663 job->x264opts = NULL;
2665 job->indepth_scan = 1;
2669 * Add the pre-scan job
2671 hb_add( fQueueEncodeLibhb, job );
2672 job->x264opts = x264opts_tmp;
2676 if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 )
2678 job->indepth_scan = 0;
2684 hb_add( fQueueEncodeLibhb, job );
2688 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
2689 strcpy(job->x264opts, [[queueToApply objectForKey:@"x264Option"] UTF8String]);
2691 hb_add( fQueueEncodeLibhb, job );
2696 job->indepth_scan = 0;
2699 hb_add( fQueueEncodeLibhb, job );
2702 NSString *destinationDirectory = [[queueToApply objectForKey:@"DestinationPath"] stringByDeletingLastPathComponent];
2703 [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
2704 /* Lets mark our new encode as 1 or "Encoding" */
2705 [queueToApply setObject:[NSNumber numberWithInt:1] forKey:@"Status"];
2706 [self saveQueueFileItem];
2708 /* we need to clean up the subtitle tracks after the job(s) have been set */
2709 int num_subtitle_tracks = hb_list_count(job->list_subtitle);
2711 for(ii = 0; ii < num_subtitle_tracks; ii++)
2713 hb_subtitle_t * subtitle;
2714 subtitle = (hb_subtitle_t *)hb_list_item(job->list_subtitle, 0);
2717 hb_list_rem(job->list_subtitle, subtitle);
2722 /* We should be all setup so let 'er rip */
2729 #pragma mark Queue Item Editing
2731 /* Rescans the chosen queue item back into the main window */
2732 - (void)rescanQueueItemToMainWindow:(NSString *) scanPath scanTitleNum: (int) scanTitleNum selectedQueueItem: (int) selectedQueueItem
2734 fqueueEditRescanItemNum = selectedQueueItem;
2735 [self writeToActivityLog: "rescanQueueItemToMainWindow: Re-scanning queue item at index:%d",fqueueEditRescanItemNum];
2736 applyQueueToScan = YES;
2737 /* Set the browsedSourceDisplayName for showNewScan */
2738 browsedSourceDisplayName = [[QueueFileArray objectAtIndex:fqueueEditRescanItemNum] objectForKey:@"SourceName"];
2739 [self performScan:scanPath scanTitleNum:scanTitleNum];
2743 /* We use this method after a queue item rescan for edit.
2744 * it largely mirrors -selectPreset in terms of structure.
2745 * Assumes that a queue item has been reloaded into the main window.
2747 - (IBAction)applyQueueSettingsToMainWindow:(id)sender
2749 NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:fqueueEditRescanItemNum];
2750 hb_job_t * job = fTitle->job;
2753 [self writeToActivityLog: "applyQueueSettingsToMainWindow: queue item found"];
2755 /* Set title number and chapters */
2756 /* since the queue only scans a single title, its already been selected in showNewScan
2757 so do not try to reset it here. However if we do decide to do full source scans on
2758 a queue edit rescan, we would need it. So leaving in for now but commenting out. */
2759 //[fSrcTitlePopUp selectItemAtIndex: [[queueToApply objectForKey:@"TitleNumber"] intValue] - 1];
2761 [fSrcChapterStartPopUp selectItemAtIndex: [[queueToApply objectForKey:@"ChapterStart"] intValue] - 1];
2762 [fSrcChapterEndPopUp selectItemAtIndex: [[queueToApply objectForKey:@"ChapterEnd"] intValue] - 1];
2765 [fDstFormatPopUp selectItemWithTitle:[queueToApply objectForKey:@"FileFormat"]];
2766 [self formatPopUpChanged:nil];
2768 /* Chapter Markers*/
2769 [fCreateChapterMarkers setState:[[queueToApply objectForKey:@"ChapterMarkers"] intValue]];
2770 /* Allow Mpeg4 64 bit formatting +4GB file sizes */
2771 [fDstMp4LargeFileCheck setState:[[queueToApply objectForKey:@"Mp4LargeFile"] intValue]];
2772 /* Mux mp4 with http optimization */
2773 [fDstMp4HttpOptFileCheck setState:[[queueToApply objectForKey:@"Mp4HttpOptimize"] intValue]];
2776 /* We set the advanced opt string here if applicable*/
2777 [fVidEncoderPopUp selectItemWithTitle:[queueToApply objectForKey:@"VideoEncoder"]];
2778 [fAdvancedOptions setOptions:[queueToApply objectForKey:@"x264Option"]];
2780 /* Lets run through the following functions to get variables set there */
2781 [self videoEncoderPopUpChanged:nil];
2782 /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/
2783 [fDstMp4iPodFileCheck setState:[[queueToApply objectForKey:@"Mp4iPodCompatible"] intValue]];
2784 [self calculateBitrate:nil];
2787 [fVidQualityMatrix selectCellAtRow:[[queueToApply objectForKey:@"VideoQualityType"] intValue] column:0];
2789 [fVidTargetSizeField setStringValue:[queueToApply objectForKey:@"VideoTargetSize"]];
2790 [fVidBitrateField setStringValue:[queueToApply objectForKey:@"VideoAvgBitrate"]];
2791 /* Since we are now using RF Values for the slider, we detect if the preset uses an old quality float.
2792 * So, check to see if the quality value is less than 1.0 which should indicate the old ".062" type
2793 * quality preset. Caveat: in the case of x264, where the RF scale starts at 0, it would misinterpret
2794 * a preset that uses 0.0 - 0.99 for RF as an old style preset. Not sure how to get around that one yet,
2795 * though it should be a corner case since it would pretty much be a preset for lossless encoding. */
2796 if ([[queueToApply objectForKey:@"VideoQualitySlider"] floatValue] < 1.0)
2798 /* For the quality slider we need to convert the old percent's to the new rf scales */
2799 float rf = (([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]);
2800 [fVidQualitySlider setFloatValue:rf];
2805 /* Since theora's qp value goes up from left to right, we can just set the slider float value */
2806 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
2808 [fVidQualitySlider setFloatValue:[[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]];
2812 /* since ffmpeg and x264 use an "inverted" slider (lower qp/rf values indicate a higher quality) we invert the value on the slider */
2813 [fVidQualitySlider setFloatValue:([fVidQualitySlider maxValue] + [fVidQualitySlider minValue]) - [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]];
2817 [self videoMatrixChanged:nil];
2818 [self writeToActivityLog: "applyQueueSettingsToMainWindow: video matrix changed"];
2819 /* Video framerate */
2820 /* For video preset video framerate, we want to make sure that Same as source does not conflict with the
2821 detected framerate in the fVidRatePopUp so we use index 0*/
2822 if ([[queueToApply objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"])
2824 [fVidRatePopUp selectItemAtIndex: 0];
2828 [fVidRatePopUp selectItemWithTitle:[queueToApply objectForKey:@"VideoFramerate"]];
2831 /* 2 Pass Encoding */
2832 [fVidTwoPassCheck setState:[[queueToApply objectForKey:@"VideoTwoPass"] intValue]];
2833 [self twoPassCheckboxChanged:nil];
2834 /* Turbo 1st pass for 2 Pass Encoding */
2835 [fVidTurboPassCheck setState:[[queueToApply objectForKey:@"VideoTurboTwoPass"] intValue]];
2840 /* Now lets add our new tracks to the audio list here */
2841 if ([[queueToApply objectForKey:@"Audio1Track"] intValue] > 0)
2843 [fAudLang1PopUp selectItemAtIndex: [[queueToApply objectForKey:@"Audio1Track"] intValue]];
2844 [self audioTrackPopUpChanged: fAudLang1PopUp];
2845 [fAudTrack1CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Encoder"]];
2846 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
2848 [fAudTrack1MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Mixdown"]];
2850 [fAudTrack1RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Samplerate"]];
2851 [fAudTrack1BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Bitrate"]];
2853 [fAudTrack1DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio1TrackDRCSlider"] floatValue]];
2854 [self audioDRCSliderChanged: fAudTrack1DrcSlider];
2858 [fAudLang1PopUp selectItemAtIndex: 0];
2859 [self audioTrackPopUpChanged: fAudLang1PopUp];
2861 if ([[queueToApply objectForKey:@"Audio2Track"] intValue] > 0)
2863 [fAudLang2PopUp selectItemAtIndex: [[queueToApply objectForKey:@"Audio2Track"] intValue]];
2864 [self audioTrackPopUpChanged: fAudLang2PopUp];
2865 [fAudTrack2CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Encoder"]];
2866 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
2868 [fAudTrack2MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Mixdown"]];
2870 [fAudTrack2RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Samplerate"]];
2871 [fAudTrack2BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Bitrate"]];
2873 [fAudTrack2DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio2TrackDRCSlider"] floatValue]];
2874 [self audioDRCSliderChanged: fAudTrack2DrcSlider];
2878 [fAudLang2PopUp selectItemAtIndex: 0];
2879 [self audioTrackPopUpChanged: fAudLang2PopUp];
2881 if ([[queueToApply objectForKey:@"Audio3Track"] intValue] > 0)
2883 [fAudLang3PopUp selectItemAtIndex: [[queueToApply objectForKey:@"Audio3Track"] intValue]];
2884 [self audioTrackPopUpChanged: fAudLang3PopUp];
2885 [fAudTrack3CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Encoder"]];
2886 [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
2888 [fAudTrack3MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Mixdown"]];
2890 [fAudTrack3RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Samplerate"]];
2891 [fAudTrack3BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Bitrate"]];
2893 [fAudTrack3DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio3TrackDRCSlider"] floatValue]];
2894 [self audioDRCSliderChanged: fAudTrack3DrcSlider];
2898 [fAudLang3PopUp selectItemAtIndex: 0];
2899 [self audioTrackPopUpChanged: fAudLang3PopUp];
2901 if ([[queueToApply objectForKey:@"Audio4Track"] intValue] > 0)
2903 [fAudLang4PopUp selectItemAtIndex: [[queueToApply objectForKey:@"Audio4Track"] intValue]];
2904 [self audioTrackPopUpChanged: fAudLang4PopUp];
2905 [fAudTrack4CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Encoder"]];
2906 [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
2908 [fAudTrack4MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Mixdown"]];
2910 [fAudTrack4RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Samplerate"]];
2911 [fAudTrack4BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Bitrate"]];
2913 [fAudTrack4DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio4TrackDRCSlider"] floatValue]];
2914 [self audioDRCSliderChanged: fAudTrack4DrcSlider];
2918 [fAudLang4PopUp selectItemAtIndex: 0];
2919 [self audioTrackPopUpChanged: fAudLang4PopUp];
2922 [self writeToActivityLog: "applyQueueSettingsToMainWindow: audio set up"];
2924 /* Crashy crashy right now, working on it */
2925 [fSubtitlesDelegate setNewSubtitles:[queueToApply objectForKey:@"SubtitleList"]];
2926 [fSubtitlesTable reloadData];
2927 /* Picture Settings */
2929 /* If Cropping is set to custom, then recall all four crop values from
2930 when the preset was created and apply them */
2931 if ([[queueToApply objectForKey:@"PictureAutoCrop"] intValue] == 0)
2933 [fPictureController setAutoCrop:NO];
2935 /* Here we use the custom crop values saved at the time the preset was saved */
2936 job->crop[0] = [[queueToApply objectForKey:@"PictureTopCrop"] intValue];
2937 job->crop[1] = [[queueToApply objectForKey:@"PictureBottomCrop"] intValue];
2938 job->crop[2] = [[queueToApply objectForKey:@"PictureLeftCrop"] intValue];
2939 job->crop[3] = [[queueToApply objectForKey:@"PictureRightCrop"] intValue];
2942 else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */
2944 [fPictureController setAutoCrop:YES];
2945 /* Here we use the auto crop values determined right after scan */
2946 job->crop[0] = AutoCropTop;
2947 job->crop[1] = AutoCropBottom;
2948 job->crop[2] = AutoCropLeft;
2949 job->crop[3] = AutoCropRight;
2953 job->modulus = [[queueToApply objectForKey:@"PictureModulus"] intValue];
2955 /* we check to make sure the presets width/height does not exceed the sources width/height */
2956 if (fTitle->width < [[queueToApply objectForKey:@"PictureWidth"] intValue] || fTitle->height < [[queueToApply objectForKey:@"PictureHeight"] intValue])
2958 /* if so, then we use the sources height and width to avoid scaling up */
2959 //job->width = fTitle->width;
2960 //job->height = fTitle->height;
2961 [self revertPictureSizeToMax:nil];
2963 else // source width/height is >= the preset height/width
2965 /* we can go ahead and use the presets values for height and width */
2966 job->width = [[queueToApply objectForKey:@"PictureWidth"] intValue];
2967 job->height = [[queueToApply objectForKey:@"PictureHeight"] intValue];
2969 job->keep_ratio = [[queueToApply objectForKey:@"PictureKeepRatio"] intValue];
2970 if (job->keep_ratio == 1)
2972 hb_fix_aspect( job, HB_KEEP_WIDTH );
2973 if( job->height > fTitle->height )
2975 job->height = fTitle->height;
2976 hb_fix_aspect( job, HB_KEEP_HEIGHT );
2979 job->anamorphic.mode = [[queueToApply objectForKey:@"PicturePAR"] intValue];
2980 job->modulus = [[queueToApply objectForKey:@"PictureModulus"] intValue];
2982 [self writeToActivityLog: "applyQueueSettingsToMainWindow: picture sizing set up"];
2987 /* We only allow *either* Decomb or Deinterlace. So check for the PictureDecombDeinterlace key.
2988 * also, older presets may not have this key, in which case we also check to see if that preset had PictureDecomb
2989 * specified, in which case we use decomb and ignore any possible Deinterlace settings as using both was less than
2992 [fPictureController setUseDecomb:1];
2993 [fPictureController setDecomb:0];
2994 [fPictureController setDeinterlace:0];
2995 if ([[queueToApply objectForKey:@"PictureDecombDeinterlace"] intValue] == 1 || [[queueToApply objectForKey:@"PictureDecomb"] intValue] > 0)
2997 /* we are using decomb */
2999 if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] > 0)
3001 [fPictureController setDecomb:[[queueToApply objectForKey:@"PictureDecomb"] intValue]];
3003 /* if we are using "Custom" in the decomb setting, also set the custom string*/
3004 if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 1)
3006 [fPictureController setDecombCustomString:[queueToApply objectForKey:@"PictureDecombCustom"]];
3012 /* We are using Deinterlace */
3014 if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] > 0)
3016 [fPictureController setUseDecomb:0];
3017 [fPictureController setDeinterlace:[[queueToApply objectForKey:@"PictureDeinterlace"] intValue]];
3018 /* if we are using "Custom" in the deinterlace setting, also set the custom string*/
3019 if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 1)
3021 [fPictureController setDeinterlaceCustomString:[queueToApply objectForKey:@"PictureDeinterlaceCustom"]];
3028 if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] > 0)
3030 [fPictureController setDetelecine:[[queueToApply objectForKey:@"PictureDetelecine"] intValue]];
3031 /* if we are using "Custom" in the detelecine setting, also set the custom string*/
3032 if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
3034 [fPictureController setDetelecineCustomString:[queueToApply objectForKey:@"PictureDetelecineCustom"]];
3039 [fPictureController setDetelecine:0];
3043 if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] > 0)
3045 [fPictureController setDenoise:[[queueToApply objectForKey:@"PictureDenoise"] intValue]];
3046 /* if we are using "Custom" in the denoise setting, also set the custom string*/
3047 if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 1)
3049 [fPictureController setDenoiseCustomString:[queueToApply objectForKey:@"PictureDenoiseCustom"]];
3054 [fPictureController setDenoise:0];
3058 if ([[queueToApply objectForKey:@"PictureDeblock"] intValue] == 1)
3060 /* if its a one, then its the old on/off deblock, set on to 5*/
3061 [fPictureController setDeblock:5];
3065 /* use the settings intValue */
3066 [fPictureController setDeblock:[[queueToApply objectForKey:@"PictureDeblock"] intValue]];
3069 if ([[queueToApply objectForKey:@"VideoGrayScale"] intValue] == 1)
3071 [fPictureController setGrayscale:1];
3075 [fPictureController setGrayscale:0];
3078 /* we call SetTitle: in fPictureController so we get an instant update in the Picture Settings window */
3079 [fPictureController SetTitle:fTitle];
3080 [self calculatePictureSizing:nil];
3082 [self writeToActivityLog: "applyQueueSettingsToMainWindow: picture filters set up"];
3083 /* somehow we need to figure out a way to tie the queue item to a preset if it used one */
3084 //[queueFileJob setObject:[fPresetSelectedDisplay stringValue] forKey:@"PresetName"];
3085 // [queueFileJob setObject:[NSNumber numberWithInt:[fPresetsOutlineView selectedRow]] forKey:@"PresetIndexNum"];
3086 if ([queueToApply objectForKey:@"PresetIndexNum"]) // This item used a preset so insert that info
3088 /* Deselect the currently selected Preset if there is one*/
3089 //[fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[[queueToApply objectForKey:@"PresetIndexNum"] intValue]] byExtendingSelection:NO];
3090 //[self selectPreset:nil];
3092 //[fPresetsOutlineView selectRow:[[queueToApply objectForKey:@"PresetIndexNum"] intValue]];
3093 /* Change UI to show "Custom" settings are being used */
3094 //[fPresetSelectedDisplay setStringValue: [[queueToApply objectForKey:@"PresetName"] stringValue]];
3096 curUserPresetChosenNum = nil;
3100 /* Deselect the currently selected Preset if there is one*/
3101 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
3102 /* Change UI to show "Custom" settings are being used */
3103 [fPresetSelectedDisplay setStringValue: @"Custom"];
3105 //curUserPresetChosenNum = nil;
3108 /* We need to set this bool back to NO, in case the user wants to do a scan */
3109 //applyQueueToScan = NO;
3111 /* Not that source is loaded and settings applied, delete the queue item from the queue */
3112 [self writeToActivityLog: "applyQueueSettingsToMainWindow: deleting queue item:%d",fqueueEditRescanItemNum];
3113 [self removeQueueFileItem:fqueueEditRescanItemNum];
3119 #pragma mark Live Preview
3120 /* Note,this is much like prepareJob, but directly sets the job vars so Picture Preview
3121 * can encode to its temp preview directory and playback. This is *not* used for any actual user
3124 - (void) prepareJobForPreview
3126 hb_list_t * list = hb_get_titles( fHandle );
3127 hb_title_t * title = (hb_title_t *) hb_list_item( list,
3128 [fSrcTitlePopUp indexOfSelectedItem] );
3129 hb_job_t * job = title->job;
3130 hb_audio_config_t * audio;
3131 /* set job->angle for libdvdnav */
3132 job->angle = [fSrcAnglePopUp indexOfSelectedItem] + 1;
3133 /* Chapter selection */
3134 job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
3135 job->chapter_end = [fSrcChapterEndPopUp indexOfSelectedItem] + 1;
3137 /* Format (Muxer) and Video Encoder */
3138 job->mux = [[fDstFormatPopUp selectedItem] tag];
3139 job->vcodec = [[fVidEncoderPopUp selectedItem] tag];
3141 job->chapter_markers = 0;
3143 if( job->vcodec & HB_VCODEC_X264 )
3146 /* Below Sends x264 options to the core library if x264 is selected*/
3147 /* Lets use this as per Nyx, Thanks Nyx!*/
3148 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
3149 /* For previews we ignore the turbo option for the first pass of two since we only use 1 pass */
3150 strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]);
3155 /* Video settings */
3156 /* Set vfr to 0 as it's only on if using same as source in the framerate popup
3157 * and detelecine is on, so we handle that in the logic below
3160 if( [fVidRatePopUp indexOfSelectedItem] > 0 )
3162 /* a specific framerate has been chosen */
3163 job->vrate = 27000000;
3164 job->vrate_base = hb_video_rates[[fVidRatePopUp indexOfSelectedItem]-1].rate;
3165 /* We are not same as source so we set job->cfr to 1
3166 * to enable constant frame rate since user has specified
3167 * a specific framerate*/
3172 /* We are same as source (variable) */
3173 job->vrate = title->rate;
3174 job->vrate_base = title->rate_base;
3175 /* We are same as source so we set job->cfr to 0
3176 * to enable true same as source framerate */
3178 /* If we are same as source and we have detelecine on, we need to turn on
3181 if ([fPictureController detelecine] == 1)
3187 switch( [fVidQualityMatrix selectedRow] )
3191 Bitrate should already have been calculated and displayed
3192 in fVidBitrateField, so let's just use it */
3194 job->vquality = -1.0;
3195 job->vbitrate = [fVidBitrateField intValue];
3198 job->vquality = [fVidQualityRFField floatValue];
3203 /* Subtitle settings */
3204 NSMutableArray *subtitlesArray = [[NSMutableArray alloc] initWithArray:[fSubtitlesDelegate getSubtitleArray] copyItems:YES];
3211 bool one_burned = FALSE;
3214 NSEnumerator *enumerator = [subtitlesArray objectEnumerator];
3216 while (tempObject = [enumerator nextObject])
3219 subtitle = [[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue];
3220 force = [[tempObject objectForKey:@"subtitleTrackForced"] intValue];
3221 burned = [[tempObject objectForKey:@"subtitleTrackBurned"] intValue];
3222 def = [[tempObject objectForKey:@"subtitleTrackDefault"] intValue];
3224 /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups,
3225 * we want to ignore it for display as well as encoding.
3229 /* if i is 0, then we are in the first item of the subtitles which we need to
3230 * check for the "Foreign Audio Search" which would be subtitleSourceTrackNum of 1
3231 * bearing in mind that for all tracks subtitleSourceTrackNum of 0 is None.
3234 /* if we are on the first track and using "Foreign Audio Search" */
3235 if (i == 0 && subtitle == 1)
3237 /* NOTE: Currently foreign language search is borked for preview.
3238 * Commented out but left in for initial commit. */
3241 [self writeToActivityLog: "Foreign Language Search: %d", 1];
3243 job->indepth_scan = 1;
3244 if (burned == 1 || job->mux != HB_MUX_MP4)
3246 if (burned != 1 && job->mux == HB_MUX_MKV)
3248 job->select_subtitle_config.dest = PASSTHRUSUB;
3252 job->select_subtitle_config.dest = RENDERSUB;
3255 job->select_subtitle_config.force = force;
3256 job->select_subtitle_config.default_track = def;
3265 /* for the actual source tracks, we must subtract the non source entries so
3266 * that the menu index matches the source subtitle_list index for convenience */
3269 /* for the first track, the source tracks start at menu index 2 ( None is 0,
3270 * Foreign Language Search is 1) so subtract 2 */
3271 subtitle = subtitle - 2;
3275 /* for all other tracks, the source tracks start at menu index 1 (None is 0)
3278 subtitle = subtitle - 1;
3281 /* We are setting a source subtitle so access the source subtitle info */
3282 hb_subtitle_t * subt;
3284 subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle);
3286 /* if we are getting the subtitles from an external srt file */
3287 if ([[tempObject objectForKey:@"subtitleSourceTrackType"] isEqualToString:@"SRT"])
3289 hb_subtitle_config_t sub_config;
3291 sub_config.offset = [[tempObject objectForKey:@"subtitleTrackSrtOffset"] intValue];
3293 /* we need to srncpy file path and char code */
3294 strncpy(sub_config.src_filename, [[tempObject objectForKey:@"subtitleSourceSrtFilePath"] UTF8String], 128);
3295 strncpy(sub_config.src_codeset, [[tempObject objectForKey:@"subtitleTrackSrtCharCode"] UTF8String], 40);
3297 sub_config.force = 0;
3298 sub_config.dest = PASSTHRUSUB;
3299 sub_config.default_track = def;
3301 hb_srt_add( job, &sub_config, [[tempObject objectForKey:@"subtitleTrackSrtLanguageIso3"] UTF8String]);
3306 hb_subtitle_config_t sub_config = subt->config;
3308 if (!burned && job->mux == HB_MUX_MKV &&
3309 subt->format == PICTURESUB)
3311 sub_config.dest = PASSTHRUSUB;
3313 else if (!burned && job->mux == HB_MUX_MP4 &&
3314 subt->format == PICTURESUB)
3316 // Skip any non-burned vobsubs when output is mp4
3319 else if ( burned && subt->format == PICTURESUB )
3321 // Only allow one subtitle to be burned into the video
3326 sub_config.force = force;
3327 sub_config.default_track = def;
3328 hb_subtitle_add( job, &sub_config, subtitle );
3338 [subtitlesArray autorelease];
3341 /* Audio tracks and mixdowns */
3342 /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
3343 int audiotrack_count = hb_list_count(job->list_audio);
3344 for( int i = 0; i < audiotrack_count;i++)
3346 hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
3347 hb_list_rem(job->list_audio, temp_audio);
3349 /* Now lets add our new tracks to the audio list here */
3350 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
3352 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3353 hb_audio_config_init(audio);
3354 audio->in.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
3355 /* We go ahead and assign values to our audio->out.<properties> */
3356 audio->out.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
3357 audio->out.codec = [[fAudTrack1CodecPopUp selectedItem] tag];
3358 audio->out.mixdown = [[fAudTrack1MixPopUp selectedItem] tag];
3359 audio->out.bitrate = [[fAudTrack1BitratePopUp selectedItem] tag];
3360 audio->out.samplerate = [[fAudTrack1RatePopUp selectedItem] tag];
3361 audio->out.dynamic_range_compression = [fAudTrack1DrcField floatValue];
3363 hb_audio_add( job, audio );
3366 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
3368 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3369 hb_audio_config_init(audio);
3370 audio->in.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
3371 /* We go ahead and assign values to our audio->out.<properties> */
3372 audio->out.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
3373 audio->out.codec = [[fAudTrack2CodecPopUp selectedItem] tag];
3374 audio->out.mixdown = [[fAudTrack2MixPopUp selectedItem] tag];
3375 audio->out.bitrate = [[fAudTrack2BitratePopUp selectedItem] tag];
3376 audio->out.samplerate = [[fAudTrack2RatePopUp selectedItem] tag];
3377 audio->out.dynamic_range_compression = [fAudTrack2DrcField floatValue];
3379 hb_audio_add( job, audio );
3384 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
3386 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3387 hb_audio_config_init(audio);
3388 audio->in.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
3389 /* We go ahead and assign values to our audio->out.<properties> */
3390 audio->out.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
3391 audio->out.codec = [[fAudTrack3CodecPopUp selectedItem] tag];
3392 audio->out.mixdown = [[fAudTrack3MixPopUp selectedItem] tag];
3393 audio->out.bitrate = [[fAudTrack3BitratePopUp selectedItem] tag];
3394 audio->out.samplerate = [[fAudTrack3RatePopUp selectedItem] tag];
3395 audio->out.dynamic_range_compression = [fAudTrack3DrcField floatValue];
3397 hb_audio_add( job, audio );
3402 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
3404 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3405 hb_audio_config_init(audio);
3406 audio->in.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
3407 /* We go ahead and assign values to our audio->out.<properties> */
3408 audio->out.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
3409 audio->out.codec = [[fAudTrack4CodecPopUp selectedItem] tag];
3410 audio->out.mixdown = [[fAudTrack4MixPopUp selectedItem] tag];
3411 audio->out.bitrate = [[fAudTrack4BitratePopUp selectedItem] tag];
3412 audio->out.samplerate = [[fAudTrack4RatePopUp selectedItem] tag];
3413 audio->out.dynamic_range_compression = [fAudTrack4DrcField floatValue];
3415 hb_audio_add( job, audio );
3424 /* Though Grayscale is not really a filter, per se
3425 * we put it here since its in the filters panel
3428 if ([fPictureController grayscale])
3437 /* Initialize the filters list */
3438 job->filters = hb_list_init();
3440 /* Now lets call the filters if applicable.
3441 * The order of the filters is critical
3445 if ([fPictureController detelecine] == 1)
3447 /* use a custom detelecine string */
3448 hb_filter_detelecine.settings = (char *) [[fPictureController detelecineCustomString] UTF8String];
3449 hb_list_add( job->filters, &hb_filter_detelecine );
3451 if ([fPictureController detelecine] == 2)
3454 hb_list_add( job->filters, &hb_filter_detelecine );
3459 if ([fPictureController useDecomb] == 1)
3462 /* we add the custom string if present */
3463 if ([fPictureController decomb] == 1)
3465 /* use a custom decomb string */
3466 hb_filter_decomb.settings = (char *) [[fPictureController decombCustomString] UTF8String];
3467 hb_list_add( job->filters, &hb_filter_decomb );
3469 if ([fPictureController decomb] == 2)
3471 /* Run old deinterlacer fd by default */
3472 //hb_filter_decomb.settings = (char *) [[fPicSettingDecomb stringValue] UTF8String];
3473 hb_list_add( job->filters, &hb_filter_decomb );
3480 if ([fPictureController deinterlace] == 1)
3482 /* we add the custom string if present */
3483 hb_filter_deinterlace.settings = (char *) [[fPictureController deinterlaceCustomString] UTF8String];
3484 hb_list_add( job->filters, &hb_filter_deinterlace );
3486 else if ([fPictureController deinterlace] == 2)
3488 /* Run old deinterlacer fd by default */
3489 hb_filter_deinterlace.settings = "-1";
3490 hb_list_add( job->filters, &hb_filter_deinterlace );
3492 else if ([fPictureController deinterlace] == 3)
3494 /* Yadif mode 0 (without spatial deinterlacing.) */
3495 hb_filter_deinterlace.settings = "2";
3496 hb_list_add( job->filters, &hb_filter_deinterlace );
3498 else if ([fPictureController deinterlace] == 4)
3500 /* Yadif (with spatial deinterlacing) */
3501 hb_filter_deinterlace.settings = "0";
3502 hb_list_add( job->filters, &hb_filter_deinterlace );
3508 if ([fPictureController denoise] == 1) // custom in popup
3510 /* we add the custom string if present */
3511 hb_filter_denoise.settings = (char *) [[fPictureController denoiseCustomString] UTF8String];
3512 hb_list_add( job->filters, &hb_filter_denoise );
3514 else if ([fPictureController denoise] == 2) // Weak in popup
3516 hb_filter_denoise.settings = "2:1:2:3";
3517 hb_list_add( job->filters, &hb_filter_denoise );
3519 else if ([fPictureController denoise] == 3) // Medium in popup
3521 hb_filter_denoise.settings = "3:2:2:3";
3522 hb_list_add( job->filters, &hb_filter_denoise );
3524 else if ([fPictureController denoise] == 4) // Strong in popup
3526 hb_filter_denoise.settings = "7:7:5:5";
3527 hb_list_add( job->filters, &hb_filter_denoise );
3531 /* Deblock (uses pp7 default) */
3532 /* NOTE: even though there is a valid deblock setting of 0 for the filter, for
3533 * the macgui's purposes a value of 0 actually means to not even use the filter
3534 * current hb_filter_deblock.settings valid ranges are from 5 - 15
3536 if ([fPictureController deblock] != 0)
3538 NSString *deblockStringValue = [NSString stringWithFormat: @"%d",[fPictureController deblock]];
3539 hb_filter_deblock.settings = (char *) [deblockStringValue UTF8String];
3540 hb_list_add( job->filters, &hb_filter_deblock );
3547 #pragma mark Job Handling
3553 NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex];
3554 hb_list_t * list = hb_get_titles( fQueueEncodeLibhb );
3555 hb_title_t * title = (hb_title_t *) hb_list_item( list,0 ); // is always zero since now its a single title scan
3556 hb_job_t * job = title->job;
3557 hb_audio_config_t * audio;
3558 /* Title Angle for dvdnav */
3559 job->angle = [[queueToApply objectForKey:@"TitleAngle"] intValue];
3561 if([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 0)
3563 /* Chapter selection */
3564 [self writeToActivityLog: "Start / Stop set to chapters"];
3565 job->chapter_start = [[queueToApply objectForKey:@"JobChapterStart"] intValue];
3566 job->chapter_end = [[queueToApply objectForKey:@"JobChapterEnd"] intValue];
3568 else if ([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 1)
3570 /* we are pts based start / stop */
3571 [self writeToActivityLog: "Start / Stop set to seconds ..."];
3573 /* Point A to Point B. Since we cannot get frame accurate start times, attempt to glean a semi-accurate start time based on a percentage of the
3574 * scanned title time as per live preview, while in some cases inaccurate its the best I can do with what I have barring a pre-scan index afaik.
3576 /* Attempt to bastardize the live preview code to get a roughly 1 second accurate point a to point b encode ... */
3577 /* get the start seconds from the start seconds field */
3578 int start_seconds = [[queueToApply objectForKey:@"StartSeconds"] intValue];
3579 //job->start_at_preview = start_seconds;
3580 /* The number of seek points equals the number of seconds announced in the title as that is our current granularity */
3581 //job->seek_points = [[queueToApply objectForKey:@"SourceTotalSeconds"] intValue];
3582 job->pts_to_start = start_seconds * 90000LL;
3583 /* Stop seconds is actually the duration of encode, so subtract the end seconds from the start seconds */
3584 int stop_seconds = [[queueToApply objectForKey:@"StopSeconds"] intValue];
3585 job->pts_to_stop = stop_seconds * 90000LL;
3587 /* A bunch of verbose activity log messages to check on what should be expected */
3588 [self writeToActivityLog: "point a to b should start at: %d seconds", start_seconds];
3589 [self writeToActivityLog: "point a to b should start at (hh:mm:ss): %d:%d:%d", start_seconds / 3600, ( start_seconds / 60 ) % 60,start_seconds % 60];
3590 [self writeToActivityLog: "point a to b duration: %d seconds", stop_seconds];
3591 [self writeToActivityLog: "point a to b duration (hh:mm:ss): %d:%d:%d", stop_seconds / 3600, ( stop_seconds / 60 ) % 60,stop_seconds % 60];
3592 [self writeToActivityLog: "point a to b should end at: %d seconds", start_seconds + stop_seconds];
3593 [self writeToActivityLog: "point a to b should end at (hh:mm:ss): %d:%d:%d", (start_seconds + stop_seconds) / 3600, ( (start_seconds + stop_seconds) / 60 ) % 60,(start_seconds + stop_seconds) % 60];
3595 else if ([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 2)
3597 /* we are frame based start / stop */
3598 [self writeToActivityLog: "Start / Stop set to frames ..."];
3600 /* Point A to Point B. Since we cannot get frame accurate start times, attempt to glean a semi-accurate start time based on a percentage of the
3601 * scanned title time as per live preview, while in some cases inaccurate its the best I can do with what I have barring a pre-scan index afaik.
3603 /* Attempt to bastardize the live preview code to get a roughly 1 second accurate point a to point b encode ... */
3604 /* get the start seconds from the start seconds field */
3605 int start_frame = [[queueToApply objectForKey:@"StartFrame"] intValue];
3606 //job->start_at_preview = start_seconds;
3607 /* The number of seek points equals the number of seconds announced in the title as that is our current granularity */
3608 //job->seek_points = [[queueToApply objectForKey:@"SourceTotalSeconds"] intValue];
3609 job->frame_to_start = start_frame;
3610 /* Stop seconds is actually the duration of encode, so subtract the end seconds from the start seconds */
3611 int stop_frame = [[queueToApply objectForKey:@"StopFrame"] intValue];
3612 job->frame_to_stop = stop_frame;
3614 /* A bunch of verbose activity log messages to check on what should be expected */
3615 [self writeToActivityLog: "point a to b should start at frame %d", start_frame];
3616 [self writeToActivityLog: "point a to b duration: %d frames", stop_frame];
3617 [self writeToActivityLog: "point a to b should end at frame %d", start_frame + stop_frame];
3623 /* Format (Muxer) and Video Encoder */
3624 job->mux = [[queueToApply objectForKey:@"JobFileFormatMux"] intValue];
3625 job->vcodec = [[queueToApply objectForKey:@"JobVideoEncoderVcodec"] intValue];
3628 /* If mpeg-4, then set mpeg-4 specific options like chapters and > 4gb file sizes */
3629 if( [[queueToApply objectForKey:@"Mp4LargeFile"] intValue] == 1)
3631 job->largeFileSize = 1;
3635 job->largeFileSize = 0;
3637 /* We set http optimized mp4 here */
3638 if( [[queueToApply objectForKey:@"Mp4HttpOptimize"] intValue] == 1 )
3640 job->mp4_optimize = 1;
3644 job->mp4_optimize = 0;
3648 /* We set the chapter marker extraction here based on the format being
3649 mpeg4 or mkv and the checkbox being checked */
3650 if ([[queueToApply objectForKey:@"ChapterMarkers"] intValue] == 1)
3652 job->chapter_markers = 1;
3654 /* now lets get our saved chapter names out the array in the queue file
3655 * and insert them back into the title chapter list. We have it here,
3656 * because unless we are inserting chapter markers there is no need to
3657 * spend the overhead of iterating through the chapter names array imo
3658 * Also, note that if for some reason we don't apply chapter names, the
3659 * chapters just come out 001, 002, etc. etc.
3662 NSMutableArray *ChapterNamesArray = [queueToApply objectForKey:@"ChapterNames"];
3664 NSEnumerator *enumerator = [ChapterNamesArray objectEnumerator];
3666 while (tempObject = [enumerator nextObject])
3668 hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
3669 if( chapter != NULL )
3671 strncpy( chapter->title, [tempObject UTF8String], 1023);
3672 chapter->title[1023] = '\0';
3679 job->chapter_markers = 0;
3682 if( job->vcodec & HB_VCODEC_X264 )
3684 if ([[queueToApply objectForKey:@"Mp4iPodCompatible"] intValue] == 1)
3694 /* Below Sends x264 options to the core library if x264 is selected*/
3695 /* Lets use this as per Nyx, Thanks Nyx!*/
3696 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
3697 /* Turbo first pass if two pass and Turbo First pass is selected */
3698 if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 && [[queueToApply objectForKey:@"VideoTurboTwoPass"] intValue] == 1 )
3700 /* pass the "Turbo" string to be appended to the existing x264 opts string into a variable for the first pass */
3701 NSString *firstPassOptStringTurbo = @":ref=1:subme=2:me=dia:analyse=none:trellis=0:no-fast-pskip=0:8x8dct=0:weightb=0";
3702 /* append the "Turbo" string variable to the existing opts string.
3703 Note: the "Turbo" string must be appended, not prepended to work properly*/
3704 NSString *firstPassOptStringCombined = [[queueToApply objectForKey:@"x264Option"] stringByAppendingString:firstPassOptStringTurbo];
3705 strcpy(job->x264opts, [firstPassOptStringCombined UTF8String]);
3709 strcpy(job->x264opts, [[queueToApply objectForKey:@"x264Option"] UTF8String]);
3715 /* Picture Size Settings */
3716 job->width = [[queueToApply objectForKey:@"PictureWidth"] intValue];
3717 job->height = [[queueToApply objectForKey:@"PictureHeight"] intValue];
3719 job->keep_ratio = [[queueToApply objectForKey:@"PictureKeepRatio"] intValue];
3720 job->anamorphic.mode = [[queueToApply objectForKey:@"PicturePAR"] intValue];
3721 job->modulus = [[queueToApply objectForKey:@"PictureModulus"] intValue];
3722 if ([[queueToApply objectForKey:@"PicturePAR"] intValue] == 3)
3724 /* insert our custom values here for capuj */
3725 job->width = [[queueToApply objectForKey:@"PicturePARStorageWidth"] intValue];
3726 job->height = [[queueToApply objectForKey:@"PicturePARStorageHeight"] intValue];
3728 job->modulus = [[queueToApply objectForKey:@"PicturePARModulus"] intValue];
3730 job->anamorphic.par_width = [[queueToApply objectForKey:@"PicturePARPixelWidth"] intValue];
3731 job->anamorphic.par_height = [[queueToApply objectForKey:@"PicturePARPixelHeight"] intValue];
3733 job->anamorphic.dar_width = [[queueToApply objectForKey:@"PicturePARDisplayWidth"] floatValue];
3734 job->anamorphic.dar_height = [[queueToApply objectForKey:@"PicturePARDisplayHeight"] floatValue];
3737 /* Here we use the crop values saved at the time the preset was saved */
3738 job->crop[0] = [[queueToApply objectForKey:@"PictureTopCrop"] intValue];
3739 job->crop[1] = [[queueToApply objectForKey:@"PictureBottomCrop"] intValue];
3740 job->crop[2] = [[queueToApply objectForKey:@"PictureLeftCrop"] intValue];
3741 job->crop[3] = [[queueToApply objectForKey:@"PictureRightCrop"] intValue];
3743 /* Video settings */
3746 /* Set vfr to 0 as it's only on if using same as source in the framerate popup
3747 * and detelecine is on, so we handle that in the logic below
3750 if( [[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue] > 0 )
3752 /* a specific framerate has been chosen */
3753 job->vrate = 27000000;
3754 job->vrate_base = hb_video_rates[[[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue]-1].rate;
3755 /* We are not same as source so we set job->cfr to 1
3756 * to enable constant frame rate since user has specified
3757 * a specific framerate*/
3762 /* We are same as source (variable) */
3763 job->vrate = [[queueToApply objectForKey:@"JobVrate"] intValue];
3764 job->vrate_base = [[queueToApply objectForKey:@"JobVrateBase"] intValue];
3765 /* We are same as source so we set job->cfr to 0
3766 * to enable true same as source framerate */
3768 /* If we are same as source and we have detelecine on, we need to turn on
3771 if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
3777 if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] != 2 )
3780 Bitrate should already have been calculated and displayed
3781 in fVidBitrateField, so let's just use it same as abr*/
3782 job->vquality = -1.0;
3783 job->vbitrate = [[queueToApply objectForKey:@"VideoAvgBitrate"] intValue];
3785 if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] == 2 )
3787 job->vquality = [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue];
3792 job->grayscale = [[queueToApply objectForKey:@"VideoGrayScale"] intValue];
3797 #pragma mark Process Subtitles to libhb
3799 /* Map the settings in the dictionaries for the SubtitleList array to match title->list_subtitle
3800 * which means that we need to account for the offset of non source language settings in from
3801 * the NSPopUpCell menu. For all of the objects in the SubtitleList array this means 0 is "None"
3802 * from the popup menu, additionally the first track has "Foreign Audio Search" at 1. So we use
3803 * an int to offset the index number for the objectForKey:@"subtitleSourceTrackNum" to map that
3804 * to the source tracks position in title->list_subtitle.
3811 bool one_burned = FALSE;
3814 NSEnumerator *enumerator = [[queueToApply objectForKey:@"SubtitleList"] objectEnumerator];
3816 while (tempObject = [enumerator nextObject])
3819 subtitle = [[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue];
3820 force = [[tempObject objectForKey:@"subtitleTrackForced"] intValue];
3821 burned = [[tempObject objectForKey:@"subtitleTrackBurned"] intValue];
3822 def = [[tempObject objectForKey:@"subtitleTrackDefault"] intValue];
3824 /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups,
3825 * we want to ignore it for display as well as encoding.
3829 /* if i is 0, then we are in the first item of the subtitles which we need to
3830 * check for the "Foreign Audio Search" which would be subtitleSourceTrackNum of 1
3831 * bearing in mind that for all tracks subtitleSourceTrackNum of 0 is None.
3834 /* if we are on the first track and using "Foreign Audio Search" */
3835 if (i == 0 && subtitle == 1)
3837 [self writeToActivityLog: "Foreign Language Search: %d", 1];
3839 job->indepth_scan = 1;
3840 if (burned == 1 || job->mux != HB_MUX_MP4)
3842 if (burned != 1 && job->mux == HB_MUX_MKV)
3844 job->select_subtitle_config.dest = PASSTHRUSUB;
3848 job->select_subtitle_config.dest = RENDERSUB;
3851 job->select_subtitle_config.force = force;
3852 job->select_subtitle_config.default_track = def;
3860 /* for the actual source tracks, we must subtract the non source entries so
3861 * that the menu index matches the source subtitle_list index for convenience */
3864 /* for the first track, the source tracks start at menu index 2 ( None is 0,
3865 * Foreign Language Search is 1) so subtract 2 */
3866 subtitle = subtitle - 2;
3870 /* for all other tracks, the source tracks start at menu index 1 (None is 0)
3873 subtitle = subtitle - 1;
3876 /* We are setting a source subtitle so access the source subtitle info */
3877 hb_subtitle_t * subt;
3879 subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle);
3881 /* if we are getting the subtitles from an external srt file */
3882 if ([[tempObject objectForKey:@"subtitleSourceTrackType"] isEqualToString:@"SRT"])
3884 hb_subtitle_config_t sub_config;
3886 sub_config.offset = [[tempObject objectForKey:@"subtitleTrackSrtOffset"] intValue];
3888 /* we need to srncpy file name and codeset */
3889 strncpy(sub_config.src_filename, [[tempObject objectForKey:@"subtitleSourceSrtFilePath"] UTF8String], 128);
3890 strncpy(sub_config.src_codeset, [[tempObject objectForKey:@"subtitleTrackSrtCharCode"] UTF8String], 40);
3892 sub_config.force = 0;
3893 sub_config.dest = PASSTHRUSUB;
3894 sub_config.default_track = def;
3896 hb_srt_add( job, &sub_config, [[tempObject objectForKey:@"subtitleTrackSrtLanguageIso3"] UTF8String]);
3902 hb_subtitle_config_t sub_config = subt->config;
3904 if (!burned && job->mux == HB_MUX_MKV &&
3905 subt->format == PICTURESUB)
3907 sub_config.dest = PASSTHRUSUB;
3909 else if (!burned && job->mux == HB_MUX_MP4 &&
3910 subt->format == PICTURESUB)
3912 // Skip any non-burned vobsubs when output is mp4
3915 else if ( burned && subt->format == PICTURESUB )
3917 // Only allow one subtitle to be burned into the video
3922 sub_config.force = force;
3923 sub_config.default_track = def;
3924 hb_subtitle_add( job, &sub_config, subtitle );
3935 /* Audio tracks and mixdowns */
3936 /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
3937 int audiotrack_count = hb_list_count(job->list_audio);
3938 for( int i = 0; i < audiotrack_count;i++)
3940 hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
3941 hb_list_rem(job->list_audio, temp_audio);
3943 /* Now lets add our new tracks to the audio list here */
3944 if ([[queueToApply objectForKey:@"Audio1Track"] intValue] > 0)
3946 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3947 hb_audio_config_init(audio);
3948 audio->in.track = [[queueToApply objectForKey:@"Audio1Track"] intValue] - 1;
3949 /* We go ahead and assign values to our audio->out.<properties> */
3950 audio->out.track = [[queueToApply objectForKey:@"Audio1Track"] intValue] - 1;
3951 audio->out.codec = [[queueToApply objectForKey:@"JobAudio1Encoder"] intValue];
3952 audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio1Mixdown"] intValue];
3953 audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio1Bitrate"] intValue];
3954 audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio1Samplerate"] intValue];
3955 audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio1TrackDRCSlider"] floatValue];
3957 hb_audio_add( job, audio );
3960 if ([[queueToApply objectForKey:@"Audio2Track"] intValue] > 0)
3963 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3964 hb_audio_config_init(audio);
3965 audio->in.track = [[queueToApply objectForKey:@"Audio2Track"] intValue] - 1;
3966 [self writeToActivityLog: "prepareJob audiotrack 2 is: %d", audio->in.track];
3967 /* We go ahead and assign values to our audio->out.<properties> */
3968 audio->out.track = [[queueToApply objectForKey:@"Audio2Track"] intValue] - 1;
3969 audio->out.codec = [[queueToApply objectForKey:@"JobAudio2Encoder"] intValue];
3970 audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio2Mixdown"] intValue];
3971 audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio2Bitrate"] intValue];
3972 audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio2Samplerate"] intValue];
3973 audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio2TrackDRCSlider"] floatValue];
3975 hb_audio_add( job, audio );
3979 if ([[queueToApply objectForKey:@"Audio3Track"] intValue] > 0)
3981 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3982 hb_audio_config_init(audio);
3983 audio->in.track = [[queueToApply objectForKey:@"Audio3Track"] intValue] - 1;
3984 /* We go ahead and assign values to our audio->out.<properties> */
3985 audio->out.track = [[queueToApply objectForKey:@"Audio3Track"] intValue] - 1;
3986 audio->out.codec = [[queueToApply objectForKey:@"JobAudio3Encoder"] intValue];
3987 audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio3Mixdown"] intValue];
3988 audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio3Bitrate"] intValue];
3989 audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio3Samplerate"] intValue];
3990 audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio3TrackDRCSlider"] floatValue];
3992 hb_audio_add( job, audio );
3996 if ([[queueToApply objectForKey:@"Audio4Track"] intValue] > 0)
3998 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3999 hb_audio_config_init(audio);
4000 audio->in.track = [[queueToApply objectForKey:@"Audio4Track"] intValue] - 1;
4001 /* We go ahead and assign values to our audio->out.<properties> */
4002 audio->out.track = [[queueToApply objectForKey:@"Audio4Track"] intValue] - 1;
4003 audio->out.codec = [[queueToApply objectForKey:@"JobAudio4Encoder"] intValue];
4004 audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio4Mixdown"] intValue];
4005 audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio4Bitrate"] intValue];
4006 audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio4Samplerate"] intValue];
4007 audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio4TrackDRCSlider"] floatValue];
4009 hb_audio_add( job, audio );
4015 job->filters = hb_list_init();
4017 /* Now lets call the filters if applicable.
4018 * The order of the filters is critical
4021 if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
4023 /* use a custom detelecine string */
4024 hb_filter_detelecine.settings = (char *) [[queueToApply objectForKey:@"PictureDetelecineCustom"] UTF8String];
4025 hb_list_add( job->filters, &hb_filter_detelecine );
4027 if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 2)
4029 /* Use libhb's default values */
4030 hb_list_add( job->filters, &hb_filter_detelecine );
4033 if ([[queueToApply objectForKey:@"PictureDecombDeinterlace"] intValue] == 1)
4036 /* we add the custom string if present */
4037 if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 1)
4039 /* use a custom decomb string */
4040 hb_filter_decomb.settings = (char *) [[queueToApply objectForKey:@"PictureDecombCustom"] UTF8String];
4041 hb_list_add( job->filters, &hb_filter_decomb );
4043 if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 2)
4045 /* Use libhb default */
4046 hb_list_add( job->filters, &hb_filter_decomb );
4054 if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 1)
4056 /* we add the custom string if present */
4057 hb_filter_deinterlace.settings = (char *) [[queueToApply objectForKey:@"PictureDeinterlaceCustom"] UTF8String];
4058 hb_list_add( job->filters, &hb_filter_deinterlace );
4060 else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 2)
4062 /* Run old deinterlacer fd by default */
4063 hb_filter_deinterlace.settings = "-1";
4064 hb_list_add( job->filters, &hb_filter_deinterlace );
4066 else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 3)
4068 /* Yadif mode 0 (without spatial deinterlacing.) */
4069 hb_filter_deinterlace.settings = "2";
4070 hb_list_add( job->filters, &hb_filter_deinterlace );
4072 else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 4)
4074 /* Yadif (with spatial deinterlacing) */
4075 hb_filter_deinterlace.settings = "0";
4076 hb_list_add( job->filters, &hb_filter_deinterlace );
4082 if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 1) // Custom in popup
4084 /* we add the custom string if present */
4085 hb_filter_denoise.settings = (char *) [[queueToApply objectForKey:@"PictureDenoiseCustom"] UTF8String];
4086 hb_list_add( job->filters, &hb_filter_denoise );
4088 else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 2) // Weak in popup
4090 hb_filter_denoise.settings = "2:1:2:3";
4091 hb_list_add( job->filters, &hb_filter_denoise );
4093 else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 3) // Medium in popup
4095 hb_filter_denoise.settings = "3:2:2:3";
4096 hb_list_add( job->filters, &hb_filter_denoise );
4098 else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 4) // Strong in popup
4100 hb_filter_denoise.settings = "7:7:5:5";
4101 hb_list_add( job->filters, &hb_filter_denoise );
4105 /* Deblock (uses pp7 default) */
4106 /* NOTE: even though there is a valid deblock setting of 0 for the filter, for
4107 * the macgui's purposes a value of 0 actually means to not even use the filter
4108 * current hb_filter_deblock.settings valid ranges are from 5 - 15
4110 if ([[queueToApply objectForKey:@"PictureDeblock"] intValue] != 0)
4112 hb_filter_deblock.settings = (char *) [[queueToApply objectForKey:@"PictureDeblock"] UTF8String];
4113 hb_list_add( job->filters, &hb_filter_deblock );
4115 [self writeToActivityLog: "prepareJob exiting"];
4120 /* addToQueue: puts up an alert before ultimately calling doAddToQueue
4122 - (IBAction) addToQueue: (id) sender
4124 /* We get the destination directory from the destination field here */
4125 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
4126 /* We check for a valid destination here */
4127 if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0)
4129 NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
4136 BOOL fileExistsInQueue;
4137 fileExistsInQueue = NO;
4139 /* We check for and existing file here */
4140 if([[NSFileManager defaultManager] fileExistsAtPath: [fDstFile2Field stringValue]])
4145 /* We now run through the queue and make sure we are not overwriting an exisiting queue item */
4147 NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
4149 while (tempObject = [enumerator nextObject])
4151 NSDictionary *thisQueueDict = tempObject;
4152 if ([[thisQueueDict objectForKey:@"DestinationPath"] isEqualToString: [fDstFile2Field stringValue]])
4154 fileExistsInQueue = YES;
4160 if(fileExists == YES)
4162 NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists.", @"" ),
4163 NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
4164 @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
4165 NULL, NULL, [NSString stringWithFormat:
4166 NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
4167 [fDstFile2Field stringValue]] );
4169 else if (fileExistsInQueue == YES)
4171 NSBeginCriticalAlertSheet( NSLocalizedString( @"There is already a queue item for this destination.", @"" ),
4172 NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
4173 @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
4174 NULL, NULL, [NSString stringWithFormat:
4175 NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
4176 [fDstFile2Field stringValue]] );
4180 [self doAddToQueue];
4184 /* overwriteAddToQueueAlertDone: called from the alert posted by addToQueue that asks
4185 the user if they want to overwrite an exiting movie file.
4187 - (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet
4188 returnCode: (int) returnCode contextInfo: (void *) contextInfo
4190 if( returnCode == NSAlertAlternateReturn )
4191 [self doAddToQueue];
4194 - (void) doAddToQueue
4196 [self addQueueFileItem ];
4201 /* Rip: puts up an alert before ultimately calling doRip
4203 - (IBAction) Rip: (id) sender
4205 [self writeToActivityLog: "Rip: Pending queue count is %d", fPendingCount];
4206 /* Rip or Cancel ? */
4208 hb_get_state2( fQueueEncodeLibhb, &s );
4210 if(s.state == HB_STATE_WORKING || s.state == HB_STATE_PAUSED)
4212 [self Cancel: sender];
4216 /* We check to see if we need to warn the user that the computer will go to sleep
4217 or shut down when encoding is finished */
4218 [self remindUserOfSleepOrShutdown];
4220 // If there are pending jobs in the queue, then this is a rip the queue
4221 if (fPendingCount > 0)
4223 /* here lets start the queue with the first pending item */
4224 [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
4229 // Before adding jobs to the queue, check for a valid destination.
4231 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
4232 if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0)
4234 NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
4238 /* We check for duplicate name here */
4239 if( [[NSFileManager defaultManager] fileExistsAtPath:[fDstFile2Field stringValue]] )
4241 NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists", @"" ),
4242 NSLocalizedString( @"Cancel", "" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
4243 @selector( overWriteAlertDone:returnCode:contextInfo: ),
4244 NULL, NULL, [NSString stringWithFormat:
4245 NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
4246 [fDstFile2Field stringValue]] );
4248 // overWriteAlertDone: will be called when the alert is dismissed. It will call doRip.
4252 /* if there are no pending jobs in the queue, then add this one to the queue and rip
4253 otherwise, just rip the queue */
4254 if(fPendingCount == 0)
4256 [self doAddToQueue];
4259 /* go right to processing the new queue encode */
4260 [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
4265 /* overWriteAlertDone: called from the alert posted by Rip: that asks the user if they
4266 want to overwrite an exiting movie file.
4268 - (void) overWriteAlertDone: (NSWindow *) sheet
4269 returnCode: (int) returnCode contextInfo: (void *) contextInfo
4271 if( returnCode == NSAlertAlternateReturn )
4273 /* if there are no jobs in the queue, then add this one to the queue and rip
4274 otherwise, just rip the queue */
4275 if( fPendingCount == 0 )
4277 [self doAddToQueue];
4280 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
4281 [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
4282 [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
4287 - (void) remindUserOfSleepOrShutdown
4289 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])
4291 /*Warn that computer will sleep after encoding*/
4294 reminduser = NSRunAlertPanel(@"The computer will sleep after encoding is done.",@"You have selected to sleep the computer after encoding. To turn off sleeping, go to the HandBrake preferences.", @"OK", @"Preferences...", nil);
4295 [NSApp requestUserAttention:NSCriticalRequest];
4296 if ( reminduser == NSAlertAlternateReturn )
4298 [self showPreferencesWindow:nil];
4301 else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"])
4303 /*Warn that computer will shut down after encoding*/
4306 reminduser = NSRunAlertPanel(@"The computer will shut down after encoding is done.",@"You have selected to shut down the computer after encoding. To turn off shut down, go to the HandBrake preferences.", @"OK", @"Preferences...", nil);
4307 [NSApp requestUserAttention:NSCriticalRequest];
4308 if ( reminduser == NSAlertAlternateReturn )
4310 [self showPreferencesWindow:nil];
4319 /* Let libhb do the job */
4320 hb_start( fQueueEncodeLibhb );
4321 /*set the fEncodeState State */
4326 //------------------------------------------------------------------------------------
4327 // Displays an alert asking user if the want to cancel encoding of current job.
4328 // Cancel: returns immediately after posting the alert. Later, when the user
4329 // acknowledges the alert, doCancelCurrentJob is called.
4330 //------------------------------------------------------------------------------------
4331 - (IBAction)Cancel: (id)sender
4333 if (!fQueueController) return;
4335 hb_pause( fQueueEncodeLibhb );
4336 NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"You are currently encoding. What would you like to do ?", nil)];
4338 // Which window to attach the sheet to?
4339 NSWindow * docWindow;
4340 if ([sender respondsToSelector: @selector(window)])
4341 docWindow = [sender window];
4343 docWindow = fWindow;
4345 NSBeginCriticalAlertSheet(
4347 NSLocalizedString(@"Continue Encoding", nil),
4348 NSLocalizedString(@"Cancel Current and Stop", nil),
4349 NSLocalizedString(@"Cancel Current and Continue", nil),
4351 nil, @selector(didDimissCancel:returnCode:contextInfo:), nil,
4352 NSLocalizedString(@"Your encode will be cancelled if you don't continue encoding.", nil));
4354 // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed
4357 - (void) didDimissCancel: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
4359 hb_resume( fQueueEncodeLibhb );
4360 if (returnCode == NSAlertOtherReturn)
4362 [self doCancelCurrentJob]; // <- this also stops libhb
4364 if (returnCode == NSAlertAlternateReturn)
4366 [self doCancelCurrentJobAndStop];
4370 //------------------------------------------------------------------------------------
4371 // Cancels and deletes the current job and stops libhb from processing the remaining
4373 //------------------------------------------------------------------------------------
4374 - (void) doCancelCurrentJob
4376 // Stop the current job. hb_stop will only cancel the current pass and then set
4377 // its state to HB_STATE_WORKDONE. It also does this asynchronously. So when we
4378 // see the state has changed to HB_STATE_WORKDONE (in updateUI), we'll delete the
4379 // remaining passes of the job and then start the queue back up if there are any
4383 hb_stop( fQueueEncodeLibhb );
4385 // Delete all remaining jobs since libhb doesn't do this on its own.
4387 while( ( job = hb_job(fQueueEncodeLibhb, 0) ) )
4388 hb_rem( fQueueEncodeLibhb, job );
4390 fEncodeState = 2; // don't alert at end of processing since this was a cancel
4392 // now that we've stopped the currently encoding job, lets mark it as cancelled
4393 [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"];
4394 // and as always, save it in the queue .plist...
4395 /* We save all of the Queue data here */
4396 [self saveQueueFileItem];
4397 // so now lets move to
4398 currentQueueEncodeIndex++ ;
4399 // ... and see if there are more items left in our queue
4400 int queueItems = [QueueFileArray count];
4401 /* If we still have more items in our queue, lets go to the next one */
4402 if (currentQueueEncodeIndex < queueItems)
4404 [self writeToActivityLog: "doCancelCurrentJob currentQueueEncodeIndex is incremented to: %d", currentQueueEncodeIndex];
4405 [self writeToActivityLog: "doCancelCurrentJob moving to the next job"];
4407 [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
4411 [self writeToActivityLog: "doCancelCurrentJob the item queue is complete"];
4416 - (void) doCancelCurrentJobAndStop
4418 hb_stop( fQueueEncodeLibhb );
4420 // Delete all remaining jobs since libhb doesn't do this on its own.
4422 while( ( job = hb_job(fQueueEncodeLibhb, 0) ) )
4423 hb_rem( fQueueEncodeLibhb, job );
4426 fEncodeState = 2; // don't alert at end of processing since this was a cancel
4428 // now that we've stopped the currently encoding job, lets mark it as cancelled
4429 [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"];
4430 // and as always, save it in the queue .plist...
4431 /* We save all of the Queue data here */
4432 [self saveQueueFileItem];
4433 // so now lets move to
4434 currentQueueEncodeIndex++ ;
4435 [self writeToActivityLog: "cancelling current job and stopping the queue"];
4437 - (IBAction) Pause: (id) sender
4440 hb_get_state2( fQueueEncodeLibhb, &s );
4442 if( s.state == HB_STATE_PAUSED )
4444 hb_resume( fQueueEncodeLibhb );
4448 hb_pause( fQueueEncodeLibhb );
4453 #pragma mark GUI Controls Changed Methods
4455 - (IBAction) titlePopUpChanged: (id) sender
4457 hb_list_t * list = hb_get_titles( fHandle );
4458 hb_title_t * title = (hb_title_t*)
4459 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4461 /* If we are a stream type and a batch scan, grok the output file name from title->name upon title change */
4462 if (title->type == HB_STREAM_TYPE && hb_list_count( list ) > 1 )
4464 /* we set the default name according to the new title->name */
4465 [fDstFile2Field setStringValue: [NSString stringWithFormat:
4466 @"%@/%@.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent],
4467 [NSString stringWithUTF8String: title->name],
4468 [[fDstFile2Field stringValue] pathExtension]]];
4470 /* Change the source to read out the parent folder also */
4471 [fSrcDVD2Field setStringValue:[NSString stringWithFormat:@"%@/%@", browsedSourceDisplayName,[NSString stringWithUTF8String: title->name]]];
4474 /* For point a to point b pts encoding, set the start and end fields to 0 and the title duration in seconds respectively */
4475 int duration = (title->hours * 3600) + (title->minutes * 60) + (title->seconds);
4476 [fSrcTimeStartEncodingField setStringValue: [NSString stringWithFormat: @"%d", 0]];
4477 [fSrcTimeEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", duration]];
4478 /* 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 */
4479 [fSrcFrameStartEncodingField setStringValue: [NSString stringWithFormat: @"%d", 1]];
4480 //[fSrcFrameEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", ((title->hours * 3600) + (title->minutes * 60) + (title->seconds)) * 24]];
4481 [fSrcFrameEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", duration * (title->rate / title->rate_base)]];
4483 /* If Auto Naming is on. We create an output filename of dvd name - title number */
4484 if( [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultAutoNaming"] > 0 && ( hb_list_count( list ) > 1 ) )
4486 [fDstFile2Field setStringValue: [NSString stringWithFormat:
4487 @"%@/%@-%d.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent],
4488 [browsedSourceDisplayName stringByDeletingPathExtension],
4490 [[fDstFile2Field stringValue] pathExtension]]];
4492 /* Update encode start / stop variables */
4496 /* Update chapter popups */
4497 [fSrcChapterStartPopUp removeAllItems];
4498 [fSrcChapterEndPopUp removeAllItems];
4499 for( int i = 0; i < hb_list_count( title->list_chapter ); i++ )
4501 [fSrcChapterStartPopUp addItemWithTitle: [NSString
4502 stringWithFormat: @"%d", i + 1]];
4503 [fSrcChapterEndPopUp addItemWithTitle: [NSString
4504 stringWithFormat: @"%d", i + 1]];
4507 [fSrcChapterStartPopUp selectItemAtIndex: 0];
4508 [fSrcChapterEndPopUp selectItemAtIndex:
4509 hb_list_count( title->list_chapter ) - 1];
4510 [self chapterPopUpChanged:nil];
4512 /* if using dvd nav, show the angle widget */
4513 if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"UseDvdNav"] boolValue])
4515 [fSrcAngleLabel setHidden:NO];
4516 [fSrcAnglePopUp setHidden:NO];
4518 [fSrcAnglePopUp removeAllItems];
4519 for( int i = 0; i < title->angle_count; i++ )
4521 [fSrcAnglePopUp addItemWithTitle: [NSString stringWithFormat: @"%d", i + 1]];
4523 [fSrcAnglePopUp selectItemAtIndex: 0];
4527 [fSrcAngleLabel setHidden:YES];
4528 [fSrcAnglePopUp setHidden:YES];
4531 /* Start Get and set the initial pic size for display */
4532 hb_job_t * job = title->job;
4535 /* Set Auto Crop to on upon selecting a new title */
4536 [fPictureController setAutoCrop:YES];
4538 /* We get the originial output picture width and height and put them
4539 in variables for use with some presets later on */
4540 PicOrigOutputWidth = job->width;
4541 PicOrigOutputHeight = job->height;
4542 AutoCropTop = job->crop[0];
4543 AutoCropBottom = job->crop[1];
4544 AutoCropLeft = job->crop[2];
4545 AutoCropRight = job->crop[3];
4547 /* Reset the new title in fPictureController && fPreviewController*/
4548 [fPictureController SetTitle:title];
4551 /* Update Subtitle Table */
4552 [fSubtitlesDelegate resetWithTitle:title];
4553 [fSubtitlesTable reloadData];
4556 /* Update chapter table */
4557 [fChapterTitlesDelegate resetWithTitle:title];
4558 [fChapterTable reloadData];
4560 /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
4561 int audiotrack_count = hb_list_count(job->list_audio);
4562 for( int i = 0; i < audiotrack_count;i++)
4564 hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
4565 hb_list_rem(job->list_audio, temp_audio);
4568 /* Update audio popups */
4569 [self addAllAudioTracksToPopUp: fAudLang1PopUp];
4570 [self addAllAudioTracksToPopUp: fAudLang2PopUp];
4571 [self addAllAudioTracksToPopUp: fAudLang3PopUp];
4572 [self addAllAudioTracksToPopUp: fAudLang4PopUp];
4573 /* search for the first instance of our prefs default language for track 1, and set track 2 to "none" */
4574 NSString * audioSearchPrefix = [[NSUserDefaults standardUserDefaults] stringForKey:@"DefaultLanguage"];
4575 [self selectAudioTrackInPopUp: fAudLang1PopUp searchPrefixString: audioSearchPrefix selectIndexIfNotFound: 1];
4576 [self selectAudioTrackInPopUp:fAudLang2PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4577 [self selectAudioTrackInPopUp:fAudLang3PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4578 [self selectAudioTrackInPopUp:fAudLang4PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4580 /* changing the title may have changed the audio channels on offer, */
4581 /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
4582 [self audioTrackPopUpChanged: fAudLang1PopUp];
4583 [self audioTrackPopUpChanged: fAudLang2PopUp];
4584 [self audioTrackPopUpChanged: fAudLang3PopUp];
4585 [self audioTrackPopUpChanged: fAudLang4PopUp];
4587 [fVidRatePopUp selectItemAtIndex: 0];
4589 /* we run the picture size values through calculatePictureSizing to get all picture setting information*/
4590 [self calculatePictureSizing:nil];
4592 /* lets call tableViewSelected to make sure that any preset we have selected is enforced after a title change */
4593 [self selectPreset:nil];
4596 - (IBAction) encodeStartStopPopUpChanged: (id) sender;
4598 if( [fEncodeStartStopPopUp isEnabled] )
4600 /* We are chapters */
4601 if( [fEncodeStartStopPopUp indexOfSelectedItem] == 0 )
4603 [fSrcChapterStartPopUp setHidden: NO];
4604 [fSrcChapterEndPopUp setHidden: NO];
4606 [fSrcTimeStartEncodingField setHidden: YES];
4607 [fSrcTimeEndEncodingField setHidden: YES];
4609 [fSrcFrameStartEncodingField setHidden: YES];
4610 [fSrcFrameEndEncodingField setHidden: YES];
4612 [self chapterPopUpChanged:nil];
4614 /* We are time based (seconds) */
4615 else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 1)
4617 [fSrcChapterStartPopUp setHidden: YES];
4618 [fSrcChapterEndPopUp setHidden: YES];
4620 [fSrcTimeStartEncodingField setHidden: NO];
4621 [fSrcTimeEndEncodingField setHidden: NO];
4623 [fSrcFrameStartEncodingField setHidden: YES];
4624 [fSrcFrameEndEncodingField setHidden: YES];
4626 [self startEndSecValueChanged:nil];
4628 /* We are frame based */
4629 else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 2)
4631 [fSrcChapterStartPopUp setHidden: YES];
4632 [fSrcChapterEndPopUp setHidden: YES];
4634 [fSrcTimeStartEncodingField setHidden: YES];
4635 [fSrcTimeEndEncodingField setHidden: YES];
4637 [fSrcFrameStartEncodingField setHidden: NO];
4638 [fSrcFrameEndEncodingField setHidden: NO];
4640 [self startEndFrameValueChanged:nil];
4645 - (IBAction) chapterPopUpChanged: (id) sender
4648 /* If start chapter popup is greater than end chapter popup,
4649 we set the end chapter popup to the same as start chapter popup */
4650 if ([fSrcChapterStartPopUp indexOfSelectedItem] > [fSrcChapterEndPopUp indexOfSelectedItem])
4652 [fSrcChapterEndPopUp selectItemAtIndex: [fSrcChapterStartPopUp indexOfSelectedItem]];
4656 hb_list_t * list = hb_get_titles( fHandle );
4657 hb_title_t * title = (hb_title_t *)
4658 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4660 hb_chapter_t * chapter;
4661 int64_t duration = 0;
4662 for( int i = [fSrcChapterStartPopUp indexOfSelectedItem];
4663 i <= [fSrcChapterEndPopUp indexOfSelectedItem]; i++ )
4665 chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
4666 duration += chapter->duration;
4669 duration /= 90000; /* pts -> seconds */
4670 [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
4671 @"%02lld:%02lld:%02lld", duration / 3600, ( duration / 60 ) % 60,
4674 [self calculateBitrate: sender];
4676 if ( [fSrcChapterStartPopUp indexOfSelectedItem] == [fSrcChapterEndPopUp indexOfSelectedItem] )
4678 /* Disable chapter markers for any source with less than two chapters as it makes no sense. */
4679 [fCreateChapterMarkers setEnabled: NO];
4680 [fCreateChapterMarkers setState: NSOffState];
4684 [fCreateChapterMarkers setEnabled: YES];
4688 - (IBAction) startEndSecValueChanged: (id) sender
4691 int duration = [fSrcTimeEndEncodingField intValue] - [fSrcTimeStartEncodingField intValue];
4692 [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
4693 @"%02d:%02d:%02d", duration / 3600, ( duration / 60 ) % 60,
4696 //[self calculateBitrate: sender];
4700 - (IBAction) startEndFrameValueChanged: (id) sender
4702 hb_list_t * list = hb_get_titles( fHandle );
4703 hb_title_t * title = (hb_title_t*)
4704 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4706 int duration = ([fSrcFrameEndEncodingField intValue] - [fSrcFrameStartEncodingField intValue]) / (title->rate / title->rate_base);
4707 [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
4708 @"%02d:%02d:%02d", duration / 3600, ( duration / 60 ) % 60,
4711 //[self calculateBitrate: sender];
4715 - (IBAction) formatPopUpChanged: (id) sender
4717 NSString * string = [fDstFile2Field stringValue];
4718 int format = [fDstFormatPopUp indexOfSelectedItem];
4720 /* Initially set the large file (64 bit formatting) output checkbox to hidden */
4721 [fDstMp4LargeFileCheck setHidden: YES];
4722 [fDstMp4HttpOptFileCheck setHidden: YES];
4723 [fDstMp4iPodFileCheck setHidden: YES];
4725 /* Update the Video Codec PopUp */
4726 /* lets get the tag of the currently selected item first so we might reset it later */
4727 int selectedVidEncoderTag;
4728 selectedVidEncoderTag = [[fVidEncoderPopUp selectedItem] tag];
4730 /* Note: we now store the video encoder int values from common.c in the tags of each popup for easy retrieval later */
4731 [fVidEncoderPopUp removeAllItems];
4732 NSMenuItem *menuItem;
4733 /* These video encoders are available to all of our current muxers, so lets list them once here */
4734 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"MPEG-4 (FFmpeg)" action: NULL keyEquivalent: @""];
4735 [menuItem setTag: HB_VCODEC_FFMPEG];
4740 /*Get Default MP4 File Extension*/
4741 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0)
4749 /* Add additional video encoders here */
4750 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
4751 [menuItem setTag: HB_VCODEC_X264];
4752 /* We show the mp4 option checkboxes here since we are mp4 */
4753 [fCreateChapterMarkers setEnabled: YES];
4754 [fDstMp4LargeFileCheck setHidden: NO];
4755 [fDstMp4HttpOptFileCheck setHidden: NO];
4756 [fDstMp4iPodFileCheck setHidden: NO];
4761 /* Add additional video encoders here */
4762 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
4763 [menuItem setTag: HB_VCODEC_X264];
4764 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"VP3 (Theora)" action: NULL keyEquivalent: @""];
4765 [menuItem setTag: HB_VCODEC_THEORA];
4766 /* We enable the create chapters checkbox here */
4767 [fCreateChapterMarkers setEnabled: YES];
4772 /* tell fSubtitlesDelegate we have a new video container */
4774 [fSubtitlesDelegate containerChanged:[[fDstFormatPopUp selectedItem] tag]];
4775 [fSubtitlesTable reloadData];
4776 /* if we have a previously selected vid encoder tag, then try to select it */
4777 if (selectedVidEncoderTag)
4779 [fVidEncoderPopUp selectItemWithTag: selectedVidEncoderTag];
4783 [fVidEncoderPopUp selectItemAtIndex: 0];
4786 [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp];
4787 [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp];
4788 [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp];
4789 [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp];
4792 [self autoSetM4vExtension: sender];
4794 [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%s", [string stringByDeletingPathExtension], ext]];
4796 if( SuccessfulScan )
4798 /* Add/replace to the correct extension */
4799 [self audioTrackPopUpChanged: fAudLang1PopUp];
4800 [self audioTrackPopUpChanged: fAudLang2PopUp];
4801 [self audioTrackPopUpChanged: fAudLang3PopUp];
4802 [self audioTrackPopUpChanged: fAudLang4PopUp];
4804 if( [fVidEncoderPopUp selectedItem] == nil )
4807 [fVidEncoderPopUp selectItemAtIndex:0];
4808 [self videoEncoderPopUpChanged:nil];
4810 /* changing the format may mean that we can / can't offer mono or 6ch, */
4811 /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
4813 /* We call the method to properly enable/disable turbo 2 pass */
4814 [self twoPassCheckboxChanged: sender];
4815 /* We call method method to change UI to reflect whether a preset is used or not*/
4818 [self customSettingUsed: sender];
4821 - (IBAction) autoSetM4vExtension: (id) sender
4823 if ( [fDstFormatPopUp indexOfSelectedItem] )
4826 NSString * extension = @"mp4";
4828 if( [[fAudTrack1CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack2CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4829 [[fAudTrack3CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4830 [[fAudTrack4CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4831 [fCreateChapterMarkers state] == NSOnState ||
4832 [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0 )
4837 if( [extension isEqualTo: [[fDstFile2Field stringValue] pathExtension]] )
4840 [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%@",
4841 [[fDstFile2Field stringValue] stringByDeletingPathExtension], extension]];
4844 /* Method to determine if we should change the UI
4845 To reflect whether or not a Preset is being used or if
4846 the user is using "Custom" settings by determining the sender*/
4847 - (IBAction) customSettingUsed: (id) sender
4849 if ([sender stringValue])
4851 /* Deselect the currently selected Preset if there is one*/
4852 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
4853 /* Change UI to show "Custom" settings are being used */
4854 [fPresetSelectedDisplay setStringValue: @"Custom"];
4856 curUserPresetChosenNum = nil;
4858 [self calculateBitrate:nil];
4863 #pragma mark - Video
4865 - (IBAction) videoEncoderPopUpChanged: (id) sender
4867 hb_job_t * job = fTitle->job;
4868 int videoEncoder = [[fVidEncoderPopUp selectedItem] tag];
4870 [fAdvancedOptions setHidden:YES];
4871 /* If we are using x264 then show the x264 advanced panel*/
4872 if (videoEncoder == HB_VCODEC_X264)
4874 [fAdvancedOptions setHidden:NO];
4875 [self autoSetM4vExtension: sender];
4878 if (videoEncoder == HB_VCODEC_FFMPEG)
4880 /* We set the iPod atom checkbox to disabled and uncheck it as its only for x264 in the mp4
4881 container. Format is taken care of in formatPopUpChanged method by hiding and unchecking
4882 anything other than MP4.
4884 [fDstMp4iPodFileCheck setEnabled: NO];
4885 [fDstMp4iPodFileCheck setState: NSOffState];
4889 [fDstMp4iPodFileCheck setEnabled: YES];
4891 [self setupQualitySlider];
4892 [self calculatePictureSizing: sender];
4893 [self twoPassCheckboxChanged: sender];
4897 - (IBAction) twoPassCheckboxChanged: (id) sender
4899 /* check to see if x264 is chosen */
4900 if([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264)
4902 if( [fVidTwoPassCheck state] == NSOnState)
4904 [fVidTurboPassCheck setHidden: NO];
4908 [fVidTurboPassCheck setHidden: YES];
4909 [fVidTurboPassCheck setState: NSOffState];
4911 /* Make sure Two Pass is checked if Turbo is checked */
4912 if( [fVidTurboPassCheck state] == NSOnState)
4914 [fVidTwoPassCheck setState: NSOnState];
4919 [fVidTurboPassCheck setHidden: YES];
4920 [fVidTurboPassCheck setState: NSOffState];
4923 /* We call method method to change UI to reflect whether a preset is used or not*/
4924 [self customSettingUsed: sender];
4927 - (IBAction ) videoFrameRateChanged: (id) sender
4929 /* We call method method to calculatePictureSizing to error check detelecine*/
4930 [self calculatePictureSizing: sender];
4932 /* We call method method to change UI to reflect whether a preset is used or not*/
4933 [self customSettingUsed: sender];
4935 - (IBAction) videoMatrixChanged: (id) sender;
4937 bool target, bitrate, quality;
4939 target = bitrate = quality = false;
4940 if( [fVidQualityMatrix isEnabled] )
4942 switch( [fVidQualityMatrix selectedRow] )
4955 [fVidTargetSizeField setEnabled: target];
4956 [fVidBitrateField setEnabled: bitrate];
4957 [fVidQualitySlider setEnabled: quality];
4958 [fVidQualityRFField setEnabled: quality];
4959 [fVidQualityRFLabel setEnabled: quality];
4960 [fVidTwoPassCheck setEnabled: !quality &&
4961 [fVidQualityMatrix isEnabled]];
4964 [fVidTwoPassCheck setState: NSOffState];
4965 [fVidTurboPassCheck setHidden: YES];
4966 [fVidTurboPassCheck setState: NSOffState];
4969 [self qualitySliderChanged: sender];
4970 [self calculateBitrate: sender];
4971 [self customSettingUsed: sender];
4974 /* Use this method to setup the quality slider for cq/rf values depending on
4975 * the video encoder selected.
4977 - (void) setupQualitySlider
4979 /* Get the current slider maxValue to check for a change in slider scale later
4980 * so that we can choose a new similar value on the new slider scale */
4981 float previousMaxValue = [fVidQualitySlider maxValue];
4982 float previousPercentOfSliderScale = [fVidQualitySlider floatValue] / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue] + 1);
4983 NSString * qpRFLabelString = @"QP:";
4985 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264)
4987 [fVidQualitySlider setMinValue:0.0];
4988 [fVidQualitySlider setMaxValue:51.0];
4989 /* As x264 allows for qp/rf values that are fractional, we get the value from the preferences */
4990 int fractionalGranularity = 1 / [[NSUserDefaults standardUserDefaults] floatForKey:@"x264CqSliderFractional"];
4991 [fVidQualitySlider setNumberOfTickMarks:(([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * fractionalGranularity) + 1];
4992 qpRFLabelString = @"RF:";
4995 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_FFMPEG )
4997 [fVidQualitySlider setMinValue:1.0];
4998 [fVidQualitySlider setMaxValue:31.0];
4999 [fVidQualitySlider setNumberOfTickMarks:31];
5002 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
5004 [fVidQualitySlider setMinValue:0.0];
5005 [fVidQualitySlider setMaxValue:63.0];
5006 [fVidQualitySlider setNumberOfTickMarks:64];
5008 [fVidQualityRFLabel setStringValue:qpRFLabelString];
5010 /* check to see if we have changed slider scales */
5011 if (previousMaxValue != [fVidQualitySlider maxValue])
5013 /* if so, convert the old setting to the new scale as close as possible based on percentages */
5014 float rf = ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue] + 1) * previousPercentOfSliderScale;
5015 [fVidQualitySlider setFloatValue:rf];
5018 [self qualitySliderChanged:nil];
5021 - (IBAction) qualitySliderChanged: (id) sender
5023 /* Our constant quality slider is in a range based
5024 * on each encoders qp/rf values. The range depends
5025 * on the encoder. Also, the range is inverse of quality
5026 * for all of the encoders *except* for theora
5027 * (ie. as the "quality" goes up, the cq or rf value
5028 * actually goes down). Since the IB sliders always set
5029 * their max value at the right end of the slider, we
5030 * will calculate the inverse, so as the slider floatValue
5031 * goes up, we will show the inverse in the rf field
5032 * so, the floatValue at the right for x264 would be 51
5033 * and our rf field needs to show 0 and vice versa.
5036 float sliderRfInverse = ([fVidQualitySlider maxValue] - [fVidQualitySlider floatValue]) + [fVidQualitySlider minValue];
5037 /* If the encoder is theora, use the float, otherwise use the inverse float*/
5038 //float sliderRfToPercent;
5039 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
5041 [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", [fVidQualitySlider floatValue]]];
5045 [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", sliderRfInverse]];
5047 [self customSettingUsed: sender];
5050 - (void) controlTextDidChange: (NSNotification *) notification
5052 [self calculateBitrate:nil];
5055 - (IBAction) calculateBitrate: (id) sender
5057 if( !fHandle || [fVidQualityMatrix selectedRow] != 0 || !SuccessfulScan )
5062 hb_list_t * list = hb_get_titles( fHandle );
5063 hb_title_t * title = (hb_title_t *) hb_list_item( list,
5064 [fSrcTitlePopUp indexOfSelectedItem] );
5065 hb_job_t * job = title->job;
5066 hb_audio_config_t * audio;
5067 /* For hb_calc_bitrate in addition to the Target Size in MB out of the
5068 * Target Size Field, we also need the job info for the Muxer, the Chapters
5069 * as well as all of the audio track info.
5070 * This used to be accomplished by simply calling prepareJob here, however
5071 * since the resilient queue sets the queue array values instead of the job
5072 * values directly, we duplicate the old prepareJob code here for the variables
5075 job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
5076 job->chapter_end = [fSrcChapterEndPopUp indexOfSelectedItem] + 1;
5077 job->mux = [[fDstFormatPopUp selectedItem] tag];
5079 /* Audio goes here */
5080 int audiotrack_count = hb_list_count(job->list_audio);
5081 for( int i = 0; i < audiotrack_count;i++)
5083 hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
5084 hb_list_rem(job->list_audio, temp_audio);
5086 /* Now we need our audio info here for each track if applicable */
5087 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
5089 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
5090 hb_audio_config_init(audio);
5091 audio->in.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
5092 /* We go ahead and assign values to our audio->out.<properties> */
5093 audio->out.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
5094 audio->out.codec = [[fAudTrack1CodecPopUp selectedItem] tag];
5095 audio->out.mixdown = [[fAudTrack1MixPopUp selectedItem] tag];
5096 audio->out.bitrate = [[fAudTrack1BitratePopUp selectedItem] tag];
5097 audio->out.samplerate = [[fAudTrack1RatePopUp selectedItem] tag];
5098 audio->out.dynamic_range_compression = [fAudTrack1DrcField floatValue];
5100 hb_audio_add( job, audio );
5103 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
5105 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
5106 hb_audio_config_init(audio);
5107 audio->in.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
5108 /* We go ahead and assign values to our audio->out.<properties> */
5109 audio->out.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
5110 audio->out.codec = [[fAudTrack2CodecPopUp selectedItem] tag];
5111 audio->out.mixdown = [[fAudTrack2MixPopUp selectedItem] tag];
5112 audio->out.bitrate = [[fAudTrack2BitratePopUp selectedItem] tag];
5113 audio->out.samplerate = [[fAudTrack2RatePopUp selectedItem] tag];
5114 audio->out.dynamic_range_compression = [fAudTrack2DrcField floatValue];
5116 hb_audio_add( job, audio );
5121 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
5123 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
5124 hb_audio_config_init(audio);
5125 audio->in.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
5126 /* We go ahead and assign values to our audio->out.<properties> */
5127 audio->out.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
5128 audio->out.codec = [[fAudTrack3CodecPopUp selectedItem] tag];
5129 audio->out.mixdown = [[fAudTrack3MixPopUp selectedItem] tag];
5130 audio->out.bitrate = [[fAudTrack3BitratePopUp selectedItem] tag];
5131 audio->out.samplerate = [[fAudTrack3RatePopUp selectedItem] tag];
5132 audio->out.dynamic_range_compression = [fAudTrack3DrcField floatValue];
5134 hb_audio_add( job, audio );
5139 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
5141 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
5142 hb_audio_config_init(audio);
5143 audio->in.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
5144 /* We go ahead and assign values to our audio->out.<properties> */
5145 audio->out.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
5146 audio->out.codec = [[fAudTrack4CodecPopUp selectedItem] tag];
5147 audio->out.mixdown = [[fAudTrack4MixPopUp selectedItem] tag];
5148 audio->out.bitrate = [[fAudTrack4BitratePopUp selectedItem] tag];
5149 audio->out.samplerate = [[fAudTrack4RatePopUp selectedItem] tag];
5150 audio->out.dynamic_range_compression = [fAudTrack4DrcField floatValue];
5152 hb_audio_add( job, audio );
5157 [fVidBitrateField setIntValue: hb_calc_bitrate( job, [fVidTargetSizeField intValue] )];
5161 #pragma mark - Picture
5163 /* lets set the picture size back to the max from right after title scan
5164 Lets use an IBAction here as down the road we could always use a checkbox
5165 in the gui to easily take the user back to max. Remember, the compiler
5166 resolves IBActions down to -(void) during compile anyway */
5167 - (IBAction) revertPictureSizeToMax: (id) sender
5169 hb_job_t * job = fTitle->job;
5170 /* Here we apply the title source and height */
5171 job->width = fTitle->width;
5172 job->height = fTitle->height;
5174 [self calculatePictureSizing: sender];
5175 /* We call method to change UI to reflect whether a preset is used or not*/
5176 [self customSettingUsed: sender];
5180 * Registers changes made in the Picture Settings Window.
5183 - (void)pictureSettingsDidChange
5185 [self calculatePictureSizing:nil];
5188 /* Get and Display Current Pic Settings in main window */
5189 - (IBAction) calculatePictureSizing: (id) sender
5191 if (fTitle->job->anamorphic.mode > 0)
5193 fTitle->job->keep_ratio = 0;
5196 if (fTitle->job->anamorphic.mode != 1) // we are not strict so show the modulus
5198 [fPictureSizeField setStringValue: [NSString stringWithFormat:@"Picture Size: %@, Modulus: %d", [fPictureController getPictureSizeInfoString], fTitle->job->modulus]];
5202 [fPictureSizeField setStringValue: [NSString stringWithFormat:@"Picture Size: %@", [fPictureController getPictureSizeInfoString]]];
5204 NSString *picCropping;
5205 /* Set the display field for crop as per boolean */
5206 if (![fPictureController autoCrop])
5208 picCropping = @"Custom";
5212 picCropping = @"Auto";
5214 picCropping = [picCropping stringByAppendingString:[NSString stringWithFormat:@" %d/%d/%d/%d",fTitle->job->crop[0],fTitle->job->crop[1],fTitle->job->crop[2],fTitle->job->crop[3]]];
5216 [fPictureCroppingField setStringValue: [NSString stringWithFormat:@"Picture Cropping: %@",picCropping]];
5218 NSString *videoFilters;
5221 if ([fPictureController detelecine] == 2)
5223 videoFilters = [videoFilters stringByAppendingString:@" - Detelecine (Default)"];
5225 else if ([fPictureController detelecine] == 1)
5227 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Detelecine (%@)",[fPictureController detelecineCustomString]]];
5231 if ([fPictureController useDecomb] == 1)
5234 if ([fPictureController decomb] == 2)
5236 videoFilters = [videoFilters stringByAppendingString:@" - Decomb (Default)"];
5238 else if ([fPictureController decomb] == 1)
5240 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Decomb (%@)",[fPictureController decombCustomString]]];
5246 if ([fPictureController deinterlace] > 0)
5248 fTitle->job->deinterlace = 1;
5252 fTitle->job->deinterlace = 0;
5255 if ([fPictureController deinterlace] == 2)
5257 videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Fast)"];
5259 else if ([fPictureController deinterlace] == 3)
5261 videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slow)"];
5263 else if ([fPictureController deinterlace] == 4)
5265 videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slower)"];
5267 else if ([fPictureController deinterlace] == 1)
5269 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deinterlace (%@)",[fPictureController deinterlaceCustomString]]];
5275 if ([fPictureController denoise] == 2)
5277 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Weak)"];
5279 else if ([fPictureController denoise] == 3)
5281 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Medium)"];
5283 else if ([fPictureController denoise] == 4)
5285 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Strong)"];
5287 else if ([fPictureController denoise] == 1)
5289 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Denoise (%@)",[fPictureController denoiseCustomString]]];
5293 if ([fPictureController deblock] > 0)
5295 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deblock (%d)",[fPictureController deblock]]];
5299 if ([fPictureController grayscale])
5301 videoFilters = [videoFilters stringByAppendingString:@" - Grayscale"];
5303 [fVideoFiltersField setStringValue: [NSString stringWithFormat:@"Video Filters: %@", videoFilters]];
5305 //[fPictureController reloadStillPreview];
5310 #pragma mark - Audio and Subtitles
5311 - (IBAction) audioCodecsPopUpChanged: (id) sender
5314 NSPopUpButton * audiotrackPopUp;
5315 NSPopUpButton * sampleratePopUp;
5316 NSPopUpButton * bitratePopUp;
5317 NSPopUpButton * audiocodecPopUp;
5318 if (sender == fAudTrack1CodecPopUp)
5320 audiotrackPopUp = fAudLang1PopUp;
5321 audiocodecPopUp = fAudTrack1CodecPopUp;
5322 sampleratePopUp = fAudTrack1RatePopUp;
5323 bitratePopUp = fAudTrack1BitratePopUp;
5325 else if (sender == fAudTrack2CodecPopUp)
5327 audiotrackPopUp = fAudLang2PopUp;
5328 audiocodecPopUp = fAudTrack2CodecPopUp;
5329 sampleratePopUp = fAudTrack2RatePopUp;
5330 bitratePopUp = fAudTrack2BitratePopUp;
5332 else if (sender == fAudTrack3CodecPopUp)
5334 audiotrackPopUp = fAudLang3PopUp;
5335 audiocodecPopUp = fAudTrack3CodecPopUp;
5336 sampleratePopUp = fAudTrack3RatePopUp;
5337 bitratePopUp = fAudTrack3BitratePopUp;
5341 audiotrackPopUp = fAudLang4PopUp;
5342 audiocodecPopUp = fAudTrack4CodecPopUp;
5343 sampleratePopUp = fAudTrack4RatePopUp;
5344 bitratePopUp = fAudTrack4BitratePopUp;
5347 /* changing the codecs on offer may mean that we can / can't offer mono or 6ch, */
5348 /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
5349 [self audioTrackPopUpChanged: audiotrackPopUp];
5353 - (IBAction) setEnabledStateOfAudioMixdownControls: (id) sender
5355 /* We will be setting the enabled/disabled state of each tracks audio controls based on
5356 * the settings of the source audio for that track. We leave the samplerate and bitrate
5357 * to audiotrackMixdownChanged
5360 /* We will first verify that a lower track number has been selected before enabling each track
5361 * for example, make sure a track is selected for track 1 before enabling track 2, etc.
5363 if ([fAudLang1PopUp indexOfSelectedItem] == 0)
5365 [fAudLang2PopUp setEnabled: NO];
5366 [fAudLang2PopUp selectItemAtIndex: 0];
5370 [fAudLang2PopUp setEnabled: YES];
5373 if ([fAudLang2PopUp indexOfSelectedItem] == 0)
5375 [fAudLang3PopUp setEnabled: NO];
5376 [fAudLang3PopUp selectItemAtIndex: 0];
5380 [fAudLang3PopUp setEnabled: YES];
5382 if ([fAudLang3PopUp indexOfSelectedItem] == 0)
5384 [fAudLang4PopUp setEnabled: NO];
5385 [fAudLang4PopUp selectItemAtIndex: 0];
5389 [fAudLang4PopUp setEnabled: YES];
5391 /* enable/disable the mixdown text and popupbutton for audio track 1 */
5392 [fAudTrack1CodecPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5393 [fAudTrack1MixPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5394 [fAudTrack1RatePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5395 [fAudTrack1BitratePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5396 [fAudTrack1DrcSlider setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5397 [fAudTrack1DrcField setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5398 if ([fAudLang1PopUp indexOfSelectedItem] == 0)
5400 [fAudTrack1CodecPopUp removeAllItems];
5401 [fAudTrack1MixPopUp removeAllItems];
5402 [fAudTrack1RatePopUp removeAllItems];
5403 [fAudTrack1BitratePopUp removeAllItems];
5404 [fAudTrack1DrcSlider setFloatValue: 0.00];
5405 [self audioDRCSliderChanged: fAudTrack1DrcSlider];
5407 else if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
5409 [fAudTrack1RatePopUp setEnabled: NO];
5410 [fAudTrack1BitratePopUp setEnabled: NO];
5411 [fAudTrack1DrcSlider setEnabled: NO];
5412 [fAudTrack1DrcField setEnabled: NO];
5415 /* enable/disable the mixdown text and popupbutton for audio track 2 */
5416 [fAudTrack2CodecPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5417 [fAudTrack2MixPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5418 [fAudTrack2RatePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5419 [fAudTrack2BitratePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5420 [fAudTrack2DrcSlider setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5421 [fAudTrack2DrcField setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5422 if ([fAudLang2PopUp indexOfSelectedItem] == 0)
5424 [fAudTrack2CodecPopUp removeAllItems];
5425 [fAudTrack2MixPopUp removeAllItems];
5426 [fAudTrack2RatePopUp removeAllItems];
5427 [fAudTrack2BitratePopUp removeAllItems];
5428 [fAudTrack2DrcSlider setFloatValue: 0.00];
5429 [self audioDRCSliderChanged: fAudTrack2DrcSlider];
5431 else if ([[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
5433 [fAudTrack2RatePopUp setEnabled: NO];
5434 [fAudTrack2BitratePopUp setEnabled: NO];
5435 [fAudTrack2DrcSlider setEnabled: NO];
5436 [fAudTrack2DrcField setEnabled: NO];
5439 /* enable/disable the mixdown text and popupbutton for audio track 3 */
5440 [fAudTrack3CodecPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5441 [fAudTrack3MixPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5442 [fAudTrack3RatePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5443 [fAudTrack3BitratePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5444 [fAudTrack3DrcSlider setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5445 [fAudTrack3DrcField setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5446 if ([fAudLang3PopUp indexOfSelectedItem] == 0)
5448 [fAudTrack3CodecPopUp removeAllItems];
5449 [fAudTrack3MixPopUp removeAllItems];
5450 [fAudTrack3RatePopUp removeAllItems];
5451 [fAudTrack3BitratePopUp removeAllItems];
5452 [fAudTrack3DrcSlider setFloatValue: 0.00];
5453 [self audioDRCSliderChanged: fAudTrack3DrcSlider];
5455 else if ([[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
5457 [fAudTrack3RatePopUp setEnabled: NO];
5458 [fAudTrack3BitratePopUp setEnabled: NO];
5459 [fAudTrack3DrcSlider setEnabled: NO];
5460 [fAudTrack3DrcField setEnabled: NO];
5463 /* enable/disable the mixdown text and popupbutton for audio track 4 */
5464 [fAudTrack4CodecPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5465 [fAudTrack4MixPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5466 [fAudTrack4RatePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5467 [fAudTrack4BitratePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5468 [fAudTrack4DrcSlider setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5469 [fAudTrack4DrcField setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5470 if ([fAudLang4PopUp indexOfSelectedItem] == 0)
5472 [fAudTrack4CodecPopUp removeAllItems];
5473 [fAudTrack4MixPopUp removeAllItems];
5474 [fAudTrack4RatePopUp removeAllItems];
5475 [fAudTrack4BitratePopUp removeAllItems];
5476 [fAudTrack4DrcSlider setFloatValue: 0.00];
5477 [self audioDRCSliderChanged: fAudTrack4DrcSlider];
5479 else if ([[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
5481 [fAudTrack4RatePopUp setEnabled: NO];
5482 [fAudTrack4BitratePopUp setEnabled: NO];
5483 [fAudTrack4DrcSlider setEnabled: NO];
5484 [fAudTrack4DrcField setEnabled: NO];
5489 - (IBAction) addAllAudioTracksToPopUp: (id) sender
5492 hb_list_t * list = hb_get_titles( fHandle );
5493 hb_title_t * title = (hb_title_t*)
5494 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
5496 hb_audio_config_t * audio;
5498 [sender removeAllItems];
5499 [sender addItemWithTitle: NSLocalizedString( @"None", @"" )];
5500 for( int i = 0; i < hb_list_count( title->list_audio ); i++ )
5502 audio = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, i );
5503 [[sender menu] addItemWithTitle:
5504 [NSString stringWithUTF8String: audio->lang.description]
5505 action: NULL keyEquivalent: @""];
5507 [sender selectItemAtIndex: 0];
5511 - (IBAction) selectAudioTrackInPopUp: (id) sender searchPrefixString: (NSString *) searchPrefixString selectIndexIfNotFound: (int) selectIndexIfNotFound
5514 /* this method can be used to find a language, or a language-and-source-format combination, by passing in the appropriate string */
5515 /* e.g. to find the first French track, pass in an NSString * of "Francais" */
5516 /* e.g. to find the first English 5.1 AC3 track, pass in an NSString * of "English (AC3) (5.1 ch)" */
5517 /* if no matching track is found, then selectIndexIfNotFound is used to choose which track to select instead */
5519 if (searchPrefixString)
5522 for( int i = 0; i < [sender numberOfItems]; i++ )
5524 /* Try to find the desired search string */
5525 if ([[[sender itemAtIndex: i] title] hasPrefix:searchPrefixString])
5527 [sender selectItemAtIndex: i];
5531 /* couldn't find the string, so select the requested "search string not found" item */
5532 /* index of 0 means select the "none" item */
5533 /* index of 1 means select the first audio track */
5534 [sender selectItemAtIndex: selectIndexIfNotFound];
5538 /* if no search string is provided, then select the selectIndexIfNotFound item */
5539 [sender selectItemAtIndex: selectIndexIfNotFound];
5543 - (IBAction) audioAddAudioTrackCodecs: (id)sender
5545 int format = [fDstFormatPopUp indexOfSelectedItem];
5547 /* setup pointers to the appropriate popups for the correct track */
5548 NSPopUpButton * audiocodecPopUp;
5549 NSPopUpButton * audiotrackPopUp;
5550 if (sender == fAudTrack1CodecPopUp)
5552 audiotrackPopUp = fAudLang1PopUp;
5553 audiocodecPopUp = fAudTrack1CodecPopUp;
5555 else if (sender == fAudTrack2CodecPopUp)
5557 audiotrackPopUp = fAudLang2PopUp;
5558 audiocodecPopUp = fAudTrack2CodecPopUp;
5560 else if (sender == fAudTrack3CodecPopUp)
5562 audiotrackPopUp = fAudLang3PopUp;
5563 audiocodecPopUp = fAudTrack3CodecPopUp;
5567 audiotrackPopUp = fAudLang4PopUp;
5568 audiocodecPopUp = fAudTrack4CodecPopUp;
5571 [audiocodecPopUp removeAllItems];
5572 /* Make sure "None" isnt selected in the source track */
5573 if ([audiotrackPopUp indexOfSelectedItem] > 0)
5575 [audiocodecPopUp setEnabled:YES];
5576 NSMenuItem *menuItem;
5577 /* We setup our appropriate popups for codecs and put the int value in the popup tag for easy retrieval */
5583 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (CoreAudio)" action: NULL keyEquivalent: @""];
5584 [menuItem setTag: HB_ACODEC_CA_AAC];
5586 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""];
5587 [menuItem setTag: HB_ACODEC_FAAC];
5589 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
5590 [menuItem setTag: HB_ACODEC_AC3];
5596 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (CoreAudio)" action: NULL keyEquivalent: @""];
5597 [menuItem setTag: HB_ACODEC_CA_AAC];
5599 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""];
5600 [menuItem setTag: HB_ACODEC_FAAC];
5602 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
5603 [menuItem setTag: HB_ACODEC_AC3];
5605 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"DTS Passthru" action: NULL keyEquivalent: @""];
5606 [menuItem setTag: HB_ACODEC_DCA];
5608 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
5609 [menuItem setTag: HB_ACODEC_LAME];
5611 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"Vorbis (vorbis)" action: NULL keyEquivalent: @""];
5612 [menuItem setTag: HB_ACODEC_VORBIS];
5615 [audiocodecPopUp selectItemAtIndex:0];
5619 [audiocodecPopUp setEnabled:NO];
5623 - (IBAction) audioTrackPopUpChanged: (id) sender
5625 /* utility function to call audioTrackPopUpChanged without passing in a mixdown-to-use */
5626 [self audioTrackPopUpChanged: sender mixdownToUse: 0];
5629 - (IBAction) audioTrackPopUpChanged: (id) sender mixdownToUse: (int) mixdownToUse
5632 /* make sure we have a selected title before continuing */
5633 if (fTitle == NULL) return;
5634 /* if the sender is the lanaguage popup and there is nothing in the codec popup, lets call
5635 * audioAddAudioTrackCodecs on the codec popup to populate it properly before moving on
5637 if (sender == fAudLang1PopUp && [[fAudTrack1CodecPopUp menu] numberOfItems] == 0)
5639 [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp];
5641 if (sender == fAudLang2PopUp && [[fAudTrack2CodecPopUp menu] numberOfItems] == 0)
5643 [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp];
5645 if (sender == fAudLang3PopUp && [[fAudTrack3CodecPopUp menu] numberOfItems] == 0)
5647 [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp];
5649 if (sender == fAudLang4PopUp && [[fAudTrack4CodecPopUp menu] numberOfItems] == 0)
5651 [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp];
5654 /* Now lets make the sender the appropriate Audio Track popup from this point on */
5655 if (sender == fAudTrack1CodecPopUp || sender == fAudTrack1MixPopUp)
5657 sender = fAudLang1PopUp;
5659 if (sender == fAudTrack2CodecPopUp || sender == fAudTrack2MixPopUp)
5661 sender = fAudLang2PopUp;
5663 if (sender == fAudTrack3CodecPopUp || sender == fAudTrack3MixPopUp)
5665 sender = fAudLang3PopUp;
5667 if (sender == fAudTrack4CodecPopUp || sender == fAudTrack4MixPopUp)
5669 sender = fAudLang4PopUp;
5672 /* pointer to this track's mixdown, codec, sample rate and bitrate NSPopUpButton's */
5673 NSPopUpButton * mixdownPopUp;
5674 NSPopUpButton * audiocodecPopUp;
5675 NSPopUpButton * sampleratePopUp;
5676 NSPopUpButton * bitratePopUp;
5677 if (sender == fAudLang1PopUp)
5679 mixdownPopUp = fAudTrack1MixPopUp;
5680 audiocodecPopUp = fAudTrack1CodecPopUp;
5681 sampleratePopUp = fAudTrack1RatePopUp;
5682 bitratePopUp = fAudTrack1BitratePopUp;
5684 else if (sender == fAudLang2PopUp)
5686 mixdownPopUp = fAudTrack2MixPopUp;
5687 audiocodecPopUp = fAudTrack2CodecPopUp;
5688 sampleratePopUp = fAudTrack2RatePopUp;
5689 bitratePopUp = fAudTrack2BitratePopUp;
5691 else if (sender == fAudLang3PopUp)
5693 mixdownPopUp = fAudTrack3MixPopUp;
5694 audiocodecPopUp = fAudTrack3CodecPopUp;
5695 sampleratePopUp = fAudTrack3RatePopUp;
5696 bitratePopUp = fAudTrack3BitratePopUp;
5700 mixdownPopUp = fAudTrack4MixPopUp;
5701 audiocodecPopUp = fAudTrack4CodecPopUp;
5702 sampleratePopUp = fAudTrack4RatePopUp;
5703 bitratePopUp = fAudTrack4BitratePopUp;
5706 /* get the index of the selected audio Track*/
5707 int thisAudioIndex = [sender indexOfSelectedItem] - 1;
5709 /* pointer for the hb_audio_s struct we will use later on */
5710 hb_audio_config_t * audio;
5713 /* check if the audio mixdown controls need their enabled state changing */
5714 [self setEnabledStateOfAudioMixdownControls:nil];
5716 if (thisAudioIndex != -1)
5720 audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, thisAudioIndex );// Should "fTitle" be title and be setup ?
5722 /* actually manipulate the proper mixdowns here */
5723 /* delete the previous audio mixdown options */
5724 [mixdownPopUp removeAllItems];
5726 acodec = [[audiocodecPopUp selectedItem] tag];
5731 /* find out if our selected output audio codec supports mono and / or 6ch */
5732 /* we also check for an input codec of AC3 or DCA,
5733 as they are the only libraries able to do the mixdown to mono / conversion to 6-ch */
5734 /* audioCodecsSupportMono and audioCodecsSupport6Ch are the same for now,
5735 but this may change in the future, so they are separated for flexibility */
5736 int audioCodecsSupportMono =
5737 (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
5738 (acodec != HB_ACODEC_LAME);
5739 int audioCodecsSupport6Ch =
5740 (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
5741 (acodec != HB_ACODEC_LAME);
5743 /* check for AC-3 passthru */
5744 if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3)
5747 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5748 [NSString stringWithUTF8String: "AC3 Passthru"]
5749 action: NULL keyEquivalent: @""];
5750 [menuItem setTag: HB_ACODEC_AC3];
5752 else if (audio->in.codec == HB_ACODEC_DCA && acodec == HB_ACODEC_DCA)
5754 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5755 [NSString stringWithUTF8String: "DTS Passthru"]
5756 action: NULL keyEquivalent: @""];
5757 [menuItem setTag: HB_ACODEC_DCA];
5762 /* add the appropriate audio mixdown menuitems to the popupbutton */
5763 /* in each case, we set the new menuitem's tag to be the amixdown value for that mixdown,
5764 so that we can reference the mixdown later */
5766 /* keep a track of the min and max mixdowns we used, so we can select the best match later */
5767 int minMixdownUsed = 0;
5768 int maxMixdownUsed = 0;
5770 /* get the input channel layout without any lfe channels */
5771 int layout = audio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK;
5773 /* do we want to add a mono option? */
5774 if (audioCodecsSupportMono == 1)
5776 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5777 [NSString stringWithUTF8String: hb_audio_mixdowns[0].human_readable_name]
5778 action: NULL keyEquivalent: @""];
5779 [menuItem setTag: hb_audio_mixdowns[0].amixdown];
5780 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[0].amixdown;
5781 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[0].amixdown);
5784 /* do we want to add a stereo option? */
5785 /* offer stereo if we have a mono source and non-mono-supporting codecs, as otherwise we won't have a mixdown at all */
5786 /* also offer stereo if we have a stereo-or-better source */
5787 if ((layout == HB_INPUT_CH_LAYOUT_MONO && audioCodecsSupportMono == 0) || layout >= HB_INPUT_CH_LAYOUT_STEREO)
5789 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5790 [NSString stringWithUTF8String: hb_audio_mixdowns[1].human_readable_name]
5791 action: NULL keyEquivalent: @""];
5792 [menuItem setTag: hb_audio_mixdowns[1].amixdown];
5793 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[1].amixdown;
5794 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[1].amixdown);
5797 /* do we want to add a dolby surround (DPL1) option? */
5798 if (layout == HB_INPUT_CH_LAYOUT_3F1R || layout == HB_INPUT_CH_LAYOUT_3F2R || layout == HB_INPUT_CH_LAYOUT_DOLBY)
5800 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5801 [NSString stringWithUTF8String: hb_audio_mixdowns[2].human_readable_name]
5802 action: NULL keyEquivalent: @""];
5803 [menuItem setTag: hb_audio_mixdowns[2].amixdown];
5804 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[2].amixdown;
5805 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[2].amixdown);
5808 /* do we want to add a dolby pro logic 2 (DPL2) option? */
5809 if (layout == HB_INPUT_CH_LAYOUT_3F2R)
5811 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5812 [NSString stringWithUTF8String: hb_audio_mixdowns[3].human_readable_name]
5813 action: NULL keyEquivalent: @""];
5814 [menuItem setTag: hb_audio_mixdowns[3].amixdown];
5815 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[3].amixdown;
5816 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[3].amixdown);
5819 /* do we want to add a 6-channel discrete option? */
5820 if (audioCodecsSupport6Ch == 1 && layout == HB_INPUT_CH_LAYOUT_3F2R && (audio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE))
5822 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5823 [NSString stringWithUTF8String: hb_audio_mixdowns[4].human_readable_name]
5824 action: NULL keyEquivalent: @""];
5825 [menuItem setTag: hb_audio_mixdowns[4].amixdown];
5826 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[4].amixdown;
5827 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[4].amixdown);
5830 /* do we want to add an AC-3 passthrough option? */
5831 if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3)
5833 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5834 [NSString stringWithUTF8String: hb_audio_mixdowns[5].human_readable_name]
5835 action: NULL keyEquivalent: @""];
5836 [menuItem setTag: HB_ACODEC_AC3];
5837 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown;
5838 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown);
5841 /* do we want to add a DTS Passthru option ? HB_ACODEC_DCA*/
5842 if (audio->in.codec == HB_ACODEC_DCA && acodec == HB_ACODEC_DCA)
5844 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5845 [NSString stringWithUTF8String: hb_audio_mixdowns[5].human_readable_name]
5846 action: NULL keyEquivalent: @""];
5847 [menuItem setTag: HB_ACODEC_DCA];
5848 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown;
5849 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown);
5852 /* auto-select the best mixdown based on our saved mixdown preference */
5854 /* for now, this is hard-coded to a "best" mixdown of HB_AMIXDOWN_DOLBYPLII */
5855 /* ultimately this should be a prefs option */
5858 /* if we passed in a mixdown to use - in order to load a preset - then try and use it */
5859 if (mixdownToUse > 0)
5861 useMixdown = mixdownToUse;
5865 useMixdown = HB_AMIXDOWN_DOLBYPLII;
5868 /* if useMixdown > maxMixdownUsed, then use maxMixdownUsed */
5869 if (useMixdown > maxMixdownUsed)
5871 useMixdown = maxMixdownUsed;
5874 /* if useMixdown < minMixdownUsed, then use minMixdownUsed */
5875 if (useMixdown < minMixdownUsed)
5877 useMixdown = minMixdownUsed;
5880 /* select the (possibly-amended) preferred mixdown */
5881 [mixdownPopUp selectItemWithTag: useMixdown];
5884 /* In the case of a source track that is not AC3 and the user tries to use AC3 Passthru (which does not work)
5885 * we force the Audio Codec choice back to a workable codec. We use CoreAudio aac for all containers.
5887 if (audio->in.codec != HB_ACODEC_AC3 && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_AC3)
5889 [audiocodecPopUp selectItemWithTag: HB_ACODEC_CA_AAC];
5892 /* In the case of a source track that is not DTS and the user tries to use DTS Passthru (which does not work)
5893 * we force the Audio Codec choice back to a workable codec. We use CoreAudio aac for all containers.
5895 if (audio->in.codec != HB_ACODEC_DCA && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_DCA)
5897 [audiocodecPopUp selectItemWithTag: HB_ACODEC_CA_AAC];
5900 /* Setup our samplerate and bitrate popups we will need based on mixdown */
5901 [self audioTrackMixdownChanged: mixdownPopUp];
5905 if( [fDstFormatPopUp indexOfSelectedItem] == 0 )
5907 [self autoSetM4vExtension: sender];
5911 - (IBAction) audioTrackMixdownChanged: (id) sender
5915 /* setup pointers to all of the other audio track controls
5916 * we will need later
5918 NSPopUpButton * mixdownPopUp;
5919 NSPopUpButton * sampleratePopUp;
5920 NSPopUpButton * bitratePopUp;
5921 NSPopUpButton * audiocodecPopUp;
5922 NSPopUpButton * audiotrackPopUp;
5923 NSSlider * drcSlider;
5924 NSTextField * drcField;
5925 if (sender == fAudTrack1MixPopUp)
5927 audiotrackPopUp = fAudLang1PopUp;
5928 audiocodecPopUp = fAudTrack1CodecPopUp;
5929 mixdownPopUp = fAudTrack1MixPopUp;
5930 sampleratePopUp = fAudTrack1RatePopUp;
5931 bitratePopUp = fAudTrack1BitratePopUp;
5932 drcSlider = fAudTrack1DrcSlider;
5933 drcField = fAudTrack1DrcField;
5935 else if (sender == fAudTrack2MixPopUp)
5937 audiotrackPopUp = fAudLang2PopUp;
5938 audiocodecPopUp = fAudTrack2CodecPopUp;
5939 mixdownPopUp = fAudTrack2MixPopUp;
5940 sampleratePopUp = fAudTrack2RatePopUp;
5941 bitratePopUp = fAudTrack2BitratePopUp;
5942 drcSlider = fAudTrack2DrcSlider;
5943 drcField = fAudTrack2DrcField;
5945 else if (sender == fAudTrack3MixPopUp)
5947 audiotrackPopUp = fAudLang3PopUp;
5948 audiocodecPopUp = fAudTrack3CodecPopUp;
5949 mixdownPopUp = fAudTrack3MixPopUp;
5950 sampleratePopUp = fAudTrack3RatePopUp;
5951 bitratePopUp = fAudTrack3BitratePopUp;
5952 drcSlider = fAudTrack3DrcSlider;
5953 drcField = fAudTrack3DrcField;
5957 audiotrackPopUp = fAudLang4PopUp;
5958 audiocodecPopUp = fAudTrack4CodecPopUp;
5959 mixdownPopUp = fAudTrack4MixPopUp;
5960 sampleratePopUp = fAudTrack4RatePopUp;
5961 bitratePopUp = fAudTrack4BitratePopUp;
5962 drcSlider = fAudTrack4DrcSlider;
5963 drcField = fAudTrack4DrcField;
5965 acodec = [[audiocodecPopUp selectedItem] tag];
5966 /* storage variable for the min and max bitrate allowed for this codec */
5972 case HB_ACODEC_FAAC:
5973 /* check if we have a 6ch discrete conversion in either audio track */
5974 if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5976 /* FAAC has a minimum of 192 kbps for 6-channel discrete */
5978 /* If either mixdown popup includes 6-channel discrete, then allow up to 448 kbps */
5984 /* FAAC is happy using our min bitrate of 32 kbps for stereo or mono */
5986 /* FAAC won't honour anything more than 160 for stereo, so let's not offer it */
5987 /* note: haven't dealt with mono separately here, FAAC will just use the max it can */
5992 case HB_ACODEC_CA_AAC:
5993 /* check if we have a 6ch discrete conversion in either audio track */
5994 if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
6007 case HB_ACODEC_LAME:
6008 /* Lame is happy using our min bitrate of 32 kbps */
6010 /* Lame won't encode if the bitrate is higher than 320 kbps */
6014 case HB_ACODEC_VORBIS:
6015 if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
6017 /* Vorbis causes a crash if we use a bitrate below 192 kbps with 6 channel */
6019 /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */
6025 /* Vorbis causes a crash if we use a bitrate below 48 kbps */
6027 /* Vorbis can cope with 384 kbps quite happily, even for stereo */
6033 /* AC3 passthru disables the bitrate dropdown anyway, so we might as well just use the min and max bitrate */
6039 /* make sure we have a selected title before continuing */
6040 if (fTitle == NULL) return;
6041 /* get the audio so we can find out what input rates are*/
6042 hb_audio_config_t * audio;
6043 audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, [audiotrackPopUp indexOfSelectedItem] - 1 );
6044 int inputbitrate = audio->in.bitrate / 1000;
6045 int inputsamplerate = audio->in.samplerate;
6047 if ([[mixdownPopUp selectedItem] tag] != HB_ACODEC_AC3 && [[mixdownPopUp selectedItem] tag] != HB_ACODEC_DCA)
6049 [bitratePopUp removeAllItems];
6051 for( int i = 0; i < hb_audio_bitrates_count; i++ )
6053 if (hb_audio_bitrates[i].rate >= minbitrate && hb_audio_bitrates[i].rate <= maxbitrate)
6055 /* add a new menuitem for this bitrate */
6056 NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle:
6057 [NSString stringWithUTF8String: hb_audio_bitrates[i].string]
6058 action: NULL keyEquivalent: @""];
6059 /* set its tag to be the actual bitrate as an integer, so we can retrieve it later */
6060 [menuItem setTag: hb_audio_bitrates[i].rate];
6064 /* select the default bitrate (but use 384 for 6-ch AAC) */
6065 if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
6067 [bitratePopUp selectItemWithTag: 384];
6071 [bitratePopUp selectItemWithTag: hb_audio_bitrates[hb_audio_bitrates_default].rate];
6074 /* populate and set the sample rate popup */
6075 /* Audio samplerate */
6076 [sampleratePopUp removeAllItems];
6077 /* we create a same as source selection (Auto) so that we can choose to use the input sample rate */
6078 NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle: @"Auto" action: NULL keyEquivalent: @""];
6079 [menuItem setTag: inputsamplerate];
6081 for( int i = 0; i < hb_audio_rates_count; i++ )
6083 NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle:
6084 [NSString stringWithUTF8String: hb_audio_rates[i].string]
6085 action: NULL keyEquivalent: @""];
6086 [menuItem setTag: hb_audio_rates[i].rate];
6088 /* We use the input sample rate as the default sample rate as downsampling just makes audio worse
6089 * and there is no compelling reason to use anything else as default, though the users default
6090 * preset will likely override any setting chosen here.
6092 [sampleratePopUp selectItemWithTag: inputsamplerate];
6095 /* Since AC3 Pass Thru and DTS Pass Thru uses the input bitrate and sample rate, we get the input tracks
6096 * bitrate and display it in the bitrate popup even though libhb happily ignores any bitrate input from
6097 * the gui. We do this for better user feedback in the audio tab as well as the queue for the most part
6099 if ([[mixdownPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[mixdownPopUp selectedItem] tag] == HB_ACODEC_DCA)
6102 /* lets also set the bitrate popup to the input bitrate as thats what passthru will use */
6103 [bitratePopUp removeAllItems];
6104 NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle:
6105 [NSString stringWithFormat:@"%d", inputbitrate]
6106 action: NULL keyEquivalent: @""];
6107 [menuItem setTag: inputbitrate];
6108 /* For ac3 passthru we disable the sample rate and bitrate popups as well as the drc slider*/
6109 [bitratePopUp setEnabled: NO];
6110 [sampleratePopUp setEnabled: NO];
6112 [drcSlider setFloatValue: 0.00];
6113 [self audioDRCSliderChanged: drcSlider];
6114 [drcSlider setEnabled: NO];
6115 [drcField setEnabled: NO];
6119 [sampleratePopUp setEnabled: YES];
6120 [bitratePopUp setEnabled: YES];
6121 [drcSlider setEnabled: YES];
6122 [drcField setEnabled: YES];
6124 [self calculateBitrate:nil];
6127 - (IBAction) audioDRCSliderChanged: (id) sender
6129 NSSlider * drcSlider;
6130 NSTextField * drcField;
6131 if (sender == fAudTrack1DrcSlider)
6133 drcSlider = fAudTrack1DrcSlider;
6134 drcField = fAudTrack1DrcField;
6136 else if (sender == fAudTrack2DrcSlider)
6138 drcSlider = fAudTrack2DrcSlider;
6139 drcField = fAudTrack2DrcField;
6141 else if (sender == fAudTrack3DrcSlider)
6143 drcSlider = fAudTrack3DrcSlider;
6144 drcField = fAudTrack3DrcField;
6148 drcSlider = fAudTrack4DrcSlider;
6149 drcField = fAudTrack4DrcField;
6152 /* If we are between 0.0 and 1.0 on the slider, snap it to 1.0 */
6153 if ([drcSlider floatValue] > 0.0 && [drcSlider floatValue] < 1.0)
6155 [drcSlider setFloatValue:1.0];
6159 [drcField setStringValue: [NSString stringWithFormat: @"%.2f", [drcSlider floatValue]]];
6160 /* For now, do not call this until we have an intelligent way to determine audio track selections
6161 * compared to presets
6163 //[self customSettingUsed: sender];
6168 - (IBAction) browseImportSrtFile: (id) sender
6171 NSOpenPanel * panel;
6173 panel = [NSOpenPanel openPanel];
6174 [panel setAllowsMultipleSelection: NO];
6175 [panel setCanChooseFiles: YES];
6176 [panel setCanChooseDirectories: NO ];
6177 NSString * sourceDirectory;
6178 if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastSrtImportDirectory"])
6180 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastSrtImportDirectory"];
6184 sourceDirectory = @"~/Desktop";
6185 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
6187 /* we open up the browse srt sheet here and call for browseImportSrtFileDone after the sheet is closed */
6188 NSArray *fileTypes = [NSArray arrayWithObjects:@"plist", @"srt", nil];
6189 [panel beginSheetForDirectory: sourceDirectory file: nil types: fileTypes
6190 modalForWindow: fWindow modalDelegate: self
6191 didEndSelector: @selector( browseImportSrtFileDone:returnCode:contextInfo: )
6192 contextInfo: sender];
6195 - (void) browseImportSrtFileDone: (NSSavePanel *) sheet
6196 returnCode: (int) returnCode contextInfo: (void *) contextInfo
6198 if( returnCode == NSOKButton )
6200 NSString *importSrtDirectory = [[sheet filename] stringByDeletingLastPathComponent];
6201 NSString *importSrtFilePath = [sheet filename];
6202 [[NSUserDefaults standardUserDefaults] setObject:importSrtDirectory forKey:@"LastSrtImportDirectory"];
6204 /* now pass the string off to fSubtitlesDelegate to add the srt file to the dropdown */
6205 [fSubtitlesDelegate createSubtitleSrtTrack:importSrtFilePath];
6207 [fSubtitlesTable reloadData];
6213 #pragma mark Open New Windows
6215 - (IBAction) openHomepage: (id) sender
6217 [[NSWorkspace sharedWorkspace] openURL: [NSURL
6218 URLWithString:@"http://handbrake.fr/"]];
6221 - (IBAction) openForums: (id) sender
6223 [[NSWorkspace sharedWorkspace] openURL: [NSURL
6224 URLWithString:@"http://handbrake.fr/forum/"]];
6226 - (IBAction) openUserGuide: (id) sender
6228 [[NSWorkspace sharedWorkspace] openURL: [NSURL
6229 URLWithString:@"http://handbrake.fr/trac/wiki/HandBrakeGuide"]];
6233 * Shows debug output window.
6235 - (IBAction)showDebugOutputPanel:(id)sender
6237 [outputPanel showOutputPanel:sender];
6241 * Shows preferences window.
6243 - (IBAction) showPreferencesWindow: (id) sender
6245 NSWindow * window = [fPreferencesController window];
6246 if (![window isVisible])
6249 [window makeKeyAndOrderFront: nil];
6253 * Shows queue window.
6255 - (IBAction) showQueueWindow:(id)sender
6257 [fQueueController showQueueWindow:sender];
6261 - (IBAction) toggleDrawer:(id)sender {
6262 [fPresetDrawer toggle:self];
6266 * Shows Picture Settings Window.
6269 - (IBAction) showPicturePanel: (id) sender
6271 [fPictureController showPictureWindow:sender];
6274 - (void) picturePanelFullScreen
6276 [fPictureController setToFullScreenMode];
6279 - (void) picturePanelWindowed
6281 [fPictureController setToWindowedMode];
6284 - (IBAction) showPreviewWindow: (id) sender
6286 [fPictureController showPreviewWindow:sender];
6290 #pragma mark Preset Outline View Methods
6291 #pragma mark - Required
6292 /* These are required by the NSOutlineView Datasource Delegate */
6295 /* used to specify the number of levels to show for each item */
6296 - (int)outlineView:(NSOutlineView *)fPresetsOutlineView numberOfChildrenOfItem:(id)item
6298 /* currently use no levels to test outline view viability */
6299 if (item == nil) // for an outline view the root level of the hierarchy is always nil
6301 return [UserPresets count];
6305 /* we need to return the count of the array in ChildrenArray for this folder */
6306 NSArray *children = nil;
6307 children = [item objectForKey:@"ChildrenArray"];
6308 if ([children count] > 0)
6310 return [children count];
6319 /* We use this to deterimine children of an item */
6320 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView child:(NSInteger)index ofItem:(id)item
6323 /* we need to return the count of the array in ChildrenArray for this folder */
6324 NSArray *children = nil;
6327 children = UserPresets;
6331 if ([item objectForKey:@"ChildrenArray"])
6333 children = [item objectForKey:@"ChildrenArray"];
6336 if ((children == nil) || ( [children count] <= (NSUInteger) index))
6342 return [children objectAtIndex:index];
6346 // We are only one level deep, so we can't be asked about children
6347 //NSAssert (NO, @"Presets View outlineView:child:ofItem: currently can't handle nested items.");
6351 /* We use this to determine if an item should be expandable */
6352 - (BOOL)outlineView:(NSOutlineView *)fPresetsOutlineView isItemExpandable:(id)item
6355 /* we need to return the count of the array in ChildrenArray for this folder */
6356 NSArray *children= nil;
6359 children = UserPresets;
6363 if ([item objectForKey:@"ChildrenArray"])
6365 children = [item objectForKey:@"ChildrenArray"];
6369 /* To deterimine if an item should show a disclosure triangle
6370 * we could do it by the children count as so:
6371 * if ([children count] < 1)
6372 * However, lets leave the triangle show even if there are no
6373 * children to help indicate a folder, just like folder in the
6374 * finder can show a disclosure triangle even when empty
6377 /* We need to determine if the item is a folder */
6378 if ([[item objectForKey:@"Folder"] intValue] == 1)
6389 - (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item
6391 // Our outline view has no levels, but we can still expand every item. Doing so
6392 // just makes the row taller. See heightOfRowByItem below.
6393 //return ![(HBQueueOutlineView*)outlineView isDragging];
6399 /* Used to tell the outline view which information is to be displayed per item */
6400 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
6402 /* We have two columns right now, icon and PresetName */
6404 if ([[tableColumn identifier] isEqualToString:@"PresetName"])
6406 return [item objectForKey:@"PresetName"];
6415 - (id)outlineView:(NSOutlineView *)outlineView itemForPersistentObject:(id)object
6417 return [NSKeyedUnarchiver unarchiveObjectWithData:object];
6419 - (id)outlineView:(NSOutlineView *)outlineView persistentObjectForItem:(id)item
6421 return [NSKeyedArchiver archivedDataWithRootObject:item];
6424 #pragma mark - Added Functionality (optional)
6425 /* Use to customize the font and display characteristics of the title cell */
6426 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
6428 if ([[tableColumn identifier] isEqualToString:@"PresetName"])
6432 NSColor *shadowColor;
6433 txtFont = [NSFont systemFontOfSize: [NSFont smallSystemFontSize]];
6434 /*check to see if its a selected row */
6435 if ([fPresetsOutlineView selectedRow] == [fPresetsOutlineView rowForItem:item])
6438 fontColor = [NSColor blackColor];
6439 shadowColor = [NSColor colorWithDeviceRed:(127.0/255.0) green:(140.0/255.0) blue:(160.0/255.0) alpha:1.0];
6443 if ([[item objectForKey:@"Type"] intValue] == 0)
6445 fontColor = [NSColor blueColor];
6447 else // User created preset, use a black font
6449 fontColor = [NSColor blackColor];
6451 /* check to see if its a folder */
6452 //if ([[item objectForKey:@"Folder"] intValue] == 1)
6454 //fontColor = [NSColor greenColor];
6459 /* We use Bold Text for the HB Default */
6460 if ([[item objectForKey:@"Default"] intValue] == 1)// 1 is HB default
6462 txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
6464 /* We use Bold Text for the User Specified Default */
6465 if ([[item objectForKey:@"Default"] intValue] == 2)// 2 is User default
6467 txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
6471 [cell setTextColor:fontColor];
6472 [cell setFont:txtFont];
6477 /* We use this to edit the name field in the outline view */
6478 - (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
6480 if ([[tableColumn identifier] isEqualToString:@"PresetName"])
6485 [theRecord setObject:object forKey:@"PresetName"];
6489 [fPresetsOutlineView reloadData];
6490 /* We save all of the preset data here */
6494 /* We use this to provide tooltips for the items in the presets outline view */
6495 - (NSString *)outlineView:(NSOutlineView *)fPresetsOutlineView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc item:(id)item mouseLocation:(NSPoint)mouseLocation
6497 //if ([[tc identifier] isEqualToString:@"PresetName"])
6499 /* initialize the tooltip contents variable */
6501 /* if there is a description for the preset, we show it in the tooltip */
6502 if ([item objectForKey:@"PresetDescription"])
6504 loc_tip = [item objectForKey:@"PresetDescription"];
6509 loc_tip = @"No description available";
6516 #pragma mark Preset Outline View Methods (dragging related)
6519 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
6521 // Dragging is only allowed for custom presets.
6522 //[[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Default"] intValue] != 1
6523 if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Type"] intValue] == 0) // 0 is built in preset
6527 // Don't retain since this is just holding temporaral drag information, and it is
6528 //only used during a drag! We could put this in the pboard actually.
6529 fDraggedNodes = items;
6530 // Provide data for our custom type, and simple NSStrings.
6531 [pboard declareTypes:[NSArray arrayWithObjects: DragDropSimplePboardType, nil] owner:self];
6533 // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!.
6534 [pboard setData:[NSData data] forType:DragDropSimplePboardType];
6539 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
6542 // Don't allow dropping ONTO an item since they can't really contain any children.
6544 BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex;
6545 if (isOnDropTypeProposal)
6546 return NSDragOperationNone;
6548 // Don't allow dropping INTO an item since they can't really contain any children as of yet.
6551 index = [fPresetsOutlineView rowForItem: item] + 1;
6555 // Don't allow dropping into the Built In Presets.
6556 if (index < presetCurrentBuiltInCount)
6558 return NSDragOperationNone;
6559 index = MAX (index, presetCurrentBuiltInCount);
6562 [outlineView setDropItem:item dropChildIndex:index];
6563 return NSDragOperationGeneric;
6568 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
6570 /* first, lets see if we are dropping into a folder */
6571 if ([[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] && [[[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] intValue] == 1) // if its a folder
6573 NSMutableArray *childrenArray = [[NSMutableArray alloc] init];
6574 childrenArray = [[fPresetsOutlineView itemAtRow:index] objectForKey:@"ChildrenArray"];
6575 [childrenArray addObject:item];
6576 [[fPresetsOutlineView itemAtRow:index] setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"];
6577 [childrenArray autorelease];
6579 else // We are not, so we just move the preset into the existing array
6581 NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet];
6583 NSEnumerator *enumerator = [fDraggedNodes objectEnumerator];
6584 while (obj = [enumerator nextObject])
6586 [moveItems addIndex:[UserPresets indexOfObject:obj]];
6588 // Successful drop, lets rearrange the view and save it all
6589 [self moveObjectsInPresetsArray:UserPresets fromIndexes:moveItems toIndex: index];
6591 [fPresetsOutlineView reloadData];
6596 - (void)moveObjectsInPresetsArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
6598 NSUInteger index = [indexSet lastIndex];
6599 NSUInteger aboveInsertIndexCount = 0;
6601 NSUInteger removeIndex;
6603 if (index >= insertIndex)
6605 removeIndex = index + aboveInsertIndexCount;
6606 aboveInsertIndexCount++;
6610 removeIndex = index;
6614 id object = [[array objectAtIndex:removeIndex] retain];
6615 [array removeObjectAtIndex:removeIndex];
6616 [array insertObject:object atIndex:insertIndex];
6619 index = [indexSet indexLessThanIndex:index];
6624 #pragma mark - Functional Preset NSOutlineView Methods
6626 - (IBAction)selectPreset:(id)sender
6629 if ([fPresetsOutlineView selectedRow] >= 0 && [[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] != 1)
6631 chosenPreset = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
6632 [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
6634 if ([[chosenPreset objectForKey:@"Default"] intValue] == 1)
6636 [fPresetSelectedDisplay setStringValue:[NSString stringWithFormat:@"%@ (Default)", [chosenPreset objectForKey:@"PresetName"]]];
6640 [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
6644 [fDstFormatPopUp selectItemWithTitle:[chosenPreset objectForKey:@"FileFormat"]];
6645 [self formatPopUpChanged:nil];
6647 /* Chapter Markers*/
6648 [fCreateChapterMarkers setState:[[chosenPreset objectForKey:@"ChapterMarkers"] intValue]];
6649 /* check to see if we have only one chapter */
6650 [self chapterPopUpChanged:nil];
6652 /* Allow Mpeg4 64 bit formatting +4GB file sizes */
6653 [fDstMp4LargeFileCheck setState:[[chosenPreset objectForKey:@"Mp4LargeFile"] intValue]];
6654 /* Mux mp4 with http optimization */
6655 [fDstMp4HttpOptFileCheck setState:[[chosenPreset objectForKey:@"Mp4HttpOptimize"] intValue]];
6658 [fVidEncoderPopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoEncoder"]];
6659 /* We set the advanced opt string here if applicable*/
6660 [fAdvancedOptions setOptions:[chosenPreset objectForKey:@"x264Option"]];
6662 /* Lets run through the following functions to get variables set there */
6663 [self videoEncoderPopUpChanged:nil];
6664 /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/
6665 [fDstMp4iPodFileCheck setState:[[chosenPreset objectForKey:@"Mp4iPodCompatible"] intValue]];
6666 [self calculateBitrate:nil];
6669 [fVidQualityMatrix selectCellAtRow:[[chosenPreset objectForKey:@"VideoQualityType"] intValue] column:0];
6671 [fVidTargetSizeField setStringValue:[chosenPreset objectForKey:@"VideoTargetSize"]];
6672 [fVidBitrateField setStringValue:[chosenPreset objectForKey:@"VideoAvgBitrate"]];
6674 /* Since we are now using RF Values for the slider, we detect if the preset uses an old quality float.
6675 * So, check to see if the quality value is less than 1.0 which should indicate the old ".062" type
6676 * quality preset. Caveat: in the case of x264, where the RF scale starts at 0, it would misinterpret
6677 * a preset that uses 0.0 - 0.99 for RF as an old style preset. Not sure how to get around that one yet,
6678 * though it should be a corner case since it would pretty much be a preset for lossless encoding. */
6679 if ([[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue] < 1.0)
6681 /* For the quality slider we need to convert the old percent's to the new rf scales */
6682 float rf = (([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * [[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]);
6683 [fVidQualitySlider setFloatValue:rf];
6688 /* Since theora's qp value goes up from left to right, we can just set the slider float value */
6689 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
6691 [fVidQualitySlider setFloatValue:[[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]];
6695 /* since ffmpeg and x264 use an "inverted" slider (lower qp/rf values indicate a higher quality) we invert the value on the slider */
6696 [fVidQualitySlider setFloatValue:([fVidQualitySlider maxValue] + [fVidQualitySlider minValue]) - [[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]];
6700 [self videoMatrixChanged:nil];
6702 /* Video framerate */
6703 /* For video preset video framerate, we want to make sure that Same as source does not conflict with the
6704 detected framerate in the fVidRatePopUp so we use index 0*/
6705 if ([[chosenPreset objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"])
6707 [fVidRatePopUp selectItemAtIndex: 0];
6711 [fVidRatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoFramerate"]];
6715 /* 2 Pass Encoding */
6716 [fVidTwoPassCheck setState:[[chosenPreset objectForKey:@"VideoTwoPass"] intValue]];
6717 [self twoPassCheckboxChanged:nil];
6719 /* Turbo 1st pass for 2 Pass Encoding */
6720 [fVidTurboPassCheck setState:[[chosenPreset objectForKey:@"VideoTurboTwoPass"] intValue]];
6723 /* First we check to see if we are using the current audio track layout based on AudioList array */
6724 if ([chosenPreset objectForKey:@"AudioList"])
6727 /* pointer to this track's mixdown, codec, sample rate and bitrate NSPopUpButton's */
6728 NSPopUpButton * trackLangPopUp = nil;
6729 NSPopUpButton * mixdownPopUp = nil;
6730 NSPopUpButton * audiocodecPopUp = nil;
6731 NSPopUpButton * sampleratePopUp = nil;
6732 NSPopUpButton * bitratePopUp = nil;
6733 NSSlider * drcSlider = nil;
6736 /* Populate the audio widgets based on the contents of the AudioList array */
6738 NSEnumerator *enumerator = [[chosenPreset objectForKey:@"AudioList"] objectEnumerator];
6740 while (tempObject = [enumerator nextObject])
6745 trackLangPopUp = fAudLang1PopUp;
6746 mixdownPopUp = fAudTrack1MixPopUp;
6747 audiocodecPopUp = fAudTrack1CodecPopUp;
6748 sampleratePopUp = fAudTrack1RatePopUp;
6749 bitratePopUp = fAudTrack1BitratePopUp;
6750 drcSlider = fAudTrack1DrcSlider;
6754 trackLangPopUp = fAudLang2PopUp;
6755 mixdownPopUp = fAudTrack2MixPopUp;
6756 audiocodecPopUp = fAudTrack2CodecPopUp;
6757 sampleratePopUp = fAudTrack2RatePopUp;
6758 bitratePopUp = fAudTrack2BitratePopUp;
6759 drcSlider = fAudTrack2DrcSlider;
6763 trackLangPopUp = fAudLang3PopUp;
6764 mixdownPopUp = fAudTrack3MixPopUp;
6765 audiocodecPopUp = fAudTrack3CodecPopUp;
6766 sampleratePopUp = fAudTrack3RatePopUp;
6767 bitratePopUp = fAudTrack3BitratePopUp;
6768 drcSlider = fAudTrack3DrcSlider;
6772 trackLangPopUp = fAudLang4PopUp;
6773 mixdownPopUp = fAudTrack4MixPopUp;
6774 audiocodecPopUp = fAudTrack4CodecPopUp;
6775 sampleratePopUp = fAudTrack4RatePopUp;
6776 bitratePopUp = fAudTrack4BitratePopUp;
6777 drcSlider = fAudTrack4DrcSlider;
6781 if ([trackLangPopUp indexOfSelectedItem] == 0)
6783 [trackLangPopUp selectItemAtIndex: 1];
6785 [self audioTrackPopUpChanged: trackLangPopUp];
6786 [audiocodecPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioEncoder"]];
6787 /* check our pref for core audio and use it in place of faac if preset is a built in */
6788 if ([[chosenPreset objectForKey:@"Type"] intValue] == 0 &&
6789 [[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES &&
6790 [[tempObject objectForKey:@"AudioEncoder"] isEqualToString: @"AAC (faac)"])
6792 [audiocodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6795 [self audioTrackPopUpChanged: audiocodecPopUp];
6796 [mixdownPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioMixdown"]];
6797 [self audioTrackMixdownChanged: mixdownPopUp];
6798 /* check to see if the selection was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6800 if ([mixdownPopUp selectedItem] == nil)
6802 [self audioTrackPopUpChanged: audiocodecPopUp];
6803 [self writeToActivityLog: "presetSelected mixdown not selected, rerun audioTrackPopUpChanged"];
6805 [sampleratePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioSamplerate"]];
6806 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6807 if (![[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"])
6809 [bitratePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioBitrate"]];
6810 /* check to see if the bitrate selection was available, if not, rerun audioTrackMixdownChanged using the mixdown to just set the
6811 *default mixdown bitrate*/
6812 if ([bitratePopUp selectedItem] == nil)
6814 [self audioTrackMixdownChanged: mixdownPopUp];
6817 [drcSlider setFloatValue:[[tempObject objectForKey:@"AudioTrackDRCSlider"] floatValue]];
6818 [self audioDRCSliderChanged: drcSlider];
6821 /* 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,
6822 * if not we will set the track to "None". Track 1 is allowed to mixdown to a suitable DPL2 mix if we cannot passthru */
6826 /* 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". */
6827 if (([[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"] || [[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"DTS Passthru"]) && [trackLangPopUp indexOfSelectedItem] != 0)
6829 hb_audio_config_t * audio;
6830 /* get the audio source audio codec */
6831 audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, [trackLangPopUp indexOfSelectedItem] - 1 );
6832 if (audio != NULL && [[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"] && audio->in.codec != HB_ACODEC_AC3 ||
6833 [[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"DTS Passthru"] && audio->in.codec != HB_ACODEC_DCA )
6835 /* We have a preset using ac3 passthru but no ac3 source audio, so set the track to "None" and bail */
6836 if ([[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"])
6838 [self writeToActivityLog: "Preset calls for AC3 Pass thru ..."];
6840 if ([[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"DTS Passthru"])
6842 [self writeToActivityLog: "Preset calls for DTS Pass thru ..."];
6844 [self writeToActivityLog: "No matching source codec, setting track %d to None", i];
6845 [trackLangPopUp selectItemAtIndex: 0];
6846 [self audioTrackPopUpChanged: trackLangPopUp];
6852 /* We now cleanup any extra audio tracks that may have been previously set if we need to */
6856 [fAudLang4PopUp selectItemAtIndex: 0];
6857 [self audioTrackPopUpChanged: fAudLang4PopUp];
6861 [fAudLang3PopUp selectItemAtIndex: 0];
6862 [self audioTrackPopUpChanged: fAudLang3PopUp];
6866 [fAudLang2PopUp selectItemAtIndex: 0];
6867 [self audioTrackPopUpChanged: fAudLang2PopUp];
6875 if ([chosenPreset objectForKey:@"Audio1Track"] > 0)
6877 if ([fAudLang1PopUp indexOfSelectedItem] == 0)
6879 [fAudLang1PopUp selectItemAtIndex: 1];
6881 [self audioTrackPopUpChanged: fAudLang1PopUp];
6882 [fAudTrack1CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Encoder"]];
6883 /* check our pref for core audio and use it in place of faac if preset is built in */
6884 if ([[chosenPreset objectForKey:@"Type"] intValue] == 0 &&
6885 [[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES &&
6886 [[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString: @"AAC (faac)"])
6888 [fAudTrack1CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6891 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
6892 [fAudTrack1MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Mixdown"]];
6893 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6895 if ([fAudTrack1MixPopUp selectedItem] == nil)
6897 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
6899 [fAudTrack1RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Samplerate"]];
6900 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6901 if (![[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString:@"AC3 Passthru"])
6903 [fAudTrack1BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Bitrate"]];
6905 [fAudTrack1DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio1TrackDRCSlider"] floatValue]];
6906 [self audioDRCSliderChanged: fAudTrack1DrcSlider];
6909 if ([chosenPreset objectForKey:@"Audio2Track"] > 0)
6911 if ([fAudLang2PopUp indexOfSelectedItem] == 0)
6913 [fAudLang2PopUp selectItemAtIndex: 1];
6915 [self audioTrackPopUpChanged: fAudLang2PopUp];
6916 [fAudTrack2CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Encoder"]];
6917 /* check our pref for core audio and use it in place of faac if preset is built in */
6918 if ([[chosenPreset objectForKey:@"Type"] intValue] == 0 &&
6919 [[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES &&
6920 [[chosenPreset objectForKey:@"Audio2Encoder"] isEqualToString: @"AAC (faac)"])
6922 [fAudTrack2CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6924 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
6925 [fAudTrack2MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Mixdown"]];
6926 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6928 if ([fAudTrack2MixPopUp selectedItem] == nil)
6930 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
6932 [fAudTrack2RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Samplerate"]];
6933 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6934 if (![[chosenPreset objectForKey:@"Audio2Encoder"] isEqualToString:@"AC3 Passthru"])
6936 [fAudTrack2BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Bitrate"]];
6938 [fAudTrack2DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio2TrackDRCSlider"] floatValue]];
6939 [self audioDRCSliderChanged: fAudTrack2DrcSlider];
6941 if ([chosenPreset objectForKey:@"Audio3Track"] > 0)
6943 if ([fAudLang3PopUp indexOfSelectedItem] == 0)
6945 [fAudLang3PopUp selectItemAtIndex: 1];
6947 [self audioTrackPopUpChanged: fAudLang3PopUp];
6948 [fAudTrack3CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Encoder"]];
6949 /* check our pref for core audio and use it in place of faac if preset is built in */
6950 if ([[chosenPreset objectForKey:@"Type"] intValue] == 0 &&
6951 [[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES &&
6952 [[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AAC (faac)"])
6954 [fAudTrack3CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6956 [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
6957 [fAudTrack3MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Mixdown"]];
6958 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6960 if ([fAudTrack3MixPopUp selectedItem] == nil)
6962 [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
6964 [fAudTrack3RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Samplerate"]];
6965 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6966 if (![[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AC3 Passthru"])
6968 [fAudTrack3BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Bitrate"]];
6970 [fAudTrack3DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio3TrackDRCSlider"] floatValue]];
6971 [self audioDRCSliderChanged: fAudTrack3DrcSlider];
6973 if ([chosenPreset objectForKey:@"Audio4Track"] > 0)
6975 if ([fAudLang4PopUp indexOfSelectedItem] == 0)
6977 [fAudLang4PopUp selectItemAtIndex: 1];
6979 [self audioTrackPopUpChanged: fAudLang4PopUp];
6980 [fAudTrack4CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Encoder"]];
6981 /* check our pref for core audio and use it in place of faac if preset is built in */
6982 if ([[chosenPreset objectForKey:@"Type"] intValue] == 0 &&
6983 [[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES &&
6984 [[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString: @"AAC (faac)"])
6986 [fAudTrack4CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6988 [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
6989 [fAudTrack4MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Mixdown"]];
6990 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6992 if ([fAudTrack4MixPopUp selectedItem] == nil)
6994 [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
6996 [fAudTrack4RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Samplerate"]];
6997 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6998 if (![[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString:@"AC3 Passthru"])
7000 [fAudTrack4BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Bitrate"]];
7002 [fAudTrack4DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio4TrackDRCSlider"] floatValue]];
7003 [self audioDRCSliderChanged: fAudTrack4DrcSlider];
7006 /* We now cleanup any extra audio tracks that may have been previously set if we need to */
7008 if (![chosenPreset objectForKey:@"Audio2Track"] || [chosenPreset objectForKey:@"Audio2Track"] == 0)
7010 [fAudLang2PopUp selectItemAtIndex: 0];
7011 [self audioTrackPopUpChanged: fAudLang2PopUp];
7013 if (![chosenPreset objectForKey:@"Audio3Track"] || [chosenPreset objectForKey:@"Audio3Track"] > 0)
7015 [fAudLang3PopUp selectItemAtIndex: 0];
7016 [self audioTrackPopUpChanged: fAudLang3PopUp];
7018 if (![chosenPreset objectForKey:@"Audio4Track"] || [chosenPreset objectForKey:@"Audio4Track"] > 0)
7020 [fAudLang4PopUp selectItemAtIndex: 0];
7021 [self audioTrackPopUpChanged: fAudLang4PopUp];
7026 [fSubPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Subtitles"]];
7027 /* Forced Subtitles */
7028 [fSubForcedCheck setState:[[chosenPreset objectForKey:@"SubtitlesForced"] intValue]];
7030 /* Picture Settings */
7031 /* Note: objectForKey:@"UsesPictureSettings" refers to picture size, which encompasses:
7032 * height, width, keep ar, anamorphic and crop settings.
7033 * picture filters are handled separately below.
7035 /* Check to see if the objectForKey:@"UsesPictureSettings is greater than 0, as 0 means use picture sizing "None"
7036 * ( 2 is use max for source and 1 is use exact size when the preset was created ) and the
7037 * preset completely ignores any picture sizing values in the preset.
7039 if ([[chosenPreset objectForKey:@"UsesPictureSettings"] intValue] > 0)
7041 hb_job_t * job = fTitle->job;
7043 /* If Cropping is set to custom, then recall all four crop values from
7044 when the preset was created and apply them */
7045 if ([[chosenPreset objectForKey:@"PictureAutoCrop"] intValue] == 0)
7047 [fPictureController setAutoCrop:NO];
7049 /* Here we use the custom crop values saved at the time the preset was saved */
7050 job->crop[0] = [[chosenPreset objectForKey:@"PictureTopCrop"] intValue];
7051 job->crop[1] = [[chosenPreset objectForKey:@"PictureBottomCrop"] intValue];
7052 job->crop[2] = [[chosenPreset objectForKey:@"PictureLeftCrop"] intValue];
7053 job->crop[3] = [[chosenPreset objectForKey:@"PictureRightCrop"] intValue];
7056 else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */
7058 [fPictureController setAutoCrop:YES];
7059 /* Here we use the auto crop values determined right after scan */
7060 job->crop[0] = AutoCropTop;
7061 job->crop[1] = AutoCropBottom;
7062 job->crop[2] = AutoCropLeft;
7063 job->crop[3] = AutoCropRight;
7068 if ([chosenPreset objectForKey:@"PictureModulus"])
7070 job->modulus = [[chosenPreset objectForKey:@"PictureModulus"] intValue];
7077 /* Check to see if the objectForKey:@"UsesPictureSettings is 2 which is "Use Max for the source */
7078 if ([[chosenPreset objectForKey:@"UsesPictureSettings"] intValue] == 2 || [[chosenPreset objectForKey:@"UsesMaxPictureSettings"] intValue] == 1)
7080 /* Use Max Picture settings for whatever the dvd is.*/
7081 [self revertPictureSizeToMax:nil];
7082 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"] intValue];
7083 if (job->keep_ratio == 1)
7085 hb_fix_aspect( job, HB_KEEP_WIDTH );
7086 if( job->height > fTitle->height )
7088 job->height = fTitle->height;
7089 hb_fix_aspect( job, HB_KEEP_HEIGHT );
7092 job->anamorphic.mode = [[chosenPreset objectForKey:@"PicturePAR"] intValue];
7094 else // /* If not 0 or 2 we assume objectForKey:@"UsesPictureSettings is 1 which is "Use picture sizing from when the preset was set" */
7096 /* we check to make sure the presets width/height does not exceed the sources width/height */
7097 if (fTitle->width < [[chosenPreset objectForKey:@"PictureWidth"] intValue] || fTitle->height < [[chosenPreset objectForKey:@"PictureHeight"] intValue])
7099 /* if so, then we use the sources height and width to avoid scaling up */
7100 //job->width = fTitle->width;
7101 //job->height = fTitle->height;
7102 [self revertPictureSizeToMax:nil];
7104 else // source width/height is >= the preset height/width
7106 /* we can go ahead and use the presets values for height and width */
7107 job->width = [[chosenPreset objectForKey:@"PictureWidth"] intValue];
7108 job->height = [[chosenPreset objectForKey:@"PictureHeight"] intValue];
7110 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"] intValue];
7111 if (job->keep_ratio == 1)
7113 hb_fix_aspect( job, HB_KEEP_WIDTH );
7114 if( job->height > fTitle->height )
7116 job->height = fTitle->height;
7117 hb_fix_aspect( job, HB_KEEP_HEIGHT );
7120 job->anamorphic.mode = [[chosenPreset objectForKey:@"PicturePAR"] intValue];
7126 /* If the preset has an objectForKey:@"UsesPictureFilters", and handle the filters here */
7127 if ([chosenPreset objectForKey:@"UsesPictureFilters"] && [[chosenPreset objectForKey:@"UsesPictureFilters"] intValue] > 0)
7131 /* We only allow *either* Decomb or Deinterlace. So check for the PictureDecombDeinterlace key.
7132 * also, older presets may not have this key, in which case we also check to see if that preset had PictureDecomb
7133 * specified, in which case we use decomb and ignore any possible Deinterlace settings as using both was less than
7136 [fPictureController setUseDecomb:1];
7137 [fPictureController setDecomb:0];
7138 [fPictureController setDeinterlace:0];
7139 if ([[chosenPreset objectForKey:@"PictureDecombDeinterlace"] intValue] == 1 || [[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0)
7141 /* we are using decomb */
7143 if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0)
7145 [fPictureController setDecomb:[[chosenPreset objectForKey:@"PictureDecomb"] intValue]];
7147 /* if we are using "Custom" in the decomb setting, also set the custom string*/
7148 if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] == 1)
7150 [fPictureController setDecombCustomString:[chosenPreset objectForKey:@"PictureDecombCustom"]];
7156 /* We are using Deinterlace */
7158 if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] > 0)
7160 [fPictureController setUseDecomb:0];
7161 [fPictureController setDeinterlace:[[chosenPreset objectForKey:@"PictureDeinterlace"] intValue]];
7162 /* if we are using "Custom" in the deinterlace setting, also set the custom string*/
7163 if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] == 1)
7165 [fPictureController setDeinterlaceCustomString:[chosenPreset objectForKey:@"PictureDeinterlaceCustom"]];
7172 if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] > 0)
7174 [fPictureController setDetelecine:[[chosenPreset objectForKey:@"PictureDetelecine"] intValue]];
7175 /* if we are using "Custom" in the detelecine setting, also set the custom string*/
7176 if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 1)
7178 [fPictureController setDetelecineCustomString:[chosenPreset objectForKey:@"PictureDetelecineCustom"]];
7183 [fPictureController setDetelecine:0];
7187 if ([[chosenPreset objectForKey:@"PictureDenoise"] intValue] > 0)
7189 [fPictureController setDenoise:[[chosenPreset objectForKey:@"PictureDenoise"] intValue]];
7190 /* if we are using "Custom" in the denoise setting, also set the custom string*/
7191 if ([[chosenPreset objectForKey:@"PictureDenoise"] intValue] == 1)
7193 [fPictureController setDenoiseCustomString:[chosenPreset objectForKey:@"PictureDenoiseCustom"]];
7198 [fPictureController setDenoise:0];
7202 if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1)
7204 /* if its a one, then its the old on/off deblock, set on to 5*/
7205 [fPictureController setDeblock:5];
7209 /* use the settings intValue */
7210 [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]];
7213 if ([[chosenPreset objectForKey:@"VideoGrayScale"] intValue] == 1)
7215 [fPictureController setGrayscale:1];
7219 [fPictureController setGrayscale:0];
7222 /* we call SetTitle: in fPictureController so we get an instant update in the Picture Settings window */
7223 [fPictureController SetTitle:fTitle];
7224 [fPictureController SetTitle:fTitle];
7225 [self calculatePictureSizing:nil];
7231 #pragma mark Manage Presets
7233 - (void) loadPresets {
7234 /* We declare the default NSFileManager into fileManager */
7235 NSFileManager * fileManager = [NSFileManager defaultManager];
7236 /*We define the location of the user presets file */
7237 UserPresetsFile = @"~/Library/Application Support/HandBrake/UserPresets.plist";
7238 UserPresetsFile = [[UserPresetsFile stringByExpandingTildeInPath]retain];
7239 /* We check for the presets.plist */
7240 if ([fileManager fileExistsAtPath:UserPresetsFile] == 0)
7242 [fileManager createFileAtPath:UserPresetsFile contents:nil attributes:nil];
7245 UserPresets = [[NSMutableArray alloc] initWithContentsOfFile:UserPresetsFile];
7246 if (nil == UserPresets)
7248 UserPresets = [[NSMutableArray alloc] init];
7249 [self addFactoryPresets:nil];
7251 [fPresetsOutlineView reloadData];
7253 [self checkBuiltInsForUpdates];
7256 - (void) checkBuiltInsForUpdates {
7258 BOOL updateBuiltInPresets = NO;
7260 NSEnumerator *enumerator = [UserPresets objectEnumerator];
7262 while (tempObject = [enumerator nextObject])
7264 /* iterate through the built in presets to see if any have an old build number */
7265 NSMutableDictionary *thisPresetDict = tempObject;
7266 /*Key Type == 0 is built in, and key PresetBuildNumber is the build number it was created with */
7267 if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0)
7269 if (![thisPresetDict objectForKey:@"PresetBuildNumber"] || [[thisPresetDict objectForKey:@"PresetBuildNumber"] intValue] < [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue])
7271 updateBuiltInPresets = YES;
7276 /* if we have built in presets to update, then do so AlertBuiltInPresetUpdate*/
7277 if ( updateBuiltInPresets == YES)
7279 if( [[NSUserDefaults standardUserDefaults] boolForKey:@"AlertBuiltInPresetUpdate"] == YES)
7281 /* Show an alert window that built in presets will be updated */
7282 /*On Screen Notification*/
7285 status = NSRunAlertPanel(@"HandBrake has determined your built in presets are out of date...",@"HandBrake will now update your built-in presets.", @"OK", nil, nil);
7286 [NSApp requestUserAttention:NSCriticalRequest];
7288 /* when alert is dismissed, go ahead and update the built in presets */
7289 [self addFactoryPresets:nil];
7295 - (IBAction) showAddPresetPanel: (id) sender
7297 /* Deselect the currently selected Preset if there is one*/
7298 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
7300 /* Populate the preset picture settings popup here */
7301 [fPresetNewPicSettingsPopUp removeAllItems];
7302 [fPresetNewPicSettingsPopUp addItemWithTitle:@"None"];
7303 [fPresetNewPicSettingsPopUp addItemWithTitle:@"Current"];
7304 [fPresetNewPicSettingsPopUp addItemWithTitle:@"Source Maximum (post source scan)"];
7305 [fPresetNewPicSettingsPopUp selectItemAtIndex: 0];
7306 /* Uncheck the preset use filters checkbox */
7307 [fPresetNewPicFiltersCheck setState:NSOffState];
7308 // fPresetNewFolderCheck
7309 [fPresetNewFolderCheck setState:NSOffState];
7310 /* Erase info from the input fields*/
7311 [fPresetNewName setStringValue: @""];
7312 [fPresetNewDesc setStringValue: @""];
7313 /* Show the panel */
7314 [NSApp beginSheet:fAddPresetPanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
7317 - (IBAction) closeAddPresetPanel: (id) sender
7319 [NSApp endSheet: fAddPresetPanel];
7320 [fAddPresetPanel orderOut: self];
7323 - (IBAction)addUserPreset:(id)sender
7325 if (![[fPresetNewName stringValue] length])
7326 NSRunAlertPanel(@"Warning!", @"You need to insert a name for the preset.", @"OK", nil , nil);
7329 /* Here we create a custom user preset */
7330 [UserPresets addObject:[self createPreset]];
7333 [self closeAddPresetPanel:nil];
7340 /* We Reload the New Table data for presets */
7341 [fPresetsOutlineView reloadData];
7342 /* We save all of the preset data here */
7350 /* We Sort the Presets By Factory or Custom */
7351 NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type"
7352 ascending:YES] autorelease];
7353 /* We Sort the Presets Alphabetically by name We do not use this now as we have drag and drop*/
7355 NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName"
7356 ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease];
7357 //NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil];
7360 /* Since we can drag and drop our custom presets, lets just sort by type and not name */
7361 NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,nil];
7362 NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors];
7363 [UserPresets setArray:sortedArray];
7368 - (IBAction)insertPreset:(id)sender
7370 int index = [fPresetsOutlineView selectedRow];
7371 [UserPresets insertObject:[self createPreset] atIndex:index];
7372 [fPresetsOutlineView reloadData];
7376 - (NSDictionary *)createPreset
7378 NSMutableDictionary *preset = [[NSMutableDictionary alloc] init];
7379 /* Preset build number */
7380 [preset setObject:[NSString stringWithFormat: @"%d", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]] forKey:@"PresetBuildNumber"];
7381 [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
7382 /* Get the New Preset Name from the field in the AddPresetPanel */
7383 [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
7384 /* Set whether or not this is to be a folder fPresetNewFolderCheck*/
7385 [preset setObject:[NSNumber numberWithBool:[fPresetNewFolderCheck state]] forKey:@"Folder"];
7386 /*Set whether or not this is a user preset or factory 0 is factory, 1 is user*/
7387 [preset setObject:[NSNumber numberWithInt:1] forKey:@"Type"];
7388 /*Set whether or not this is default, at creation set to 0*/
7389 [preset setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
7390 if ([fPresetNewFolderCheck state] == YES)
7392 /* initialize and set an empty array for children here since we are a new folder */
7393 NSMutableArray *childrenArray = [[NSMutableArray alloc] init];
7394 [preset setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"];
7395 [childrenArray autorelease];
7397 else // we are not creating a preset folder, so we go ahead with the rest of the preset info
7399 /*Get the whether or not to apply pic Size and Cropping (includes Anamorphic)*/
7400 [preset setObject:[NSNumber numberWithInt:[fPresetNewPicSettingsPopUp indexOfSelectedItem]] forKey:@"UsesPictureSettings"];
7401 /* Get whether or not to use the current Picture Filter settings for the preset */
7402 [preset setObject:[NSNumber numberWithInt:[fPresetNewPicFiltersCheck state]] forKey:@"UsesPictureFilters"];
7404 /* Get New Preset Description from the field in the AddPresetPanel*/
7405 [preset setObject:[fPresetNewDesc stringValue] forKey:@"PresetDescription"];
7407 [preset setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"];
7408 /* Chapter Markers fCreateChapterMarkers*/
7409 [preset setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"];
7410 /* Allow Mpeg4 64 bit formatting +4GB file sizes */
7411 [preset setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"];
7412 /* Mux mp4 with http optimization */
7413 [preset setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"];
7414 /* Add iPod uuid atom */
7415 [preset setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"];
7419 [preset setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"];
7420 /* x264 Option String */
7421 [preset setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"];
7423 [preset setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"];
7424 [preset setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
7425 [preset setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"];
7426 [preset setObject:[NSNumber numberWithFloat:[fVidQualityRFField floatValue]] forKey:@"VideoQualitySlider"];
7428 /* Video framerate */
7429 if ([fVidRatePopUp indexOfSelectedItem] == 0) // Same as source is selected
7431 [preset setObject:@"Same as source" forKey:@"VideoFramerate"];
7433 else // we can record the actual titleOfSelectedItem
7435 [preset setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"];
7438 /* 2 Pass Encoding */
7439 [preset setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"];
7440 /* Turbo 2 pass Encoding fVidTurboPassCheck*/
7441 [preset setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"];
7442 /*Picture Settings*/
7443 hb_job_t * job = fTitle->job;
7444 /* Picture Sizing */
7445 /* Use Max Picture settings for whatever the dvd is.*/
7446 [preset setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
7447 [preset setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
7448 [preset setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
7449 [preset setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
7450 [preset setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"];
7451 [preset setObject:[NSNumber numberWithInt:fTitle->job->modulus] forKey:@"PictureModulus"];
7453 /* Set crop settings here */
7454 [preset setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
7455 [preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
7456 [preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
7457 [preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
7458 [preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
7460 /* Picture Filters */
7461 [preset setObject:[NSNumber numberWithInt:[fPictureController useDecomb]] forKey:@"PictureDecombDeinterlace"];
7462 [preset setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"];
7463 [preset setObject:[fPictureController deinterlaceCustomString] forKey:@"PictureDeinterlaceCustom"];
7464 [preset setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"];
7465 [preset setObject:[fPictureController detelecineCustomString] forKey:@"PictureDetelecineCustom"];
7466 [preset setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"];
7467 [preset setObject:[fPictureController denoiseCustomString] forKey:@"PictureDenoiseCustom"];
7468 [preset setObject:[NSNumber numberWithInt:[fPictureController deblock]] forKey:@"PictureDeblock"];
7469 [preset setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"];
7470 [preset setObject:[fPictureController decombCustomString] forKey:@"PictureDecombCustom"];
7471 [preset setObject:[NSNumber numberWithInt:[fPictureController grayscale]] forKey:@"VideoGrayScale"];
7474 NSMutableArray *audioListArray = [[NSMutableArray alloc] init];
7475 /* we actually call the methods for the nests here */
7476 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
7478 NSMutableDictionary *audioTrack1Array = [[NSMutableDictionary alloc] init];
7479 [audioTrack1Array setObject:[NSNumber numberWithInt:[fAudLang1PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
7480 [audioTrack1Array setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
7481 [audioTrack1Array setObject:[fAudTrack1CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
7482 [audioTrack1Array setObject:[fAudTrack1MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
7483 [audioTrack1Array setObject:[fAudTrack1RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
7484 [audioTrack1Array setObject:[fAudTrack1BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
7485 [audioTrack1Array setObject:[NSNumber numberWithFloat:[fAudTrack1DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
7486 [audioTrack1Array autorelease];
7487 [audioListArray addObject:audioTrack1Array];
7490 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
7492 NSMutableDictionary *audioTrack2Array = [[NSMutableDictionary alloc] init];
7493 [audioTrack2Array setObject:[NSNumber numberWithInt:[fAudLang2PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
7494 [audioTrack2Array setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
7495 [audioTrack2Array setObject:[fAudTrack2CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
7496 [audioTrack2Array setObject:[fAudTrack2MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
7497 [audioTrack2Array setObject:[fAudTrack2RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
7498 [audioTrack2Array setObject:[fAudTrack2BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
7499 [audioTrack2Array setObject:[NSNumber numberWithFloat:[fAudTrack2DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
7500 [audioTrack2Array autorelease];
7501 [audioListArray addObject:audioTrack2Array];
7504 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
7506 NSMutableDictionary *audioTrack3Array = [[NSMutableDictionary alloc] init];
7507 [audioTrack3Array setObject:[NSNumber numberWithInt:[fAudLang3PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
7508 [audioTrack3Array setObject:[fAudLang3PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
7509 [audioTrack3Array setObject:[fAudTrack3CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
7510 [audioTrack3Array setObject:[fAudTrack3MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
7511 [audioTrack3Array setObject:[fAudTrack3RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
7512 [audioTrack3Array setObject:[fAudTrack3BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
7513 [audioTrack3Array setObject:[NSNumber numberWithFloat:[fAudTrack3DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
7514 [audioTrack3Array autorelease];
7515 [audioListArray addObject:audioTrack3Array];
7518 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
7520 NSMutableDictionary *audioTrack4Array = [[NSMutableDictionary alloc] init];
7521 [audioTrack4Array setObject:[NSNumber numberWithInt:[fAudLang4PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
7522 [audioTrack4Array setObject:[fAudLang4PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
7523 [audioTrack4Array setObject:[fAudTrack4CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
7524 [audioTrack4Array setObject:[fAudTrack4MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
7525 [audioTrack4Array setObject:[fAudTrack4RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
7526 [audioTrack4Array setObject:[fAudTrack4BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
7527 [audioTrack4Array setObject:[NSNumber numberWithFloat:[fAudTrack4DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
7528 [audioTrack4Array autorelease];
7529 [audioListArray addObject:audioTrack4Array];
7533 [preset setObject:[NSMutableArray arrayWithArray: audioListArray] forKey:@"AudioList"];
7536 /* Temporarily remove subtitles from creating a new preset as it has to be converted over to use the new
7537 * subititle array code. */
7539 //[preset setObject:[fSubPopUp titleOfSelectedItem] forKey:@"Subtitles"];
7540 /* Forced Subtitles */
7541 //[preset setObject:[NSNumber numberWithInt:[fSubForcedCheck state]] forKey:@"SubtitlesForced"];
7543 [preset autorelease];
7550 [UserPresets writeToFile:UserPresetsFile atomically:YES];
7551 /* We get the default preset in case it changed */
7552 [self getDefaultPresets:nil];
7556 - (IBAction)deletePreset:(id)sender
7560 if ( [fPresetsOutlineView numberOfSelectedRows] == 0 )
7564 /* Alert user before deleting preset */
7566 status = NSRunAlertPanel(@"Warning!", @"Are you sure that you want to delete the selected preset?", @"OK", @"Cancel", nil);
7568 if ( status == NSAlertDefaultReturn )
7570 int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
7571 NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
7572 NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod];
7574 NSEnumerator *enumerator;
7575 NSMutableArray *presetsArrayToMod;
7576 NSMutableArray *tempArray;
7578 /* If we are a root level preset, we are modding the UserPresets array */
7579 if (presetToModLevel == 0)
7581 presetsArrayToMod = UserPresets;
7583 else // We have a parent preset, so we modify the chidren array object for key
7585 presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"];
7588 enumerator = [presetsArrayToMod objectEnumerator];
7589 tempArray = [NSMutableArray array];
7591 while (tempObject = [enumerator nextObject])
7593 NSDictionary *thisPresetDict = tempObject;
7594 if (thisPresetDict == presetToMod)
7596 [tempArray addObject:tempObject];
7600 [presetsArrayToMod removeObjectsInArray:tempArray];
7601 [fPresetsOutlineView reloadData];
7608 #pragma mark Import Export Preset(s)
7610 - (IBAction) browseExportPresetFile: (id) sender
7612 /* Open a panel to let the user choose where and how to save the export file */
7613 NSSavePanel * panel = [NSSavePanel savePanel];
7614 /* We get the current file name and path from the destination field here */
7615 NSString *defaultExportDirectory = [NSString stringWithFormat: @"%@/Desktop/", NSHomeDirectory()];
7617 [panel beginSheetForDirectory: defaultExportDirectory file: @"HB_Export.plist"
7618 modalForWindow: fWindow modalDelegate: self
7619 didEndSelector: @selector( browseExportPresetFileDone:returnCode:contextInfo: )
7623 - (void) browseExportPresetFileDone: (NSSavePanel *) sheet
7624 returnCode: (int) returnCode contextInfo: (void *) contextInfo
7626 if( returnCode == NSOKButton )
7628 NSString *presetExportDirectory = [[sheet filename] stringByDeletingLastPathComponent];
7629 NSString *exportPresetsFile = [sheet filename];
7630 [[NSUserDefaults standardUserDefaults] setObject:presetExportDirectory forKey:@"LastPresetExportDirectory"];
7631 /* We check for the presets.plist */
7632 if ([[NSFileManager defaultManager] fileExistsAtPath:exportPresetsFile] == 0)
7634 [[NSFileManager defaultManager] createFileAtPath:exportPresetsFile contents:nil attributes:nil];
7636 NSMutableArray * presetsToExport = [[NSMutableArray alloc] initWithContentsOfFile:exportPresetsFile];
7637 if (nil == presetsToExport)
7639 presetsToExport = [[NSMutableArray alloc] init];
7641 /* now get and add selected presets to export */
7644 if ([fPresetsOutlineView selectedRow] >= 0 && [[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] != 1)
7646 [presetsToExport addObject:[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
7647 [presetsToExport writeToFile:exportPresetsFile atomically:YES];
7655 - (IBAction) browseImportPresetFile: (id) sender
7658 NSOpenPanel * panel;
7660 panel = [NSOpenPanel openPanel];
7661 [panel setAllowsMultipleSelection: NO];
7662 [panel setCanChooseFiles: YES];
7663 [panel setCanChooseDirectories: NO ];
7664 NSString * sourceDirectory;
7665 if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastPresetImportDirectory"])
7667 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastPresetImportDirectory"];
7671 sourceDirectory = @"~/Desktop";
7672 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
7674 /* we open up the browse sources sheet here and call for browseSourcesDone after the sheet is closed
7675 * to evaluate whether we want to specify a title, we pass the sender in the contextInfo variable
7677 /* set this for allowed file types, not sure if we should allow xml or not */
7678 NSArray *fileTypes = [NSArray arrayWithObjects:@"plist", @"xml", nil];
7679 [panel beginSheetForDirectory: sourceDirectory file: nil types: fileTypes
7680 modalForWindow: fWindow modalDelegate: self
7681 didEndSelector: @selector( browseImportPresetDone:returnCode:contextInfo: )
7682 contextInfo: sender];
7685 - (void) browseImportPresetDone: (NSSavePanel *) sheet
7686 returnCode: (int) returnCode contextInfo: (void *) contextInfo
7688 if( returnCode == NSOKButton )
7690 NSString *importPresetsDirectory = [[sheet filename] stringByDeletingLastPathComponent];
7691 NSString *importPresetsFile = [sheet filename];
7692 [[NSUserDefaults standardUserDefaults] setObject:importPresetsDirectory forKey:@"LastPresetImportDirectory"];
7693 /* NOTE: here we need to do some sanity checking to verify we do not hose up our presets file */
7694 NSMutableArray * presetsToImport = [[NSMutableArray alloc] initWithContentsOfFile:importPresetsFile];
7695 /* iterate though the new array of presets to import and add them to our presets array */
7697 NSEnumerator *enumerator = [presetsToImport objectEnumerator];
7699 while (tempObject = [enumerator nextObject])
7701 /* make any changes to the incoming preset we see fit */
7702 /* make sure the incoming preset is not tagged as default */
7703 [tempObject setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
7704 /* prepend "(imported) to the name of the incoming preset for clarification since it can be changed */
7705 NSString * prependedName = [@"(import) " stringByAppendingString:[tempObject objectForKey:@"PresetName"]] ;
7706 [tempObject setObject:prependedName forKey:@"PresetName"];
7708 /* actually add the new preset to our presets array */
7709 [UserPresets addObject:tempObject];
7712 [presetsToImport autorelease];
7720 #pragma mark Manage Default Preset
7722 - (IBAction)getDefaultPresets:(id)sender
7724 presetHbDefault = nil;
7725 presetUserDefault = nil;
7726 presetUserDefaultParent = nil;
7727 presetUserDefaultParentParent = nil;
7728 NSMutableDictionary *presetHbDefaultParent = nil;
7729 NSMutableDictionary *presetHbDefaultParentParent = nil;
7732 BOOL userDefaultFound = NO;
7733 presetCurrentBuiltInCount = 0;
7734 /* First we iterate through the root UserPresets array to check for defaults */
7735 NSEnumerator *enumerator = [UserPresets objectEnumerator];
7737 while (tempObject = [enumerator nextObject])
7739 NSMutableDictionary *thisPresetDict = tempObject;
7740 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7742 presetHbDefault = thisPresetDict;
7744 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7746 presetUserDefault = thisPresetDict;
7747 userDefaultFound = YES;
7749 if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset
7751 presetCurrentBuiltInCount++; // <--increment the current number of built in presets
7755 /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */
7756 if ([thisPresetDict objectForKey:@"ChildrenArray"])
7758 NSMutableDictionary *thisPresetDictParent = thisPresetDict;
7759 NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7761 while (tempObject = [enumerator nextObject])
7763 NSMutableDictionary *thisPresetDict = tempObject;
7764 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7766 presetHbDefault = thisPresetDict;
7767 presetHbDefaultParent = thisPresetDictParent;
7769 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7771 presetUserDefault = thisPresetDict;
7772 presetUserDefaultParent = thisPresetDictParent;
7773 userDefaultFound = YES;
7776 /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */
7777 if ([thisPresetDict objectForKey:@"ChildrenArray"])
7779 NSMutableDictionary *thisPresetDictParentParent = thisPresetDict;
7780 NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7782 while (tempObject = [enumerator nextObject])
7784 NSMutableDictionary *thisPresetDict = tempObject;
7785 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7787 presetHbDefault = thisPresetDict;
7788 presetHbDefaultParent = thisPresetDictParent;
7789 presetHbDefaultParentParent = thisPresetDictParentParent;
7791 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7793 presetUserDefault = thisPresetDict;
7794 presetUserDefaultParent = thisPresetDictParent;
7795 presetUserDefaultParentParent = thisPresetDictParentParent;
7796 userDefaultFound = YES;
7805 /* check to see if a user specified preset was found, if not then assign the parents for
7806 * the presetHbDefault so that we can open the parents for the nested presets
7808 if (userDefaultFound == NO)
7810 presetUserDefaultParent = presetHbDefaultParent;
7811 presetUserDefaultParentParent = presetHbDefaultParentParent;
7815 - (IBAction)setDefaultPreset:(id)sender
7817 /* We need to determine if the item is a folder */
7818 if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] == 1)
7824 NSEnumerator *enumerator = [UserPresets objectEnumerator];
7826 /* First make sure the old user specified default preset is removed */
7827 while (tempObject = [enumerator nextObject])
7829 NSMutableDictionary *thisPresetDict = tempObject;
7830 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7832 [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
7835 /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */
7836 if ([thisPresetDict objectForKey:@"ChildrenArray"])
7838 NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7841 while (tempObject = [enumerator nextObject])
7843 NSMutableDictionary *thisPresetDict1 = tempObject;
7844 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7846 [[[thisPresetDict objectForKey:@"ChildrenArray"] objectAtIndex:ii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
7848 /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */
7849 if ([thisPresetDict1 objectForKey:@"ChildrenArray"])
7851 NSEnumerator *enumerator = [[thisPresetDict1 objectForKey:@"ChildrenArray"] objectEnumerator];
7854 while (tempObject = [enumerator nextObject])
7856 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7858 [[[thisPresetDict1 objectForKey:@"ChildrenArray"] objectAtIndex:iii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
7871 int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
7872 NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
7873 NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod];
7876 NSMutableArray *presetsArrayToMod;
7877 NSMutableArray *tempArray;
7879 /* If we are a root level preset, we are modding the UserPresets array */
7880 if (presetToModLevel == 0)
7882 presetsArrayToMod = UserPresets;
7884 else // We have a parent preset, so we modify the chidren array object for key
7886 presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"];
7889 enumerator = [presetsArrayToMod objectEnumerator];
7890 tempArray = [NSMutableArray array];
7892 while (tempObject = [enumerator nextObject])
7894 NSDictionary *thisPresetDict = tempObject;
7895 if (thisPresetDict == presetToMod)
7897 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 2
7899 [[presetsArrayToMod objectAtIndex:iiii] setObject:[NSNumber numberWithInt:2] forKey:@"Default"];
7906 /* We save all of the preset data here */
7908 /* We Reload the New Table data for presets */
7909 [fPresetsOutlineView reloadData];
7912 - (IBAction)selectDefaultPreset:(id)sender
7914 NSMutableDictionary *presetToMod;
7915 /* if there is a user specified default, we use it */
7916 if (presetUserDefault)
7918 presetToMod = presetUserDefault;
7920 else if (presetHbDefault) //else we use the built in default presetHbDefault
7922 presetToMod = presetHbDefault;
7929 if (presetUserDefaultParent != nil)
7931 [fPresetsOutlineView expandItem:presetUserDefaultParent];
7934 if (presetUserDefaultParentParent != nil)
7936 [fPresetsOutlineView expandItem:presetUserDefaultParentParent];
7940 [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[fPresetsOutlineView rowForItem: presetToMod]] byExtendingSelection:NO];
7941 [self selectPreset:nil];
7946 #pragma mark Manage Built In Presets
7949 - (IBAction)deleteFactoryPresets:(id)sender
7952 NSEnumerator *enumerator = [UserPresets objectEnumerator];
7956 NSMutableArray *tempArray;
7959 tempArray = [NSMutableArray array];
7960 /* we look here to see if the preset is we move on to the next one */
7961 while ( tempObject = [enumerator nextObject] )
7963 /* if the preset is "Factory" then we put it in the array of
7964 presets to delete */
7965 if ([[tempObject objectForKey:@"Type"] intValue] == 0)
7967 [tempArray addObject:tempObject];
7971 [UserPresets removeObjectsInArray:tempArray];
7972 [fPresetsOutlineView reloadData];
7977 /* We use this method to recreate new, updated factory presets */
7978 - (IBAction)addFactoryPresets:(id)sender
7981 /* First, we delete any existing built in presets */
7982 [self deleteFactoryPresets: sender];
7983 /* Then we generate new built in presets programmatically with fPresetsBuiltin
7984 * which is all setup in HBPresets.h and HBPresets.m*/
7985 [fPresetsBuiltin generateBuiltinPresets:UserPresets];
7986 /* update build number for built in presets */
7987 /* iterate though the new array of presets to import and add them to our presets array */
7989 NSEnumerator *enumerator = [UserPresets objectEnumerator];
7991 while (tempObject = [enumerator nextObject])
7993 /* Record the apps current build number in the PresetBuildNumber key */
7994 if ([[tempObject objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset
7996 /* Preset build number */
7997 [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:[[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]] forKey:@"PresetBuildNumber"];
8001 /* report the built in preset updating to the activity log */
8002 [self writeToActivityLog: "built in presets updated to build number: %d", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]];
8012 /*******************************
8013 * Subclass of the HBPresetsOutlineView *
8014 *******************************/
8016 @implementation HBPresetsOutlineView
8017 - (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent*)dragEvent offset:(NSPointPointer)dragImageOffset
8021 // By default, NSTableView only drags an image of the first column. Change this to
8022 // drag an image of the queue's icon and PresetName columns.
8023 NSArray * cols = [NSArray arrayWithObjects: [self tableColumnWithIdentifier:@"PresetName"], nil];
8024 return [super dragImageForRowsWithIndexes:dragRows tableColumns:cols event:dragEvent offset:dragImageOffset];
8029 - (void) mouseDown:(NSEvent *)theEvent
8031 [super mouseDown:theEvent];
8037 - (BOOL) isDragging;