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. */
9 #import "HBOutputPanelController.h"
10 #import "HBPreferencesController.h"
11 #import "HBDVDDetector.h"
13 #import "HBPreviewController.h"
15 #define DragDropSimplePboardType @"MyCustomOutlineViewPboardType"
17 /* We setup the toolbar values here ShowPreviewIdentifier */
18 static NSString * ToggleDrawerIdentifier = @"Toggle Drawer Item Identifier";
19 static NSString * StartEncodingIdentifier = @"Start Encoding Item Identifier";
20 static NSString * PauseEncodingIdentifier = @"Pause Encoding Item Identifier";
21 static NSString * ShowQueueIdentifier = @"Show Queue Item Identifier";
22 static NSString * AddToQueueIdentifier = @"Add to Queue Item Identifier";
23 static NSString * ShowPictureIdentifier = @"Show Picture Window Item Identifier";
24 static NSString * ShowPreviewIdentifier = @"Show Preview Window Item Identifier";
25 static NSString * ShowActivityIdentifier = @"Debug Output Item Identifier";
26 static NSString * ChooseSourceIdentifier = @"Choose Source Item Identifier";
29 /*******************************
30 * HBController implementation *
31 *******************************/
32 @implementation HBController
42 /* replace bundled app icon with one which is 32/64-bit savvy */
43 #if defined( __LP64__ )
44 fApplicationIcon = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForImageResource:@"HandBrake-64.icns"]];
46 fApplicationIcon = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForImageResource:@"HandBrake.icns"]];
48 if( fApplicationIcon != nil )
49 [NSApp setApplicationIconImage:fApplicationIcon];
51 [HBPreferencesController registerUserDefaults];
53 fQueueEncodeLibhb = NULL;
54 /* Check for check for the app support directory here as
55 * outputPanel needs it right away, as may other future methods
57 NSString *libraryDir = [NSSearchPathForDirectoriesInDomains( NSLibraryDirectory,
59 YES ) objectAtIndex:0];
60 AppSupportDirectory = [[libraryDir stringByAppendingPathComponent:@"Application Support"]
61 stringByAppendingPathComponent:@"HandBrake"];
62 if( ![[NSFileManager defaultManager] fileExistsAtPath:AppSupportDirectory] )
64 [[NSFileManager defaultManager] createDirectoryAtPath:AppSupportDirectory
67 /* Check for and create the App Support Preview directory if necessary */
68 NSString *PreviewDirectory = [AppSupportDirectory stringByAppendingPathComponent:@"Previews"];
69 if( ![[NSFileManager defaultManager] fileExistsAtPath:PreviewDirectory] )
71 [[NSFileManager defaultManager] createDirectoryAtPath:PreviewDirectory
74 outputPanel = [[HBOutputPanelController alloc] init];
75 fPictureController = [[PictureController alloc] init];
76 fQueueController = [[HBQueueController alloc] init];
77 fAdvancedOptions = [[HBAdvancedController alloc] init];
78 /* we init the HBPresets class which currently is only used
79 * for updating built in presets, may move more functionality
82 fPresetsBuiltin = [[HBPresets alloc] init];
83 fPreferencesController = [[HBPreferencesController alloc] init];
84 /* Lets report the HandBrake version number here to the activity log and text log file */
85 NSString *versionStringFull = [[NSString stringWithFormat: @"Handbrake Version: %@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]] stringByAppendingString: [NSString stringWithFormat: @" (%@)", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]]];
86 [self writeToActivityLog: "%s", [versionStringFull UTF8String]];
88 /* Get the PID number for this hb instance, used in multi instance encoding */
89 //pidNum = [self getThisHBInstancePID];
90 /* Report this pid to the activity log */
91 //[self writeToActivityLog: "Pid for this instance:%d", pidNum];
97 - (void) applicationDidFinishLaunching: (NSNotification *) notification
99 /* Init libhb with check for updates libhb style set to "0" so its ignored and lets sparkle take care of it */
100 int loggingLevel = [[[NSUserDefaults standardUserDefaults] objectForKey:@"LoggingLevel"] intValue];
101 fHandle = hb_init(loggingLevel, 0);
102 /* Optional dvd nav UseDvdNav*/
103 hb_dvd_set_dvdnav([[[NSUserDefaults standardUserDefaults] objectForKey:@"UseDvdNav"] boolValue]);
104 /* Init a separate instance of libhb for user scanning and setting up jobs */
105 fQueueEncodeLibhb = hb_init(loggingLevel, 0);
107 // Set the Growl Delegate
108 [GrowlApplicationBridge setGrowlDelegate: self];
109 /* Init others controllers */
110 [fPictureController SetHandle: fHandle];
111 [fPictureController setHBController: self];
113 [fQueueController setHandle: fQueueEncodeLibhb];
114 [fQueueController setHBController: self];
116 fChapterTitlesDelegate = [[ChapterTitles alloc] init];
117 [fChapterTable setDataSource:fChapterTitlesDelegate];
118 [fChapterTable setDelegate:fChapterTitlesDelegate];
120 /* setup the subtitles delegate and connections to table */
121 fSubtitlesDelegate = [[HBSubtitles alloc] init];
122 [fSubtitlesTable setDataSource:fSubtitlesDelegate];
123 [fSubtitlesTable setDelegate:fSubtitlesDelegate];
124 [fSubtitlesTable setRowHeight:25.0];
126 [fPresetsOutlineView setAutosaveName:@"Presets View"];
127 [fPresetsOutlineView setAutosaveExpandedItems:YES];
129 dockIconProgress = 0;
131 /* Init QueueFile .plist */
132 [self loadQueueFile];
133 /* Run hbInstances to get any info on other instances as well as set the
134 * pid number for this instance in the case of multi-instance encoding. */
135 hbInstanceNum = [self hbInstances];
137 /* Call UpdateUI every 1/2 sec */
139 [[NSRunLoop currentRunLoop] addTimer:[NSTimer
140 scheduledTimerWithTimeInterval:0.5
142 selector:@selector(updateUI:)
143 userInfo:nil repeats:YES]
144 forMode:NSDefaultRunLoopMode];
147 // Open debug output window now if it was visible when HB was closed
148 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"OutputPanelIsOpen"])
149 [self showDebugOutputPanel:nil];
151 // Open queue window now if it was visible when HB was closed
152 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"QueueWindowIsOpen"])
153 [self showQueueWindow:nil];
155 [self openMainWindow:nil];
157 /* We have to set the bool to tell hb what to do after a scan
158 * Initially we set it to NO until we start processing the queue
160 applyQueueToScan = NO;
162 /* Now we re-check the queue array to see if there are
163 * any remaining encodes to be done in it and ask the
164 * user if they want to reload the queue */
165 if ([QueueFileArray count] > 0)
167 /* run getQueueStats to see whats in the queue file */
168 [self getQueueStats];
169 /* this results in these values
170 * fEncodingQueueItem = 0;
172 * fCompletedCount = 0;
173 * fCanceledCount = 0;
177 /*On Screen Notification*/
178 NSString * alertTitle;
180 /* We check to see if there is already another instance of hb running.
181 * Note: hbInstances == 1 means we are the only instance of HandBrake.app
183 if (hbInstanceNum > 1)
185 alertTitle = [NSString stringWithFormat:
186 NSLocalizedString(@"There is already an instance of HandBrake running.", @"")];
187 NSBeginCriticalAlertSheet(
189 NSLocalizedString(@"Reload Queue", nil),
193 nil, @selector(didDimissReloadQueue:returnCode:contextInfo:), nil,
194 NSLocalizedString(@" HandBrake will now load up the existing queue.", nil));
198 if (fWorkingCount > 0)
200 alertTitle = [NSString stringWithFormat:
201 NSLocalizedString(@"HandBrake Has Detected %d Previously Encoding Item and %d Pending Item(s) In Your Queue.", @""),
202 fWorkingCount,fPendingCount];
206 alertTitle = [NSString stringWithFormat:
207 NSLocalizedString(@"HandBrake Has Detected %d Pending Item(s) In Your Queue.", @""),
211 NSBeginCriticalAlertSheet(
213 NSLocalizedString(@"Reload Queue", nil),
215 NSLocalizedString(@"Empty Queue", nil),
217 nil, @selector(didDimissReloadQueue:returnCode:contextInfo:), nil,
218 NSLocalizedString(@" Do you want to reload them ?", nil));
224 /* We show whichever open source window specified in LaunchSourceBehavior preference key */
225 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"])
227 [self browseSources:nil];
230 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source (Title Specific)"])
232 [self browseSources:(id)fOpenSourceTitleMMenu];
235 currentQueueEncodeNameString = @"";
239 #pragma mark Multiple Instances
241 /* hbInstances checks to see if other instances of HB are running and also sets the pid for this instance for multi-instance queue encoding */
243 /* Note for now since we are in early phases of multi-instance I have put in quite a bit of logging. Can be removed as we see fit. */
246 /* check to see if another instance of HandBrake.app is running */
247 NSArray *runningAppDictionaries = [[NSWorkspace sharedWorkspace] launchedApplications];
248 NSDictionary *runningAppsDictionary;
250 NSString * thisInstanceAppPath = [[NSBundle mainBundle] bundlePath];
251 NSString * runningInstanceAppPath;
252 int runningInstancePidNum;
253 [self writeToActivityLog: "hbInstances path to this instance: %s", [thisInstanceAppPath UTF8String]];
254 for (runningAppsDictionary in runningAppDictionaries)
256 if ([[runningAppsDictionary valueForKey:@"NSApplicationName"] isEqualToString:@"HandBrake"])
259 /*Report the path to each active instances app path */
260 runningInstancePidNum = [[runningAppsDictionary valueForKey:@"NSApplicationProcessIdentifier"] intValue];
261 runningInstanceAppPath = [runningAppsDictionary valueForKey:@"NSApplicationPath"];
262 [self writeToActivityLog: "hbInstance found instance pidnum:%d at path: %s", runningInstancePidNum, [runningInstanceAppPath UTF8String]];
263 /* see if this is us by comparing the app path */
264 if ([runningInstanceAppPath isEqualToString: thisInstanceAppPath])
266 /* If so this is our pidnum */
267 [self writeToActivityLog: "hbInstance MATCH FOUND, our pidnum is:%d", runningInstancePidNum];
268 /* Get the PID number for this hb instance, used in multi instance encoding */
269 pidNum = runningInstancePidNum;
270 /* Report this pid to the activity log */
271 [self writeToActivityLog: "Pid for this instance:%d", pidNum];
272 /* Tell fQueueController what our pidNum is */
273 [fQueueController setPidNum:pidNum];
282 - (void) didDimissReloadQueue: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
284 if (returnCode == NSAlertOtherReturn)
286 [self clearQueueAllItems];
288 /* We show whichever open source window specified in LaunchSourceBehavior preference key */
289 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"])
291 [self browseSources:nil];
294 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source (Title Specific)"])
296 [self browseSources:(id)fOpenSourceTitleMMenu];
301 if ([self hbInstances] == 1)
303 [self setQueueEncodingItemsAsPending];
305 [self showQueueWindow:NULL];
309 - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *) app
314 hb_get_state( fQueueEncodeLibhb, &s );
316 if ( s.state != HB_STATE_IDLE )
318 int result = NSRunCriticalAlertPanel(
319 NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),
320 NSLocalizedString(@"If you quit HandBrake your current encode will be reloaded into your queue at next launch. Do you want to quit anyway?", nil),
321 NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil, @"A movie" );
323 if (result == NSAlertDefaultReturn)
325 return NSTerminateNow;
328 return NSTerminateCancel;
331 // Warn if items still in the queue
332 else if ( fPendingCount > 0 )
334 int result = NSRunCriticalAlertPanel(
335 NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),
336 NSLocalizedString(@"There are pending encodes in your queue. Do you want to quit anyway?",nil),
337 NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil);
339 if ( result == NSAlertDefaultReturn )
340 return NSTerminateNow;
342 return NSTerminateCancel;
345 return NSTerminateNow;
348 - (void)applicationWillTerminate:(NSNotification *)aNotification
350 [currentQueueEncodeNameString release];
351 [browsedSourceDisplayName release];
352 [outputPanel release];
353 [fQueueController release];
354 [fPreviewController release];
355 [fPictureController release];
356 [fApplicationIcon release];
359 hb_close(&fQueueEncodeLibhb);
365 - (void) awakeFromNib
368 [fWindow setExcludedFromWindowsMenu:NO];
370 [fAdvancedOptions setView:fAdvancedView];
372 /* lets setup our presets drawer for drag and drop here */
373 [fPresetsOutlineView registerForDraggedTypes: [NSArray arrayWithObject:DragDropSimplePboardType] ];
374 [fPresetsOutlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
375 [fPresetsOutlineView setVerticalMotionCanBeginDrag: YES];
377 /* Initialize currentScanCount so HB can use it to
378 evaluate successive scans */
379 currentScanCount = 0;
382 /* Init UserPresets .plist */
385 fRipIndicatorShown = NO; // initially out of view in the nib
387 /* For 64 bit builds, the threaded animation in the progress
388 * indicators conflicts with the animation in the advanced tab
389 * for reasons not completely clear. jbrjake found a note in the
390 * 10.5 dev notes regarding this possiblility. It was also noted
391 * that unless specified, setUsesThreadedAnimation defaults to true.
392 * So, at least for now we set the indicator animation to NO for
393 * both the scan and regular progress indicators for both 32 and 64 bit
394 * as it test out fine on both and there is no reason our progress indicators
395 * should require their own thread.
398 [fScanIndicator setUsesThreadedAnimation:NO];
399 [fRipIndicator setUsesThreadedAnimation:NO];
403 /* Show/Dont Show Presets drawer upon launch based
404 on user preference DefaultPresetsDrawerShow*/
405 if( [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultPresetsDrawerShow"] > 0 )
407 [fPresetDrawer setDelegate:self];
408 NSSize drawerSize = NSSizeFromString( [[NSUserDefaults standardUserDefaults]
409 stringForKey:@"Drawer Size"] );
410 if( drawerSize.width )
411 [fPresetDrawer setContentSize: drawerSize];
412 [fPresetDrawer open];
415 /* Initially set the dvd angle widgets to hidden (dvdnav only) */
416 [fSrcAngleLabel setHidden:YES];
417 [fSrcAnglePopUp setHidden:YES];
419 /* Setup the start / stop popup */
420 [fEncodeStartStopPopUp removeAllItems];
421 [fEncodeStartStopPopUp addItemWithTitle: @"Chapters"];
422 [fEncodeStartStopPopUp addItemWithTitle: @"Seconds"];
423 [fEncodeStartStopPopUp addItemWithTitle: @"Frames"];
424 /* Align the start / stop widgets with the chapter popups */
425 [fSrcTimeStartEncodingField setFrameOrigin:[fSrcChapterStartPopUp frame].origin];
426 [fSrcTimeEndEncodingField setFrameOrigin:[fSrcChapterEndPopUp frame].origin];
428 [fSrcFrameStartEncodingField setFrameOrigin:[fSrcChapterStartPopUp frame].origin];
429 [fSrcFrameEndEncodingField setFrameOrigin:[fSrcChapterEndPopUp frame].origin];
432 NSMenuItem *menuItem;
433 [fDstFormatPopUp removeAllItems];
435 menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MP4 file" action: NULL keyEquivalent: @""];
436 [menuItem setTag: HB_MUX_MP4];
438 menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MKV file" action: NULL keyEquivalent: @""];
439 [menuItem setTag: HB_MUX_MKV];
441 [fDstFormatPopUp selectItemAtIndex: 0];
443 [self formatPopUpChanged:nil];
445 /* We enable the create chapters checkbox here since we are .mp4 */
446 [fCreateChapterMarkers setEnabled: YES];
447 if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultChapterMarkers"] > 0)
449 [fCreateChapterMarkers setState: NSOnState];
455 [fDstFile2Field setStringValue: [NSString stringWithFormat:
456 @"%@/Desktop/Movie.mp4", NSHomeDirectory()]];
459 [fVidEncoderPopUp removeAllItems];
460 [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"];
464 [fVidTargetSizeField setIntValue: 700];
465 [fVidBitrateField setIntValue: 1000];
467 [fVidQualityMatrix selectCell: fVidBitrateCell];
468 [self videoMatrixChanged:nil];
470 /* Video framerate */
471 [fVidRatePopUp removeAllItems];
472 [fVidRatePopUp addItemWithTitle: NSLocalizedString( @"Same as source", @"" )];
473 for( int i = 0; i < hb_video_rates_count; i++ )
475 if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.3f",23.976]])
477 [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
478 [NSString stringWithUTF8String: hb_video_rates[i].string], @" (NTSC Film)"]];
480 else if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%d",25]])
482 [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
483 [NSString stringWithUTF8String: hb_video_rates[i].string], @" (PAL Film/Video)"]];
485 else if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.2f",29.97]])
487 [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
488 [NSString stringWithUTF8String: hb_video_rates[i].string], @" (NTSC Video)"]];
492 [fVidRatePopUp addItemWithTitle:
493 [NSString stringWithUTF8String: hb_video_rates[i].string]];
496 [fVidRatePopUp selectItemAtIndex: 0];
498 /* Set Auto Crop to On at launch */
499 [fPictureController setAutoCrop:YES];
502 [fAudTrack1BitratePopUp removeAllItems];
503 for( int i = 0; i < hb_audio_bitrates_count; i++ )
505 [fAudTrack1BitratePopUp addItemWithTitle:
506 [NSString stringWithUTF8String: hb_audio_bitrates[i].string]];
509 [fAudTrack1BitratePopUp selectItemAtIndex: hb_audio_bitrates_default];
511 /* Audio samplerate */
512 [fAudTrack1RatePopUp removeAllItems];
513 for( int i = 0; i < hb_audio_rates_count; i++ )
515 [fAudTrack1RatePopUp addItemWithTitle:
516 [NSString stringWithUTF8String: hb_audio_rates[i].string]];
518 [fAudTrack1RatePopUp selectItemAtIndex: hb_audio_rates_default];
521 [fStatusField setStringValue: @""];
526 /* We disable the Turbo 1st pass checkbox since we are not x264 */
527 [fVidTurboPassCheck setEnabled: NO];
528 [fVidTurboPassCheck setState: NSOffState];
531 /* lets get our default prefs here */
532 [self getDefaultPresets:nil];
533 /* lets initialize the current successful scancount here to 0 */
534 currentSuccessfulScanCount = 0;
537 - (void) enableUI: (bool) b
539 NSControl * controls[] =
540 { fSrcTitleField, fSrcTitlePopUp,
541 fSrcChapterField, fSrcChapterStartPopUp, fSrcChapterToField,
542 fSrcChapterEndPopUp, fSrcDuration1Field, fSrcDuration2Field,
543 fDstFormatField, fDstFormatPopUp, fDstFile1Field, fDstFile2Field,
544 fDstBrowseButton, fVidRateField, fVidRatePopUp,fVidEncoderField, fVidEncoderPopUp, fVidQualityField,
545 fPictureSizeField,fPictureCroppingField, fVideoFiltersField,fVidQualityMatrix, fSubField, fSubPopUp,
546 fAudSourceLabel, fAudCodecLabel, fAudMixdownLabel, fAudSamplerateLabel, fAudBitrateLabel,
547 fAudTrack1Label, fAudTrack2Label, fAudTrack3Label, fAudTrack4Label,
548 fAudLang1PopUp, fAudLang2PopUp, fAudLang3PopUp, fAudLang4PopUp,
549 fAudTrack1CodecPopUp, fAudTrack2CodecPopUp, fAudTrack3CodecPopUp, fAudTrack4CodecPopUp,
550 fAudTrack1MixPopUp, fAudTrack2MixPopUp, fAudTrack3MixPopUp, fAudTrack4MixPopUp,
551 fAudTrack1RatePopUp, fAudTrack2RatePopUp, fAudTrack3RatePopUp, fAudTrack4RatePopUp,
552 fAudTrack1BitratePopUp, fAudTrack2BitratePopUp, fAudTrack3BitratePopUp, fAudTrack4BitratePopUp,
553 fAudDrcLabel, fAudTrack1DrcSlider, fAudTrack1DrcField, fAudTrack2DrcSlider,
554 fAudTrack2DrcField, fAudTrack3DrcSlider, fAudTrack3DrcField, fAudTrack4DrcSlider,fAudTrack4DrcField,
555 fQueueStatus,fPresetsAdd,fPresetsDelete,fSrcAngleLabel,fSrcAnglePopUp,
556 fCreateChapterMarkers,fVidTurboPassCheck,fDstMp4LargeFileCheck,fSubForcedCheck,fPresetsOutlineView,
557 fAudDrcLabel,fDstMp4HttpOptFileCheck,fDstMp4iPodFileCheck,fVidQualityRFField,fVidQualityRFLabel,
558 fEncodeStartStopPopUp,fSrcTimeStartEncodingField,fSrcTimeEndEncodingField,fSrcFrameStartEncodingField,
559 fSrcFrameEndEncodingField, fLoadChaptersButton, fSaveChaptersButton, fFrameratePfrCheck};
562 i < sizeof( controls ) / sizeof( NSControl * ); i++ )
564 if( [[controls[i] className] isEqualToString: @"NSTextField"] )
566 NSTextField * tf = (NSTextField *) controls[i];
567 if( ![tf isBezeled] )
569 [tf setTextColor: b ? [NSColor controlTextColor] :
570 [NSColor disabledControlTextColor]];
574 [controls[i] setEnabled: b];
580 /* if we're enabling the interface, check if the audio mixdown controls need to be enabled or not */
581 /* these will have been enabled by the mass control enablement above anyway, so we're sense-checking it here */
582 [self setEnabledStateOfAudioMixdownControls:nil];
583 /* we also call calculatePictureSizing here to sense check if we already have vfr selected */
584 [self calculatePictureSizing:nil];
588 [fPresetsOutlineView setEnabled: NO];
592 [self videoMatrixChanged:nil];
593 [fAdvancedOptions enableUI:b];
597 /***********************************************************************
599 ***********************************************************************
600 * Shows a progression bar on the dock icon, filled according to
601 * 'progress' (0.0 <= progress <= 1.0).
602 * Called with progress < 0.0 or progress > 1.0, restores the original
604 **********************************************************************/
605 - (void) UpdateDockIcon: (float) progress
608 NSBitmapImageRep * bmp;
610 uint32_t black = htonl( 0x000000FF );
611 uint32_t red = htonl( 0xFF0000FF );
612 uint32_t white = htonl( 0xFFFFFFFF );
613 int row_start, row_end;
616 if( progress < 0.0 || progress > 1.0 )
618 [NSApp setApplicationIconImage: fApplicationIcon];
622 /* Get it in a raw bitmap form */
623 tiff = [fApplicationIcon TIFFRepresentationUsingCompression:
624 NSTIFFCompressionNone factor: 1.0];
625 bmp = [NSBitmapImageRep imageRepWithData: tiff];
627 /* Draw the progression bar */
628 /* It's pretty simple (ugly?) now, but I'm no designer */
630 row_start = 3 * (int) [bmp size].height / 4;
631 row_end = 7 * (int) [bmp size].height / 8;
633 for( i = row_start; i < row_start + 2; i++ )
635 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
636 for( j = 0; j < (int) [bmp size].width; j++ )
641 for( i = row_start + 2; i < row_end - 2; i++ )
643 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
646 for( j = 2; j < (int) [bmp size].width - 2; j++ )
648 if( j < 2 + (int) ( ( [bmp size].width - 4.0 ) * progress ) )
660 for( i = row_end - 2; i < row_end; i++ )
662 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
663 for( j = 0; j < (int) [bmp size].width; j++ )
669 /* Now update the dock icon */
670 tiff = [bmp TIFFRepresentationUsingCompression:
671 NSTIFFCompressionNone factor: 1.0];
672 NSImage* icon = [[NSImage alloc] initWithData: tiff];
673 [NSApp setApplicationIconImage: icon];
677 - (void) updateUI: (NSTimer *) timer
680 /* Update UI for fHandle (user scanning instance of libhb ) */
683 list = hb_get_titles( fHandle );
684 /* check to see if there has been a new scan done
685 this bypasses the constraints of HB_STATE_WORKING
686 not allowing setting a newly scanned source */
687 int checkScanCount = hb_get_scancount( fHandle );
688 if( checkScanCount > currentScanCount )
690 currentScanCount = checkScanCount;
691 [fScanIndicator setIndeterminate: NO];
692 [fScanIndicator setDoubleValue: 0.0];
693 [fScanIndicator setHidden: YES];
694 [self showNewScan:nil];
698 hb_get_state( fHandle, &s );
704 #define p s.param.scanning
705 case HB_STATE_SCANNING:
707 [fSrcDVD2Field setStringValue: [NSString stringWithFormat:
708 NSLocalizedString( @"Scanning title %d of %d...", @"" ),
709 p.title_cur, p.title_count]];
710 [fScanIndicator setHidden: NO];
711 [fScanIndicator setDoubleValue: 100.0 * ((double)( p.title_cur - 1 ) / p.title_count)];
716 #define p s.param.scandone
717 case HB_STATE_SCANDONE:
719 [fScanIndicator setIndeterminate: NO];
720 [fScanIndicator setDoubleValue: 0.0];
721 [fScanIndicator setHidden: YES];
722 [self writeToActivityLog:"ScanDone state received from fHandle"];
723 [self showNewScan:nil];
724 [[fWindow toolbar] validateVisibleItems];
730 #define p s.param.working
731 case HB_STATE_WORKING:
738 #define p s.param.muxing
739 case HB_STATE_MUXING:
746 case HB_STATE_PAUSED:
749 case HB_STATE_WORKDONE:
756 /* Update UI for fQueueEncodeLibhb */
758 // list = hb_get_titles( fQueueEncodeLibhb ); //fQueueEncodeLibhb
759 /* check to see if there has been a new scan done
760 this bypasses the constraints of HB_STATE_WORKING
761 not allowing setting a newly scanned source */
763 checkScanCount = hb_get_scancount( fQueueEncodeLibhb );
764 if( checkScanCount > currentScanCount )
766 currentScanCount = checkScanCount;
770 hb_get_state( fQueueEncodeLibhb, &s );
776 #define p s.param.scanning
777 case HB_STATE_SCANNING:
779 [fStatusField setStringValue: [NSString stringWithFormat:
780 NSLocalizedString( @"Queue Scanning title %d of %d...", @"" ),
781 p.title_cur, p.title_count]];
783 /* Set the status string in fQueueController as well */
784 [fQueueController setQueueStatusString: [NSString stringWithFormat:
785 NSLocalizedString( @"Queue Scanning title %d of %d...", @"" ),
786 p.title_cur, p.title_count]];
791 #define p s.param.scandone
792 case HB_STATE_SCANDONE:
794 [self writeToActivityLog:"ScanDone state received from fQueueEncodeLibhb"];
795 [self processNewQueueEncode];
796 [[fWindow toolbar] validateVisibleItems];
803 #define p s.param.working
805 case HB_STATE_SEARCHING:
807 NSMutableString * string;
808 NSString * pass_desc;
810 /* Update text field */
812 //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];
813 /* For now, do not announce "pass x of x for the search phase ... */
814 string = [NSMutableString stringWithFormat: NSLocalizedString( @"Searching for start point ... : %.2f %%", @"" ), 100.0 * p.progress];
818 [string appendFormat:
819 NSLocalizedString( @" (ETA %02dh%02dm%02ds)", @"" ), p.hours, p.minutes, p.seconds];
822 [fStatusField setStringValue: string];
823 /* Set the status string in fQueueController as well */
824 [fQueueController setQueueStatusString: string];
826 CGFloat progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
827 [fRipIndicator setIndeterminate: NO];
828 [fRipIndicator setDoubleValue:100.0 * progress_total];
830 // If progress bar hasn't been revealed at the bottom of the window, do
831 // that now. This code used to be in doRip. I moved it to here to handle
832 // the case where hb_start is called by HBQueueController and not from
834 if( !fRipIndicatorShown )
836 NSRect frame = [fWindow frame];
837 if( frame.size.width <= 591 )
838 frame.size.width = 591;
839 frame.size.height += 36;
840 frame.origin.y -= 36;
841 [fWindow setFrame:frame display:YES animate:YES];
842 fRipIndicatorShown = YES;
846 /* Update dock icon */
847 /* Note not done yet */
852 case HB_STATE_WORKING:
854 NSMutableString * string;
855 NSString * pass_desc;
856 /* Update text field */
857 if (p.job_cur == 1 && p.job_count > 1)
859 if ([[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SubtitleList"] && [[[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex]objectForKey:@"SubtitleList"] objectAtIndex:0] objectForKey:@"subtitleSourceTrackNum"] intValue] == 1)
861 pass_desc = @"(subtitle scan)";
873 string = [NSMutableString stringWithFormat: NSLocalizedString( @"Encoding: pass %d %@ of %d, %.2f %%", @"" ), p.job_cur, pass_desc, p.job_count, 100.0 * p.progress];
877 [string appendFormat:
878 NSLocalizedString( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)", @"" ),
879 p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];
882 [fStatusField setStringValue: string];
883 /* Set the status string in fQueueController as well but add currentQueueEncodeNameString to delineate
884 * which encode is being worked on by this instance in a multiple instance situation
886 NSString * queueStatusString = [NSString stringWithFormat:@"%@ -> %@",string,currentQueueEncodeNameString];
887 [fQueueController setQueueStatusString:queueStatusString];
890 CGFloat progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
891 [fRipIndicator setIndeterminate: NO];
892 [fRipIndicator setDoubleValue:100.0 * progress_total];
894 // If progress bar hasn't been revealed at the bottom of the window, do
895 // that now. This code used to be in doRip. I moved it to here to handle
896 // the case where hb_start is called by HBQueueController and not from
898 if( !fRipIndicatorShown )
900 NSRect frame = [fWindow frame];
901 if( frame.size.width <= 591 )
902 frame.size.width = 591;
903 frame.size.height += 36;
904 frame.origin.y -= 36;
905 [fWindow setFrame:frame display:YES animate:YES];
906 fRipIndicatorShown = YES;
910 /* Update dock icon */
911 if( dockIconProgress < 100.0 * progress_total )
913 [self UpdateDockIcon: progress_total];
914 dockIconProgress += 5;
921 #define p s.param.muxing
922 case HB_STATE_MUXING:
924 /* Update text field */
925 [fStatusField setStringValue: NSLocalizedString( @"Muxing...", @"" )];
926 /* Set the status string in fQueueController as well */
927 [fQueueController setQueueStatusString: NSLocalizedString( @"Muxing...", @"" )];
929 [fRipIndicator setIndeterminate: YES];
930 [fRipIndicator startAnimation: nil];
932 /* Update dock icon */
933 [self UpdateDockIcon: 1.0];
939 case HB_STATE_PAUSED:
940 [fStatusField setStringValue: NSLocalizedString( @"Paused", @"" )];
941 [fQueueController setQueueStatusString: NSLocalizedString( @"Paused", @"" )];
945 case HB_STATE_WORKDONE:
947 // HB_STATE_WORKDONE happpens as a result of libhb finishing all its jobs
948 // or someone calling hb_stop. In the latter case, hb_stop does not clear
949 // out the remaining passes/jobs in the queue. We'll do that here.
951 // Delete all remaining jobs of this encode.
952 [fStatusField setStringValue: NSLocalizedString( @"Encode Finished.", @"" )];
953 /* Set the status string in fQueueController as well */
954 [fQueueController setQueueStatusString: NSLocalizedString( @"Encode Finished.", @"" )];
955 [fRipIndicator setIndeterminate: NO];
956 [fRipIndicator stopAnimation: nil];
957 [fRipIndicator setDoubleValue: 0.0];
958 [[fWindow toolbar] validateVisibleItems];
960 /* Restore dock icon */
961 [self UpdateDockIcon: -1.0];
962 dockIconProgress = 0;
964 if( fRipIndicatorShown )
966 NSRect frame = [fWindow frame];
967 if( frame.size.width <= 591 )
968 frame.size.width = 591;
969 frame.size.height += -36;
970 frame.origin.y -= -36;
971 [fWindow setFrame:frame display:YES animate:YES];
972 fRipIndicatorShown = NO;
974 /* Since we are done with this encode, tell output to stop writing to the
975 * individual encode log
977 [outputPanel endEncodeLog];
978 /* Check to see if the encode state has not been cancelled
979 to determine if we should check for encode done notifications */
980 if( fEncodeState != 2 )
982 NSString *pathOfFinishedEncode;
983 /* Get the output file name for the finished encode */
984 pathOfFinishedEncode = [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"];
986 /* Both the Growl Alert and Sending to MetaX can be done as encodes roll off the queue */
988 [self showGrowlDoneNotification:pathOfFinishedEncode];
990 [self sendToMetaX:pathOfFinishedEncode];
992 /* since we have successfully completed an encode, we increment the queue counter */
993 [self incrementQueueItemDone:currentQueueEncodeIndex];
995 /* all end of queue actions below need to be done after all queue encodes have finished
996 * and there are no pending jobs left to process
998 if (fPendingCount == 0)
1000 /* If Alert Window or Window and Growl has been selected */
1001 if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window"] ||
1002 [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"] )
1004 /*On Screen Notification*/
1007 status = NSRunAlertPanel(@"Put down that cocktail...",@"Your HandBrake queue is done!", @"OK", nil, nil);
1008 [NSApp requestUserAttention:NSCriticalRequest];
1011 /* If sleep has been selected */
1012 if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"] )
1015 NSDictionary* errorDict;
1016 NSAppleEventDescriptor* returnDescriptor = nil;
1017 NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:
1018 @"tell application \"Finder\" to sleep"];
1019 returnDescriptor = [scriptObject executeAndReturnError: &errorDict];
1020 [scriptObject release];
1022 /* If Shutdown has been selected */
1023 if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"] )
1026 NSDictionary* errorDict;
1027 NSAppleEventDescriptor* returnDescriptor = nil;
1028 NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:
1029 @"tell application \"Finder\" to shut down"];
1030 returnDescriptor = [scriptObject executeAndReturnError: &errorDict];
1031 [scriptObject release];
1040 /* Since we can use multiple instance off of the same queue file it is imperative that we keep the QueueFileArray updated off of the QueueFile.plist
1041 * so we go ahead and do it in this existing timer as opposed to using a new one */
1043 NSMutableArray * tempQueueArray = [[NSMutableArray alloc] initWithContentsOfFile:QueueFile];
1044 [QueueFileArray setArray:tempQueueArray];
1045 [tempQueueArray release];
1046 /* Send Fresh QueueFileArray to fQueueController to update queue window */
1047 [fQueueController setQueueArray: QueueFileArray];
1048 [self getQueueStats];
1052 /* We use this to write messages to stderr from the macgui which show up in the activity window and log*/
1053 - (void) writeToActivityLog:(const char *) format, ...
1056 va_start(args, format);
1060 vsnprintf( str, 1024, format, args );
1062 time_t _now = time( NULL );
1063 struct tm * now = localtime( &_now );
1064 fprintf(stderr, "[%02d:%02d:%02d] macgui: %s\n", now->tm_hour, now->tm_min, now->tm_sec, str );
1070 #pragma mark Toolbar
1071 // ============================================================
1072 // NSToolbar Related Methods
1073 // ============================================================
1075 - (void) setupToolbar {
1076 NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: @"HandBrake Toolbar"] autorelease];
1078 [toolbar setAllowsUserCustomization: YES];
1079 [toolbar setAutosavesConfiguration: YES];
1080 [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
1082 [toolbar setDelegate: self];
1084 [fWindow setToolbar: toolbar];
1087 - (NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier:
1088 (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted {
1089 NSToolbarItem * item = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdent] autorelease];
1091 if ([itemIdent isEqualToString: ToggleDrawerIdentifier])
1093 [item setLabel: @"Toggle Presets"];
1094 [item setPaletteLabel: @"Toggler Presets"];
1095 [item setToolTip: @"Open/Close Preset Drawer"];
1096 [item setImage: [NSImage imageNamed: @"Drawer"]];
1097 [item setTarget: self];
1098 [item setAction: @selector(toggleDrawer:)];
1099 [item setAutovalidates: NO];
1101 else if ([itemIdent isEqualToString: StartEncodingIdentifier])
1103 [item setLabel: @"Start"];
1104 [item setPaletteLabel: @"Start Encoding"];
1105 [item setToolTip: @"Start Encoding"];
1106 [item setImage: [NSImage imageNamed: @"Play"]];
1107 [item setTarget: self];
1108 [item setAction: @selector(Rip:)];
1110 else if ([itemIdent isEqualToString: ShowQueueIdentifier])
1112 [item setLabel: @"Show Queue"];
1113 [item setPaletteLabel: @"Show Queue"];
1114 [item setToolTip: @"Show Queue"];
1115 [item setImage: [NSImage imageNamed: @"Queue"]];
1116 [item setTarget: self];
1117 [item setAction: @selector(showQueueWindow:)];
1118 [item setAutovalidates: NO];
1120 else if ([itemIdent isEqualToString: AddToQueueIdentifier])
1122 [item setLabel: @"Add to Queue"];
1123 [item setPaletteLabel: @"Add to Queue"];
1124 [item setToolTip: @"Add to Queue"];
1125 [item setImage: [NSImage imageNamed: @"AddToQueue"]];
1126 [item setTarget: self];
1127 [item setAction: @selector(addToQueue:)];
1129 else if ([itemIdent isEqualToString: PauseEncodingIdentifier])
1131 [item setLabel: @"Pause"];
1132 [item setPaletteLabel: @"Pause Encoding"];
1133 [item setToolTip: @"Pause Encoding"];
1134 [item setImage: [NSImage imageNamed: @"Pause"]];
1135 [item setTarget: self];
1136 [item setAction: @selector(Pause:)];
1138 else if ([itemIdent isEqualToString: ShowPictureIdentifier])
1140 [item setLabel: @"Picture Settings"];
1141 [item setPaletteLabel: @"Show Picture Settings"];
1142 [item setToolTip: @"Show Picture Settings"];
1143 [item setImage: [NSImage imageNamed: @"pref-picture"]];
1144 [item setTarget: self];
1145 [item setAction: @selector(showPicturePanel:)];
1147 else if ([itemIdent isEqualToString: ShowPreviewIdentifier])
1149 [item setLabel: @"Preview Window"];
1150 [item setPaletteLabel: @"Show Preview"];
1151 [item setToolTip: @"Show Preview"];
1152 //[item setImage: [NSImage imageNamed: @"pref-picture"]];
1153 [item setImage: [NSImage imageNamed: @"Brushed_Window"]];
1154 [item setTarget: self];
1155 [item setAction: @selector(showPreviewWindow:)];
1157 else if ([itemIdent isEqualToString: ShowActivityIdentifier])
1159 [item setLabel: @"Activity Window"];
1160 [item setPaletteLabel: @"Show Activity Window"];
1161 [item setToolTip: @"Show Activity Window"];
1162 [item setImage: [NSImage imageNamed: @"ActivityWindow"]];
1163 [item setTarget: self];
1164 [item setAction: @selector(showDebugOutputPanel:)];
1165 [item setAutovalidates: NO];
1167 else if ([itemIdent isEqualToString: ChooseSourceIdentifier])
1169 [item setLabel: @"Source"];
1170 [item setPaletteLabel: @"Source"];
1171 [item setToolTip: @"Choose Video Source"];
1172 [item setImage: [NSImage imageNamed: @"Source"]];
1173 [item setTarget: self];
1174 [item setAction: @selector(browseSources:)];
1184 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
1186 return [NSArray arrayWithObjects: ChooseSourceIdentifier, NSToolbarSeparatorItemIdentifier, StartEncodingIdentifier,
1187 PauseEncodingIdentifier, AddToQueueIdentifier, ShowQueueIdentifier, NSToolbarFlexibleSpaceItemIdentifier,
1188 NSToolbarSpaceItemIdentifier, ShowPictureIdentifier, ShowPreviewIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier, nil];
1191 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
1193 return [NSArray arrayWithObjects: StartEncodingIdentifier, PauseEncodingIdentifier, AddToQueueIdentifier,
1194 ChooseSourceIdentifier, ShowQueueIdentifier, ShowPictureIdentifier, ShowPreviewIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier,
1195 NSToolbarCustomizeToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier,
1196 NSToolbarSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier, nil];
1199 - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem
1201 NSString * ident = [toolbarItem itemIdentifier];
1207 hb_get_state( fHandle, &s );
1208 if (s.state == HB_STATE_SCANNING)
1211 if ([ident isEqualToString: ChooseSourceIdentifier])
1213 [toolbarItem setImage: [NSImage imageNamed: @"Stop"]];
1214 [toolbarItem setLabel: @"Cancel Scan"];
1215 [toolbarItem setPaletteLabel: @"Cancel Scanning"];
1216 [toolbarItem setToolTip: @"Cancel Scanning Source"];
1220 if ([ident isEqualToString: StartEncodingIdentifier] || [ident isEqualToString: AddToQueueIdentifier])
1225 if ([ident isEqualToString: ChooseSourceIdentifier])
1227 [toolbarItem setImage: [NSImage imageNamed: @"Source"]];
1228 [toolbarItem setLabel: @"Source"];
1229 [toolbarItem setPaletteLabel: @"Source"];
1230 [toolbarItem setToolTip: @"Choose Video Source"];
1235 hb_get_state2( fQueueEncodeLibhb, &s );
1237 if (s.state == HB_STATE_WORKING || s.state == HB_STATE_SEARCHING || s.state == HB_STATE_MUXING)
1239 if ([ident isEqualToString: StartEncodingIdentifier])
1241 [toolbarItem setImage: [NSImage imageNamed: @"Stop"]];
1242 [toolbarItem setLabel: @"Stop"];
1243 [toolbarItem setPaletteLabel: @"Stop"];
1244 [toolbarItem setToolTip: @"Stop Encoding"];
1247 if ([ident isEqualToString: PauseEncodingIdentifier])
1249 [toolbarItem setImage: [NSImage imageNamed: @"Pause"]];
1250 [toolbarItem setLabel: @"Pause"];
1251 [toolbarItem setPaletteLabel: @"Pause Encoding"];
1252 [toolbarItem setToolTip: @"Pause Encoding"];
1257 if ([ident isEqualToString: AddToQueueIdentifier])
1259 if ([ident isEqualToString: ShowPictureIdentifier])
1261 if ([ident isEqualToString: ShowPreviewIdentifier])
1265 else if (s.state == HB_STATE_PAUSED)
1267 if ([ident isEqualToString: PauseEncodingIdentifier])
1269 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
1270 [toolbarItem setLabel: @"Resume"];
1271 [toolbarItem setPaletteLabel: @"Resume Encoding"];
1272 [toolbarItem setToolTip: @"Resume Encoding"];
1275 if ([ident isEqualToString: StartEncodingIdentifier])
1277 if ([ident isEqualToString: AddToQueueIdentifier])
1279 if ([ident isEqualToString: ShowPictureIdentifier])
1281 if ([ident isEqualToString: ShowPreviewIdentifier])
1284 else if (s.state == HB_STATE_SCANNING)
1286 else if (s.state == HB_STATE_WORKDONE || s.state == HB_STATE_SCANDONE || SuccessfulScan)
1288 if ([ident isEqualToString: StartEncodingIdentifier])
1290 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
1291 if (hb_count(fHandle) > 0)
1292 [toolbarItem setLabel: @"Start Queue"];
1294 [toolbarItem setLabel: @"Start"];
1295 [toolbarItem setPaletteLabel: @"Start Encoding"];
1296 [toolbarItem setToolTip: @"Start Encoding"];
1299 if ([ident isEqualToString: AddToQueueIdentifier])
1301 if ([ident isEqualToString: ShowPictureIdentifier])
1303 if ([ident isEqualToString: ShowPreviewIdentifier])
1308 /* If there are any pending queue items, make sure the start/stop button is active */
1309 if ([ident isEqualToString: StartEncodingIdentifier] && fPendingCount > 0)
1311 if ([ident isEqualToString: ShowQueueIdentifier])
1313 if ([ident isEqualToString: ToggleDrawerIdentifier])
1315 if ([ident isEqualToString: ChooseSourceIdentifier])
1317 if ([ident isEqualToString: ShowActivityIdentifier])
1323 - (BOOL) validateMenuItem: (NSMenuItem *) menuItem
1325 SEL action = [menuItem action];
1328 hb_get_state2( fHandle, &s );
1332 if (action == @selector(addToQueue:) || action == @selector(showPicturePanel:) || action == @selector(showAddPresetPanel:))
1333 return SuccessfulScan && [fWindow attachedSheet] == nil;
1335 if (action == @selector(browseSources:))
1337 if (s.state == HB_STATE_SCANNING)
1340 return [fWindow attachedSheet] == nil;
1342 if (action == @selector(selectDefaultPreset:))
1343 return [fPresetsOutlineView selectedRow] >= 0 && [fWindow attachedSheet] == nil;
1344 if (action == @selector(Pause:))
1346 if (s.state == HB_STATE_WORKING)
1348 if(![[menuItem title] isEqualToString:@"Pause Encoding"])
1349 [menuItem setTitle:@"Pause Encoding"];
1352 else if (s.state == HB_STATE_PAUSED)
1354 if(![[menuItem title] isEqualToString:@"Resume Encoding"])
1355 [menuItem setTitle:@"Resume Encoding"];
1361 if (action == @selector(Rip:))
1363 if (s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING || s.state == HB_STATE_PAUSED)
1365 if(![[menuItem title] isEqualToString:@"Stop Encoding"])
1366 [menuItem setTitle:@"Stop Encoding"];
1369 else if (SuccessfulScan)
1371 if(![[menuItem title] isEqualToString:@"Start Encoding"])
1372 [menuItem setTitle:@"Start Encoding"];
1373 return [fWindow attachedSheet] == nil;
1379 if( action == @selector(setDefaultPreset:) )
1381 return [fPresetsOutlineView selectedRow] != -1;
1388 #pragma mark Encode Done Actions
1389 // register a test notification and make
1390 // it enabled by default
1391 #define SERVICE_NAME @"Encode Done"
1392 - (NSDictionary *)registrationDictionaryForGrowl
1394 NSDictionary *registrationDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
1395 [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_ALL,
1396 [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_DEFAULT,
1399 return registrationDictionary;
1402 -(void)showGrowlDoneNotification:(NSString *) filePath
1404 /* This end of encode action is called as each encode rolls off of the queue */
1405 NSString * finishedEncode = filePath;
1406 /* strip off the path to just show the file name */
1407 finishedEncode = [finishedEncode lastPathComponent];
1408 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] ||
1409 [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])
1411 NSString * growlMssg = [NSString stringWithFormat: @"your HandBrake encode %@ is done!",finishedEncode];
1412 [GrowlApplicationBridge
1413 notifyWithTitle:@"Put down that cocktail..."
1414 description:growlMssg
1415 notificationName:SERVICE_NAME
1423 -(void)sendToMetaX:(NSString *) filePath
1425 /* This end of encode action is called as each encode rolls off of the queue */
1426 if([[NSUserDefaults standardUserDefaults] boolForKey: @"sendToMetaX"] == YES)
1428 NSString *sendToApp = [[NSUserDefaults standardUserDefaults] objectForKey: @"SendCompletedEncodeToApp"];
1429 if (![sendToApp isEqualToString:@"None"])
1431 [self writeToActivityLog: "trying to send encode to: %s", [sendToApp UTF8String]];
1432 NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@%@%@", @"tell application \"",sendToApp,@"\" to open (POSIX file \"", filePath, @"\")"]];
1433 [myScript executeAndReturnError: nil];
1440 #pragma mark Get New Source
1442 /*Opens the source browse window, called from Open Source widgets */
1443 - (IBAction) browseSources: (id) sender
1447 hb_get_state( fHandle, &s );
1448 if (s.state == HB_STATE_SCANNING)
1450 [self cancelScanning:nil];
1455 NSOpenPanel * panel;
1457 panel = [NSOpenPanel openPanel];
1458 [panel setAllowsMultipleSelection: NO];
1459 [panel setCanChooseFiles: YES];
1460 [panel setCanChooseDirectories: YES ];
1461 NSString * sourceDirectory;
1462 if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"])
1464 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"];
1468 sourceDirectory = @"~/Desktop";
1469 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
1471 /* we open up the browse sources sheet here and call for browseSourcesDone after the sheet is closed
1472 * to evaluate whether we want to specify a title, we pass the sender in the contextInfo variable
1474 [panel beginSheetForDirectory: sourceDirectory file: nil types: nil
1475 modalForWindow: fWindow modalDelegate: self
1476 didEndSelector: @selector( browseSourcesDone:returnCode:contextInfo: )
1477 contextInfo: sender];
1480 - (void) browseSourcesDone: (NSOpenPanel *) sheet
1481 returnCode: (int) returnCode contextInfo: (void *) contextInfo
1483 /* we convert the sender content of contextInfo back into a variable called sender
1484 * mostly just for consistency for evaluation later
1486 id sender = (id)contextInfo;
1487 /* User selected a file to open */
1488 if( returnCode == NSOKButton )
1490 /* Free display name allocated previously by this code */
1491 [browsedSourceDisplayName release];
1493 NSString *scanPath = [[sheet filenames] objectAtIndex: 0];
1494 /* we set the last searched source directory in the prefs here */
1495 NSString *sourceDirectory = [scanPath stringByDeletingLastPathComponent];
1496 [[NSUserDefaults standardUserDefaults] setObject:sourceDirectory forKey:@"LastSourceDirectory"];
1497 /* we order out sheet, which is the browse window as we need to open
1498 * the title selection sheet right away
1500 [sheet orderOut: self];
1502 if (sender == fOpenSourceTitleMMenu || [[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
1504 /* We put the chosen source path in the source display text field for the
1505 * source title selection sheet in which the user specifies the specific title to be
1506 * scanned as well as the short source name in fSrcDsplyNameTitleScan just for display
1507 * purposes in the title panel
1510 [fScanSrcTitlePathField setStringValue:scanPath];
1511 NSString *displayTitlescanSourceName;
1513 if ([[scanPath lastPathComponent] isEqualToString: @"VIDEO_TS"])
1515 /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name
1516 we have to use the title->path value so we get the proper name of the volume if a physical dvd is the source*/
1517 displayTitlescanSourceName = [[scanPath stringByDeletingLastPathComponent] lastPathComponent];
1521 /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */
1522 displayTitlescanSourceName = [scanPath lastPathComponent];
1524 /* we set the source display name in the title selection dialogue */
1525 [fSrcDsplyNameTitleScan setStringValue:displayTitlescanSourceName];
1526 /* we set the attempted scans display name for main window to displayTitlescanSourceName*/
1527 browsedSourceDisplayName = [displayTitlescanSourceName retain];
1528 /* We show the actual sheet where the user specifies the title to be scanned
1529 * as we are going to do a title specific scan
1531 [self showSourceTitleScanPanel:nil];
1535 /* We are just doing a standard full source scan, so we specify "0" to libhb */
1536 NSString *path = [[sheet filenames] objectAtIndex: 0];
1538 /* We check to see if the chosen file at path is a package */
1539 if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:path])
1541 [self writeToActivityLog: "trying to open a package at: %s", [path UTF8String]];
1542 /* We check to see if this is an .eyetv package */
1543 if ([[path pathExtension] isEqualToString: @"eyetv"])
1545 [self writeToActivityLog:"trying to open eyetv package"];
1546 /* We're looking at an EyeTV package - try to open its enclosed
1548 browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain];
1550 int n = [[path stringByAppendingString: @"/"]
1551 completePathIntoString: &mpgname caseSensitive: YES
1552 matchesIntoArray: nil
1553 filterTypes: [NSArray arrayWithObject: @"mpg"]];
1556 /* Found an mpeg inside the eyetv package, make it our scan path
1557 and call performScan on the enclosed mpeg */
1559 [self writeToActivityLog:"found mpeg in eyetv package"];
1560 [self performScan:path scanTitleNum:0];
1564 /* We did not find an mpeg file in our package, so we do not call performScan */
1565 [self writeToActivityLog:"no valid mpeg in eyetv package"];
1568 /* We check to see if this is a .dvdmedia package */
1569 else if ([[path pathExtension] isEqualToString: @"dvdmedia"])
1571 /* path IS a package - but dvdmedia packages can be treaded like normal directories */
1572 browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain];
1573 [self writeToActivityLog:"trying to open dvdmedia package"];
1574 [self performScan:path scanTitleNum:0];
1578 /* The package is not an eyetv package, so we do not call performScan */
1579 [self writeToActivityLog:"unable to open package"];
1582 else // path is not a package, so we treat it as a dvd parent folder or VIDEO_TS folder
1584 /* path is not a package, so we call perform scan directly on our file */
1585 if ([[path lastPathComponent] isEqualToString: @"VIDEO_TS"])
1587 [self writeToActivityLog:"trying to open video_ts folder (video_ts folder chosen)"];
1588 /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name*/
1589 browsedSourceDisplayName = [[[path stringByDeletingLastPathComponent] lastPathComponent] retain];
1593 [self writeToActivityLog:"trying to open video_ts folder (parent directory chosen)"];
1594 /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */
1595 /* make sure we remove any path extension as this can also be an '.mpg' file */
1596 browsedSourceDisplayName = [[path lastPathComponent] retain];
1598 applyQueueToScan = NO;
1599 [self performScan:path scanTitleNum:0];
1607 - (IBAction)showAboutPanel:(id)sender
1609 NSMutableDictionary* d = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
1610 fApplicationIcon, @"ApplicationIcon",
1612 [NSApp orderFrontStandardAboutPanelWithOptions:d];
1616 /* Here we open the title selection sheet where we can specify an exact title to be scanned */
1617 - (IBAction) showSourceTitleScanPanel: (id) sender
1619 /* We default the title number to be scanned to "0" which results in a full source scan, unless the
1622 [fScanSrcTitleNumField setStringValue: @"0"];
1623 /* Show the panel */
1624 [NSApp beginSheet:fScanSrcTitlePanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
1627 - (IBAction) closeSourceTitleScanPanel: (id) sender
1629 [NSApp endSheet: fScanSrcTitlePanel];
1630 [fScanSrcTitlePanel orderOut: self];
1632 if(sender == fScanSrcTitleOpenButton)
1634 /* We setup the scan status in the main window to indicate a source title scan */
1635 [fSrcDVD2Field setStringValue: @"Opening a new source title ..."];
1636 [fScanIndicator setHidden: NO];
1637 [fScanIndicator setIndeterminate: YES];
1638 [fScanIndicator startAnimation: nil];
1640 /* We use the performScan method to actually perform the specified scan passing the path and the title
1643 applyQueueToScan = NO;
1644 [self performScan:[fScanSrcTitlePathField stringValue] scanTitleNum:[fScanSrcTitleNumField intValue]];
1648 /* Here we actually tell hb_scan to perform the source scan, using the path to source and title number*/
1649 - (void) performScan:(NSString *) scanPath scanTitleNum: (int) scanTitleNum
1652 /* use a bool to determine whether or not we can decrypt using vlc */
1653 BOOL cancelScanDecrypt = 0;
1655 NSString *path = scanPath;
1656 HBDVDDetector *detector = [HBDVDDetector detectorForPath:path];
1658 // Notify ChapterTitles that there's no title
1659 [fChapterTitlesDelegate resetWithTitle:nil];
1660 [fChapterTable reloadData];
1662 // Notify Subtitles that there's no title
1663 [fSubtitlesDelegate resetWithTitle:nil];
1664 [fSubtitlesTable reloadData];
1666 [self enableUI: NO];
1668 if( [detector isVideoDVD] )
1670 // The chosen path was actually on a DVD, so use the raw block
1671 // device path instead.
1672 path = [detector devicePath];
1673 [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]];
1675 /* lets check for vlc here to make sure we have a dylib available to use for decrypting */
1676 void *dvdcss = dlopen("libdvdcss.2.dylib", RTLD_LAZY);
1679 /*compatible vlc not found, so we set the bool to cancel scanning to 1 */
1680 cancelScanDecrypt = 1;
1681 [self writeToActivityLog: "VLC app not found for decrypting physical dvd"];
1683 status = NSRunAlertPanel(@"HandBrake could not find VLC or your VLC is incompatible (Note: 32 bit vlc is not compatible with 64 bit HandBrake and vice-versa).",@"Please download and install VLC media player if you wish to read encrypted DVDs.", @"Get VLC", @"Cancel Scan", @"Attempt Scan Anyway");
1684 [NSApp requestUserAttention:NSCriticalRequest];
1686 if (status == NSAlertDefaultReturn)
1688 /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
1689 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/vlc/download-macosx.html"]];
1691 else if (status == NSAlertAlternateReturn)
1693 /* User chose to cancel the scan */
1694 [self writeToActivityLog: "cannot open physical dvd , scan cancelled"];
1698 /* 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 */
1699 cancelScanDecrypt = 0;
1700 [self writeToActivityLog: "user overrode vlc warning -trying to open physical dvd without decryption"];
1706 /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */
1707 [self writeToActivityLog: "VLC app found for decrypting physical dvd"];
1713 if (cancelScanDecrypt == 0)
1715 /* we actually pass the scan off to libhb here */
1716 /* If there is no title number passed to scan, we use "0"
1717 * which causes the default behavior of a full source scan
1723 if (scanTitleNum > 0)
1725 [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum];
1727 /* We use our advance pref to determine how many previews to scan */
1728 int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue];
1729 /* set title to NULL */
1731 hb_scan( fHandle, [path UTF8String], scanTitleNum, hb_num_previews, 1 );
1732 [fSrcDVD2Field setStringValue:@"Scanning new source ..."];
1736 - (IBAction) cancelScanning:(id)sender
1738 hb_scan_stop(fHandle);
1741 - (IBAction) showNewScan:(id)sender
1745 int feature_title=0; // Used to store the main feature title
1747 list = hb_get_titles( fHandle );
1749 if( !hb_list_count( list ) )
1751 /* We display a message if a valid dvd source was not chosen */
1752 [fSrcDVD2Field setStringValue: @"No Valid Source Found"];
1753 SuccessfulScan = NO;
1755 // Notify ChapterTitles that there's no title
1756 [fSubtitlesDelegate resetWithTitle:nil];
1757 [fSubtitlesTable reloadData];
1759 // Notify Subtitles that there's no title
1760 [fChapterTitlesDelegate resetWithTitle:nil];
1761 [fChapterTable reloadData];
1765 if (applyQueueToScan == YES)
1767 /* we are a rescan of an existing queue item and need to apply the queued settings to the scan */
1768 [self writeToActivityLog: "showNewScan: This is a queued item rescan"];
1771 else if (applyQueueToScan == NO)
1773 [self writeToActivityLog: "showNewScan: This is a new source item scan"];
1777 [self writeToActivityLog: "showNewScan: cannot grok scan status"];
1780 /* We increment the successful scancount here by one,
1781 which we use at the end of this function to tell the gui
1782 if this is the first successful scan since launch and whether
1783 or not we should set all settings to the defaults */
1784 currentSuccessfulScanCount++;
1786 [[fWindow toolbar] validateVisibleItems];
1788 [fSrcTitlePopUp removeAllItems];
1789 for( int i = 0; i < hb_list_count( list ); i++ )
1791 title = (hb_title_t *) hb_list_item( list, i );
1793 currentSource = [NSString stringWithUTF8String: title->name];
1794 /*Set DVD Name at top of window with the browsedSourceDisplayName grokked right before -performScan */
1795 if (!browsedSourceDisplayName)
1797 browsedSourceDisplayName = @"NoNameDetected";
1799 [fSrcDVD2Field setStringValue:browsedSourceDisplayName];
1801 /* If its a queue rescan for edit, get the queue item output path */
1802 /* if not, its a new source scan. */
1803 /* Check to see if the last destination has been set,use if so, if not, use Desktop */
1804 if (applyQueueToScan == YES)
1806 [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@", [[QueueFileArray objectAtIndex:fqueueEditRescanItemNum] objectForKey:@"DestinationPath"]]];
1808 else if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"])
1810 [fDstFile2Field setStringValue: [NSString stringWithFormat:
1811 @"%@/%@.mp4", [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"],[browsedSourceDisplayName stringByDeletingPathExtension]]];
1815 [fDstFile2Field setStringValue: [NSString stringWithFormat:
1816 @"%@/Desktop/%@.mp4", NSHomeDirectory(),[browsedSourceDisplayName stringByDeletingPathExtension]]];
1819 /* See if this is the main feature according to libhb */
1820 if (title->index == title->job->feature)
1825 [fSrcTitlePopUp addItemWithTitle: [NSString
1826 stringWithFormat: @"%s %d - %02dh%02dm%02ds",
1827 title->name,title->index, title->hours, title->minutes,
1831 /* if we are a stream, select the first title */
1832 if (title->type == HB_STREAM_TYPE)
1834 [fSrcTitlePopUp selectItemAtIndex: 0];
1838 /* if not then select the main feature title */
1839 [fSrcTitlePopUp selectItemAtIndex: feature_title];
1841 [self titlePopUpChanged:nil];
1843 SuccessfulScan = YES;
1844 [self enableUI: YES];
1846 /* if its the initial successful scan after awakeFromNib */
1847 if (currentSuccessfulScanCount == 1)
1849 [self encodeStartStopPopUpChanged:nil];
1851 [self selectDefaultPreset:nil];
1853 // Open preview window now if it was visible when HB was closed
1854 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PreviewWindowIsOpen"])
1855 [self showPreviewWindow:nil];
1857 // Open picture sizing window now if it was visible when HB was closed
1858 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PictureSizeWindowIsOpen"])
1859 [self showPicturePanel:nil];
1862 if (applyQueueToScan == YES)
1864 /* we are a rescan of an existing queue item and need to apply the queued settings to the scan */
1865 [self writeToActivityLog: "showNewScan: calling applyQueueSettingsToMainWindow"];
1866 [self applyQueueSettingsToMainWindow:nil];
1877 #pragma mark New Output Destination
1879 - (IBAction) browseFile: (id) sender
1881 /* Open a panel to let the user choose and update the text field */
1882 NSSavePanel * panel = [NSSavePanel savePanel];
1883 /* We get the current file name and path from the destination field here */
1884 [panel beginSheetForDirectory: [[fDstFile2Field stringValue] stringByDeletingLastPathComponent] file: [[fDstFile2Field stringValue] lastPathComponent]
1885 modalForWindow: fWindow modalDelegate: self
1886 didEndSelector: @selector( browseFileDone:returnCode:contextInfo: )
1890 - (void) browseFileDone: (NSSavePanel *) sheet
1891 returnCode: (int) returnCode contextInfo: (void *) contextInfo
1893 if( returnCode == NSOKButton )
1895 [fDstFile2Field setStringValue: [sheet filename]];
1896 /* Save this path to the prefs so that on next browse destination window it opens there */
1897 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1898 [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
1904 #pragma mark Main Window Control
1906 - (IBAction) openMainWindow: (id) sender
1908 [fWindow makeKeyAndOrderFront:nil];
1911 - (BOOL) windowShouldClose: (id) sender
1916 - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
1919 [fWindow makeKeyAndOrderFront:nil];
1927 - (NSSize) drawerWillResizeContents:(NSDrawer *) drawer toSize:(NSSize) contentSize {
1928 [[NSUserDefaults standardUserDefaults] setObject:NSStringFromSize( contentSize ) forKey:@"Drawer Size"];
1933 #pragma mark Queue File
1935 - (void) loadQueueFile {
1936 /* We declare the default NSFileManager into fileManager */
1937 NSFileManager * fileManager = [NSFileManager defaultManager];
1938 /*We define the location of the user presets file */
1939 QueueFile = @"~/Library/Application Support/HandBrake/Queue.plist";
1940 QueueFile = [[QueueFile stringByExpandingTildeInPath]retain];
1941 /* We check for the Queue.plist */
1942 if ([fileManager fileExistsAtPath:QueueFile] == 0)
1944 [fileManager createFileAtPath:QueueFile contents:nil attributes:nil];
1947 QueueFileArray = [[NSMutableArray alloc] initWithContentsOfFile:QueueFile];
1948 /* lets check to see if there is anything in the queue file .plist */
1949 if (nil == QueueFileArray)
1951 /* if not, then lets initialize an empty array */
1952 QueueFileArray = [[NSMutableArray alloc] init];
1956 /* ONLY clear out encoded items if we are single instance */
1957 if (hbInstanceNum == 1)
1959 [self clearQueueEncodedItems];
1964 - (void)addQueueFileItem
1966 [QueueFileArray addObject:[self createQueueFileItem]];
1967 [self saveQueueFileItem];
1971 - (void) removeQueueFileItem:(int) queueItemToRemove
1973 [QueueFileArray removeObjectAtIndex:queueItemToRemove];
1974 [self saveQueueFileItem];
1978 - (void)saveQueueFileItem
1980 [QueueFileArray writeToFile:QueueFile atomically:YES];
1981 [fQueueController setQueueArray: QueueFileArray];
1982 [self getQueueStats];
1985 - (void)getQueueStats
1987 /* lets get the stats on the status of the queue array */
1989 fEncodingQueueItem = 0;
1991 fCompletedCount = 0;
1995 /* We use a number system to set the encode status of the queue item
1997 * 0 == already encoded
1998 * 1 == is being encoded
1999 * 2 == is yet to be encoded
2004 NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2006 while (tempObject = [enumerator nextObject])
2008 NSDictionary *thisQueueDict = tempObject;
2009 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 0) // Completed
2013 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 1) // being encoded
2016 fEncodingQueueItem = i;
2017 /* check to see if we are the instance doing this encoding */
2018 if ([thisQueueDict objectForKey:@"EncodingPID"] && [[thisQueueDict objectForKey:@"EncodingPID"] intValue] == pidNum)
2020 currentQueueEncodeIndex = i;
2024 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 2) // pending
2028 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 3) // cancelled
2035 /* Set the queue status field in the main window */
2036 NSMutableString * string;
2037 if (fPendingCount == 1)
2039 string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode pending in the queue", @"" ), fPendingCount];
2043 string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode(s) pending in the queue", @"" ), fPendingCount];
2045 [fQueueStatus setStringValue:string];
2048 /* Used to get the next pending queue item index and return it if found */
2049 - (int)getNextPendingQueueIndex
2051 /* initialize nextPendingIndex to -1, this value tells incrementQueueItemDone that there are no pending items in the queue */
2052 int nextPendingIndex = -1;
2053 BOOL nextPendingFound = NO;
2054 NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2057 while (tempObject = [enumerator nextObject])
2059 NSDictionary *thisQueueDict = tempObject;
2060 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 2 && nextPendingFound == NO) // pending
2062 nextPendingFound = YES;
2063 nextPendingIndex = [QueueFileArray indexOfObject: tempObject];
2064 [self writeToActivityLog: "getNextPendingQueueIndex next pending encod index is:%d", nextPendingIndex];
2068 return nextPendingIndex;
2072 /* This method will set any item marked as encoding back to pending
2073 * currently used right after a queue reload
2075 - (void) setQueueEncodingItemsAsPending
2077 NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2079 NSMutableArray *tempArray;
2080 tempArray = [NSMutableArray array];
2081 /* we look here to see if the preset is we move on to the next one */
2082 while ( tempObject = [enumerator nextObject] )
2084 /* If the queue item is marked as "encoding" (1)
2085 * then change its status back to pending (2) which effectively
2086 * puts it back into the queue to be encoded
2088 if ([[tempObject objectForKey:@"Status"] intValue] == 1)
2090 [tempObject setObject:[NSNumber numberWithInt: 2] forKey:@"Status"];
2092 [tempArray addObject:tempObject];
2095 [QueueFileArray setArray:tempArray];
2096 [self saveQueueFileItem];
2100 /* This method will clear the queue of any encodes that are not still pending
2101 * this includes both successfully completed encodes as well as cancelled encodes */
2102 - (void) clearQueueEncodedItems
2104 NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2106 NSMutableArray *tempArray;
2107 tempArray = [NSMutableArray array];
2108 /* we look here to see if the preset is we move on to the next one */
2109 while ( tempObject = [enumerator nextObject] )
2111 /* If the queue item is either completed (0) or cancelled (3) from the
2112 * last session, then we put it in tempArray to be deleted from QueueFileArray.
2113 * NOTE: this means we retain pending (2) and also an item that is marked as
2114 * still encoding (1). If the queue has an item that is still marked as encoding
2115 * from a previous session, we can conlude that HB was either shutdown, or crashed
2116 * during the encodes so we keep it and tell the user in the "Load Queue Alert"
2118 if ([[tempObject objectForKey:@"Status"] intValue] == 0 || [[tempObject objectForKey:@"Status"] intValue] == 3)
2120 [tempArray addObject:tempObject];
2124 [QueueFileArray removeObjectsInArray:tempArray];
2125 [self saveQueueFileItem];
2128 /* This method will clear the queue of all encodes. effectively creating an empty queue */
2129 - (void) clearQueueAllItems
2131 NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2133 NSMutableArray *tempArray;
2134 tempArray = [NSMutableArray array];
2135 /* we look here to see if the preset is we move on to the next one */
2136 while ( tempObject = [enumerator nextObject] )
2138 [tempArray addObject:tempObject];
2141 [QueueFileArray removeObjectsInArray:tempArray];
2142 [self saveQueueFileItem];
2145 /* This method will duplicate prepareJob however into the
2146 * queue .plist instead of into the job structure so it can
2147 * be recalled later */
2148 - (NSDictionary *)createQueueFileItem
2150 NSMutableDictionary *queueFileJob = [[NSMutableDictionary alloc] init];
2152 hb_list_t * list = hb_get_titles( fHandle );
2153 hb_title_t * title = (hb_title_t *) hb_list_item( list,
2154 [fSrcTitlePopUp indexOfSelectedItem] );
2155 hb_job_t * job = title->job;
2159 /* We use a number system to set the encode status of the queue item
2160 * 0 == already encoded
2161 * 1 == is being encoded
2162 * 2 == is yet to be encoded
2165 [queueFileJob setObject:[NSNumber numberWithInt:2] forKey:@"Status"];
2166 /* Source and Destination Information */
2168 [queueFileJob setObject:[NSString stringWithUTF8String: title->path] forKey:@"SourcePath"];
2169 [queueFileJob setObject:[fSrcDVD2Field stringValue] forKey:@"SourceName"];
2170 [queueFileJob setObject:[NSNumber numberWithInt:title->index] forKey:@"TitleNumber"];
2171 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcAnglePopUp indexOfSelectedItem] + 1] forKey:@"TitleAngle"];
2173 /* Determine and set a variable to tell hb what start and stop times to use ... chapters vs seconds */
2174 if( [fEncodeStartStopPopUp indexOfSelectedItem] == 0 )
2176 [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"fEncodeStartStop"];
2178 else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 1)
2180 [queueFileJob setObject:[NSNumber numberWithInt:1] forKey:@"fEncodeStartStop"];
2182 else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 2)
2184 [queueFileJob setObject:[NSNumber numberWithInt:2] forKey:@"fEncodeStartStop"];
2186 /* Chapter encode info */
2187 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterStartPopUp indexOfSelectedItem] + 1] forKey:@"ChapterStart"];
2188 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterEndPopUp indexOfSelectedItem] + 1] forKey:@"ChapterEnd"];
2189 /* Time (pts) encode info */
2190 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcTimeStartEncodingField intValue]] forKey:@"StartSeconds"];
2191 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcTimeEndEncodingField intValue] - [fSrcTimeStartEncodingField intValue]] forKey:@"StopSeconds"];
2192 /* Frame number encode info */
2193 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcFrameStartEncodingField intValue]] forKey:@"StartFrame"];
2194 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcFrameEndEncodingField intValue] - [fSrcFrameStartEncodingField intValue]] forKey:@"StopFrame"];
2197 /* The number of seek points equals the number of seconds announced in the title as that is our current granularity */
2198 int title_duration_seconds = (title->hours * 3600) + (title->minutes * 60) + (title->seconds);
2199 [queueFileJob setObject:[NSNumber numberWithInt:title_duration_seconds] forKey:@"SourceTotalSeconds"];
2201 [queueFileJob setObject:[fDstFile2Field stringValue] forKey:@"DestinationPath"];
2203 /* Lets get the preset info if there is any */
2204 [queueFileJob setObject:[fPresetSelectedDisplay stringValue] forKey:@"PresetName"];
2205 [queueFileJob setObject:[NSNumber numberWithInt:[fPresetsOutlineView selectedRow]] forKey:@"PresetIndexNum"];
2207 [queueFileJob setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"];
2208 /* Chapter Markers*/
2209 /* If we have only one chapter or a title without chapters, set chapter markers to off */
2210 if ([fSrcChapterStartPopUp indexOfSelectedItem] == [fSrcChapterEndPopUp indexOfSelectedItem])
2212 [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"ChapterMarkers"];
2216 [queueFileJob setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"];
2219 /* We need to get the list of chapter names to put into an array and store
2220 * in our queue, so they can be reapplied in prepareJob when this queue
2221 * item comes up if Chapter Markers is set to on.
2224 NSMutableArray *ChapterNamesArray = [[NSMutableArray alloc] init];
2225 int chaptercount = hb_list_count( fTitle->list_chapter );
2226 for( i = 0; i < chaptercount; i++ )
2228 hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item( fTitle->list_chapter, i );
2229 if( chapter != NULL )
2231 [ChapterNamesArray addObject:[NSString stringWithCString:chapter->title encoding:NSUTF8StringEncoding]];
2234 [queueFileJob setObject:[NSMutableArray arrayWithArray: ChapterNamesArray] forKey:@"ChapterNames"];
2235 [ChapterNamesArray autorelease];
2237 /* Allow Mpeg4 64 bit formatting +4GB file sizes */
2238 [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"];
2239 /* Mux mp4 with http optimization */
2240 [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"];
2241 /* Add iPod uuid atom */
2242 [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"];
2246 [queueFileJob setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"];
2247 /* x264 Option String */
2248 [queueFileJob setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"];
2250 [queueFileJob setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"];
2251 [queueFileJob setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
2252 [queueFileJob setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"];
2253 [queueFileJob setObject:[NSNumber numberWithFloat:[fVidQualityRFField floatValue]] forKey:@"VideoQualitySlider"];
2255 [queueFileJob setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"];
2256 [queueFileJob setObject:[NSNumber numberWithInt:[fFrameratePfrCheck state]] forKey:@"VideoFrameratePFR"];
2258 /* 2 Pass Encoding */
2259 [queueFileJob setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"];
2260 /* Turbo 2 pass Encoding fVidTurboPassCheck*/
2261 [queueFileJob setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"];
2263 /* Picture Sizing */
2264 /* Use Max Picture settings for whatever the dvd is.*/
2265 [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
2266 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
2267 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
2268 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
2269 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"];
2270 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->modulus] forKey:@"PictureModulus"];
2271 /* if we are custom anamorphic, store the exact storage, par and display dims */
2272 if (fTitle->job->anamorphic.mode == 3)
2274 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->modulus] forKey:@"PicturePARModulus"];
2276 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PicturePARStorageWidth"];
2277 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PicturePARStorageHeight"];
2279 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.par_width] forKey:@"PicturePARPixelWidth"];
2280 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.par_height] forKey:@"PicturePARPixelHeight"];
2282 [queueFileJob setObject:[NSNumber numberWithFloat:fTitle->job->anamorphic.dar_width] forKey:@"PicturePARDisplayWidth"];
2283 [queueFileJob setObject:[NSNumber numberWithFloat:fTitle->job->anamorphic.dar_height] forKey:@"PicturePARDisplayHeight"];
2286 NSString * pictureSummary;
2287 pictureSummary = [fPictureSizeField stringValue];
2288 [queueFileJob setObject:pictureSummary forKey:@"PictureSizingSummary"];
2289 /* Set crop settings here */
2290 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
2291 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
2292 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
2293 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
2294 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
2296 /* Picture Filters */
2297 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"];
2298 [queueFileJob setObject:[fPictureController detelecineCustomString] forKey:@"PictureDetelecineCustom"];
2300 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController useDecomb]] forKey:@"PictureDecombDeinterlace"];
2301 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"];
2302 [queueFileJob setObject:[fPictureController decombCustomString] forKey:@"PictureDecombCustom"];
2304 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"];
2305 [queueFileJob setObject:[fPictureController deinterlaceCustomString] forKey:@"PictureDeinterlaceCustom"];
2307 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"];
2308 [queueFileJob setObject:[fPictureController denoiseCustomString] forKey:@"PictureDenoiseCustom"];
2310 [queueFileJob setObject:[NSString stringWithFormat:@"%d",[fPictureController deblock]] forKey:@"PictureDeblock"];
2312 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController grayscale]] forKey:@"VideoGrayScale"];
2315 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
2317 [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang1PopUp indexOfSelectedItem]] forKey:@"Audio1Track"];
2318 [queueFileJob setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"Audio1TrackDescription"];
2319 [queueFileJob setObject:[fAudTrack1CodecPopUp titleOfSelectedItem] forKey:@"Audio1Encoder"];
2320 [queueFileJob setObject:[fAudTrack1MixPopUp titleOfSelectedItem] forKey:@"Audio1Mixdown"];
2321 [queueFileJob setObject:[fAudTrack1RatePopUp titleOfSelectedItem] forKey:@"Audio1Samplerate"];
2322 [queueFileJob setObject:[fAudTrack1BitratePopUp titleOfSelectedItem] forKey:@"Audio1Bitrate"];
2323 [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack1DrcSlider floatValue]] forKey:@"Audio1TrackDRCSlider"];
2325 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
2327 [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang2PopUp indexOfSelectedItem]] forKey:@"Audio2Track"];
2328 [queueFileJob setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"Audio2TrackDescription"];
2329 [queueFileJob setObject:[fAudTrack2CodecPopUp titleOfSelectedItem] forKey:@"Audio2Encoder"];
2330 [queueFileJob setObject:[fAudTrack2MixPopUp titleOfSelectedItem] forKey:@"Audio2Mixdown"];
2331 [queueFileJob setObject:[fAudTrack2RatePopUp titleOfSelectedItem] forKey:@"Audio2Samplerate"];
2332 [queueFileJob setObject:[fAudTrack2BitratePopUp titleOfSelectedItem] forKey:@"Audio2Bitrate"];
2333 [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack2DrcSlider floatValue]] forKey:@"Audio2TrackDRCSlider"];
2335 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
2337 [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang3PopUp indexOfSelectedItem]] forKey:@"Audio3Track"];
2338 [queueFileJob setObject:[fAudLang3PopUp titleOfSelectedItem] forKey:@"Audio3TrackDescription"];
2339 [queueFileJob setObject:[fAudTrack3CodecPopUp titleOfSelectedItem] forKey:@"Audio3Encoder"];
2340 [queueFileJob setObject:[fAudTrack3MixPopUp titleOfSelectedItem] forKey:@"Audio3Mixdown"];
2341 [queueFileJob setObject:[fAudTrack3RatePopUp titleOfSelectedItem] forKey:@"Audio3Samplerate"];
2342 [queueFileJob setObject:[fAudTrack3BitratePopUp titleOfSelectedItem] forKey:@"Audio3Bitrate"];
2343 [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack3DrcSlider floatValue]] forKey:@"Audio3TrackDRCSlider"];
2345 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
2347 [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang4PopUp indexOfSelectedItem]] forKey:@"Audio4Track"];
2348 [queueFileJob setObject:[fAudLang4PopUp titleOfSelectedItem] forKey:@"Audio4TrackDescription"];
2349 [queueFileJob setObject:[fAudTrack4CodecPopUp titleOfSelectedItem] forKey:@"Audio4Encoder"];
2350 [queueFileJob setObject:[fAudTrack4MixPopUp titleOfSelectedItem] forKey:@"Audio4Mixdown"];
2351 [queueFileJob setObject:[fAudTrack4RatePopUp titleOfSelectedItem] forKey:@"Audio4Samplerate"];
2352 [queueFileJob setObject:[fAudTrack4BitratePopUp titleOfSelectedItem] forKey:@"Audio4Bitrate"];
2353 [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack4DrcSlider floatValue]] forKey:@"Audio4TrackDRCSlider"];
2357 NSMutableArray *subtitlesArray = [[NSMutableArray alloc] initWithArray:[fSubtitlesDelegate getSubtitleArray] copyItems:YES];
2358 [queueFileJob setObject:[NSArray arrayWithArray: subtitlesArray] forKey:@"SubtitleList"];
2359 [subtitlesArray autorelease];
2361 /* Now we go ahead and set the "job->values in the plist for passing right to fQueueEncodeLibhb */
2363 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterStartPopUp indexOfSelectedItem] + 1] forKey:@"JobChapterStart"];
2365 [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterEndPopUp indexOfSelectedItem] + 1] forKey:@"JobChapterEnd"];
2368 [queueFileJob setObject:[NSNumber numberWithInt:[[fDstFormatPopUp selectedItem] tag]] forKey:@"JobFileFormatMux"];
2372 [queueFileJob setObject:[NSNumber numberWithInt:[[fVidEncoderPopUp selectedItem] tag]] forKey:@"JobVideoEncoderVcodec"];
2375 [queueFileJob setObject:[NSNumber numberWithInt:[fVidRatePopUp indexOfSelectedItem]] forKey:@"JobIndexVideoFramerate"];
2376 [queueFileJob setObject:[NSNumber numberWithInt:title->rate] forKey:@"JobVrate"];
2377 [queueFileJob setObject:[NSNumber numberWithInt:title->rate_base] forKey:@"JobVrateBase"];
2379 /* Picture Sizing */
2380 /* Use Max Picture settings for whatever the dvd is.*/
2381 [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
2382 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
2383 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
2384 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
2385 [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"];
2387 /* Set crop settings here */
2388 [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
2389 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
2390 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
2391 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
2392 [queueFileJob setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
2396 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
2398 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1CodecPopUp selectedItem] tag]] forKey:@"JobAudio1Encoder"];
2399 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1MixPopUp selectedItem] tag]] forKey:@"JobAudio1Mixdown"];
2400 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1RatePopUp selectedItem] tag]] forKey:@"JobAudio1Samplerate"];
2401 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1BitratePopUp selectedItem] tag]] forKey:@"JobAudio1Bitrate"];
2403 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
2405 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2CodecPopUp selectedItem] tag]] forKey:@"JobAudio2Encoder"];
2406 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2MixPopUp selectedItem] tag]] forKey:@"JobAudio2Mixdown"];
2407 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2RatePopUp selectedItem] tag]] forKey:@"JobAudio2Samplerate"];
2408 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2BitratePopUp selectedItem] tag]] forKey:@"JobAudio2Bitrate"];
2410 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
2412 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3CodecPopUp selectedItem] tag]] forKey:@"JobAudio3Encoder"];
2413 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3MixPopUp selectedItem] tag]] forKey:@"JobAudio3Mixdown"];
2414 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3RatePopUp selectedItem] tag]] forKey:@"JobAudio3Samplerate"];
2415 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3BitratePopUp selectedItem] tag]] forKey:@"JobAudio3Bitrate"];
2417 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
2419 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4CodecPopUp selectedItem] tag]] forKey:@"JobAudio4Encoder"];
2420 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4MixPopUp selectedItem] tag]] forKey:@"JobAudio4Mixdown"];
2421 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4RatePopUp selectedItem] tag]] forKey:@"JobAudio4Samplerate"];
2422 [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4BitratePopUp selectedItem] tag]] forKey:@"JobAudio4Bitrate"];
2425 /* we need to auto relase the queueFileJob and return it */
2426 [queueFileJob autorelease];
2427 return queueFileJob;
2431 /* this is actually called from the queue controller to modify the queue array and return it back to the queue controller */
2432 - (void)moveObjectsInQueueArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
2434 NSUInteger index = [indexSet lastIndex];
2435 NSUInteger aboveInsertIndexCount = 0;
2438 NSUInteger removeIndex;
2440 if (index >= insertIndex)
2442 removeIndex = index + aboveInsertIndexCount;
2443 aboveInsertIndexCount++;
2447 removeIndex = index;
2451 id object = [[QueueFileArray objectAtIndex:removeIndex] retain];
2452 [QueueFileArray removeObjectAtIndex:removeIndex];
2453 [QueueFileArray insertObject:object atIndex:insertIndex];
2456 index = [indexSet indexLessThanIndex:index];
2458 /* We save all of the Queue data here
2459 * and it also gets sent back to the queue controller*/
2460 [self saveQueueFileItem];
2466 #pragma mark Queue Job Processing
2468 - (void) incrementQueueItemDone:(int) queueItemDoneIndexNum
2470 /* Mark the encode just finished as done (status 0)*/
2471 [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:0] forKey:@"Status"];
2473 /* We save all of the Queue data here */
2474 [self saveQueueFileItem];
2476 /* Since we have now marked a queue item as done
2477 * we can go ahead and increment currentQueueEncodeIndex
2478 * so that if there is anything left in the queue we can
2479 * go ahead and move to the next item if we want to */
2480 int queueItems = [QueueFileArray count];
2481 /* Check to see if there are any more pending items in the queue */
2482 int newQueueItemIndex = [self getNextPendingQueueIndex];
2483 /* If we still have more pending items in our queue, lets go to the next one */
2484 if (newQueueItemIndex >= 0 && newQueueItemIndex < queueItems)
2486 /*Set our currentQueueEncodeIndex now to the newly found Pending encode as we own it */
2487 currentQueueEncodeIndex = newQueueItemIndex;
2488 /* now we mark the queue item as Status = 1 ( being encoded ) so another instance can not come along and try to scan it while we are scanning */
2489 [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:1] forKey:@"Status"];
2490 [self writeToActivityLog: "incrementQueueItemDone new pending items found: %d", currentQueueEncodeIndex];
2491 [self saveQueueFileItem];
2492 /* now we can go ahead and scan the new pending queue item */
2493 [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
2498 [self writeToActivityLog: "incrementQueueItemDone there are no more pending encodes"];
2502 /* Here we actually tell hb_scan to perform the source scan, using the path to source and title number*/
2503 - (void) performNewQueueScan:(NSString *) scanPath scanTitleNum: (int) scanTitleNum
2505 /* Tell HB to output a new activity log file for this encode */
2506 [outputPanel startEncodeLog:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"]];
2508 /* We now flag the queue item as being owned by this instance of HB using the PID */
2509 [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:pidNum] forKey:@"EncodingPID"];
2510 /* Get the currentQueueEncodeNameString from the queue item to display in the status field */
2511 currentQueueEncodeNameString = [[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"] lastPathComponent]retain];
2512 /* We save all of the Queue data here */
2513 [self saveQueueFileItem];
2515 /* use a bool to determine whether or not we can decrypt using vlc */
2516 BOOL cancelScanDecrypt = 0;
2517 /* set the bool so that showNewScan knows to apply the appropriate queue
2518 * settings as this is a queue rescan
2520 NSString *path = scanPath;
2521 HBDVDDetector *detector = [HBDVDDetector detectorForPath:path];
2523 if( [detector isVideoDVD] )
2525 // The chosen path was actually on a DVD, so use the raw block
2526 // device path instead.
2527 path = [detector devicePath];
2528 [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]];
2530 /* lets check for vlc here to make sure we have a dylib available to use for decrypting */
2531 void *dvdcss = dlopen("libdvdcss.2.dylib", RTLD_LAZY);
2534 /*vlc not found in /Applications so we set the bool to cancel scanning to 1 */
2535 cancelScanDecrypt = 1;
2536 [self writeToActivityLog: "VLC app not found for decrypting physical dvd"];
2538 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");
2539 [NSApp requestUserAttention:NSCriticalRequest];
2541 if (status == NSAlertDefaultReturn)
2543 /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
2544 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/"]];
2546 else if (status == NSAlertAlternateReturn)
2548 /* User chose to cancel the scan */
2549 [self writeToActivityLog: "cannot open physical dvd , scan cancelled"];
2553 /* 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 */
2554 cancelScanDecrypt = 0;
2555 [self writeToActivityLog: "user overrode vlc warning -trying to open physical dvd without decryption"];
2561 /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */
2563 [self writeToActivityLog: "VLC app found for decrypting physical dvd"];
2567 if (cancelScanDecrypt == 0)
2569 /* we actually pass the scan off to libhb here */
2570 /* If there is no title number passed to scan, we use "0"
2571 * which causes the default behavior of a full source scan
2577 if (scanTitleNum > 0)
2579 [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum];
2582 /* We use our advance pref to determine how many previews to scan */
2583 int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue];
2584 hb_scan( fQueueEncodeLibhb, [path UTF8String], scanTitleNum, hb_num_previews, 0 );
2588 /* This assumes that we have re-scanned and loaded up a new queue item to send to libhb as fQueueEncodeLibhb */
2589 - (void) processNewQueueEncode
2591 hb_list_t * list = hb_get_titles( fQueueEncodeLibhb );
2592 hb_title_t * title = (hb_title_t *) hb_list_item( list,0 ); // is always zero since now its a single title scan
2593 hb_job_t * job = title->job;
2595 if( !hb_list_count( list ) )
2597 [self writeToActivityLog: "processNewQueueEncode WARNING nothing found in the title list"];
2600 NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex];
2601 [self writeToActivityLog: "Preset: %s", [[queueToApply objectForKey:@"PresetName"] UTF8String]];
2602 [self writeToActivityLog: "processNewQueueEncode number of passes expected is: %d", ([[queueToApply objectForKey:@"VideoTwoPass"] intValue] + 1)];
2603 job->file = [[queueToApply objectForKey:@"DestinationPath"] UTF8String];
2607 * If scanning we need to do some extra setup of the job.
2609 if( job->indepth_scan == 1 )
2614 * When subtitle scan is enabled do a fast pre-scan job
2615 * which will determine which subtitles to enable, if any.
2618 x264opts_tmp = job->x264opts;
2620 job->x264opts = NULL;
2622 job->indepth_scan = 1;
2626 * Add the pre-scan job
2628 hb_add( fQueueEncodeLibhb, job );
2629 job->x264opts = x264opts_tmp;
2633 if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 )
2635 job->indepth_scan = 0;
2641 hb_add( fQueueEncodeLibhb, job );
2645 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
2646 strcpy(job->x264opts, [[queueToApply objectForKey:@"x264Option"] UTF8String]);
2648 hb_add( fQueueEncodeLibhb, job );
2653 job->indepth_scan = 0;
2656 hb_add( fQueueEncodeLibhb, job );
2659 NSString *destinationDirectory = [[queueToApply objectForKey:@"DestinationPath"] stringByDeletingLastPathComponent];
2660 [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
2661 /* Lets mark our new encode as 1 or "Encoding" */
2662 [queueToApply setObject:[NSNumber numberWithInt:1] forKey:@"Status"];
2663 [self saveQueueFileItem];
2665 /* we need to clean up the subtitle tracks after the job(s) have been set */
2666 int num_subtitle_tracks = hb_list_count(job->list_subtitle);
2668 for(ii = 0; ii < num_subtitle_tracks; ii++)
2670 hb_subtitle_t * subtitle;
2671 subtitle = (hb_subtitle_t *)hb_list_item(job->list_subtitle, 0);
2674 hb_list_rem(job->list_subtitle, subtitle);
2678 /* We should be all setup so let 'er rip */
2685 #pragma mark Queue Item Editing
2687 /* Rescans the chosen queue item back into the main window */
2688 - (void)rescanQueueItemToMainWindow:(NSString *) scanPath scanTitleNum: (int) scanTitleNum selectedQueueItem: (int) selectedQueueItem
2690 fqueueEditRescanItemNum = selectedQueueItem;
2691 [self writeToActivityLog: "rescanQueueItemToMainWindow: Re-scanning queue item at index:%d",fqueueEditRescanItemNum];
2692 applyQueueToScan = YES;
2693 /* Set the browsedSourceDisplayName for showNewScan */
2694 browsedSourceDisplayName = [[QueueFileArray objectAtIndex:fqueueEditRescanItemNum] objectForKey:@"SourceName"];
2695 [self performScan:scanPath scanTitleNum:scanTitleNum];
2699 /* We use this method after a queue item rescan for edit.
2700 * it largely mirrors -selectPreset in terms of structure.
2701 * Assumes that a queue item has been reloaded into the main window.
2703 - (IBAction)applyQueueSettingsToMainWindow:(id)sender
2705 NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:fqueueEditRescanItemNum];
2706 hb_job_t * job = fTitle->job;
2709 [self writeToActivityLog: "applyQueueSettingsToMainWindow: queue item found"];
2711 /* Set title number and chapters */
2712 /* since the queue only scans a single title, its already been selected in showNewScan
2713 so do not try to reset it here. However if we do decide to do full source scans on
2714 a queue edit rescan, we would need it. So leaving in for now but commenting out. */
2715 //[fSrcTitlePopUp selectItemAtIndex: [[queueToApply objectForKey:@"TitleNumber"] intValue] - 1];
2717 [fSrcChapterStartPopUp selectItemAtIndex: [[queueToApply objectForKey:@"ChapterStart"] intValue] - 1];
2718 [fSrcChapterEndPopUp selectItemAtIndex: [[queueToApply objectForKey:@"ChapterEnd"] intValue] - 1];
2721 [fDstFormatPopUp selectItemWithTitle:[queueToApply objectForKey:@"FileFormat"]];
2722 [self formatPopUpChanged:nil];
2724 /* Chapter Markers*/
2725 [fCreateChapterMarkers setState:[[queueToApply objectForKey:@"ChapterMarkers"] intValue]];
2726 /* Allow Mpeg4 64 bit formatting +4GB file sizes */
2727 [fDstMp4LargeFileCheck setState:[[queueToApply objectForKey:@"Mp4LargeFile"] intValue]];
2728 /* Mux mp4 with http optimization */
2729 [fDstMp4HttpOptFileCheck setState:[[queueToApply objectForKey:@"Mp4HttpOptimize"] intValue]];
2732 /* We set the advanced opt string here if applicable*/
2733 [fVidEncoderPopUp selectItemWithTitle:[queueToApply objectForKey:@"VideoEncoder"]];
2734 [fAdvancedOptions setOptions:[queueToApply objectForKey:@"x264Option"]];
2736 /* Lets run through the following functions to get variables set there */
2737 [self videoEncoderPopUpChanged:nil];
2738 /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/
2739 [fDstMp4iPodFileCheck setState:[[queueToApply objectForKey:@"Mp4iPodCompatible"] intValue]];
2740 [self calculateBitrate:nil];
2743 [fVidQualityMatrix selectCellAtRow:[[queueToApply objectForKey:@"VideoQualityType"] intValue] column:0];
2745 [fVidTargetSizeField setStringValue:[queueToApply objectForKey:@"VideoTargetSize"]];
2746 [fVidBitrateField setStringValue:[queueToApply objectForKey:@"VideoAvgBitrate"]];
2747 /* Since we are now using RF Values for the slider, we detect if the preset uses an old quality float.
2748 * So, check to see if the quality value is less than 1.0 which should indicate the old ".062" type
2749 * quality preset. Caveat: in the case of x264, where the RF scale starts at 0, it would misinterpret
2750 * a preset that uses 0.0 - 0.99 for RF as an old style preset. Not sure how to get around that one yet,
2751 * though it should be a corner case since it would pretty much be a preset for lossless encoding. */
2752 if ([[queueToApply objectForKey:@"VideoQualitySlider"] floatValue] < 1.0)
2754 /* For the quality slider we need to convert the old percent's to the new rf scales */
2755 float rf = (([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]);
2756 [fVidQualitySlider setFloatValue:rf];
2761 /* Since theora's qp value goes up from left to right, we can just set the slider float value */
2762 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
2764 [fVidQualitySlider setFloatValue:[[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]];
2768 /* since ffmpeg and x264 use an "inverted" slider (lower qp/rf values indicate a higher quality) we invert the value on the slider */
2769 [fVidQualitySlider setFloatValue:([fVidQualitySlider maxValue] + [fVidQualitySlider minValue]) - [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]];
2773 [self videoMatrixChanged:nil];
2775 /* Video framerate */
2776 /* For video preset video framerate, we want to make sure that Same as source does not conflict with the
2777 detected framerate in the fVidRatePopUp so we use index 0*/
2778 if ([[queueToApply objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"])
2780 [fVidRatePopUp selectItemAtIndex: 0];
2784 [fVidRatePopUp selectItemWithTitle:[queueToApply objectForKey:@"VideoFramerate"]];
2787 /* 2 Pass Encoding */
2788 [fVidTwoPassCheck setState:[[queueToApply objectForKey:@"VideoTwoPass"] intValue]];
2789 [self twoPassCheckboxChanged:nil];
2790 /* Turbo 1st pass for 2 Pass Encoding */
2791 [fVidTurboPassCheck setState:[[queueToApply objectForKey:@"VideoTurboTwoPass"] intValue]];
2796 /* Now lets add our new tracks to the audio list here */
2797 if ([[queueToApply objectForKey:@"Audio1Track"] intValue] > 0)
2799 [fAudLang1PopUp selectItemAtIndex: [[queueToApply objectForKey:@"Audio1Track"] intValue]];
2800 [self audioTrackPopUpChanged: fAudLang1PopUp];
2801 [fAudTrack1CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Encoder"]];
2802 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
2804 [fAudTrack1MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Mixdown"]];
2806 [fAudTrack1RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Samplerate"]];
2807 [fAudTrack1BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Bitrate"]];
2809 [fAudTrack1DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio1TrackDRCSlider"] floatValue]];
2810 [self audioDRCSliderChanged: fAudTrack1DrcSlider];
2814 [fAudLang1PopUp selectItemAtIndex: 0];
2815 [self audioTrackPopUpChanged: fAudLang1PopUp];
2817 if ([[queueToApply objectForKey:@"Audio2Track"] intValue] > 0)
2819 [fAudLang2PopUp selectItemAtIndex: [[queueToApply objectForKey:@"Audio2Track"] intValue]];
2820 [self audioTrackPopUpChanged: fAudLang2PopUp];
2821 [fAudTrack2CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Encoder"]];
2822 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
2824 [fAudTrack2MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Mixdown"]];
2826 [fAudTrack2RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Samplerate"]];
2827 [fAudTrack2BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Bitrate"]];
2829 [fAudTrack2DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio2TrackDRCSlider"] floatValue]];
2830 [self audioDRCSliderChanged: fAudTrack2DrcSlider];
2834 [fAudLang2PopUp selectItemAtIndex: 0];
2835 [self audioTrackPopUpChanged: fAudLang2PopUp];
2837 if ([[queueToApply objectForKey:@"Audio3Track"] intValue] > 0)
2839 [fAudLang3PopUp selectItemAtIndex: [[queueToApply objectForKey:@"Audio3Track"] intValue]];
2840 [self audioTrackPopUpChanged: fAudLang3PopUp];
2841 [fAudTrack3CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Encoder"]];
2842 [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
2844 [fAudTrack3MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Mixdown"]];
2846 [fAudTrack3RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Samplerate"]];
2847 [fAudTrack3BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Bitrate"]];
2849 [fAudTrack3DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio3TrackDRCSlider"] floatValue]];
2850 [self audioDRCSliderChanged: fAudTrack3DrcSlider];
2854 [fAudLang3PopUp selectItemAtIndex: 0];
2855 [self audioTrackPopUpChanged: fAudLang3PopUp];
2857 if ([[queueToApply objectForKey:@"Audio4Track"] intValue] > 0)
2859 [fAudLang4PopUp selectItemAtIndex: [[queueToApply objectForKey:@"Audio4Track"] intValue]];
2860 [self audioTrackPopUpChanged: fAudLang4PopUp];
2861 [fAudTrack4CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Encoder"]];
2862 [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
2864 [fAudTrack4MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Mixdown"]];
2866 [fAudTrack4RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Samplerate"]];
2867 [fAudTrack4BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Bitrate"]];
2869 [fAudTrack4DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio4TrackDRCSlider"] floatValue]];
2870 [self audioDRCSliderChanged: fAudTrack4DrcSlider];
2874 [fAudLang4PopUp selectItemAtIndex: 0];
2875 [self audioTrackPopUpChanged: fAudLang4PopUp];
2879 /* Crashy crashy right now, working on it */
2880 [fSubtitlesDelegate setNewSubtitles:[queueToApply objectForKey:@"SubtitleList"]];
2881 [fSubtitlesTable reloadData];
2882 /* Picture Settings */
2884 /* If Cropping is set to custom, then recall all four crop values from
2885 when the preset was created and apply them */
2886 if ([[queueToApply objectForKey:@"PictureAutoCrop"] intValue] == 0)
2888 [fPictureController setAutoCrop:NO];
2890 /* Here we use the custom crop values saved at the time the preset was saved */
2891 job->crop[0] = [[queueToApply objectForKey:@"PictureTopCrop"] intValue];
2892 job->crop[1] = [[queueToApply objectForKey:@"PictureBottomCrop"] intValue];
2893 job->crop[2] = [[queueToApply objectForKey:@"PictureLeftCrop"] intValue];
2894 job->crop[3] = [[queueToApply objectForKey:@"PictureRightCrop"] intValue];
2897 else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */
2899 [fPictureController setAutoCrop:YES];
2900 /* Here we use the auto crop values determined right after scan */
2901 job->crop[0] = AutoCropTop;
2902 job->crop[1] = AutoCropBottom;
2903 job->crop[2] = AutoCropLeft;
2904 job->crop[3] = AutoCropRight;
2908 job->modulus = [[queueToApply objectForKey:@"PictureModulus"] intValue];
2910 /* we check to make sure the presets width/height does not exceed the sources width/height */
2911 if (fTitle->width < [[queueToApply objectForKey:@"PictureWidth"] intValue] || fTitle->height < [[queueToApply objectForKey:@"PictureHeight"] intValue])
2913 /* if so, then we use the sources height and width to avoid scaling up */
2914 //job->width = fTitle->width;
2915 //job->height = fTitle->height;
2916 [self revertPictureSizeToMax:nil];
2918 else // source width/height is >= the preset height/width
2920 /* we can go ahead and use the presets values for height and width */
2921 job->width = [[queueToApply objectForKey:@"PictureWidth"] intValue];
2922 job->height = [[queueToApply objectForKey:@"PictureHeight"] intValue];
2924 job->keep_ratio = [[queueToApply objectForKey:@"PictureKeepRatio"] intValue];
2925 if (job->keep_ratio == 1)
2927 hb_fix_aspect( job, HB_KEEP_WIDTH );
2928 if( job->height > fTitle->height )
2930 job->height = fTitle->height;
2931 hb_fix_aspect( job, HB_KEEP_HEIGHT );
2934 job->anamorphic.mode = [[queueToApply objectForKey:@"PicturePAR"] intValue];
2935 job->modulus = [[queueToApply objectForKey:@"PictureModulus"] intValue];
2939 /* We only allow *either* Decomb or Deinterlace. So check for the PictureDecombDeinterlace key.
2940 * also, older presets may not have this key, in which case we also check to see if that preset had PictureDecomb
2941 * specified, in which case we use decomb and ignore any possible Deinterlace settings as using both was less than
2944 [fPictureController setUseDecomb:1];
2945 [fPictureController setDecomb:0];
2946 [fPictureController setDeinterlace:0];
2947 if ([[queueToApply objectForKey:@"PictureDecombDeinterlace"] intValue] == 1 || [[queueToApply objectForKey:@"PictureDecomb"] intValue] > 0)
2949 /* we are using decomb */
2951 if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] > 0)
2953 [fPictureController setDecomb:[[queueToApply objectForKey:@"PictureDecomb"] intValue]];
2955 /* if we are using "Custom" in the decomb setting, also set the custom string*/
2956 if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 1)
2958 [fPictureController setDecombCustomString:[queueToApply objectForKey:@"PictureDecombCustom"]];
2964 /* We are using Deinterlace */
2966 if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] > 0)
2968 [fPictureController setUseDecomb:0];
2969 [fPictureController setDeinterlace:[[queueToApply objectForKey:@"PictureDeinterlace"] intValue]];
2970 /* if we are using "Custom" in the deinterlace setting, also set the custom string*/
2971 if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 1)
2973 [fPictureController setDeinterlaceCustomString:[queueToApply objectForKey:@"PictureDeinterlaceCustom"]];
2980 if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] > 0)
2982 [fPictureController setDetelecine:[[queueToApply objectForKey:@"PictureDetelecine"] intValue]];
2983 /* if we are using "Custom" in the detelecine setting, also set the custom string*/
2984 if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
2986 [fPictureController setDetelecineCustomString:[queueToApply objectForKey:@"PictureDetelecineCustom"]];
2991 [fPictureController setDetelecine:0];
2995 if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] > 0)
2997 [fPictureController setDenoise:[[queueToApply objectForKey:@"PictureDenoise"] intValue]];
2998 /* if we are using "Custom" in the denoise setting, also set the custom string*/
2999 if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 1)
3001 [fPictureController setDenoiseCustomString:[queueToApply objectForKey:@"PictureDenoiseCustom"]];
3006 [fPictureController setDenoise:0];
3010 if ([[queueToApply objectForKey:@"PictureDeblock"] intValue] == 1)
3012 /* if its a one, then its the old on/off deblock, set on to 5*/
3013 [fPictureController setDeblock:5];
3017 /* use the settings intValue */
3018 [fPictureController setDeblock:[[queueToApply objectForKey:@"PictureDeblock"] intValue]];
3021 if ([[queueToApply objectForKey:@"VideoGrayScale"] intValue] == 1)
3023 [fPictureController setGrayscale:1];
3027 [fPictureController setGrayscale:0];
3030 /* we call SetTitle: in fPictureController so we get an instant update in the Picture Settings window */
3031 [fPictureController SetTitle:fTitle];
3032 [self calculatePictureSizing:nil];
3034 /* somehow we need to figure out a way to tie the queue item to a preset if it used one */
3035 //[queueFileJob setObject:[fPresetSelectedDisplay stringValue] forKey:@"PresetName"];
3036 //[queueFileJob setObject:[NSNumber numberWithInt:[fPresetsOutlineView selectedRow]] forKey:@"PresetIndexNum"];
3037 if ([queueToApply objectForKey:@"PresetIndexNum"]) // This item used a preset so insert that info
3039 /* Deselect the currently selected Preset if there is one*/
3040 //[fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[[queueToApply objectForKey:@"PresetIndexNum"] intValue]] byExtendingSelection:NO];
3041 //[self selectPreset:nil];
3043 //[fPresetsOutlineView selectRow:[[queueToApply objectForKey:@"PresetIndexNum"] intValue]];
3044 /* Change UI to show "Custom" settings are being used */
3045 //[fPresetSelectedDisplay setStringValue: [[queueToApply objectForKey:@"PresetName"] stringValue]];
3047 curUserPresetChosenNum = nil;
3051 /* Deselect the currently selected Preset if there is one*/
3052 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
3053 /* Change UI to show "Custom" settings are being used */
3054 [fPresetSelectedDisplay setStringValue: @"Custom"];
3056 //curUserPresetChosenNum = nil;
3059 /* We need to set this bool back to NO, in case the user wants to do a scan */
3060 //applyQueueToScan = NO;
3062 /* Not that source is loaded and settings applied, delete the queue item from the queue */
3063 [self writeToActivityLog: "applyQueueSettingsToMainWindow: deleting queue item:%d",fqueueEditRescanItemNum];
3064 [self removeQueueFileItem:fqueueEditRescanItemNum];
3070 #pragma mark Live Preview
3071 /* Note,this is much like prepareJob, but directly sets the job vars so Picture Preview
3072 * can encode to its temp preview directory and playback. This is *not* used for any actual user
3075 - (void) prepareJobForPreview
3077 hb_list_t * list = hb_get_titles( fHandle );
3078 hb_title_t * title = (hb_title_t *) hb_list_item( list,
3079 [fSrcTitlePopUp indexOfSelectedItem] );
3080 hb_job_t * job = title->job;
3081 hb_audio_config_t * audio;
3082 /* set job->angle for libdvdnav */
3083 job->angle = [fSrcAnglePopUp indexOfSelectedItem] + 1;
3084 /* Chapter selection */
3085 job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
3086 job->chapter_end = [fSrcChapterEndPopUp indexOfSelectedItem] + 1;
3088 /* Format (Muxer) and Video Encoder */
3089 job->mux = [[fDstFormatPopUp selectedItem] tag];
3090 job->vcodec = [[fVidEncoderPopUp selectedItem] tag];
3092 job->chapter_markers = 0;
3094 if( job->vcodec & HB_VCODEC_X264 )
3097 /* Below Sends x264 options to the core library if x264 is selected*/
3098 /* Lets use this as per Nyx, Thanks Nyx!*/
3099 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
3100 /* For previews we ignore the turbo option for the first pass of two since we only use 1 pass */
3101 strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]);
3106 /* Video settings */
3107 /* Set vfr to 0 as it's only on if using same as source in the framerate popup
3108 * and detelecine is on, so we handle that in the logic below
3111 if( [fVidRatePopUp indexOfSelectedItem] > 0 )
3113 /* a specific framerate has been chosen */
3114 job->vrate = 27000000;
3115 job->vrate_base = hb_video_rates[[fVidRatePopUp indexOfSelectedItem]-1].rate;
3116 /* We are not same as source so we set job->cfr to 1
3117 * to enable constant frame rate since user has specified
3118 * a specific framerate*/
3119 if ([fFrameratePfrCheck state] == 1)
3130 /* We are same as source (variable) */
3131 job->vrate = title->rate;
3132 job->vrate_base = title->rate_base;
3133 /* We are same as source so we set job->cfr to 0
3134 * to enable true same as source framerate */
3136 /* If we are same as source and we have detelecine on, we need to turn on
3139 if ([fPictureController detelecine] == 1)
3145 switch( [fVidQualityMatrix selectedRow] )
3149 Bitrate should already have been calculated and displayed
3150 in fVidBitrateField, so let's just use it */
3152 job->vquality = -1.0;
3153 job->vbitrate = [fVidBitrateField intValue];
3156 job->vquality = [fVidQualityRFField floatValue];
3161 /* Subtitle settings */
3162 NSMutableArray *subtitlesArray = [[NSMutableArray alloc] initWithArray:[fSubtitlesDelegate getSubtitleArray] copyItems:YES];
3169 bool one_burned = FALSE;
3172 NSEnumerator *enumerator = [subtitlesArray objectEnumerator];
3174 while (tempObject = [enumerator nextObject])
3177 subtitle = [[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue];
3178 force = [[tempObject objectForKey:@"subtitleTrackForced"] intValue];
3179 burned = [[tempObject objectForKey:@"subtitleTrackBurned"] intValue];
3180 def = [[tempObject objectForKey:@"subtitleTrackDefault"] intValue];
3182 /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups,
3183 * we want to ignore it for display as well as encoding.
3187 /* if i is 0, then we are in the first item of the subtitles which we need to
3188 * check for the "Foreign Audio Search" which would be subtitleSourceTrackNum of 1
3189 * bearing in mind that for all tracks subtitleSourceTrackNum of 0 is None.
3192 /* if we are on the first track and using "Foreign Audio Search" */
3193 if (i == 0 && subtitle == 1)
3195 /* NOTE: Currently foreign language search is borked for preview.
3196 * Commented out but left in for initial commit. */
3199 [self writeToActivityLog: "Foreign Language Search: %d", 1];
3201 job->indepth_scan = 1;
3205 job->select_subtitle_config.dest = PASSTHRUSUB;
3209 job->select_subtitle_config.dest = RENDERSUB;
3212 job->select_subtitle_config.force = force;
3213 job->select_subtitle_config.default_track = def;
3218 /* for the actual source tracks, we must subtract the non source entries so
3219 * that the menu index matches the source subtitle_list index for convenience */
3222 /* for the first track, the source tracks start at menu index 2 ( None is 0,
3223 * Foreign Language Search is 1) so subtract 2 */
3224 subtitle = subtitle - 2;
3228 /* for all other tracks, the source tracks start at menu index 1 (None is 0)
3231 subtitle = subtitle - 1;
3234 /* We are setting a source subtitle so access the source subtitle info */
3235 hb_subtitle_t * subt;
3237 subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle);
3239 /* if we are getting the subtitles from an external srt file */
3240 if ([[tempObject objectForKey:@"subtitleSourceTrackType"] isEqualToString:@"SRT"])
3242 hb_subtitle_config_t sub_config;
3244 sub_config.offset = [[tempObject objectForKey:@"subtitleTrackSrtOffset"] intValue];
3246 /* we need to srncpy file path and char code */
3247 strncpy(sub_config.src_filename, [[tempObject objectForKey:@"subtitleSourceSrtFilePath"] UTF8String], 128);
3248 strncpy(sub_config.src_codeset, [[tempObject objectForKey:@"subtitleTrackSrtCharCode"] UTF8String], 40);
3250 sub_config.force = 0;
3251 sub_config.dest = PASSTHRUSUB;
3252 sub_config.default_track = def;
3254 hb_srt_add( job, &sub_config, [[tempObject objectForKey:@"subtitleTrackSrtLanguageIso3"] UTF8String]);
3259 hb_subtitle_config_t sub_config = subt->config;
3261 if ( !burned && subt->format == PICTURESUB )
3263 sub_config.dest = PASSTHRUSUB;
3265 else if ( burned && subt->format == PICTURESUB )
3267 // Only allow one subtitle to be burned into the video
3272 sub_config.force = force;
3273 sub_config.default_track = def;
3274 hb_subtitle_add( job, &sub_config, subtitle );
3284 [subtitlesArray autorelease];
3287 /* Audio tracks and mixdowns */
3288 /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
3289 int audiotrack_count = hb_list_count(job->list_audio);
3290 for( int i = 0; i < audiotrack_count;i++)
3292 hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
3293 hb_list_rem(job->list_audio, temp_audio);
3295 /* Now lets add our new tracks to the audio list here */
3296 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
3298 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3299 hb_audio_config_init(audio);
3300 audio->in.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
3301 /* We go ahead and assign values to our audio->out.<properties> */
3302 audio->out.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
3303 audio->out.codec = [[fAudTrack1CodecPopUp selectedItem] tag];
3304 audio->out.mixdown = [[fAudTrack1MixPopUp selectedItem] tag];
3305 audio->out.bitrate = [[fAudTrack1BitratePopUp selectedItem] tag];
3306 audio->out.samplerate = [[fAudTrack1RatePopUp selectedItem] tag];
3307 audio->out.dynamic_range_compression = [fAudTrack1DrcField floatValue];
3309 hb_audio_add( job, audio );
3312 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
3314 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3315 hb_audio_config_init(audio);
3316 audio->in.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
3317 /* We go ahead and assign values to our audio->out.<properties> */
3318 audio->out.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
3319 audio->out.codec = [[fAudTrack2CodecPopUp selectedItem] tag];
3320 audio->out.mixdown = [[fAudTrack2MixPopUp selectedItem] tag];
3321 audio->out.bitrate = [[fAudTrack2BitratePopUp selectedItem] tag];
3322 audio->out.samplerate = [[fAudTrack2RatePopUp selectedItem] tag];
3323 audio->out.dynamic_range_compression = [fAudTrack2DrcField floatValue];
3325 hb_audio_add( job, audio );
3330 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
3332 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3333 hb_audio_config_init(audio);
3334 audio->in.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
3335 /* We go ahead and assign values to our audio->out.<properties> */
3336 audio->out.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
3337 audio->out.codec = [[fAudTrack3CodecPopUp selectedItem] tag];
3338 audio->out.mixdown = [[fAudTrack3MixPopUp selectedItem] tag];
3339 audio->out.bitrate = [[fAudTrack3BitratePopUp selectedItem] tag];
3340 audio->out.samplerate = [[fAudTrack3RatePopUp selectedItem] tag];
3341 audio->out.dynamic_range_compression = [fAudTrack3DrcField floatValue];
3343 hb_audio_add( job, audio );
3348 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
3350 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3351 hb_audio_config_init(audio);
3352 audio->in.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
3353 /* We go ahead and assign values to our audio->out.<properties> */
3354 audio->out.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
3355 audio->out.codec = [[fAudTrack4CodecPopUp selectedItem] tag];
3356 audio->out.mixdown = [[fAudTrack4MixPopUp selectedItem] tag];
3357 audio->out.bitrate = [[fAudTrack4BitratePopUp selectedItem] tag];
3358 audio->out.samplerate = [[fAudTrack4RatePopUp selectedItem] tag];
3359 audio->out.dynamic_range_compression = [fAudTrack4DrcField floatValue];
3361 hb_audio_add( job, audio );
3370 /* Though Grayscale is not really a filter, per se
3371 * we put it here since its in the filters panel
3374 if ([fPictureController grayscale])
3383 /* Initialize the filters list */
3384 job->filters = hb_list_init();
3386 /* Now lets call the filters if applicable.
3387 * The order of the filters is critical
3391 hb_filter_detelecine.settings = NULL;
3392 if ([fPictureController detelecine] == 1)
3394 /* use a custom detelecine string */
3395 hb_filter_detelecine.settings = (char *) [[fPictureController detelecineCustomString] UTF8String];
3396 hb_list_add( job->filters, &hb_filter_detelecine );
3398 if ([fPictureController detelecine] == 2)
3401 hb_list_add( job->filters, &hb_filter_detelecine );
3406 if ([fPictureController useDecomb] == 1)
3409 /* we add the custom string if present */
3410 hb_filter_decomb.settings = NULL;
3411 if ([fPictureController decomb] == 1)
3413 /* use a custom decomb string */
3414 hb_filter_decomb.settings = (char *) [[fPictureController decombCustomString] UTF8String];
3415 hb_list_add( job->filters, &hb_filter_decomb );
3417 if ([fPictureController decomb] == 2)
3419 /* Run old deinterlacer fd by default */
3420 //hb_filter_decomb.settings = (char *) [[fPicSettingDecomb stringValue] UTF8String];
3421 hb_list_add( job->filters, &hb_filter_decomb );
3428 if ([fPictureController deinterlace] == 1)
3430 /* we add the custom string if present */
3431 hb_filter_deinterlace.settings = (char *) [[fPictureController deinterlaceCustomString] UTF8String];
3432 hb_list_add( job->filters, &hb_filter_deinterlace );
3434 else if ([fPictureController deinterlace] == 2)
3436 /* Run old deinterlacer fd by default */
3437 hb_filter_deinterlace.settings = "-1";
3438 hb_list_add( job->filters, &hb_filter_deinterlace );
3440 else if ([fPictureController deinterlace] == 3)
3442 /* Yadif mode 0 (without spatial deinterlacing.) */
3443 hb_filter_deinterlace.settings = "2";
3444 hb_list_add( job->filters, &hb_filter_deinterlace );
3446 else if ([fPictureController deinterlace] == 4)
3448 /* Yadif (with spatial deinterlacing) */
3449 hb_filter_deinterlace.settings = "0";
3450 hb_list_add( job->filters, &hb_filter_deinterlace );
3456 if ([fPictureController denoise] == 1) // custom in popup
3458 /* we add the custom string if present */
3459 hb_filter_denoise.settings = (char *) [[fPictureController denoiseCustomString] UTF8String];
3460 hb_list_add( job->filters, &hb_filter_denoise );
3462 else if ([fPictureController denoise] == 2) // Weak in popup
3464 hb_filter_denoise.settings = "2:1:2:3";
3465 hb_list_add( job->filters, &hb_filter_denoise );
3467 else if ([fPictureController denoise] == 3) // Medium in popup
3469 hb_filter_denoise.settings = "3:2:2:3";
3470 hb_list_add( job->filters, &hb_filter_denoise );
3472 else if ([fPictureController denoise] == 4) // Strong in popup
3474 hb_filter_denoise.settings = "7:7:5:5";
3475 hb_list_add( job->filters, &hb_filter_denoise );
3479 /* Deblock (uses pp7 default) */
3480 /* NOTE: even though there is a valid deblock setting of 0 for the filter, for
3481 * the macgui's purposes a value of 0 actually means to not even use the filter
3482 * current hb_filter_deblock.settings valid ranges are from 5 - 15
3484 if ([fPictureController deblock] != 0)
3486 NSString *deblockStringValue = [NSString stringWithFormat: @"%d",[fPictureController deblock]];
3487 hb_filter_deblock.settings = (char *) [deblockStringValue UTF8String];
3488 hb_list_add( job->filters, &hb_filter_deblock );
3495 #pragma mark Job Handling
3501 NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex];
3502 hb_list_t * list = hb_get_titles( fQueueEncodeLibhb );
3503 hb_title_t * title = (hb_title_t *) hb_list_item( list,0 ); // is always zero since now its a single title scan
3504 hb_job_t * job = title->job;
3505 hb_audio_config_t * audio;
3506 /* Title Angle for dvdnav */
3507 job->angle = [[queueToApply objectForKey:@"TitleAngle"] intValue];
3509 if([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 0)
3511 /* Chapter selection */
3512 [self writeToActivityLog: "Start / Stop set to chapters"];
3513 job->chapter_start = [[queueToApply objectForKey:@"JobChapterStart"] intValue];
3514 job->chapter_end = [[queueToApply objectForKey:@"JobChapterEnd"] intValue];
3516 else if ([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 1)
3518 /* we are pts based start / stop */
3519 [self writeToActivityLog: "Start / Stop set to seconds ..."];
3521 /* Point A to Point B. Time to time in seconds.*/
3522 /* get the start seconds from the start seconds field */
3523 int start_seconds = [[queueToApply objectForKey:@"StartSeconds"] intValue];
3524 job->pts_to_start = start_seconds * 90000LL;
3525 /* Stop seconds is actually the duration of encode, so subtract the end seconds from the start seconds */
3526 int stop_seconds = [[queueToApply objectForKey:@"StopSeconds"] intValue];
3527 job->pts_to_stop = stop_seconds * 90000LL;
3530 else if ([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 2)
3532 /* we are frame based start / stop */
3533 [self writeToActivityLog: "Start / Stop set to frames ..."];
3535 /* Point A to Point B. Frame to frame */
3536 /* get the start frame from the start frame field */
3537 int start_frame = [[queueToApply objectForKey:@"StartFrame"] intValue];
3538 job->frame_to_start = start_frame;
3539 /* get the frame to stop on from the end frame field */
3540 int stop_frame = [[queueToApply objectForKey:@"StopFrame"] intValue];
3541 job->frame_to_stop = stop_frame;
3548 /* Format (Muxer) and Video Encoder */
3549 job->mux = [[queueToApply objectForKey:@"JobFileFormatMux"] intValue];
3550 job->vcodec = [[queueToApply objectForKey:@"JobVideoEncoderVcodec"] intValue];
3553 /* If mpeg-4, then set mpeg-4 specific options like chapters and > 4gb file sizes */
3554 if( [[queueToApply objectForKey:@"Mp4LargeFile"] intValue] == 1)
3556 job->largeFileSize = 1;
3560 job->largeFileSize = 0;
3562 /* We set http optimized mp4 here */
3563 if( [[queueToApply objectForKey:@"Mp4HttpOptimize"] intValue] == 1 )
3565 job->mp4_optimize = 1;
3569 job->mp4_optimize = 0;
3573 /* We set the chapter marker extraction here based on the format being
3574 mpeg4 or mkv and the checkbox being checked */
3575 if ([[queueToApply objectForKey:@"ChapterMarkers"] intValue] == 1)
3577 job->chapter_markers = 1;
3579 /* now lets get our saved chapter names out the array in the queue file
3580 * and insert them back into the title chapter list. We have it here,
3581 * because unless we are inserting chapter markers there is no need to
3582 * spend the overhead of iterating through the chapter names array imo
3583 * Also, note that if for some reason we don't apply chapter names, the
3584 * chapters just come out 001, 002, etc. etc.
3587 NSMutableArray *ChapterNamesArray = [queueToApply objectForKey:@"ChapterNames"];
3589 NSEnumerator *enumerator = [ChapterNamesArray objectEnumerator];
3591 while (tempObject = [enumerator nextObject])
3593 hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
3594 if( chapter != NULL )
3596 strncpy( chapter->title, [tempObject UTF8String], 1023);
3597 chapter->title[1023] = '\0';
3604 job->chapter_markers = 0;
3607 if( job->vcodec & HB_VCODEC_X264 )
3609 if ([[queueToApply objectForKey:@"Mp4iPodCompatible"] intValue] == 1)
3619 /* Below Sends x264 options to the core library if x264 is selected*/
3620 /* Lets use this as per Nyx, Thanks Nyx!*/
3621 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
3622 /* Turbo first pass if two pass and Turbo First pass is selected */
3623 if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 && [[queueToApply objectForKey:@"VideoTurboTwoPass"] intValue] == 1 )
3625 /* pass the "Turbo" string to be appended to the existing x264 opts string into a variable for the first pass */
3626 NSString *firstPassOptStringTurbo = @":ref=1:subme=2:me=dia:analyse=none:trellis=0:no-fast-pskip=0:8x8dct=0:weightb=0";
3627 /* append the "Turbo" string variable to the existing opts string.
3628 Note: the "Turbo" string must be appended, not prepended to work properly*/
3629 NSString *firstPassOptStringCombined = [[queueToApply objectForKey:@"x264Option"] stringByAppendingString:firstPassOptStringTurbo];
3630 strcpy(job->x264opts, [firstPassOptStringCombined UTF8String]);
3634 strcpy(job->x264opts, [[queueToApply objectForKey:@"x264Option"] UTF8String]);
3640 /* Picture Size Settings */
3641 job->width = [[queueToApply objectForKey:@"PictureWidth"] intValue];
3642 job->height = [[queueToApply objectForKey:@"PictureHeight"] intValue];
3644 job->keep_ratio = [[queueToApply objectForKey:@"PictureKeepRatio"] intValue];
3645 job->anamorphic.mode = [[queueToApply objectForKey:@"PicturePAR"] intValue];
3646 job->modulus = [[queueToApply objectForKey:@"PictureModulus"] intValue];
3647 if ([[queueToApply objectForKey:@"PicturePAR"] intValue] == 3)
3649 /* insert our custom values here for capuj */
3650 job->width = [[queueToApply objectForKey:@"PicturePARStorageWidth"] intValue];
3651 job->height = [[queueToApply objectForKey:@"PicturePARStorageHeight"] intValue];
3653 job->modulus = [[queueToApply objectForKey:@"PicturePARModulus"] intValue];
3655 job->anamorphic.par_width = [[queueToApply objectForKey:@"PicturePARPixelWidth"] intValue];
3656 job->anamorphic.par_height = [[queueToApply objectForKey:@"PicturePARPixelHeight"] intValue];
3658 job->anamorphic.dar_width = [[queueToApply objectForKey:@"PicturePARDisplayWidth"] floatValue];
3659 job->anamorphic.dar_height = [[queueToApply objectForKey:@"PicturePARDisplayHeight"] floatValue];
3662 /* Here we use the crop values saved at the time the preset was saved */
3663 job->crop[0] = [[queueToApply objectForKey:@"PictureTopCrop"] intValue];
3664 job->crop[1] = [[queueToApply objectForKey:@"PictureBottomCrop"] intValue];
3665 job->crop[2] = [[queueToApply objectForKey:@"PictureLeftCrop"] intValue];
3666 job->crop[3] = [[queueToApply objectForKey:@"PictureRightCrop"] intValue];
3668 /* Video settings */
3671 /* Set vfr to 0 as it's only on if using same as source in the framerate popup
3672 * and detelecine is on, so we handle that in the logic below
3675 if( [[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue] > 0 )
3677 /* a specific framerate has been chosen */
3678 job->vrate = 27000000;
3679 job->vrate_base = hb_video_rates[[[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue]-1].rate;
3680 /* We are not same as source so we set job->cfr to 1
3681 * to enable constant frame rate since user has specified
3682 * a specific framerate*/
3684 if ([[queueToApply objectForKey:@"VideoFrameratePFR"] intValue] == 1)
3695 /* We are same as source (variable) */
3696 job->vrate = [[queueToApply objectForKey:@"JobVrate"] intValue];
3697 job->vrate_base = [[queueToApply objectForKey:@"JobVrateBase"] intValue];
3698 /* We are same as source so we set job->cfr to 0
3699 * to enable true same as source framerate */
3701 /* If we are same as source and we have detelecine on, we need to turn on
3704 if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
3710 if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] != 2 )
3713 Bitrate should already have been calculated and displayed
3714 in fVidBitrateField, so let's just use it same as abr*/
3715 job->vquality = -1.0;
3716 job->vbitrate = [[queueToApply objectForKey:@"VideoAvgBitrate"] intValue];
3718 if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] == 2 )
3720 job->vquality = [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue];
3725 job->grayscale = [[queueToApply objectForKey:@"VideoGrayScale"] intValue];
3730 #pragma mark Process Subtitles to libhb
3732 /* Map the settings in the dictionaries for the SubtitleList array to match title->list_subtitle
3733 * which means that we need to account for the offset of non source language settings in from
3734 * the NSPopUpCell menu. For all of the objects in the SubtitleList array this means 0 is "None"
3735 * from the popup menu, additionally the first track has "Foreign Audio Search" at 1. So we use
3736 * an int to offset the index number for the objectForKey:@"subtitleSourceTrackNum" to map that
3737 * to the source tracks position in title->list_subtitle.
3744 bool one_burned = FALSE;
3747 NSEnumerator *enumerator = [[queueToApply objectForKey:@"SubtitleList"] objectEnumerator];
3749 while (tempObject = [enumerator nextObject])
3752 subtitle = [[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue];
3753 force = [[tempObject objectForKey:@"subtitleTrackForced"] intValue];
3754 burned = [[tempObject objectForKey:@"subtitleTrackBurned"] intValue];
3755 def = [[tempObject objectForKey:@"subtitleTrackDefault"] intValue];
3757 /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups,
3758 * we want to ignore it for display as well as encoding.
3762 /* if i is 0, then we are in the first item of the subtitles which we need to
3763 * check for the "Foreign Audio Search" which would be subtitleSourceTrackNum of 1
3764 * bearing in mind that for all tracks subtitleSourceTrackNum of 0 is None.
3767 /* if we are on the first track and using "Foreign Audio Search" */
3768 if (i == 0 && subtitle == 1)
3770 [self writeToActivityLog: "Foreign Language Search: %d", 1];
3772 job->indepth_scan = 1;
3776 job->select_subtitle_config.dest = PASSTHRUSUB;
3780 job->select_subtitle_config.dest = RENDERSUB;
3783 job->select_subtitle_config.force = force;
3784 job->select_subtitle_config.default_track = def;
3789 /* for the actual source tracks, we must subtract the non source entries so
3790 * that the menu index matches the source subtitle_list index for convenience */
3793 /* for the first track, the source tracks start at menu index 2 ( None is 0,
3794 * Foreign Language Search is 1) so subtract 2 */
3795 subtitle = subtitle - 2;
3799 /* for all other tracks, the source tracks start at menu index 1 (None is 0)
3802 subtitle = subtitle - 1;
3805 /* We are setting a source subtitle so access the source subtitle info */
3806 hb_subtitle_t * subt;
3808 subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle);
3810 /* if we are getting the subtitles from an external srt file */
3811 if ([[tempObject objectForKey:@"subtitleSourceTrackType"] isEqualToString:@"SRT"])
3813 hb_subtitle_config_t sub_config;
3815 sub_config.offset = [[tempObject objectForKey:@"subtitleTrackSrtOffset"] intValue];
3817 /* we need to srncpy file name and codeset */
3818 strncpy(sub_config.src_filename, [[tempObject objectForKey:@"subtitleSourceSrtFilePath"] UTF8String], 128);
3819 strncpy(sub_config.src_codeset, [[tempObject objectForKey:@"subtitleTrackSrtCharCode"] UTF8String], 40);
3821 sub_config.force = 0;
3822 sub_config.dest = PASSTHRUSUB;
3823 sub_config.default_track = def;
3825 hb_srt_add( job, &sub_config, [[tempObject objectForKey:@"subtitleTrackSrtLanguageIso3"] UTF8String]);
3831 hb_subtitle_config_t sub_config = subt->config;
3833 if ( !burned && subt->format == PICTURESUB )
3835 sub_config.dest = PASSTHRUSUB;
3837 else if ( burned && subt->format == PICTURESUB )
3839 // Only allow one subtitle to be burned into the video
3844 sub_config.force = force;
3845 sub_config.default_track = def;
3846 hb_subtitle_add( job, &sub_config, subtitle );
3857 /* Audio tracks and mixdowns */
3858 /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
3859 int audiotrack_count = hb_list_count(job->list_audio);
3860 for( int i = 0; i < audiotrack_count;i++)
3862 hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
3863 hb_list_rem(job->list_audio, temp_audio);
3865 /* Now lets add our new tracks to the audio list here */
3866 if ([[queueToApply objectForKey:@"Audio1Track"] intValue] > 0)
3868 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3869 hb_audio_config_init(audio);
3870 audio->in.track = [[queueToApply objectForKey:@"Audio1Track"] intValue] - 1;
3871 /* We go ahead and assign values to our audio->out.<properties> */
3872 audio->out.track = [[queueToApply objectForKey:@"Audio1Track"] intValue] - 1;
3873 audio->out.codec = [[queueToApply objectForKey:@"JobAudio1Encoder"] intValue];
3874 audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio1Mixdown"] intValue];
3875 audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio1Bitrate"] intValue];
3876 audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio1Samplerate"] intValue];
3877 audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio1TrackDRCSlider"] floatValue];
3879 hb_audio_add( job, audio );
3882 if ([[queueToApply objectForKey:@"Audio2Track"] intValue] > 0)
3885 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3886 hb_audio_config_init(audio);
3887 audio->in.track = [[queueToApply objectForKey:@"Audio2Track"] intValue] - 1;
3888 [self writeToActivityLog: "prepareJob audiotrack 2 is: %d", audio->in.track];
3889 /* We go ahead and assign values to our audio->out.<properties> */
3890 audio->out.track = [[queueToApply objectForKey:@"Audio2Track"] intValue] - 1;
3891 audio->out.codec = [[queueToApply objectForKey:@"JobAudio2Encoder"] intValue];
3892 audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio2Mixdown"] intValue];
3893 audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio2Bitrate"] intValue];
3894 audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio2Samplerate"] intValue];
3895 audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio2TrackDRCSlider"] floatValue];
3897 hb_audio_add( job, audio );
3901 if ([[queueToApply objectForKey:@"Audio3Track"] intValue] > 0)
3903 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3904 hb_audio_config_init(audio);
3905 audio->in.track = [[queueToApply objectForKey:@"Audio3Track"] intValue] - 1;
3906 /* We go ahead and assign values to our audio->out.<properties> */
3907 audio->out.track = [[queueToApply objectForKey:@"Audio3Track"] intValue] - 1;
3908 audio->out.codec = [[queueToApply objectForKey:@"JobAudio3Encoder"] intValue];
3909 audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio3Mixdown"] intValue];
3910 audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio3Bitrate"] intValue];
3911 audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio3Samplerate"] intValue];
3912 audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio3TrackDRCSlider"] floatValue];
3914 hb_audio_add( job, audio );
3918 if ([[queueToApply objectForKey:@"Audio4Track"] intValue] > 0)
3920 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3921 hb_audio_config_init(audio);
3922 audio->in.track = [[queueToApply objectForKey:@"Audio4Track"] intValue] - 1;
3923 /* We go ahead and assign values to our audio->out.<properties> */
3924 audio->out.track = [[queueToApply objectForKey:@"Audio4Track"] intValue] - 1;
3925 audio->out.codec = [[queueToApply objectForKey:@"JobAudio4Encoder"] intValue];
3926 audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio4Mixdown"] intValue];
3927 audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio4Bitrate"] intValue];
3928 audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio4Samplerate"] intValue];
3929 audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio4TrackDRCSlider"] floatValue];
3931 hb_audio_add( job, audio );
3937 job->filters = hb_list_init();
3939 /* Now lets call the filters if applicable.
3940 * The order of the filters is critical
3943 hb_filter_detelecine.settings = NULL;
3944 if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
3946 /* use a custom detelecine string */
3947 hb_filter_detelecine.settings = (char *) [[queueToApply objectForKey:@"PictureDetelecineCustom"] UTF8String];
3948 hb_list_add( job->filters, &hb_filter_detelecine );
3950 if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 2)
3952 /* Use libhb's default values */
3953 hb_list_add( job->filters, &hb_filter_detelecine );
3956 if ([[queueToApply objectForKey:@"PictureDecombDeinterlace"] intValue] == 1)
3959 /* we add the custom string if present */
3960 hb_filter_decomb.settings = NULL;
3961 if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 1)
3963 /* use a custom decomb string */
3964 hb_filter_decomb.settings = (char *) [[queueToApply objectForKey:@"PictureDecombCustom"] UTF8String];
3965 hb_list_add( job->filters, &hb_filter_decomb );
3967 if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 2)
3969 /* Use libhb default */
3970 hb_list_add( job->filters, &hb_filter_decomb );
3978 if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 1)
3980 /* we add the custom string if present */
3981 hb_filter_deinterlace.settings = (char *) [[queueToApply objectForKey:@"PictureDeinterlaceCustom"] UTF8String];
3982 hb_list_add( job->filters, &hb_filter_deinterlace );
3984 else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 2)
3986 /* Run old deinterlacer fd by default */
3987 hb_filter_deinterlace.settings = "-1";
3988 hb_list_add( job->filters, &hb_filter_deinterlace );
3990 else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 3)
3992 /* Yadif mode 0 (without spatial deinterlacing.) */
3993 hb_filter_deinterlace.settings = "2";
3994 hb_list_add( job->filters, &hb_filter_deinterlace );
3996 else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 4)
3998 /* Yadif (with spatial deinterlacing) */
3999 hb_filter_deinterlace.settings = "0";
4000 hb_list_add( job->filters, &hb_filter_deinterlace );
4006 if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 1) // Custom in popup
4008 /* we add the custom string if present */
4009 hb_filter_denoise.settings = (char *) [[queueToApply objectForKey:@"PictureDenoiseCustom"] UTF8String];
4010 hb_list_add( job->filters, &hb_filter_denoise );
4012 else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 2) // Weak in popup
4014 hb_filter_denoise.settings = "2:1:2:3";
4015 hb_list_add( job->filters, &hb_filter_denoise );
4017 else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 3) // Medium in popup
4019 hb_filter_denoise.settings = "3:2:2:3";
4020 hb_list_add( job->filters, &hb_filter_denoise );
4022 else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 4) // Strong in popup
4024 hb_filter_denoise.settings = "7:7:5:5";
4025 hb_list_add( job->filters, &hb_filter_denoise );
4029 /* Deblock (uses pp7 default) */
4030 /* NOTE: even though there is a valid deblock setting of 0 for the filter, for
4031 * the macgui's purposes a value of 0 actually means to not even use the filter
4032 * current hb_filter_deblock.settings valid ranges are from 5 - 15
4034 if ([[queueToApply objectForKey:@"PictureDeblock"] intValue] != 0)
4036 hb_filter_deblock.settings = (char *) [[queueToApply objectForKey:@"PictureDeblock"] UTF8String];
4037 hb_list_add( job->filters, &hb_filter_deblock );
4039 [self writeToActivityLog: "prepareJob exiting"];
4044 /* addToQueue: puts up an alert before ultimately calling doAddToQueue
4046 - (IBAction) addToQueue: (id) sender
4048 /* We get the destination directory from the destination field here */
4049 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
4050 /* We check for a valid destination here */
4051 if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0)
4053 NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
4060 BOOL fileExistsInQueue;
4061 fileExistsInQueue = NO;
4063 /* We check for and existing file here */
4064 if([[NSFileManager defaultManager] fileExistsAtPath: [fDstFile2Field stringValue]])
4069 /* We now run through the queue and make sure we are not overwriting an exisiting queue item */
4071 NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
4073 while (tempObject = [enumerator nextObject])
4075 NSDictionary *thisQueueDict = tempObject;
4076 if ([[thisQueueDict objectForKey:@"DestinationPath"] isEqualToString: [fDstFile2Field stringValue]])
4078 fileExistsInQueue = YES;
4084 if(fileExists == YES)
4086 NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists.", @"" ),
4087 NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
4088 @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
4089 NULL, NULL, [NSString stringWithFormat:
4090 NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
4091 [fDstFile2Field stringValue]] );
4093 else if (fileExistsInQueue == YES)
4095 NSBeginCriticalAlertSheet( NSLocalizedString( @"There is already a queue item for this destination.", @"" ),
4096 NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
4097 @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
4098 NULL, NULL, [NSString stringWithFormat:
4099 NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
4100 [fDstFile2Field stringValue]] );
4104 [self doAddToQueue];
4108 /* overwriteAddToQueueAlertDone: called from the alert posted by addToQueue that asks
4109 the user if they want to overwrite an exiting movie file.
4111 - (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet
4112 returnCode: (int) returnCode contextInfo: (void *) contextInfo
4114 if( returnCode == NSAlertAlternateReturn )
4115 [self doAddToQueue];
4118 - (void) doAddToQueue
4120 [self addQueueFileItem ];
4125 /* Rip: puts up an alert before ultimately calling doRip
4127 - (IBAction) Rip: (id) sender
4129 [self writeToActivityLog: "Rip: Pending queue count is %d", fPendingCount];
4130 /* Rip or Cancel ? */
4132 hb_get_state2( fQueueEncodeLibhb, &s );
4134 if(s.state == HB_STATE_WORKING || s.state == HB_STATE_PAUSED)
4136 [self Cancel: sender];
4140 /* We check to see if we need to warn the user that the computer will go to sleep
4141 or shut down when encoding is finished */
4142 [self remindUserOfSleepOrShutdown];
4144 // If there are pending jobs in the queue, then this is a rip the queue
4145 if (fPendingCount > 0)
4147 currentQueueEncodeIndex = [self getNextPendingQueueIndex];
4148 /* here lets start the queue with the first pending item */
4149 [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
4154 // Before adding jobs to the queue, check for a valid destination.
4156 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
4157 if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0)
4159 NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
4163 /* We check for duplicate name here */
4164 if( [[NSFileManager defaultManager] fileExistsAtPath:[fDstFile2Field stringValue]] )
4166 NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists", @"" ),
4167 NSLocalizedString( @"Cancel", "" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
4168 @selector( overWriteAlertDone:returnCode:contextInfo: ),
4169 NULL, NULL, [NSString stringWithFormat:
4170 NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
4171 [fDstFile2Field stringValue]] );
4173 // overWriteAlertDone: will be called when the alert is dismissed. It will call doRip.
4177 /* if there are no pending jobs in the queue, then add this one to the queue and rip
4178 otherwise, just rip the queue */
4179 if(fPendingCount == 0)
4181 [self doAddToQueue];
4184 /* go right to processing the new queue encode */
4185 currentQueueEncodeIndex = [self getNextPendingQueueIndex];
4186 [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
4191 /* overWriteAlertDone: called from the alert posted by Rip: that asks the user if they
4192 want to overwrite an exiting movie file.
4194 - (void) overWriteAlertDone: (NSWindow *) sheet
4195 returnCode: (int) returnCode contextInfo: (void *) contextInfo
4197 if( returnCode == NSAlertAlternateReturn )
4199 /* if there are no jobs in the queue, then add this one to the queue and rip
4200 otherwise, just rip the queue */
4201 if( fPendingCount == 0 )
4203 [self doAddToQueue];
4206 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
4207 [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
4208 [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
4213 - (void) remindUserOfSleepOrShutdown
4215 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])
4217 /*Warn that computer will sleep after encoding*/
4220 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);
4221 [NSApp requestUserAttention:NSCriticalRequest];
4222 if ( reminduser == NSAlertAlternateReturn )
4224 [self showPreferencesWindow:nil];
4227 else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"])
4229 /*Warn that computer will shut down after encoding*/
4232 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);
4233 [NSApp requestUserAttention:NSCriticalRequest];
4234 if ( reminduser == NSAlertAlternateReturn )
4236 [self showPreferencesWindow:nil];
4245 /* Let libhb do the job */
4246 hb_start( fQueueEncodeLibhb );
4247 /*set the fEncodeState State */
4252 //------------------------------------------------------------------------------------
4253 // Displays an alert asking user if the want to cancel encoding of current job.
4254 // Cancel: returns immediately after posting the alert. Later, when the user
4255 // acknowledges the alert, doCancelCurrentJob is called.
4256 //------------------------------------------------------------------------------------
4257 - (IBAction)Cancel: (id)sender
4259 if (!fQueueController) return;
4261 hb_pause( fQueueEncodeLibhb );
4262 NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"You are currently encoding. What would you like to do ?", nil)];
4264 // Which window to attach the sheet to?
4265 NSWindow * docWindow;
4266 if ([sender respondsToSelector: @selector(window)])
4267 docWindow = [sender window];
4269 docWindow = fWindow;
4271 NSBeginCriticalAlertSheet(
4273 NSLocalizedString(@"Continue Encoding", nil),
4274 NSLocalizedString(@"Cancel Current and Stop", nil),
4275 NSLocalizedString(@"Cancel Current and Continue", nil),
4277 nil, @selector(didDimissCancel:returnCode:contextInfo:), nil,
4278 NSLocalizedString(@"Your encode will be cancelled if you don't continue encoding.", nil));
4280 // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed
4283 - (void) didDimissCancel: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
4285 hb_resume( fQueueEncodeLibhb );
4286 if (returnCode == NSAlertOtherReturn)
4288 [self doCancelCurrentJob]; // <- this also stops libhb
4290 if (returnCode == NSAlertAlternateReturn)
4292 [self doCancelCurrentJobAndStop];
4296 //------------------------------------------------------------------------------------
4297 // Cancels and deletes the current job and stops libhb from processing the remaining
4299 //------------------------------------------------------------------------------------
4300 - (void) doCancelCurrentJob
4302 // Stop the current job. hb_stop will only cancel the current pass and then set
4303 // its state to HB_STATE_WORKDONE. It also does this asynchronously. So when we
4304 // see the state has changed to HB_STATE_WORKDONE (in updateUI), we'll delete the
4305 // remaining passes of the job and then start the queue back up if there are any
4309 hb_stop( fQueueEncodeLibhb );
4311 // Delete all remaining jobs since libhb doesn't do this on its own.
4313 while( ( job = hb_job(fQueueEncodeLibhb, 0) ) )
4314 hb_rem( fQueueEncodeLibhb, job );
4316 fEncodeState = 2; // don't alert at end of processing since this was a cancel
4318 // now that we've stopped the currently encoding job, lets mark it as cancelled
4319 [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"];
4320 // and as always, save it in the queue .plist...
4321 /* We save all of the Queue data here */
4322 [self saveQueueFileItem];
4324 // ... and see if there are more items left in our queue
4325 int queueItems = [QueueFileArray count];
4326 /* If we still have more items in our queue, lets go to the next one */
4327 /* Check to see if there are any more pending items in the queue */
4328 int newQueueItemIndex = [self getNextPendingQueueIndex];
4329 /* If we still have more pending items in our queue, lets go to the next one */
4330 if (newQueueItemIndex >= 0 && newQueueItemIndex < queueItems)
4332 /*Set our currentQueueEncodeIndex now to the newly found Pending encode as we own it */
4333 currentQueueEncodeIndex = newQueueItemIndex;
4334 /* now we mark the queue item as Status = 1 ( being encoded ) so another instance can not come along and try to scan it while we are scanning */
4335 [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:1] forKey:@"Status"];
4336 [self writeToActivityLog: "incrementQueueItemDone new pending items found: %d", currentQueueEncodeIndex];
4337 [self saveQueueFileItem];
4338 /* now we can go ahead and scan the new pending queue item */
4339 [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
4344 [self writeToActivityLog: "incrementQueueItemDone there are no more pending encodes"];
4348 - (void) doCancelCurrentJobAndStop
4350 hb_stop( fQueueEncodeLibhb );
4352 // Delete all remaining jobs since libhb doesn't do this on its own.
4354 while( ( job = hb_job(fQueueEncodeLibhb, 0) ) )
4355 hb_rem( fQueueEncodeLibhb, job );
4358 fEncodeState = 2; // don't alert at end of processing since this was a cancel
4360 // now that we've stopped the currently encoding job, lets mark it as cancelled
4361 [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"];
4362 // and as always, save it in the queue .plist...
4363 /* We save all of the Queue data here */
4364 [self saveQueueFileItem];
4365 // so now lets move to
4366 currentQueueEncodeIndex++ ;
4367 [self writeToActivityLog: "cancelling current job and stopping the queue"];
4369 - (IBAction) Pause: (id) sender
4372 hb_get_state2( fQueueEncodeLibhb, &s );
4374 if( s.state == HB_STATE_PAUSED )
4376 hb_resume( fQueueEncodeLibhb );
4380 hb_pause( fQueueEncodeLibhb );
4385 #pragma mark GUI Controls Changed Methods
4387 - (IBAction) titlePopUpChanged: (id) sender
4389 hb_list_t * list = hb_get_titles( fHandle );
4390 hb_title_t * title = (hb_title_t*)
4391 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4393 /* If we are a stream type and a batch scan, grok the output file name from title->name upon title change */
4394 if (title->type == HB_STREAM_TYPE && hb_list_count( list ) > 1 )
4396 /* we set the default name according to the new title->name */
4397 [fDstFile2Field setStringValue: [NSString stringWithFormat:
4398 @"%@/%@.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent],
4399 [NSString stringWithUTF8String: title->name],
4400 [[fDstFile2Field stringValue] pathExtension]]];
4402 /* Change the source to read out the parent folder also */
4403 [fSrcDVD2Field setStringValue:[NSString stringWithFormat:@"%@/%@", browsedSourceDisplayName,[NSString stringWithUTF8String: title->name]]];
4406 /* For point a to point b pts encoding, set the start and end fields to 0 and the title duration in seconds respectively */
4407 int duration = (title->hours * 3600) + (title->minutes * 60) + (title->seconds);
4408 [fSrcTimeStartEncodingField setStringValue: [NSString stringWithFormat: @"%d", 0]];
4409 [fSrcTimeEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", duration]];
4410 /* 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 */
4411 [fSrcFrameStartEncodingField setStringValue: [NSString stringWithFormat: @"%d", 1]];
4412 //[fSrcFrameEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", ((title->hours * 3600) + (title->minutes * 60) + (title->seconds)) * 24]];
4413 [fSrcFrameEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", duration * (title->rate / title->rate_base)]];
4415 /* If Auto Naming is on. We create an output filename of dvd name - title number */
4416 if( [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultAutoNaming"] > 0 && ( hb_list_count( list ) > 1 ) )
4418 [fDstFile2Field setStringValue: [NSString stringWithFormat:
4419 @"%@/%@-%d.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent],
4420 [browsedSourceDisplayName stringByDeletingPathExtension],
4422 [[fDstFile2Field stringValue] pathExtension]]];
4424 /* Update encode start / stop variables */
4428 /* Update chapter popups */
4429 [fSrcChapterStartPopUp removeAllItems];
4430 [fSrcChapterEndPopUp removeAllItems];
4431 for( int i = 0; i < hb_list_count( title->list_chapter ); i++ )
4433 [fSrcChapterStartPopUp addItemWithTitle: [NSString
4434 stringWithFormat: @"%d", i + 1]];
4435 [fSrcChapterEndPopUp addItemWithTitle: [NSString
4436 stringWithFormat: @"%d", i + 1]];
4439 [fSrcChapterStartPopUp selectItemAtIndex: 0];
4440 [fSrcChapterEndPopUp selectItemAtIndex:
4441 hb_list_count( title->list_chapter ) - 1];
4442 [self chapterPopUpChanged:nil];
4444 /* if using dvd nav, show the angle widget */
4445 if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"UseDvdNav"] boolValue])
4447 [fSrcAngleLabel setHidden:NO];
4448 [fSrcAnglePopUp setHidden:NO];
4450 [fSrcAnglePopUp removeAllItems];
4451 for( int i = 0; i < title->angle_count; i++ )
4453 [fSrcAnglePopUp addItemWithTitle: [NSString stringWithFormat: @"%d", i + 1]];
4455 [fSrcAnglePopUp selectItemAtIndex: 0];
4459 [fSrcAngleLabel setHidden:YES];
4460 [fSrcAnglePopUp setHidden:YES];
4463 /* Start Get and set the initial pic size for display */
4464 hb_job_t * job = title->job;
4467 /* Set Auto Crop to on upon selecting a new title */
4468 [fPictureController setAutoCrop:YES];
4470 /* We get the originial output picture width and height and put them
4471 in variables for use with some presets later on */
4472 PicOrigOutputWidth = job->width;
4473 PicOrigOutputHeight = job->height;
4474 AutoCropTop = job->crop[0];
4475 AutoCropBottom = job->crop[1];
4476 AutoCropLeft = job->crop[2];
4477 AutoCropRight = job->crop[3];
4479 /* Reset the new title in fPictureController && fPreviewController*/
4480 [fPictureController SetTitle:title];
4483 /* Update Subtitle Table */
4484 [fSubtitlesDelegate resetWithTitle:title];
4485 [fSubtitlesTable reloadData];
4488 /* Update chapter table */
4489 [fChapterTitlesDelegate resetWithTitle:title];
4490 [fChapterTable reloadData];
4492 /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
4493 int audiotrack_count = hb_list_count(job->list_audio);
4494 for( int i = 0; i < audiotrack_count;i++)
4496 hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
4497 hb_list_rem(job->list_audio, temp_audio);
4500 /* Update audio popups */
4501 [self addAllAudioTracksToPopUp: fAudLang1PopUp];
4502 [self addAllAudioTracksToPopUp: fAudLang2PopUp];
4503 [self addAllAudioTracksToPopUp: fAudLang3PopUp];
4504 [self addAllAudioTracksToPopUp: fAudLang4PopUp];
4505 /* search for the first instance of our prefs default language for track 1, and set track 2 to "none" */
4506 NSString * audioSearchPrefix = [[NSUserDefaults standardUserDefaults] stringForKey:@"DefaultLanguage"];
4507 [self selectAudioTrackInPopUp: fAudLang1PopUp searchPrefixString: audioSearchPrefix selectIndexIfNotFound: 1];
4508 [self selectAudioTrackInPopUp:fAudLang2PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4509 [self selectAudioTrackInPopUp:fAudLang3PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4510 [self selectAudioTrackInPopUp:fAudLang4PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4512 /* changing the title may have changed the audio channels on offer, */
4513 /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
4514 [self audioTrackPopUpChanged: fAudLang1PopUp];
4515 [self audioTrackPopUpChanged: fAudLang2PopUp];
4516 [self audioTrackPopUpChanged: fAudLang3PopUp];
4517 [self audioTrackPopUpChanged: fAudLang4PopUp];
4519 [fVidRatePopUp selectItemAtIndex: 0];
4521 /* we run the picture size values through calculatePictureSizing to get all picture setting information*/
4522 [self calculatePictureSizing:nil];
4524 /* lets call tableViewSelected to make sure that any preset we have selected is enforced after a title change */
4525 [self selectPreset:nil];
4528 - (IBAction) encodeStartStopPopUpChanged: (id) sender;
4530 if( [fEncodeStartStopPopUp isEnabled] )
4532 /* We are chapters */
4533 if( [fEncodeStartStopPopUp indexOfSelectedItem] == 0 )
4535 [fSrcChapterStartPopUp setHidden: NO];
4536 [fSrcChapterEndPopUp setHidden: NO];
4538 [fSrcTimeStartEncodingField setHidden: YES];
4539 [fSrcTimeEndEncodingField setHidden: YES];
4541 [fSrcFrameStartEncodingField setHidden: YES];
4542 [fSrcFrameEndEncodingField setHidden: YES];
4544 [self chapterPopUpChanged:nil];
4546 /* We are time based (seconds) */
4547 else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 1)
4549 [fSrcChapterStartPopUp setHidden: YES];
4550 [fSrcChapterEndPopUp setHidden: YES];
4552 [fSrcTimeStartEncodingField setHidden: NO];
4553 [fSrcTimeEndEncodingField setHidden: NO];
4555 [fSrcFrameStartEncodingField setHidden: YES];
4556 [fSrcFrameEndEncodingField setHidden: YES];
4558 [self startEndSecValueChanged:nil];
4560 /* We are frame based */
4561 else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 2)
4563 [fSrcChapterStartPopUp setHidden: YES];
4564 [fSrcChapterEndPopUp setHidden: YES];
4566 [fSrcTimeStartEncodingField setHidden: YES];
4567 [fSrcTimeEndEncodingField setHidden: YES];
4569 [fSrcFrameStartEncodingField setHidden: NO];
4570 [fSrcFrameEndEncodingField setHidden: NO];
4572 [self startEndFrameValueChanged:nil];
4577 - (IBAction) chapterPopUpChanged: (id) sender
4580 /* If start chapter popup is greater than end chapter popup,
4581 we set the end chapter popup to the same as start chapter popup */
4582 if ([fSrcChapterStartPopUp indexOfSelectedItem] > [fSrcChapterEndPopUp indexOfSelectedItem])
4584 [fSrcChapterEndPopUp selectItemAtIndex: [fSrcChapterStartPopUp indexOfSelectedItem]];
4588 hb_list_t * list = hb_get_titles( fHandle );
4589 hb_title_t * title = (hb_title_t *)
4590 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4592 hb_chapter_t * chapter;
4593 int64_t duration = 0;
4594 for( int i = [fSrcChapterStartPopUp indexOfSelectedItem];
4595 i <= [fSrcChapterEndPopUp indexOfSelectedItem]; i++ )
4597 chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
4598 duration += chapter->duration;
4601 duration /= 90000; /* pts -> seconds */
4602 [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
4603 @"%02lld:%02lld:%02lld", duration / 3600, ( duration / 60 ) % 60,
4606 [self calculateBitrate: sender];
4608 if ( [fSrcChapterStartPopUp indexOfSelectedItem] == [fSrcChapterEndPopUp indexOfSelectedItem] )
4610 /* Disable chapter markers for any source with less than two chapters as it makes no sense. */
4611 [fCreateChapterMarkers setEnabled: NO];
4612 [fCreateChapterMarkers setState: NSOffState];
4616 [fCreateChapterMarkers setEnabled: YES];
4620 - (IBAction) startEndSecValueChanged: (id) sender
4623 int duration = [fSrcTimeEndEncodingField intValue] - [fSrcTimeStartEncodingField intValue];
4624 [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
4625 @"%02d:%02d:%02d", duration / 3600, ( duration / 60 ) % 60,
4628 //[self calculateBitrate: sender];
4632 - (IBAction) startEndFrameValueChanged: (id) sender
4634 hb_list_t * list = hb_get_titles( fHandle );
4635 hb_title_t * title = (hb_title_t*)
4636 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4638 int duration = ([fSrcFrameEndEncodingField intValue] - [fSrcFrameStartEncodingField intValue]) / (title->rate / title->rate_base);
4639 [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
4640 @"%02d:%02d:%02d", duration / 3600, ( duration / 60 ) % 60,
4643 //[self calculateBitrate: sender];
4647 - (IBAction) formatPopUpChanged: (id) sender
4649 NSString * string = [fDstFile2Field stringValue];
4650 int format = [fDstFormatPopUp indexOfSelectedItem];
4652 /* Initially set the large file (64 bit formatting) output checkbox to hidden */
4653 [fDstMp4LargeFileCheck setHidden: YES];
4654 [fDstMp4HttpOptFileCheck setHidden: YES];
4655 [fDstMp4iPodFileCheck setHidden: YES];
4657 /* Update the Video Codec PopUp */
4658 /* lets get the tag of the currently selected item first so we might reset it later */
4659 int selectedVidEncoderTag;
4660 selectedVidEncoderTag = [[fVidEncoderPopUp selectedItem] tag];
4662 /* Note: we now store the video encoder int values from common.c in the tags of each popup for easy retrieval later */
4663 [fVidEncoderPopUp removeAllItems];
4664 NSMenuItem *menuItem;
4665 /* These video encoders are available to all of our current muxers, so lets list them once here */
4666 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"MPEG-4 (FFmpeg)" action: NULL keyEquivalent: @""];
4667 [menuItem setTag: HB_VCODEC_FFMPEG];
4672 /*Get Default MP4 File Extension*/
4673 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0)
4681 /* Add additional video encoders here */
4682 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
4683 [menuItem setTag: HB_VCODEC_X264];
4684 /* We show the mp4 option checkboxes here since we are mp4 */
4685 [fCreateChapterMarkers setEnabled: YES];
4686 [fDstMp4LargeFileCheck setHidden: NO];
4687 [fDstMp4HttpOptFileCheck setHidden: NO];
4688 [fDstMp4iPodFileCheck setHidden: NO];
4693 /* Add additional video encoders here */
4694 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
4695 [menuItem setTag: HB_VCODEC_X264];
4696 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"VP3 (Theora)" action: NULL keyEquivalent: @""];
4697 [menuItem setTag: HB_VCODEC_THEORA];
4698 /* We enable the create chapters checkbox here */
4699 [fCreateChapterMarkers setEnabled: YES];
4704 /* tell fSubtitlesDelegate we have a new video container */
4706 [fSubtitlesDelegate containerChanged:[[fDstFormatPopUp selectedItem] tag]];
4707 [fSubtitlesTable reloadData];
4708 /* if we have a previously selected vid encoder tag, then try to select it */
4709 if (selectedVidEncoderTag)
4711 [fVidEncoderPopUp selectItemWithTag: selectedVidEncoderTag];
4715 [fVidEncoderPopUp selectItemAtIndex: 0];
4718 [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp];
4719 [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp];
4720 [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp];
4721 [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp];
4724 [self autoSetM4vExtension: sender];
4726 [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%s", [string stringByDeletingPathExtension], ext]];
4728 if( SuccessfulScan )
4730 /* Add/replace to the correct extension */
4731 [self audioTrackPopUpChanged: fAudLang1PopUp];
4732 [self audioTrackPopUpChanged: fAudLang2PopUp];
4733 [self audioTrackPopUpChanged: fAudLang3PopUp];
4734 [self audioTrackPopUpChanged: fAudLang4PopUp];
4736 if( [fVidEncoderPopUp selectedItem] == nil )
4739 [fVidEncoderPopUp selectItemAtIndex:0];
4740 [self videoEncoderPopUpChanged:nil];
4742 /* changing the format may mean that we can / can't offer mono or 6ch, */
4743 /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
4745 /* We call the method to properly enable/disable turbo 2 pass */
4746 [self twoPassCheckboxChanged: sender];
4747 /* We call method method to change UI to reflect whether a preset is used or not*/
4750 [self customSettingUsed: sender];
4753 - (IBAction) autoSetM4vExtension: (id) sender
4755 if ( [fDstFormatPopUp indexOfSelectedItem] )
4758 NSString * extension = @"mp4";
4760 if( [[fAudTrack1CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack2CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4761 [[fAudTrack3CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4762 [[fAudTrack4CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4763 [fCreateChapterMarkers state] == NSOnState ||
4764 [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0 )
4769 if( [extension isEqualTo: [[fDstFile2Field stringValue] pathExtension]] )
4772 [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%@",
4773 [[fDstFile2Field stringValue] stringByDeletingPathExtension], extension]];
4776 /* Method to determine if we should change the UI
4777 To reflect whether or not a Preset is being used or if
4778 the user is using "Custom" settings by determining the sender*/
4779 - (IBAction) customSettingUsed: (id) sender
4781 if ([sender stringValue])
4783 /* Deselect the currently selected Preset if there is one*/
4784 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
4785 /* Change UI to show "Custom" settings are being used */
4786 [fPresetSelectedDisplay setStringValue: @"Custom"];
4788 curUserPresetChosenNum = nil;
4790 [self calculateBitrate:nil];
4795 #pragma mark - Video
4797 - (IBAction) videoEncoderPopUpChanged: (id) sender
4799 hb_job_t * job = fTitle->job;
4800 int videoEncoder = [[fVidEncoderPopUp selectedItem] tag];
4802 [fAdvancedOptions setHidden:YES];
4803 /* If we are using x264 then show the x264 advanced panel*/
4804 if (videoEncoder == HB_VCODEC_X264)
4806 [fAdvancedOptions setHidden:NO];
4807 [self autoSetM4vExtension: sender];
4810 if (videoEncoder == HB_VCODEC_FFMPEG)
4812 /* We set the iPod atom checkbox to disabled and uncheck it as its only for x264 in the mp4
4813 container. Format is taken care of in formatPopUpChanged method by hiding and unchecking
4814 anything other than MP4.
4816 [fDstMp4iPodFileCheck setEnabled: NO];
4817 [fDstMp4iPodFileCheck setState: NSOffState];
4821 [fDstMp4iPodFileCheck setEnabled: YES];
4823 [self setupQualitySlider];
4824 [self calculatePictureSizing: sender];
4825 [self twoPassCheckboxChanged: sender];
4829 - (IBAction) twoPassCheckboxChanged: (id) sender
4831 /* check to see if x264 is chosen */
4832 if([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264)
4834 if( [fVidTwoPassCheck state] == NSOnState)
4836 [fVidTurboPassCheck setHidden: NO];
4840 [fVidTurboPassCheck setHidden: YES];
4841 [fVidTurboPassCheck setState: NSOffState];
4843 /* Make sure Two Pass is checked if Turbo is checked */
4844 if( [fVidTurboPassCheck state] == NSOnState)
4846 [fVidTwoPassCheck setState: NSOnState];
4851 [fVidTurboPassCheck setHidden: YES];
4852 [fVidTurboPassCheck setState: NSOffState];
4855 /* We call method method to change UI to reflect whether a preset is used or not*/
4856 [self customSettingUsed: sender];
4859 - (IBAction ) videoFrameRateChanged: (id) sender
4861 /* Hide and set the PFR Checkbox to OFF if we are set to Same as Source */
4862 if ([fVidRatePopUp indexOfSelectedItem] == 0)
4864 [fFrameratePfrCheck setHidden:YES];
4865 [fFrameratePfrCheck setState:0];
4869 [fFrameratePfrCheck setHidden:NO];
4872 /* We call method method to calculatePictureSizing to error check detelecine*/
4873 [self calculatePictureSizing: sender];
4875 /* We call method method to change UI to reflect whether a preset is used or not*/
4876 [self customSettingUsed: sender];
4878 - (IBAction) videoMatrixChanged: (id) sender;
4880 bool target, bitrate, quality;
4882 target = bitrate = quality = false;
4883 if( [fVidQualityMatrix isEnabled] )
4885 switch( [fVidQualityMatrix selectedRow] )
4898 [fVidTargetSizeField setEnabled: target];
4899 [fVidBitrateField setEnabled: bitrate];
4900 [fVidQualitySlider setEnabled: quality];
4901 [fVidQualityRFField setEnabled: quality];
4902 [fVidQualityRFLabel setEnabled: quality];
4903 [fVidTwoPassCheck setEnabled: !quality &&
4904 [fVidQualityMatrix isEnabled]];
4907 [fVidTwoPassCheck setState: NSOffState];
4908 [fVidTurboPassCheck setHidden: YES];
4909 [fVidTurboPassCheck setState: NSOffState];
4912 [self qualitySliderChanged: sender];
4913 [self calculateBitrate: sender];
4914 [self customSettingUsed: sender];
4917 /* Use this method to setup the quality slider for cq/rf values depending on
4918 * the video encoder selected.
4920 - (void) setupQualitySlider
4922 /* Get the current slider maxValue to check for a change in slider scale later
4923 * so that we can choose a new similar value on the new slider scale */
4924 float previousMaxValue = [fVidQualitySlider maxValue];
4925 float previousPercentOfSliderScale = [fVidQualitySlider floatValue] / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue] + 1);
4926 NSString * qpRFLabelString = @"QP:";
4928 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264)
4930 [fVidQualitySlider setMinValue:0.0];
4931 [fVidQualitySlider setMaxValue:51.0];
4932 /* As x264 allows for qp/rf values that are fractional, we get the value from the preferences */
4933 int fractionalGranularity = 1 / [[NSUserDefaults standardUserDefaults] floatForKey:@"x264CqSliderFractional"];
4934 [fVidQualitySlider setNumberOfTickMarks:(([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * fractionalGranularity) + 1];
4935 qpRFLabelString = @"RF:";
4938 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_FFMPEG )
4940 [fVidQualitySlider setMinValue:1.0];
4941 [fVidQualitySlider setMaxValue:31.0];
4942 [fVidQualitySlider setNumberOfTickMarks:31];
4945 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
4947 [fVidQualitySlider setMinValue:0.0];
4948 [fVidQualitySlider setMaxValue:63.0];
4949 [fVidQualitySlider setNumberOfTickMarks:64];
4951 [fVidQualityRFLabel setStringValue:qpRFLabelString];
4953 /* check to see if we have changed slider scales */
4954 if (previousMaxValue != [fVidQualitySlider maxValue])
4956 /* if so, convert the old setting to the new scale as close as possible based on percentages */
4957 float rf = ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue] + 1) * previousPercentOfSliderScale;
4958 [fVidQualitySlider setFloatValue:rf];
4961 [self qualitySliderChanged:nil];
4964 - (IBAction) qualitySliderChanged: (id) sender
4966 /* Our constant quality slider is in a range based
4967 * on each encoders qp/rf values. The range depends
4968 * on the encoder. Also, the range is inverse of quality
4969 * for all of the encoders *except* for theora
4970 * (ie. as the "quality" goes up, the cq or rf value
4971 * actually goes down). Since the IB sliders always set
4972 * their max value at the right end of the slider, we
4973 * will calculate the inverse, so as the slider floatValue
4974 * goes up, we will show the inverse in the rf field
4975 * so, the floatValue at the right for x264 would be 51
4976 * and our rf field needs to show 0 and vice versa.
4979 float sliderRfInverse = ([fVidQualitySlider maxValue] - [fVidQualitySlider floatValue]) + [fVidQualitySlider minValue];
4980 /* If the encoder is theora, use the float, otherwise use the inverse float*/
4981 //float sliderRfToPercent;
4982 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
4984 [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", [fVidQualitySlider floatValue]]];
4988 [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", sliderRfInverse]];
4990 [self customSettingUsed: sender];
4993 - (void) controlTextDidChange: (NSNotification *) notification
4995 [self calculateBitrate:nil];
4998 - (IBAction) calculateBitrate: (id) sender
5000 if( !fHandle || [fVidQualityMatrix selectedRow] != 0 || !SuccessfulScan )
5005 hb_list_t * list = hb_get_titles( fHandle );
5006 hb_title_t * title = (hb_title_t *) hb_list_item( list,
5007 [fSrcTitlePopUp indexOfSelectedItem] );
5008 hb_job_t * job = title->job;
5009 hb_audio_config_t * audio;
5010 /* For hb_calc_bitrate in addition to the Target Size in MB out of the
5011 * Target Size Field, we also need the job info for the Muxer, the Chapters
5012 * as well as all of the audio track info.
5013 * This used to be accomplished by simply calling prepareJob here, however
5014 * since the resilient queue sets the queue array values instead of the job
5015 * values directly, we duplicate the old prepareJob code here for the variables
5018 job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
5019 job->chapter_end = [fSrcChapterEndPopUp indexOfSelectedItem] + 1;
5020 job->mux = [[fDstFormatPopUp selectedItem] tag];
5022 /* Audio goes here */
5023 int audiotrack_count = hb_list_count(job->list_audio);
5024 for( int i = 0; i < audiotrack_count;i++)
5026 hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
5027 hb_list_rem(job->list_audio, temp_audio);
5029 /* Now we need our audio info here for each track if applicable */
5030 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
5032 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
5033 hb_audio_config_init(audio);
5034 audio->in.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
5035 /* We go ahead and assign values to our audio->out.<properties> */
5036 audio->out.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
5037 audio->out.codec = [[fAudTrack1CodecPopUp selectedItem] tag];
5038 audio->out.mixdown = [[fAudTrack1MixPopUp selectedItem] tag];
5039 audio->out.bitrate = [[fAudTrack1BitratePopUp selectedItem] tag];
5040 audio->out.samplerate = [[fAudTrack1RatePopUp selectedItem] tag];
5041 audio->out.dynamic_range_compression = [fAudTrack1DrcField floatValue];
5043 hb_audio_add( job, audio );
5046 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
5048 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
5049 hb_audio_config_init(audio);
5050 audio->in.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
5051 /* We go ahead and assign values to our audio->out.<properties> */
5052 audio->out.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
5053 audio->out.codec = [[fAudTrack2CodecPopUp selectedItem] tag];
5054 audio->out.mixdown = [[fAudTrack2MixPopUp selectedItem] tag];
5055 audio->out.bitrate = [[fAudTrack2BitratePopUp selectedItem] tag];
5056 audio->out.samplerate = [[fAudTrack2RatePopUp selectedItem] tag];
5057 audio->out.dynamic_range_compression = [fAudTrack2DrcField floatValue];
5059 hb_audio_add( job, audio );
5064 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
5066 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
5067 hb_audio_config_init(audio);
5068 audio->in.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
5069 /* We go ahead and assign values to our audio->out.<properties> */
5070 audio->out.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
5071 audio->out.codec = [[fAudTrack3CodecPopUp selectedItem] tag];
5072 audio->out.mixdown = [[fAudTrack3MixPopUp selectedItem] tag];
5073 audio->out.bitrate = [[fAudTrack3BitratePopUp selectedItem] tag];
5074 audio->out.samplerate = [[fAudTrack3RatePopUp selectedItem] tag];
5075 audio->out.dynamic_range_compression = [fAudTrack3DrcField floatValue];
5077 hb_audio_add( job, audio );
5082 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
5084 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
5085 hb_audio_config_init(audio);
5086 audio->in.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
5087 /* We go ahead and assign values to our audio->out.<properties> */
5088 audio->out.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
5089 audio->out.codec = [[fAudTrack4CodecPopUp selectedItem] tag];
5090 audio->out.mixdown = [[fAudTrack4MixPopUp selectedItem] tag];
5091 audio->out.bitrate = [[fAudTrack4BitratePopUp selectedItem] tag];
5092 audio->out.samplerate = [[fAudTrack4RatePopUp selectedItem] tag];
5093 audio->out.dynamic_range_compression = [fAudTrack4DrcField floatValue];
5095 hb_audio_add( job, audio );
5100 [fVidBitrateField setIntValue: hb_calc_bitrate( job, [fVidTargetSizeField intValue] )];
5104 #pragma mark - Picture
5106 /* lets set the picture size back to the max from right after title scan
5107 Lets use an IBAction here as down the road we could always use a checkbox
5108 in the gui to easily take the user back to max. Remember, the compiler
5109 resolves IBActions down to -(void) during compile anyway */
5110 - (IBAction) revertPictureSizeToMax: (id) sender
5112 hb_job_t * job = fTitle->job;
5113 /* Here we apply the title source and height */
5114 job->width = fTitle->width;
5115 job->height = fTitle->height;
5117 [self calculatePictureSizing: sender];
5118 /* We call method to change UI to reflect whether a preset is used or not*/
5119 [self customSettingUsed: sender];
5123 * Registers changes made in the Picture Settings Window.
5126 - (void)pictureSettingsDidChange
5128 [self calculatePictureSizing:nil];
5131 /* Get and Display Current Pic Settings in main window */
5132 - (IBAction) calculatePictureSizing: (id) sender
5134 if (fTitle->job->anamorphic.mode > 0)
5136 fTitle->job->keep_ratio = 0;
5139 if (fTitle->job->anamorphic.mode != 1) // we are not strict so show the modulus
5141 [fPictureSizeField setStringValue: [NSString stringWithFormat:@"Picture Size: %@, Modulus: %d", [fPictureController getPictureSizeInfoString], fTitle->job->modulus]];
5145 [fPictureSizeField setStringValue: [NSString stringWithFormat:@"Picture Size: %@", [fPictureController getPictureSizeInfoString]]];
5147 NSString *picCropping;
5148 /* Set the display field for crop as per boolean */
5149 if (![fPictureController autoCrop])
5151 picCropping = @"Custom";
5155 picCropping = @"Auto";
5157 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]]];
5159 [fPictureCroppingField setStringValue: [NSString stringWithFormat:@"Picture Cropping: %@",picCropping]];
5161 NSString *videoFilters;
5164 if ([fPictureController detelecine] == 2)
5166 videoFilters = [videoFilters stringByAppendingString:@" - Detelecine (Default)"];
5168 else if ([fPictureController detelecine] == 1)
5170 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Detelecine (%@)",[fPictureController detelecineCustomString]]];
5174 if ([fPictureController useDecomb] == 1)
5177 if ([fPictureController decomb] == 2)
5179 videoFilters = [videoFilters stringByAppendingString:@" - Decomb (Default)"];
5181 else if ([fPictureController decomb] == 1)
5183 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Decomb (%@)",[fPictureController decombCustomString]]];
5189 if ([fPictureController deinterlace] > 0)
5191 fTitle->job->deinterlace = 1;
5195 fTitle->job->deinterlace = 0;
5198 if ([fPictureController deinterlace] == 2)
5200 videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Fast)"];
5202 else if ([fPictureController deinterlace] == 3)
5204 videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slow)"];
5206 else if ([fPictureController deinterlace] == 4)
5208 videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slower)"];
5210 else if ([fPictureController deinterlace] == 1)
5212 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deinterlace (%@)",[fPictureController deinterlaceCustomString]]];
5218 if ([fPictureController denoise] == 2)
5220 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Weak)"];
5222 else if ([fPictureController denoise] == 3)
5224 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Medium)"];
5226 else if ([fPictureController denoise] == 4)
5228 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Strong)"];
5230 else if ([fPictureController denoise] == 1)
5232 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Denoise (%@)",[fPictureController denoiseCustomString]]];
5236 if ([fPictureController deblock] > 0)
5238 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deblock (%d)",[fPictureController deblock]]];
5242 if ([fPictureController grayscale])
5244 videoFilters = [videoFilters stringByAppendingString:@" - Grayscale"];
5246 [fVideoFiltersField setStringValue: [NSString stringWithFormat:@"Video Filters: %@", videoFilters]];
5248 //[fPictureController reloadStillPreview];
5253 #pragma mark - Audio and Subtitles
5254 - (IBAction) audioCodecsPopUpChanged: (id) sender
5257 NSPopUpButton * audiotrackPopUp;
5258 NSPopUpButton * sampleratePopUp;
5259 NSPopUpButton * bitratePopUp;
5260 NSPopUpButton * audiocodecPopUp;
5261 if (sender == fAudTrack1CodecPopUp)
5263 audiotrackPopUp = fAudLang1PopUp;
5264 audiocodecPopUp = fAudTrack1CodecPopUp;
5265 sampleratePopUp = fAudTrack1RatePopUp;
5266 bitratePopUp = fAudTrack1BitratePopUp;
5268 else if (sender == fAudTrack2CodecPopUp)
5270 audiotrackPopUp = fAudLang2PopUp;
5271 audiocodecPopUp = fAudTrack2CodecPopUp;
5272 sampleratePopUp = fAudTrack2RatePopUp;
5273 bitratePopUp = fAudTrack2BitratePopUp;
5275 else if (sender == fAudTrack3CodecPopUp)
5277 audiotrackPopUp = fAudLang3PopUp;
5278 audiocodecPopUp = fAudTrack3CodecPopUp;
5279 sampleratePopUp = fAudTrack3RatePopUp;
5280 bitratePopUp = fAudTrack3BitratePopUp;
5284 audiotrackPopUp = fAudLang4PopUp;
5285 audiocodecPopUp = fAudTrack4CodecPopUp;
5286 sampleratePopUp = fAudTrack4RatePopUp;
5287 bitratePopUp = fAudTrack4BitratePopUp;
5290 /* changing the codecs on offer may mean that we can / can't offer mono or 6ch, */
5291 /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
5292 [self audioTrackPopUpChanged: audiotrackPopUp];
5296 - (IBAction) setEnabledStateOfAudioMixdownControls: (id) sender
5298 /* We will be setting the enabled/disabled state of each tracks audio controls based on
5299 * the settings of the source audio for that track. We leave the samplerate and bitrate
5300 * to audiotrackMixdownChanged
5303 /* We will first verify that a lower track number has been selected before enabling each track
5304 * for example, make sure a track is selected for track 1 before enabling track 2, etc.
5307 /* If the source has no audio then disable audio track 1 */
5308 if (hb_list_count( fTitle->list_audio ) == 0)
5310 [fAudLang1PopUp selectItemAtIndex:0];
5313 if ([fAudLang1PopUp indexOfSelectedItem] == 0)
5315 [fAudLang2PopUp setEnabled: NO];
5316 [fAudLang2PopUp selectItemAtIndex: 0];
5320 [fAudLang2PopUp setEnabled: YES];
5323 if ([fAudLang2PopUp indexOfSelectedItem] == 0)
5325 [fAudLang3PopUp setEnabled: NO];
5326 [fAudLang3PopUp selectItemAtIndex: 0];
5330 [fAudLang3PopUp setEnabled: YES];
5332 if ([fAudLang3PopUp indexOfSelectedItem] == 0)
5334 [fAudLang4PopUp setEnabled: NO];
5335 [fAudLang4PopUp selectItemAtIndex: 0];
5339 [fAudLang4PopUp setEnabled: YES];
5341 /* enable/disable the mixdown text and popupbutton for audio track 1 */
5342 [fAudTrack1CodecPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5343 [fAudTrack1MixPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5344 [fAudTrack1RatePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5345 [fAudTrack1BitratePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5346 [fAudTrack1DrcSlider setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5347 [fAudTrack1DrcField setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5348 if ([fAudLang1PopUp indexOfSelectedItem] == 0)
5350 [fAudTrack1CodecPopUp removeAllItems];
5351 [fAudTrack1MixPopUp removeAllItems];
5352 [fAudTrack1RatePopUp removeAllItems];
5353 [fAudTrack1BitratePopUp removeAllItems];
5354 [fAudTrack1DrcSlider setFloatValue: 0.00];
5355 [self audioDRCSliderChanged: fAudTrack1DrcSlider];
5357 else if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
5359 [fAudTrack1RatePopUp setEnabled: NO];
5360 [fAudTrack1BitratePopUp setEnabled: NO];
5361 [fAudTrack1DrcSlider setEnabled: NO];
5362 [fAudTrack1DrcField setEnabled: NO];
5365 /* enable/disable the mixdown text and popupbutton for audio track 2 */
5366 [fAudTrack2CodecPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5367 [fAudTrack2MixPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5368 [fAudTrack2RatePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5369 [fAudTrack2BitratePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5370 [fAudTrack2DrcSlider setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5371 [fAudTrack2DrcField setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5372 if ([fAudLang2PopUp indexOfSelectedItem] == 0)
5374 [fAudTrack2CodecPopUp removeAllItems];
5375 [fAudTrack2MixPopUp removeAllItems];
5376 [fAudTrack2RatePopUp removeAllItems];
5377 [fAudTrack2BitratePopUp removeAllItems];
5378 [fAudTrack2DrcSlider setFloatValue: 0.00];
5379 [self audioDRCSliderChanged: fAudTrack2DrcSlider];
5381 else if ([[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
5383 [fAudTrack2RatePopUp setEnabled: NO];
5384 [fAudTrack2BitratePopUp setEnabled: NO];
5385 [fAudTrack2DrcSlider setEnabled: NO];
5386 [fAudTrack2DrcField setEnabled: NO];
5389 /* enable/disable the mixdown text and popupbutton for audio track 3 */
5390 [fAudTrack3CodecPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5391 [fAudTrack3MixPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5392 [fAudTrack3RatePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5393 [fAudTrack3BitratePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5394 [fAudTrack3DrcSlider setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5395 [fAudTrack3DrcField setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5396 if ([fAudLang3PopUp indexOfSelectedItem] == 0)
5398 [fAudTrack3CodecPopUp removeAllItems];
5399 [fAudTrack3MixPopUp removeAllItems];
5400 [fAudTrack3RatePopUp removeAllItems];
5401 [fAudTrack3BitratePopUp removeAllItems];
5402 [fAudTrack3DrcSlider setFloatValue: 0.00];
5403 [self audioDRCSliderChanged: fAudTrack3DrcSlider];
5405 else if ([[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
5407 [fAudTrack3RatePopUp setEnabled: NO];
5408 [fAudTrack3BitratePopUp setEnabled: NO];
5409 [fAudTrack3DrcSlider setEnabled: NO];
5410 [fAudTrack3DrcField setEnabled: NO];
5413 /* enable/disable the mixdown text and popupbutton for audio track 4 */
5414 [fAudTrack4CodecPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5415 [fAudTrack4MixPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5416 [fAudTrack4RatePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5417 [fAudTrack4BitratePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5418 [fAudTrack4DrcSlider setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5419 [fAudTrack4DrcField setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5420 if ([fAudLang4PopUp indexOfSelectedItem] == 0)
5422 [fAudTrack4CodecPopUp removeAllItems];
5423 [fAudTrack4MixPopUp removeAllItems];
5424 [fAudTrack4RatePopUp removeAllItems];
5425 [fAudTrack4BitratePopUp removeAllItems];
5426 [fAudTrack4DrcSlider setFloatValue: 0.00];
5427 [self audioDRCSliderChanged: fAudTrack4DrcSlider];
5429 else if ([[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
5431 [fAudTrack4RatePopUp setEnabled: NO];
5432 [fAudTrack4BitratePopUp setEnabled: NO];
5433 [fAudTrack4DrcSlider setEnabled: NO];
5434 [fAudTrack4DrcField setEnabled: NO];
5439 - (IBAction) addAllAudioTracksToPopUp: (id) sender
5442 hb_list_t * list = hb_get_titles( fHandle );
5443 hb_title_t * title = (hb_title_t*)
5444 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
5446 hb_audio_config_t * audio;
5448 [sender removeAllItems];
5449 [sender addItemWithTitle: NSLocalizedString( @"None", @"" )];
5450 for( int i = 0; i < hb_list_count( title->list_audio ); i++ )
5452 audio = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, i );
5453 [[sender menu] addItemWithTitle:
5454 [NSString stringWithUTF8String: audio->lang.description]
5455 action: NULL keyEquivalent: @""];
5457 [sender selectItemAtIndex: 0];
5461 - (IBAction) selectAudioTrackInPopUp: (id) sender searchPrefixString: (NSString *) searchPrefixString selectIndexIfNotFound: (int) selectIndexIfNotFound
5464 /* this method can be used to find a language, or a language-and-source-format combination, by passing in the appropriate string */
5465 /* e.g. to find the first French track, pass in an NSString * of "Francais" */
5466 /* e.g. to find the first English 5.1 AC3 track, pass in an NSString * of "English (AC3) (5.1 ch)" */
5467 /* if no matching track is found, then selectIndexIfNotFound is used to choose which track to select instead */
5468 if (hb_list_count( fTitle->list_audio ) != 0)
5470 if (searchPrefixString)
5473 for( int i = 0; i < [sender numberOfItems]; i++ )
5475 /* Try to find the desired search string */
5476 if ([[[sender itemAtIndex: i] title] hasPrefix:searchPrefixString])
5478 [sender selectItemAtIndex: i];
5482 /* couldn't find the string, so select the requested "search string not found" item */
5483 /* index of 0 means select the "none" item */
5484 /* index of 1 means select the first audio track */
5485 [sender selectItemAtIndex: selectIndexIfNotFound];
5489 /* if no search string is provided, then select the selectIndexIfNotFound item */
5490 [sender selectItemAtIndex: selectIndexIfNotFound];
5495 [sender selectItemAtIndex: 0];
5499 - (IBAction) audioAddAudioTrackCodecs: (id)sender
5501 int format = [fDstFormatPopUp indexOfSelectedItem];
5503 /* setup pointers to the appropriate popups for the correct track */
5504 NSPopUpButton * audiocodecPopUp;
5505 NSPopUpButton * audiotrackPopUp;
5506 if (sender == fAudTrack1CodecPopUp)
5508 audiotrackPopUp = fAudLang1PopUp;
5509 audiocodecPopUp = fAudTrack1CodecPopUp;
5511 else if (sender == fAudTrack2CodecPopUp)
5513 audiotrackPopUp = fAudLang2PopUp;
5514 audiocodecPopUp = fAudTrack2CodecPopUp;
5516 else if (sender == fAudTrack3CodecPopUp)
5518 audiotrackPopUp = fAudLang3PopUp;
5519 audiocodecPopUp = fAudTrack3CodecPopUp;
5523 audiotrackPopUp = fAudLang4PopUp;
5524 audiocodecPopUp = fAudTrack4CodecPopUp;
5527 [audiocodecPopUp removeAllItems];
5528 /* Make sure "None" isnt selected in the source track */
5529 if ([audiotrackPopUp indexOfSelectedItem] > 0)
5531 [audiocodecPopUp setEnabled:YES];
5532 NSMenuItem *menuItem;
5533 /* We setup our appropriate popups for codecs and put the int value in the popup tag for easy retrieval */
5539 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (CoreAudio)" action: NULL keyEquivalent: @""];
5540 [menuItem setTag: HB_ACODEC_CA_AAC];
5542 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""];
5543 [menuItem setTag: HB_ACODEC_FAAC];
5545 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
5546 [menuItem setTag: HB_ACODEC_LAME];
5548 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
5549 [menuItem setTag: HB_ACODEC_AC3];
5555 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (CoreAudio)" action: NULL keyEquivalent: @""];
5556 [menuItem setTag: HB_ACODEC_CA_AAC];
5558 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""];
5559 [menuItem setTag: HB_ACODEC_FAAC];
5561 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
5562 [menuItem setTag: HB_ACODEC_AC3];
5564 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"DTS Passthru" action: NULL keyEquivalent: @""];
5565 [menuItem setTag: HB_ACODEC_DCA];
5567 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
5568 [menuItem setTag: HB_ACODEC_LAME];
5570 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"Vorbis (vorbis)" action: NULL keyEquivalent: @""];
5571 [menuItem setTag: HB_ACODEC_VORBIS];
5574 [audiocodecPopUp selectItemAtIndex:0];
5578 [audiocodecPopUp setEnabled:NO];
5582 - (IBAction) audioTrackPopUpChanged: (id) sender
5584 /* utility function to call audioTrackPopUpChanged without passing in a mixdown-to-use */
5585 [self audioTrackPopUpChanged: sender mixdownToUse: 0];
5588 - (IBAction) audioTrackPopUpChanged: (id) sender mixdownToUse: (int) mixdownToUse
5591 /* make sure we have a selected title before continuing */
5592 if (fTitle == NULL) return;
5593 /* make sure we have a source audio track before continuing */
5594 if (hb_list_count( fTitle->list_audio ) == 0)
5596 [sender selectItemAtIndex:0];
5599 /* if the sender is the lanaguage popup and there is nothing in the codec popup, lets call
5600 * audioAddAudioTrackCodecs on the codec popup to populate it properly before moving on
5602 if (sender == fAudLang1PopUp && [[fAudTrack1CodecPopUp menu] numberOfItems] == 0)
5604 [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp];
5606 if (sender == fAudLang2PopUp && [[fAudTrack2CodecPopUp menu] numberOfItems] == 0)
5608 [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp];
5610 if (sender == fAudLang3PopUp && [[fAudTrack3CodecPopUp menu] numberOfItems] == 0)
5612 [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp];
5614 if (sender == fAudLang4PopUp && [[fAudTrack4CodecPopUp menu] numberOfItems] == 0)
5616 [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp];
5619 /* Now lets make the sender the appropriate Audio Track popup from this point on */
5620 if (sender == fAudTrack1CodecPopUp || sender == fAudTrack1MixPopUp)
5622 sender = fAudLang1PopUp;
5624 if (sender == fAudTrack2CodecPopUp || sender == fAudTrack2MixPopUp)
5626 sender = fAudLang2PopUp;
5628 if (sender == fAudTrack3CodecPopUp || sender == fAudTrack3MixPopUp)
5630 sender = fAudLang3PopUp;
5632 if (sender == fAudTrack4CodecPopUp || sender == fAudTrack4MixPopUp)
5634 sender = fAudLang4PopUp;
5637 /* pointer to this track's mixdown, codec, sample rate and bitrate NSPopUpButton's */
5638 NSPopUpButton * mixdownPopUp;
5639 NSPopUpButton * audiocodecPopUp;
5640 NSPopUpButton * sampleratePopUp;
5641 NSPopUpButton * bitratePopUp;
5642 if (sender == fAudLang1PopUp)
5644 mixdownPopUp = fAudTrack1MixPopUp;
5645 audiocodecPopUp = fAudTrack1CodecPopUp;
5646 sampleratePopUp = fAudTrack1RatePopUp;
5647 bitratePopUp = fAudTrack1BitratePopUp;
5649 else if (sender == fAudLang2PopUp)
5651 mixdownPopUp = fAudTrack2MixPopUp;
5652 audiocodecPopUp = fAudTrack2CodecPopUp;
5653 sampleratePopUp = fAudTrack2RatePopUp;
5654 bitratePopUp = fAudTrack2BitratePopUp;
5656 else if (sender == fAudLang3PopUp)
5658 mixdownPopUp = fAudTrack3MixPopUp;
5659 audiocodecPopUp = fAudTrack3CodecPopUp;
5660 sampleratePopUp = fAudTrack3RatePopUp;
5661 bitratePopUp = fAudTrack3BitratePopUp;
5665 mixdownPopUp = fAudTrack4MixPopUp;
5666 audiocodecPopUp = fAudTrack4CodecPopUp;
5667 sampleratePopUp = fAudTrack4RatePopUp;
5668 bitratePopUp = fAudTrack4BitratePopUp;
5671 /* get the index of the selected audio Track*/
5672 int thisAudioIndex = [sender indexOfSelectedItem] - 1;
5674 /* pointer for the hb_audio_s struct we will use later on */
5675 hb_audio_config_t * audio;
5678 /* check if the audio mixdown controls need their enabled state changing */
5679 [self setEnabledStateOfAudioMixdownControls:nil];
5681 if (thisAudioIndex != -1)
5685 audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, thisAudioIndex );// Should "fTitle" be title and be setup ?
5687 /* actually manipulate the proper mixdowns here */
5688 /* delete the previous audio mixdown options */
5689 [mixdownPopUp removeAllItems];
5691 acodec = [[audiocodecPopUp selectedItem] tag];
5696 /* find out if our selected output audio codec supports 6ch */
5697 int audioCodecsSupport6Ch = (audio->in.codec && acodec != HB_ACODEC_LAME);
5699 /* check for AC-3 passthru */
5700 if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3)
5703 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5704 [NSString stringWithUTF8String: "AC3 Passthru"]
5705 action: NULL keyEquivalent: @""];
5706 [menuItem setTag: HB_ACODEC_AC3];
5708 else if (audio->in.codec == HB_ACODEC_DCA && acodec == HB_ACODEC_DCA)
5710 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5711 [NSString stringWithUTF8String: "DTS Passthru"]
5712 action: NULL keyEquivalent: @""];
5713 [menuItem setTag: HB_ACODEC_DCA];
5718 /* add the appropriate audio mixdown menuitems to the popupbutton */
5719 /* in each case, we set the new menuitem's tag to be the amixdown value for that mixdown,
5720 so that we can reference the mixdown later */
5722 /* keep a track of the min and max mixdowns we used, so we can select the best match later */
5723 int minMixdownUsed = 0;
5724 int maxMixdownUsed = 0;
5726 /* get the input channel layout without any lfe channels */
5727 int layout = audio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK;
5729 /* add a mono option */
5730 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5731 [NSString stringWithUTF8String: hb_audio_mixdowns[0].human_readable_name]
5732 action: NULL keyEquivalent: @""];
5733 [menuItem setTag: hb_audio_mixdowns[0].amixdown];
5734 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[0].amixdown;
5735 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[0].amixdown);
5737 /* do we want to add a stereo option? */
5738 /* offer stereo if we have a stereo-or-better source */
5739 if (layout >= HB_INPUT_CH_LAYOUT_STEREO)
5741 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5742 [NSString stringWithUTF8String: hb_audio_mixdowns[1].human_readable_name]
5743 action: NULL keyEquivalent: @""];
5744 [menuItem setTag: hb_audio_mixdowns[1].amixdown];
5745 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[1].amixdown;
5746 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[1].amixdown);
5749 /* do we want to add a dolby surround (DPL1) option? */
5750 if (layout == HB_INPUT_CH_LAYOUT_3F1R || layout == HB_INPUT_CH_LAYOUT_3F2R || layout == HB_INPUT_CH_LAYOUT_DOLBY)
5752 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5753 [NSString stringWithUTF8String: hb_audio_mixdowns[2].human_readable_name]
5754 action: NULL keyEquivalent: @""];
5755 [menuItem setTag: hb_audio_mixdowns[2].amixdown];
5756 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[2].amixdown;
5757 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[2].amixdown);
5760 /* do we want to add a dolby pro logic 2 (DPL2) option? */
5761 if (layout == HB_INPUT_CH_LAYOUT_3F2R)
5763 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5764 [NSString stringWithUTF8String: hb_audio_mixdowns[3].human_readable_name]
5765 action: NULL keyEquivalent: @""];
5766 [menuItem setTag: hb_audio_mixdowns[3].amixdown];
5767 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[3].amixdown;
5768 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[3].amixdown);
5771 /* do we want to add a 6-channel discrete option? */
5772 if (audioCodecsSupport6Ch == 1 && layout == HB_INPUT_CH_LAYOUT_3F2R && (audio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE))
5774 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5775 [NSString stringWithUTF8String: hb_audio_mixdowns[4].human_readable_name]
5776 action: NULL keyEquivalent: @""];
5777 [menuItem setTag: hb_audio_mixdowns[4].amixdown];
5778 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[4].amixdown;
5779 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[4].amixdown);
5782 /* do we want to add an AC-3 passthrough option? */
5783 if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3)
5785 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5786 [NSString stringWithUTF8String: hb_audio_mixdowns[5].human_readable_name]
5787 action: NULL keyEquivalent: @""];
5788 [menuItem setTag: HB_ACODEC_AC3];
5789 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown;
5790 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown);
5793 /* do we want to add a DTS Passthru option ? HB_ACODEC_DCA*/
5794 if (audio->in.codec == HB_ACODEC_DCA && acodec == HB_ACODEC_DCA)
5796 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5797 [NSString stringWithUTF8String: hb_audio_mixdowns[5].human_readable_name]
5798 action: NULL keyEquivalent: @""];
5799 [menuItem setTag: HB_ACODEC_DCA];
5800 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown;
5801 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown);
5804 /* auto-select the best mixdown based on our saved mixdown preference */
5806 /* for now, this is hard-coded to a "best" mixdown of HB_AMIXDOWN_DOLBYPLII */
5807 /* ultimately this should be a prefs option */
5810 /* if we passed in a mixdown to use - in order to load a preset - then try and use it */
5811 if (mixdownToUse > 0)
5813 useMixdown = mixdownToUse;
5817 useMixdown = HB_AMIXDOWN_DOLBYPLII;
5820 /* if useMixdown > maxMixdownUsed, then use maxMixdownUsed */
5821 if (useMixdown > maxMixdownUsed)
5823 useMixdown = maxMixdownUsed;
5826 /* if useMixdown < minMixdownUsed, then use minMixdownUsed */
5827 if (useMixdown < minMixdownUsed)
5829 useMixdown = minMixdownUsed;
5832 /* select the (possibly-amended) preferred mixdown */
5833 [mixdownPopUp selectItemWithTag: useMixdown];
5836 /* In the case of a source track that is not AC3 and the user tries to use AC3 Passthru (which does not work)
5837 * we force the Audio Codec choice back to a workable codec. We use CoreAudio aac for all containers.
5839 if (audio->in.codec != HB_ACODEC_AC3 && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_AC3)
5841 [audiocodecPopUp selectItemWithTag: HB_ACODEC_CA_AAC];
5844 /* In the case of a source track that is not DTS and the user tries to use DTS Passthru (which does not work)
5845 * we force the Audio Codec choice back to a workable codec. We use CoreAudio aac for all containers.
5847 if (audio->in.codec != HB_ACODEC_DCA && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_DCA)
5849 [audiocodecPopUp selectItemWithTag: HB_ACODEC_CA_AAC];
5852 /* Setup our samplerate and bitrate popups we will need based on mixdown */
5853 [self audioTrackMixdownChanged: mixdownPopUp];
5857 if( [fDstFormatPopUp indexOfSelectedItem] == 0 )
5859 [self autoSetM4vExtension: sender];
5863 - (IBAction) audioTrackMixdownChanged: (id) sender
5867 /* setup pointers to all of the other audio track controls
5868 * we will need later
5870 NSPopUpButton * mixdownPopUp;
5871 NSPopUpButton * sampleratePopUp;
5872 NSPopUpButton * bitratePopUp;
5873 NSPopUpButton * audiocodecPopUp;
5874 NSPopUpButton * audiotrackPopUp;
5875 NSSlider * drcSlider;
5876 NSTextField * drcField;
5877 if (sender == fAudTrack1MixPopUp)
5879 audiotrackPopUp = fAudLang1PopUp;
5880 audiocodecPopUp = fAudTrack1CodecPopUp;
5881 mixdownPopUp = fAudTrack1MixPopUp;
5882 sampleratePopUp = fAudTrack1RatePopUp;
5883 bitratePopUp = fAudTrack1BitratePopUp;
5884 drcSlider = fAudTrack1DrcSlider;
5885 drcField = fAudTrack1DrcField;
5887 else if (sender == fAudTrack2MixPopUp)
5889 audiotrackPopUp = fAudLang2PopUp;
5890 audiocodecPopUp = fAudTrack2CodecPopUp;
5891 mixdownPopUp = fAudTrack2MixPopUp;
5892 sampleratePopUp = fAudTrack2RatePopUp;
5893 bitratePopUp = fAudTrack2BitratePopUp;
5894 drcSlider = fAudTrack2DrcSlider;
5895 drcField = fAudTrack2DrcField;
5897 else if (sender == fAudTrack3MixPopUp)
5899 audiotrackPopUp = fAudLang3PopUp;
5900 audiocodecPopUp = fAudTrack3CodecPopUp;
5901 mixdownPopUp = fAudTrack3MixPopUp;
5902 sampleratePopUp = fAudTrack3RatePopUp;
5903 bitratePopUp = fAudTrack3BitratePopUp;
5904 drcSlider = fAudTrack3DrcSlider;
5905 drcField = fAudTrack3DrcField;
5909 audiotrackPopUp = fAudLang4PopUp;
5910 audiocodecPopUp = fAudTrack4CodecPopUp;
5911 mixdownPopUp = fAudTrack4MixPopUp;
5912 sampleratePopUp = fAudTrack4RatePopUp;
5913 bitratePopUp = fAudTrack4BitratePopUp;
5914 drcSlider = fAudTrack4DrcSlider;
5915 drcField = fAudTrack4DrcField;
5917 acodec = [[audiocodecPopUp selectedItem] tag];
5918 /* storage variable for the min and max bitrate allowed for this codec */
5924 case HB_ACODEC_FAAC:
5925 /* check if we have a 6ch discrete conversion in either audio track */
5926 if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5928 /* FAAC has a minimum of 192 kbps for 6-channel discrete */
5930 /* If either mixdown popup includes 6-channel discrete, then allow up to 768 kbps */
5936 /* FAAC is happy using our min bitrate of 32 kbps for stereo or mono */
5938 /* note: haven't dealt with mono separately here, FAAC will just use the max it can */
5943 case HB_ACODEC_CA_AAC:
5944 /* check if we have a 6ch discrete conversion in either audio track */
5945 if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5958 case HB_ACODEC_LAME:
5959 /* Lame is happy using our min bitrate of 32 kbps */
5961 /* Lame won't encode if the bitrate is higher than 320 kbps */
5965 case HB_ACODEC_VORBIS:
5966 if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5968 /* Vorbis causes a crash if we use a bitrate below 192 kbps with 6 channel */
5970 /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */
5976 /* Vorbis causes a crash if we use a bitrate below 48 kbps */
5978 /* Vorbis can cope with 384 kbps quite happily, even for stereo */
5984 /* AC3 passthru disables the bitrate dropdown anyway, so we might as well just use the min and max bitrate */
5990 /* make sure we have a selected title before continuing */
5991 if (fTitle == NULL || hb_list_count( fTitle->list_audio ) == 0) return;
5992 /* get the audio so we can find out what input rates are*/
5993 hb_audio_config_t * audio;
5994 audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, [audiotrackPopUp indexOfSelectedItem] - 1 );
5995 int inputbitrate = audio->in.bitrate / 1000;
5996 int inputsamplerate = audio->in.samplerate;
5998 if ([[mixdownPopUp selectedItem] tag] != HB_ACODEC_AC3 && [[mixdownPopUp selectedItem] tag] != HB_ACODEC_DCA)
6000 [bitratePopUp removeAllItems];
6002 for( int i = 0; i < hb_audio_bitrates_count; i++ )
6004 if (hb_audio_bitrates[i].rate >= minbitrate && hb_audio_bitrates[i].rate <= maxbitrate)
6006 /* add a new menuitem for this bitrate */
6007 NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle:
6008 [NSString stringWithUTF8String: hb_audio_bitrates[i].string]
6009 action: NULL keyEquivalent: @""];
6010 /* set its tag to be the actual bitrate as an integer, so we can retrieve it later */
6011 [menuItem setTag: hb_audio_bitrates[i].rate];
6015 /* select the default bitrate (but use 384 for 6-ch AAC) */
6016 if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
6018 [bitratePopUp selectItemWithTag: 384];
6022 [bitratePopUp selectItemWithTag: hb_audio_bitrates[hb_audio_bitrates_default].rate];
6025 /* populate and set the sample rate popup */
6026 /* Audio samplerate */
6027 [sampleratePopUp removeAllItems];
6028 /* we create a same as source selection (Auto) so that we can choose to use the input sample rate */
6029 NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle: @"Auto" action: NULL keyEquivalent: @""];
6030 [menuItem setTag: inputsamplerate];
6032 for( int i = 0; i < hb_audio_rates_count; i++ )
6034 NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle:
6035 [NSString stringWithUTF8String: hb_audio_rates[i].string]
6036 action: NULL keyEquivalent: @""];
6037 [menuItem setTag: hb_audio_rates[i].rate];
6039 /* We use the input sample rate as the default sample rate as downsampling just makes audio worse
6040 * and there is no compelling reason to use anything else as default, though the users default
6041 * preset will likely override any setting chosen here.
6043 [sampleratePopUp selectItemWithTag: inputsamplerate];
6046 /* Since AC3 Pass Thru and DTS Pass Thru uses the input bitrate and sample rate, we get the input tracks
6047 * bitrate and display it in the bitrate popup even though libhb happily ignores any bitrate input from
6048 * the gui. We do this for better user feedback in the audio tab as well as the queue for the most part
6050 if ([[mixdownPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[mixdownPopUp selectedItem] tag] == HB_ACODEC_DCA)
6053 /* lets also set the bitrate popup to the input bitrate as thats what passthru will use */
6054 [bitratePopUp removeAllItems];
6055 NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle:
6056 [NSString stringWithFormat:@"%d", inputbitrate]
6057 action: NULL keyEquivalent: @""];
6058 [menuItem setTag: inputbitrate];
6059 /* For ac3 passthru we disable the sample rate and bitrate popups as well as the drc slider*/
6060 [bitratePopUp setEnabled: NO];
6061 [sampleratePopUp setEnabled: NO];
6063 [drcSlider setFloatValue: 0.00];
6064 [self audioDRCSliderChanged: drcSlider];
6065 [drcSlider setEnabled: NO];
6066 [drcField setEnabled: NO];
6070 [sampleratePopUp setEnabled: YES];
6071 [bitratePopUp setEnabled: YES];
6072 [drcSlider setEnabled: YES];
6073 [drcField setEnabled: YES];
6075 [self calculateBitrate:nil];
6078 - (IBAction) audioDRCSliderChanged: (id) sender
6080 NSSlider * drcSlider;
6081 NSTextField * drcField;
6082 if (sender == fAudTrack1DrcSlider)
6084 drcSlider = fAudTrack1DrcSlider;
6085 drcField = fAudTrack1DrcField;
6087 else if (sender == fAudTrack2DrcSlider)
6089 drcSlider = fAudTrack2DrcSlider;
6090 drcField = fAudTrack2DrcField;
6092 else if (sender == fAudTrack3DrcSlider)
6094 drcSlider = fAudTrack3DrcSlider;
6095 drcField = fAudTrack3DrcField;
6099 drcSlider = fAudTrack4DrcSlider;
6100 drcField = fAudTrack4DrcField;
6103 /* If we are between 0.0 and 1.0 on the slider, snap it to 1.0 */
6104 if ([drcSlider floatValue] > 0.0 && [drcSlider floatValue] < 1.0)
6106 [drcSlider setFloatValue:1.0];
6110 [drcField setStringValue: [NSString stringWithFormat: @"%.2f", [drcSlider floatValue]]];
6111 /* For now, do not call this until we have an intelligent way to determine audio track selections
6112 * compared to presets
6114 //[self customSettingUsed: sender];
6119 - (IBAction) browseImportSrtFile: (id) sender
6122 NSOpenPanel * panel;
6124 panel = [NSOpenPanel openPanel];
6125 [panel setAllowsMultipleSelection: NO];
6126 [panel setCanChooseFiles: YES];
6127 [panel setCanChooseDirectories: NO ];
6128 NSString * sourceDirectory;
6129 if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastSrtImportDirectory"])
6131 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastSrtImportDirectory"];
6135 sourceDirectory = @"~/Desktop";
6136 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
6138 /* we open up the browse srt sheet here and call for browseImportSrtFileDone after the sheet is closed */
6139 NSArray *fileTypes = [NSArray arrayWithObjects:@"plist", @"srt", nil];
6140 [panel beginSheetForDirectory: sourceDirectory file: nil types: fileTypes
6141 modalForWindow: fWindow modalDelegate: self
6142 didEndSelector: @selector( browseImportSrtFileDone:returnCode:contextInfo: )
6143 contextInfo: sender];
6146 - (void) browseImportSrtFileDone: (NSSavePanel *) sheet
6147 returnCode: (int) returnCode contextInfo: (void *) contextInfo
6149 if( returnCode == NSOKButton )
6151 NSString *importSrtDirectory = [[sheet filename] stringByDeletingLastPathComponent];
6152 NSString *importSrtFilePath = [sheet filename];
6153 [[NSUserDefaults standardUserDefaults] setObject:importSrtDirectory forKey:@"LastSrtImportDirectory"];
6155 /* now pass the string off to fSubtitlesDelegate to add the srt file to the dropdown */
6156 [fSubtitlesDelegate createSubtitleSrtTrack:importSrtFilePath];
6158 [fSubtitlesTable reloadData];
6164 #pragma mark Open New Windows
6166 - (IBAction) openHomepage: (id) sender
6168 [[NSWorkspace sharedWorkspace] openURL: [NSURL
6169 URLWithString:@"http://handbrake.fr/"]];
6172 - (IBAction) openForums: (id) sender
6174 [[NSWorkspace sharedWorkspace] openURL: [NSURL
6175 URLWithString:@"http://handbrake.fr/forum/"]];
6177 - (IBAction) openUserGuide: (id) sender
6179 [[NSWorkspace sharedWorkspace] openURL: [NSURL
6180 URLWithString:@"http://handbrake.fr/trac/wiki/HandBrakeGuide"]];
6184 * Shows debug output window.
6186 - (IBAction)showDebugOutputPanel:(id)sender
6188 [outputPanel showOutputPanel:sender];
6192 * Shows preferences window.
6194 - (IBAction) showPreferencesWindow: (id) sender
6196 NSWindow * window = [fPreferencesController window];
6197 if (![window isVisible])
6200 [window makeKeyAndOrderFront: nil];
6204 * Shows queue window.
6206 - (IBAction) showQueueWindow:(id)sender
6208 [fQueueController showQueueWindow:sender];
6212 - (IBAction) toggleDrawer:(id)sender {
6213 [fPresetDrawer toggle:self];
6217 * Shows Picture Settings Window.
6220 - (IBAction) showPicturePanel: (id) sender
6222 [fPictureController showPictureWindow:sender];
6225 - (void) picturePanelFullScreen
6227 [fPictureController setToFullScreenMode];
6230 - (void) picturePanelWindowed
6232 [fPictureController setToWindowedMode];
6235 - (IBAction) showPreviewWindow: (id) sender
6237 [fPictureController showPreviewWindow:sender];
6241 #pragma mark Preset Outline View Methods
6242 #pragma mark - Required
6243 /* These are required by the NSOutlineView Datasource Delegate */
6246 /* used to specify the number of levels to show for each item */
6247 - (int)outlineView:(NSOutlineView *)fPresetsOutlineView numberOfChildrenOfItem:(id)item
6249 /* currently use no levels to test outline view viability */
6250 if (item == nil) // for an outline view the root level of the hierarchy is always nil
6252 return [UserPresets count];
6256 /* we need to return the count of the array in ChildrenArray for this folder */
6257 NSArray *children = nil;
6258 children = [item objectForKey:@"ChildrenArray"];
6259 if ([children count] > 0)
6261 return [children count];
6270 /* We use this to deterimine children of an item */
6271 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView child:(NSInteger)index ofItem:(id)item
6274 /* we need to return the count of the array in ChildrenArray for this folder */
6275 NSArray *children = nil;
6278 children = UserPresets;
6282 if ([item objectForKey:@"ChildrenArray"])
6284 children = [item objectForKey:@"ChildrenArray"];
6287 if ((children == nil) || ( [children count] <= (NSUInteger) index))
6293 return [children objectAtIndex:index];
6297 // We are only one level deep, so we can't be asked about children
6298 //NSAssert (NO, @"Presets View outlineView:child:ofItem: currently can't handle nested items.");
6302 /* We use this to determine if an item should be expandable */
6303 - (BOOL)outlineView:(NSOutlineView *)fPresetsOutlineView isItemExpandable:(id)item
6306 /* we need to return the count of the array in ChildrenArray for this folder */
6307 NSArray *children= nil;
6310 children = UserPresets;
6314 if ([item objectForKey:@"ChildrenArray"])
6316 children = [item objectForKey:@"ChildrenArray"];
6320 /* To deterimine if an item should show a disclosure triangle
6321 * we could do it by the children count as so:
6322 * if ([children count] < 1)
6323 * However, lets leave the triangle show even if there are no
6324 * children to help indicate a folder, just like folder in the
6325 * finder can show a disclosure triangle even when empty
6328 /* We need to determine if the item is a folder */
6329 if ([[item objectForKey:@"Folder"] intValue] == 1)
6340 - (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item
6342 // Our outline view has no levels, but we can still expand every item. Doing so
6343 // just makes the row taller. See heightOfRowByItem below.
6344 //return ![(HBQueueOutlineView*)outlineView isDragging];
6350 /* Used to tell the outline view which information is to be displayed per item */
6351 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
6353 /* We have two columns right now, icon and PresetName */
6355 if ([[tableColumn identifier] isEqualToString:@"PresetName"])
6357 return [item objectForKey:@"PresetName"];
6366 - (id)outlineView:(NSOutlineView *)outlineView itemForPersistentObject:(id)object
6368 return [NSKeyedUnarchiver unarchiveObjectWithData:object];
6370 - (id)outlineView:(NSOutlineView *)outlineView persistentObjectForItem:(id)item
6372 return [NSKeyedArchiver archivedDataWithRootObject:item];
6375 #pragma mark - Added Functionality (optional)
6376 /* Use to customize the font and display characteristics of the title cell */
6377 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
6379 if ([[tableColumn identifier] isEqualToString:@"PresetName"])
6383 NSColor *shadowColor;
6384 txtFont = [NSFont systemFontOfSize: [NSFont smallSystemFontSize]];
6385 /*check to see if its a selected row */
6386 if ([fPresetsOutlineView selectedRow] == [fPresetsOutlineView rowForItem:item])
6389 fontColor = [NSColor blackColor];
6390 shadowColor = [NSColor colorWithDeviceRed:(127.0/255.0) green:(140.0/255.0) blue:(160.0/255.0) alpha:1.0];
6394 if ([[item objectForKey:@"Type"] intValue] == 0)
6396 fontColor = [NSColor blueColor];
6398 else // User created preset, use a black font
6400 fontColor = [NSColor blackColor];
6402 /* check to see if its a folder */
6403 //if ([[item objectForKey:@"Folder"] intValue] == 1)
6405 //fontColor = [NSColor greenColor];
6410 /* We use Bold Text for the HB Default */
6411 if ([[item objectForKey:@"Default"] intValue] == 1)// 1 is HB default
6413 txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
6415 /* We use Bold Text for the User Specified Default */
6416 if ([[item objectForKey:@"Default"] intValue] == 2)// 2 is User default
6418 txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
6422 [cell setTextColor:fontColor];
6423 [cell setFont:txtFont];
6428 /* We use this to edit the name field in the outline view */
6429 - (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
6431 if ([[tableColumn identifier] isEqualToString:@"PresetName"])
6436 [theRecord setObject:object forKey:@"PresetName"];
6440 [fPresetsOutlineView reloadData];
6441 /* We save all of the preset data here */
6445 /* We use this to provide tooltips for the items in the presets outline view */
6446 - (NSString *)outlineView:(NSOutlineView *)fPresetsOutlineView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc item:(id)item mouseLocation:(NSPoint)mouseLocation
6448 //if ([[tc identifier] isEqualToString:@"PresetName"])
6450 /* initialize the tooltip contents variable */
6452 /* if there is a description for the preset, we show it in the tooltip */
6453 if ([item objectForKey:@"PresetDescription"])
6455 loc_tip = [item objectForKey:@"PresetDescription"];
6460 loc_tip = @"No description available";
6467 #pragma mark Preset Outline View Methods (dragging related)
6470 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
6472 // Dragging is only allowed for custom presets.
6473 //[[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Default"] intValue] != 1
6474 if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Type"] intValue] == 0) // 0 is built in preset
6478 // Don't retain since this is just holding temporaral drag information, and it is
6479 //only used during a drag! We could put this in the pboard actually.
6480 fDraggedNodes = items;
6481 // Provide data for our custom type, and simple NSStrings.
6482 [pboard declareTypes:[NSArray arrayWithObjects: DragDropSimplePboardType, nil] owner:self];
6484 // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!.
6485 [pboard setData:[NSData data] forType:DragDropSimplePboardType];
6490 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
6493 // Don't allow dropping ONTO an item since they can't really contain any children.
6495 BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex;
6496 if (isOnDropTypeProposal)
6497 return NSDragOperationNone;
6499 // Don't allow dropping INTO an item since they can't really contain any children as of yet.
6502 index = [fPresetsOutlineView rowForItem: item] + 1;
6506 // Don't allow dropping into the Built In Presets.
6507 if (index < presetCurrentBuiltInCount)
6509 return NSDragOperationNone;
6510 index = MAX (index, presetCurrentBuiltInCount);
6513 [outlineView setDropItem:item dropChildIndex:index];
6514 return NSDragOperationGeneric;
6519 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
6521 /* first, lets see if we are dropping into a folder */
6522 if ([[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] && [[[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] intValue] == 1) // if its a folder
6524 NSMutableArray *childrenArray = [[NSMutableArray alloc] init];
6525 childrenArray = [[fPresetsOutlineView itemAtRow:index] objectForKey:@"ChildrenArray"];
6526 [childrenArray addObject:item];
6527 [[fPresetsOutlineView itemAtRow:index] setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"];
6528 [childrenArray autorelease];
6530 else // We are not, so we just move the preset into the existing array
6532 NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet];
6534 NSEnumerator *enumerator = [fDraggedNodes objectEnumerator];
6535 while (obj = [enumerator nextObject])
6537 [moveItems addIndex:[UserPresets indexOfObject:obj]];
6539 // Successful drop, lets rearrange the view and save it all
6540 [self moveObjectsInPresetsArray:UserPresets fromIndexes:moveItems toIndex: index];
6542 [fPresetsOutlineView reloadData];
6547 - (void)moveObjectsInPresetsArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
6549 NSUInteger index = [indexSet lastIndex];
6550 NSUInteger aboveInsertIndexCount = 0;
6552 NSUInteger removeIndex;
6554 if (index >= insertIndex)
6556 removeIndex = index + aboveInsertIndexCount;
6557 aboveInsertIndexCount++;
6561 removeIndex = index;
6565 id object = [[array objectAtIndex:removeIndex] retain];
6566 [array removeObjectAtIndex:removeIndex];
6567 [array insertObject:object atIndex:insertIndex];
6570 index = [indexSet indexLessThanIndex:index];
6575 #pragma mark - Functional Preset NSOutlineView Methods
6577 - (IBAction)selectPreset:(id)sender
6580 if ([fPresetsOutlineView selectedRow] >= 0 && [[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] != 1)
6582 chosenPreset = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
6583 [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
6585 if ([[chosenPreset objectForKey:@"Default"] intValue] == 1)
6587 [fPresetSelectedDisplay setStringValue:[NSString stringWithFormat:@"%@ (Default)", [chosenPreset objectForKey:@"PresetName"]]];
6591 [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
6595 [fDstFormatPopUp selectItemWithTitle:[chosenPreset objectForKey:@"FileFormat"]];
6596 [self formatPopUpChanged:nil];
6598 /* Chapter Markers*/
6599 [fCreateChapterMarkers setState:[[chosenPreset objectForKey:@"ChapterMarkers"] intValue]];
6600 /* check to see if we have only one chapter */
6601 [self chapterPopUpChanged:nil];
6603 /* Allow Mpeg4 64 bit formatting +4GB file sizes */
6604 [fDstMp4LargeFileCheck setState:[[chosenPreset objectForKey:@"Mp4LargeFile"] intValue]];
6605 /* Mux mp4 with http optimization */
6606 [fDstMp4HttpOptFileCheck setState:[[chosenPreset objectForKey:@"Mp4HttpOptimize"] intValue]];
6609 [fVidEncoderPopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoEncoder"]];
6610 /* We set the advanced opt string here if applicable*/
6611 [fAdvancedOptions setOptions:[chosenPreset objectForKey:@"x264Option"]];
6613 /* Lets run through the following functions to get variables set there */
6614 [self videoEncoderPopUpChanged:nil];
6615 /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/
6616 [fDstMp4iPodFileCheck setState:[[chosenPreset objectForKey:@"Mp4iPodCompatible"] intValue]];
6617 [self calculateBitrate:nil];
6620 [fVidQualityMatrix selectCellAtRow:[[chosenPreset objectForKey:@"VideoQualityType"] intValue] column:0];
6622 [fVidTargetSizeField setStringValue:[chosenPreset objectForKey:@"VideoTargetSize"]];
6623 [fVidBitrateField setStringValue:[chosenPreset objectForKey:@"VideoAvgBitrate"]];
6625 /* Since we are now using RF Values for the slider, we detect if the preset uses an old quality float.
6626 * So, check to see if the quality value is less than 1.0 which should indicate the old ".062" type
6627 * quality preset. Caveat: in the case of x264, where the RF scale starts at 0, it would misinterpret
6628 * a preset that uses 0.0 - 0.99 for RF as an old style preset. Not sure how to get around that one yet,
6629 * though it should be a corner case since it would pretty much be a preset for lossless encoding. */
6630 if ([[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue] < 1.0)
6632 /* For the quality slider we need to convert the old percent's to the new rf scales */
6633 float rf = (([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * [[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]);
6634 [fVidQualitySlider setFloatValue:rf];
6639 /* Since theora's qp value goes up from left to right, we can just set the slider float value */
6640 if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
6642 [fVidQualitySlider setFloatValue:[[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]];
6646 /* since ffmpeg and x264 use an "inverted" slider (lower qp/rf values indicate a higher quality) we invert the value on the slider */
6647 [fVidQualitySlider setFloatValue:([fVidQualitySlider maxValue] + [fVidQualitySlider minValue]) - [[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]];
6651 [self videoMatrixChanged:nil];
6653 /* Video framerate */
6654 /* For video preset video framerate, we want to make sure that Same as source does not conflict with the
6655 detected framerate in the fVidRatePopUp so we use index 0*/
6656 if ([[chosenPreset objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"])
6658 [fVidRatePopUp selectItemAtIndex: 0];
6662 [fVidRatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoFramerate"]];
6665 [fFrameratePfrCheck setState:[[chosenPreset objectForKey:@"VideoFrameratePFR"] intValue]];
6666 [self videoFrameRateChanged:nil];
6668 /* 2 Pass Encoding */
6669 [fVidTwoPassCheck setState:[[chosenPreset objectForKey:@"VideoTwoPass"] intValue]];
6670 [self twoPassCheckboxChanged:nil];
6672 /* Turbo 1st pass for 2 Pass Encoding */
6673 [fVidTurboPassCheck setState:[[chosenPreset objectForKey:@"VideoTurboTwoPass"] intValue]];
6676 /* First we check to see if we are using the current audio track layout based on AudioList array */
6677 if ([chosenPreset objectForKey:@"AudioList"])
6680 /* pointer to this track's mixdown, codec, sample rate and bitrate NSPopUpButton's */
6681 NSPopUpButton * trackLangPreviousPopUp = nil;
6682 NSPopUpButton * trackLangPopUp = nil;
6683 NSPopUpButton * mixdownPopUp = nil;
6684 NSPopUpButton * audiocodecPopUp = nil;
6685 NSPopUpButton * sampleratePopUp = nil;
6686 NSPopUpButton * bitratePopUp = nil;
6687 NSSlider * drcSlider = nil;
6690 /* Populate the audio widgets based on the contents of the AudioList array */
6692 NSEnumerator *enumerator = [[chosenPreset objectForKey:@"AudioList"] objectEnumerator];
6694 while (tempObject = [enumerator nextObject])
6699 trackLangPopUp = fAudLang1PopUp;
6700 mixdownPopUp = fAudTrack1MixPopUp;
6701 audiocodecPopUp = fAudTrack1CodecPopUp;
6702 sampleratePopUp = fAudTrack1RatePopUp;
6703 bitratePopUp = fAudTrack1BitratePopUp;
6704 drcSlider = fAudTrack1DrcSlider;
6708 trackLangPreviousPopUp = fAudLang1PopUp;
6709 trackLangPopUp = fAudLang2PopUp;
6710 mixdownPopUp = fAudTrack2MixPopUp;
6711 audiocodecPopUp = fAudTrack2CodecPopUp;
6712 sampleratePopUp = fAudTrack2RatePopUp;
6713 bitratePopUp = fAudTrack2BitratePopUp;
6714 drcSlider = fAudTrack2DrcSlider;
6718 trackLangPreviousPopUp = fAudLang2PopUp;
6719 trackLangPopUp = fAudLang3PopUp;
6720 mixdownPopUp = fAudTrack3MixPopUp;
6721 audiocodecPopUp = fAudTrack3CodecPopUp;
6722 sampleratePopUp = fAudTrack3RatePopUp;
6723 bitratePopUp = fAudTrack3BitratePopUp;
6724 drcSlider = fAudTrack3DrcSlider;
6728 trackLangPreviousPopUp = fAudLang3PopUp;
6729 trackLangPopUp = fAudLang4PopUp;
6730 mixdownPopUp = fAudTrack4MixPopUp;
6731 audiocodecPopUp = fAudTrack4CodecPopUp;
6732 sampleratePopUp = fAudTrack4RatePopUp;
6733 bitratePopUp = fAudTrack4BitratePopUp;
6734 drcSlider = fAudTrack4DrcSlider;
6738 if ([trackLangPopUp indexOfSelectedItem] == 0)
6742 [trackLangPopUp selectItemAtIndex: 1];
6746 /* if we are greater than track 1, select
6747 * the same track as the previous track */
6748 [trackLangPopUp selectItemAtIndex: [trackLangPreviousPopUp indexOfSelectedItem]];
6751 [self audioTrackPopUpChanged: trackLangPopUp];
6752 [audiocodecPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioEncoder"]];
6753 /* check our pref for core audio and use it in place of faac if preset is a built in */
6754 if ([[chosenPreset objectForKey:@"Type"] intValue] == 0 &&
6755 [[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES &&
6756 [[tempObject objectForKey:@"AudioEncoder"] isEqualToString: @"AAC (faac)"])
6758 [audiocodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6761 [self audioTrackPopUpChanged: audiocodecPopUp];
6762 [mixdownPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioMixdown"]];
6763 [self audioTrackMixdownChanged: mixdownPopUp];
6764 /* check to see if the selection was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6766 if ([mixdownPopUp selectedItem] == nil)
6768 [self audioTrackPopUpChanged: audiocodecPopUp];
6769 [self writeToActivityLog: "presetSelected mixdown not selected, rerun audioTrackPopUpChanged"];
6771 [sampleratePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioSamplerate"]];
6772 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6773 if (![[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"])
6775 [bitratePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioBitrate"]];
6776 /* check to see if the bitrate selection was available, if not, rerun audioTrackMixdownChanged using the mixdown to just set the
6777 *default mixdown bitrate*/
6778 if ([bitratePopUp selectedItem] == nil)
6780 [self audioTrackMixdownChanged: mixdownPopUp];
6783 [drcSlider setFloatValue:[[tempObject objectForKey:@"AudioTrackDRCSlider"] floatValue]];
6784 [self audioDRCSliderChanged: drcSlider];
6787 /* 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,
6788 * if not we will set the track to "None". Track 1 is allowed to mixdown to a suitable DPL2 mix if we cannot passthru */
6792 /* 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". */
6793 if (([[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"] || [[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"DTS Passthru"]) && [trackLangPopUp indexOfSelectedItem] != 0)
6795 hb_audio_config_t * audio;
6796 /* get the audio source audio codec */
6797 audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, [trackLangPopUp indexOfSelectedItem] - 1 );
6798 if (audio != NULL && [[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"] && audio->in.codec != HB_ACODEC_AC3 ||
6799 [[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"DTS Passthru"] && audio->in.codec != HB_ACODEC_DCA )
6801 /* We have a preset using ac3 passthru but no ac3 source audio, so set the track to "None" and bail */
6802 if ([[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"])
6804 [self writeToActivityLog: "Preset calls for AC3 Pass thru ..."];
6806 if ([[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"DTS Passthru"])
6808 [self writeToActivityLog: "Preset calls for DTS Pass thru ..."];
6810 [self writeToActivityLog: "No matching source codec, setting track %d to None", i];
6811 [trackLangPopUp selectItemAtIndex: 0];
6812 [self audioTrackPopUpChanged: trackLangPopUp];
6818 /* We now cleanup any extra audio tracks that may have been previously set if we need to */
6822 [fAudLang4PopUp selectItemAtIndex: 0];
6823 [self audioTrackPopUpChanged: fAudLang4PopUp];
6827 [fAudLang3PopUp selectItemAtIndex: 0];
6828 [self audioTrackPopUpChanged: fAudLang3PopUp];
6832 [fAudLang2PopUp selectItemAtIndex: 0];
6833 [self audioTrackPopUpChanged: fAudLang2PopUp];
6841 if ([chosenPreset objectForKey:@"Audio1Track"] > 0)
6843 if ([fAudLang1PopUp indexOfSelectedItem] == 0)
6845 [fAudLang1PopUp selectItemAtIndex: 1];
6847 [self audioTrackPopUpChanged: fAudLang1PopUp];
6848 [fAudTrack1CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Encoder"]];
6849 /* check our pref for core audio and use it in place of faac if preset is built in */
6850 if ([[chosenPreset objectForKey:@"Type"] intValue] == 0 &&
6851 [[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES &&
6852 [[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString: @"AAC (faac)"])
6854 [fAudTrack1CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6857 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
6858 [fAudTrack1MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Mixdown"]];
6859 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6861 if ([fAudTrack1MixPopUp selectedItem] == nil)
6863 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
6865 [fAudTrack1RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Samplerate"]];
6866 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6867 if (![[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString:@"AC3 Passthru"])
6869 [fAudTrack1BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Bitrate"]];
6871 [fAudTrack1DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio1TrackDRCSlider"] floatValue]];
6872 [self audioDRCSliderChanged: fAudTrack1DrcSlider];
6875 if ([chosenPreset objectForKey:@"Audio2Track"] > 0)
6877 if ([fAudLang2PopUp indexOfSelectedItem] == 0)
6879 [fAudLang2PopUp selectItemAtIndex: 1];
6881 [self audioTrackPopUpChanged: fAudLang2PopUp];
6882 [fAudTrack2CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Encoder"]];
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:@"Audio2Encoder"] isEqualToString: @"AAC (faac)"])
6888 [fAudTrack2CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6890 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
6891 [fAudTrack2MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Mixdown"]];
6892 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6894 if ([fAudTrack2MixPopUp selectedItem] == nil)
6896 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
6898 [fAudTrack2RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Samplerate"]];
6899 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6900 if (![[chosenPreset objectForKey:@"Audio2Encoder"] isEqualToString:@"AC3 Passthru"])
6902 [fAudTrack2BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Bitrate"]];
6904 [fAudTrack2DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio2TrackDRCSlider"] floatValue]];
6905 [self audioDRCSliderChanged: fAudTrack2DrcSlider];
6907 if ([chosenPreset objectForKey:@"Audio3Track"] > 0)
6909 if ([fAudLang3PopUp indexOfSelectedItem] == 0)
6911 [fAudLang3PopUp selectItemAtIndex: 1];
6913 [self audioTrackPopUpChanged: fAudLang3PopUp];
6914 [fAudTrack3CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Encoder"]];
6915 /* check our pref for core audio and use it in place of faac if preset is built in */
6916 if ([[chosenPreset objectForKey:@"Type"] intValue] == 0 &&
6917 [[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES &&
6918 [[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AAC (faac)"])
6920 [fAudTrack3CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6922 [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
6923 [fAudTrack3MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Mixdown"]];
6924 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6926 if ([fAudTrack3MixPopUp selectedItem] == nil)
6928 [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
6930 [fAudTrack3RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Samplerate"]];
6931 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6932 if (![[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AC3 Passthru"])
6934 [fAudTrack3BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Bitrate"]];
6936 [fAudTrack3DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio3TrackDRCSlider"] floatValue]];
6937 [self audioDRCSliderChanged: fAudTrack3DrcSlider];
6939 if ([chosenPreset objectForKey:@"Audio4Track"] > 0)
6941 if ([fAudLang4PopUp indexOfSelectedItem] == 0)
6943 [fAudLang4PopUp selectItemAtIndex: 1];
6945 [self audioTrackPopUpChanged: fAudLang4PopUp];
6946 [fAudTrack4CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Encoder"]];
6947 /* check our pref for core audio and use it in place of faac if preset is built in */
6948 if ([[chosenPreset objectForKey:@"Type"] intValue] == 0 &&
6949 [[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES &&
6950 [[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString: @"AAC (faac)"])
6952 [fAudTrack4CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6954 [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
6955 [fAudTrack4MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Mixdown"]];
6956 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6958 if ([fAudTrack4MixPopUp selectedItem] == nil)
6960 [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
6962 [fAudTrack4RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Samplerate"]];
6963 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6964 if (![[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString:@"AC3 Passthru"])
6966 [fAudTrack4BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Bitrate"]];
6968 [fAudTrack4DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio4TrackDRCSlider"] floatValue]];
6969 [self audioDRCSliderChanged: fAudTrack4DrcSlider];
6972 /* We now cleanup any extra audio tracks that may have been previously set if we need to */
6974 if (![chosenPreset objectForKey:@"Audio2Track"] || [chosenPreset objectForKey:@"Audio2Track"] == 0)
6976 [fAudLang2PopUp selectItemAtIndex: 0];
6977 [self audioTrackPopUpChanged: fAudLang2PopUp];
6979 if (![chosenPreset objectForKey:@"Audio3Track"] || [chosenPreset objectForKey:@"Audio3Track"] > 0)
6981 [fAudLang3PopUp selectItemAtIndex: 0];
6982 [self audioTrackPopUpChanged: fAudLang3PopUp];
6984 if (![chosenPreset objectForKey:@"Audio4Track"] || [chosenPreset objectForKey:@"Audio4Track"] > 0)
6986 [fAudLang4PopUp selectItemAtIndex: 0];
6987 [self audioTrackPopUpChanged: fAudLang4PopUp];
6992 [fSubPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Subtitles"]];
6993 /* Forced Subtitles */
6994 [fSubForcedCheck setState:[[chosenPreset objectForKey:@"SubtitlesForced"] intValue]];
6996 /* Picture Settings */
6997 /* Note: objectForKey:@"UsesPictureSettings" refers to picture size, which encompasses:
6998 * height, width, keep ar, anamorphic and crop settings.
6999 * picture filters are handled separately below.
7001 /* Check to see if the objectForKey:@"UsesPictureSettings is greater than 0, as 0 means use picture sizing "None"
7002 * ( 2 is use max for source and 1 is use exact size when the preset was created ) and the
7003 * preset completely ignores any picture sizing values in the preset.
7005 if ([[chosenPreset objectForKey:@"UsesPictureSettings"] intValue] > 0)
7007 hb_job_t * job = fTitle->job;
7009 /* If Cropping is set to custom, then recall all four crop values from
7010 when the preset was created and apply them */
7011 if ([[chosenPreset objectForKey:@"PictureAutoCrop"] intValue] == 0)
7013 [fPictureController setAutoCrop:NO];
7015 /* Here we use the custom crop values saved at the time the preset was saved */
7016 job->crop[0] = [[chosenPreset objectForKey:@"PictureTopCrop"] intValue];
7017 job->crop[1] = [[chosenPreset objectForKey:@"PictureBottomCrop"] intValue];
7018 job->crop[2] = [[chosenPreset objectForKey:@"PictureLeftCrop"] intValue];
7019 job->crop[3] = [[chosenPreset objectForKey:@"PictureRightCrop"] intValue];
7022 else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */
7024 [fPictureController setAutoCrop:YES];
7025 /* Here we use the auto crop values determined right after scan */
7026 job->crop[0] = AutoCropTop;
7027 job->crop[1] = AutoCropBottom;
7028 job->crop[2] = AutoCropLeft;
7029 job->crop[3] = AutoCropRight;
7034 if ([chosenPreset objectForKey:@"PictureModulus"])
7036 job->modulus = [[chosenPreset objectForKey:@"PictureModulus"] intValue];
7043 /* Check to see if the objectForKey:@"UsesPictureSettings is 2 which is "Use Max for the source */
7044 if ([[chosenPreset objectForKey:@"UsesPictureSettings"] intValue] == 2 || [[chosenPreset objectForKey:@"UsesMaxPictureSettings"] intValue] == 1)
7046 /* Use Max Picture settings for whatever the dvd is.*/
7047 [self revertPictureSizeToMax:nil];
7048 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"] intValue];
7049 if (job->keep_ratio == 1)
7051 hb_fix_aspect( job, HB_KEEP_WIDTH );
7052 if( job->height > fTitle->height )
7054 job->height = fTitle->height;
7055 hb_fix_aspect( job, HB_KEEP_HEIGHT );
7058 job->anamorphic.mode = [[chosenPreset objectForKey:@"PicturePAR"] intValue];
7060 else // /* If not 0 or 2 we assume objectForKey:@"UsesPictureSettings is 1 which is "Use picture sizing from when the preset was set" */
7062 /* we check to make sure the presets width/height does not exceed the sources width/height */
7063 if (fTitle->width < [[chosenPreset objectForKey:@"PictureWidth"] intValue] || fTitle->height < [[chosenPreset objectForKey:@"PictureHeight"] intValue])
7065 /* if so, then we use the sources height and width to avoid scaling up */
7066 //job->width = fTitle->width;
7067 //job->height = fTitle->height;
7068 [self revertPictureSizeToMax:nil];
7070 else // source width/height is >= the preset height/width
7072 /* we can go ahead and use the presets values for height and width */
7073 job->width = [[chosenPreset objectForKey:@"PictureWidth"] intValue];
7074 job->height = [[chosenPreset objectForKey:@"PictureHeight"] intValue];
7076 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"] intValue];
7077 if (job->keep_ratio == 1)
7079 int height = fTitle->height;
7081 if ( job->height && job->height < fTitle->height )
7082 height = job->height;
7084 hb_fix_aspect( job, HB_KEEP_WIDTH );
7085 // Make sure the resulting height is less than
7086 // the title height and less than the height
7087 // requested in the preset.
7088 if( job->height > height )
7090 job->height = height;
7091 hb_fix_aspect( job, HB_KEEP_HEIGHT );
7094 job->anamorphic.mode = [[chosenPreset objectForKey:@"PicturePAR"] intValue];
7095 if ( job->anamorphic.mode > 0 )
7097 int w, h, par_w, par_h;
7099 job->anamorphic.par_width = fTitle->pixel_aspect_width;
7100 job->anamorphic.par_height = fTitle->pixel_aspect_height;
7101 job->maxWidth = job->width;
7102 job->maxHeight = job->height;
7103 hb_set_anamorphic_size( job, &w, &h, &par_w, &par_h );
7114 /* If the preset has an objectForKey:@"UsesPictureFilters", and handle the filters here */
7115 if ([chosenPreset objectForKey:@"UsesPictureFilters"] && [[chosenPreset objectForKey:@"UsesPictureFilters"] intValue] > 0)
7119 /* We only allow *either* Decomb or Deinterlace. So check for the PictureDecombDeinterlace key.
7120 * also, older presets may not have this key, in which case we also check to see if that preset had PictureDecomb
7121 * specified, in which case we use decomb and ignore any possible Deinterlace settings as using both was less than
7124 [fPictureController setUseDecomb:1];
7125 [fPictureController setDecomb:0];
7126 [fPictureController setDeinterlace:0];
7127 if ([[chosenPreset objectForKey:@"PictureDecombDeinterlace"] intValue] == 1 || [[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0)
7129 /* we are using decomb */
7131 if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0)
7133 [fPictureController setDecomb:[[chosenPreset objectForKey:@"PictureDecomb"] intValue]];
7135 /* if we are using "Custom" in the decomb setting, also set the custom string*/
7136 if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] == 1)
7138 [fPictureController setDecombCustomString:[chosenPreset objectForKey:@"PictureDecombCustom"]];
7144 /* We are using Deinterlace */
7146 if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] > 0)
7148 [fPictureController setUseDecomb:0];
7149 [fPictureController setDeinterlace:[[chosenPreset objectForKey:@"PictureDeinterlace"] intValue]];
7150 /* if we are using "Custom" in the deinterlace setting, also set the custom string*/
7151 if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] == 1)
7153 [fPictureController setDeinterlaceCustomString:[chosenPreset objectForKey:@"PictureDeinterlaceCustom"]];
7160 if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] > 0)
7162 [fPictureController setDetelecine:[[chosenPreset objectForKey:@"PictureDetelecine"] intValue]];
7163 /* if we are using "Custom" in the detelecine setting, also set the custom string*/
7164 if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 1)
7166 [fPictureController setDetelecineCustomString:[chosenPreset objectForKey:@"PictureDetelecineCustom"]];
7171 [fPictureController setDetelecine:0];
7175 if ([[chosenPreset objectForKey:@"PictureDenoise"] intValue] > 0)
7177 [fPictureController setDenoise:[[chosenPreset objectForKey:@"PictureDenoise"] intValue]];
7178 /* if we are using "Custom" in the denoise setting, also set the custom string*/
7179 if ([[chosenPreset objectForKey:@"PictureDenoise"] intValue] == 1)
7181 [fPictureController setDenoiseCustomString:[chosenPreset objectForKey:@"PictureDenoiseCustom"]];
7186 [fPictureController setDenoise:0];
7190 if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1)
7192 /* if its a one, then its the old on/off deblock, set on to 5*/
7193 [fPictureController setDeblock:5];
7197 /* use the settings intValue */
7198 [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]];
7201 if ([[chosenPreset objectForKey:@"VideoGrayScale"] intValue] == 1)
7203 [fPictureController setGrayscale:1];
7207 [fPictureController setGrayscale:0];
7210 /* we call SetTitle: in fPictureController so we get an instant update in the Picture Settings window */
7211 [fPictureController SetTitle:fTitle];
7212 [fPictureController SetTitle:fTitle];
7213 [self calculatePictureSizing:nil];
7219 #pragma mark Manage Presets
7221 - (void) loadPresets {
7222 /* We declare the default NSFileManager into fileManager */
7223 NSFileManager * fileManager = [NSFileManager defaultManager];
7224 /*We define the location of the user presets file */
7225 UserPresetsFile = @"~/Library/Application Support/HandBrake/UserPresets.plist";
7226 UserPresetsFile = [[UserPresetsFile stringByExpandingTildeInPath]retain];
7227 /* We check for the presets.plist */
7228 if ([fileManager fileExistsAtPath:UserPresetsFile] == 0)
7230 [fileManager createFileAtPath:UserPresetsFile contents:nil attributes:nil];
7233 UserPresets = [[NSMutableArray alloc] initWithContentsOfFile:UserPresetsFile];
7234 if (nil == UserPresets)
7236 UserPresets = [[NSMutableArray alloc] init];
7237 [self addFactoryPresets:nil];
7239 [fPresetsOutlineView reloadData];
7241 [self checkBuiltInsForUpdates];
7244 - (void) checkBuiltInsForUpdates {
7246 BOOL updateBuiltInPresets = NO;
7248 NSEnumerator *enumerator = [UserPresets objectEnumerator];
7250 while (tempObject = [enumerator nextObject])
7252 /* iterate through the built in presets to see if any have an old build number */
7253 NSMutableDictionary *thisPresetDict = tempObject;
7254 /*Key Type == 0 is built in, and key PresetBuildNumber is the build number it was created with */
7255 if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0)
7257 if (![thisPresetDict objectForKey:@"PresetBuildNumber"] || [[thisPresetDict objectForKey:@"PresetBuildNumber"] intValue] < [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue])
7259 updateBuiltInPresets = YES;
7264 /* if we have built in presets to update, then do so AlertBuiltInPresetUpdate*/
7265 if ( updateBuiltInPresets == YES)
7267 if( [[NSUserDefaults standardUserDefaults] boolForKey:@"AlertBuiltInPresetUpdate"] == YES)
7269 /* Show an alert window that built in presets will be updated */
7270 /*On Screen Notification*/
7273 status = NSRunAlertPanel(@"HandBrake has determined your built in presets are out of date...",@"HandBrake will now update your built-in presets.", @"OK", nil, nil);
7274 [NSApp requestUserAttention:NSCriticalRequest];
7276 /* when alert is dismissed, go ahead and update the built in presets */
7277 [self addFactoryPresets:nil];
7283 - (IBAction) addPresetPicDropdownChanged: (id) sender
7285 if ([fPresetNewPicSettingsPopUp indexOfSelectedItem] == 1)
7287 [fPresetNewPicWidthHeightBox setHidden:NO];
7291 [fPresetNewPicWidthHeightBox setHidden:YES];
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:@"Custom"];
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: @""];
7314 /* Initialize custom height and width settings to current values */
7316 [fPresetNewPicWidth setStringValue: [NSString stringWithFormat:@"%d",fTitle->job->width]];
7317 [fPresetNewPicHeight setStringValue: [NSString stringWithFormat:@"%d",fTitle->job->height]];
7318 [self addPresetPicDropdownChanged:nil];
7319 /* Show the panel */
7320 [NSApp beginSheet:fAddPresetPanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
7323 - (IBAction) closeAddPresetPanel: (id) sender
7325 [NSApp endSheet: fAddPresetPanel];
7326 [fAddPresetPanel orderOut: self];
7329 - (IBAction)addUserPreset:(id)sender
7331 if (![[fPresetNewName stringValue] length])
7332 NSRunAlertPanel(@"Warning!", @"You need to insert a name for the preset.", @"OK", nil , nil);
7335 /* Here we create a custom user preset */
7336 [UserPresets addObject:[self createPreset]];
7339 [self closeAddPresetPanel:nil];
7346 /* We Reload the New Table data for presets */
7347 [fPresetsOutlineView reloadData];
7348 /* We save all of the preset data here */
7356 /* We Sort the Presets By Factory or Custom */
7357 NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type"
7358 ascending:YES] autorelease];
7359 /* We Sort the Presets Alphabetically by name We do not use this now as we have drag and drop*/
7361 NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName"
7362 ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease];
7363 //NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil];
7366 /* Since we can drag and drop our custom presets, lets just sort by type and not name */
7367 NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,nil];
7368 NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors];
7369 [UserPresets setArray:sortedArray];
7374 - (IBAction)insertPreset:(id)sender
7376 int index = [fPresetsOutlineView selectedRow];
7377 [UserPresets insertObject:[self createPreset] atIndex:index];
7378 [fPresetsOutlineView reloadData];
7382 - (NSDictionary *)createPreset
7384 NSMutableDictionary *preset = [[NSMutableDictionary alloc] init];
7385 /* Preset build number */
7386 [preset setObject:[NSString stringWithFormat: @"%d", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]] forKey:@"PresetBuildNumber"];
7387 [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
7388 /* Get the New Preset Name from the field in the AddPresetPanel */
7389 [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
7390 /* Set whether or not this is to be a folder fPresetNewFolderCheck*/
7391 [preset setObject:[NSNumber numberWithBool:[fPresetNewFolderCheck state]] forKey:@"Folder"];
7392 /*Set whether or not this is a user preset or factory 0 is factory, 1 is user*/
7393 [preset setObject:[NSNumber numberWithInt:1] forKey:@"Type"];
7394 /*Set whether or not this is default, at creation set to 0*/
7395 [preset setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
7396 if ([fPresetNewFolderCheck state] == YES)
7398 /* initialize and set an empty array for children here since we are a new folder */
7399 NSMutableArray *childrenArray = [[NSMutableArray alloc] init];
7400 [preset setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"];
7401 [childrenArray autorelease];
7403 else // we are not creating a preset folder, so we go ahead with the rest of the preset info
7405 /*Get the whether or not to apply pic Size and Cropping (includes Anamorphic)*/
7406 [preset setObject:[NSNumber numberWithInt:[fPresetNewPicSettingsPopUp indexOfSelectedItem]] forKey:@"UsesPictureSettings"];
7407 /* Get whether or not to use the current Picture Filter settings for the preset */
7408 [preset setObject:[NSNumber numberWithInt:[fPresetNewPicFiltersCheck state]] forKey:@"UsesPictureFilters"];
7410 /* Get New Preset Description from the field in the AddPresetPanel*/
7411 [preset setObject:[fPresetNewDesc stringValue] forKey:@"PresetDescription"];
7413 [preset setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"];
7414 /* Chapter Markers fCreateChapterMarkers*/
7415 [preset setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"];
7416 /* Allow Mpeg4 64 bit formatting +4GB file sizes */
7417 [preset setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"];
7418 /* Mux mp4 with http optimization */
7419 [preset setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"];
7420 /* Add iPod uuid atom */
7421 [preset setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"];
7425 [preset setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"];
7426 /* x264 Option String */
7427 [preset setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"];
7429 [preset setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"];
7430 [preset setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
7431 [preset setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"];
7432 [preset setObject:[NSNumber numberWithFloat:[fVidQualityRFField floatValue]] forKey:@"VideoQualitySlider"];
7434 /* Video framerate */
7435 if ([fVidRatePopUp indexOfSelectedItem] == 0) // Same as source is selected
7437 [preset setObject:@"Same as source" forKey:@"VideoFramerate"];
7439 else // we can record the actual titleOfSelectedItem
7441 [preset setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"];
7443 [preset setObject:[NSNumber numberWithInt:[fFrameratePfrCheck state]] forKey:@"VideoFrameratePFR"];
7445 /* 2 Pass Encoding */
7446 [preset setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"];
7447 /* Turbo 2 pass Encoding fVidTurboPassCheck*/
7448 [preset setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"];
7449 /*Picture Settings*/
7450 hb_job_t * job = fTitle->job;
7452 /* Picture Sizing */
7453 [preset setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
7454 [preset setObject:[NSNumber numberWithInt:[fPresetNewPicWidth intValue]] forKey:@"PictureWidth"];
7455 [preset setObject:[NSNumber numberWithInt:[fPresetNewPicHeight intValue]] forKey:@"PictureHeight"];
7456 [preset setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
7457 [preset setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"];
7458 [preset setObject:[NSNumber numberWithInt:fTitle->job->modulus] forKey:@"PictureModulus"];
7460 /* Set crop settings here */
7461 [preset setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
7462 [preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
7463 [preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
7464 [preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
7465 [preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
7467 /* Picture Filters */
7468 [preset setObject:[NSNumber numberWithInt:[fPictureController useDecomb]] forKey:@"PictureDecombDeinterlace"];
7469 [preset setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"];
7470 [preset setObject:[fPictureController deinterlaceCustomString] forKey:@"PictureDeinterlaceCustom"];
7471 [preset setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"];
7472 [preset setObject:[fPictureController detelecineCustomString] forKey:@"PictureDetelecineCustom"];
7473 [preset setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"];
7474 [preset setObject:[fPictureController denoiseCustomString] forKey:@"PictureDenoiseCustom"];
7475 [preset setObject:[NSNumber numberWithInt:[fPictureController deblock]] forKey:@"PictureDeblock"];
7476 [preset setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"];
7477 [preset setObject:[fPictureController decombCustomString] forKey:@"PictureDecombCustom"];
7478 [preset setObject:[NSNumber numberWithInt:[fPictureController grayscale]] forKey:@"VideoGrayScale"];
7481 NSMutableArray *audioListArray = [[NSMutableArray alloc] init];
7482 /* we actually call the methods for the nests here */
7483 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
7485 NSMutableDictionary *audioTrack1Array = [[NSMutableDictionary alloc] init];
7486 [audioTrack1Array setObject:[NSNumber numberWithInt:[fAudLang1PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
7487 [audioTrack1Array setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
7488 [audioTrack1Array setObject:[fAudTrack1CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
7489 [audioTrack1Array setObject:[fAudTrack1MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
7490 [audioTrack1Array setObject:[fAudTrack1RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
7491 [audioTrack1Array setObject:[fAudTrack1BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
7492 [audioTrack1Array setObject:[NSNumber numberWithFloat:[fAudTrack1DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
7493 [audioTrack1Array autorelease];
7494 [audioListArray addObject:audioTrack1Array];
7497 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
7499 NSMutableDictionary *audioTrack2Array = [[NSMutableDictionary alloc] init];
7500 [audioTrack2Array setObject:[NSNumber numberWithInt:[fAudLang2PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
7501 [audioTrack2Array setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
7502 [audioTrack2Array setObject:[fAudTrack2CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
7503 [audioTrack2Array setObject:[fAudTrack2MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
7504 [audioTrack2Array setObject:[fAudTrack2RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
7505 [audioTrack2Array setObject:[fAudTrack2BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
7506 [audioTrack2Array setObject:[NSNumber numberWithFloat:[fAudTrack2DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
7507 [audioTrack2Array autorelease];
7508 [audioListArray addObject:audioTrack2Array];
7511 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
7513 NSMutableDictionary *audioTrack3Array = [[NSMutableDictionary alloc] init];
7514 [audioTrack3Array setObject:[NSNumber numberWithInt:[fAudLang3PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
7515 [audioTrack3Array setObject:[fAudLang3PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
7516 [audioTrack3Array setObject:[fAudTrack3CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
7517 [audioTrack3Array setObject:[fAudTrack3MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
7518 [audioTrack3Array setObject:[fAudTrack3RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
7519 [audioTrack3Array setObject:[fAudTrack3BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
7520 [audioTrack3Array setObject:[NSNumber numberWithFloat:[fAudTrack3DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
7521 [audioTrack3Array autorelease];
7522 [audioListArray addObject:audioTrack3Array];
7525 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
7527 NSMutableDictionary *audioTrack4Array = [[NSMutableDictionary alloc] init];
7528 [audioTrack4Array setObject:[NSNumber numberWithInt:[fAudLang4PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
7529 [audioTrack4Array setObject:[fAudLang4PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
7530 [audioTrack4Array setObject:[fAudTrack4CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
7531 [audioTrack4Array setObject:[fAudTrack4MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
7532 [audioTrack4Array setObject:[fAudTrack4RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
7533 [audioTrack4Array setObject:[fAudTrack4BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
7534 [audioTrack4Array setObject:[NSNumber numberWithFloat:[fAudTrack4DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
7535 [audioTrack4Array autorelease];
7536 [audioListArray addObject:audioTrack4Array];
7540 [preset setObject:[NSMutableArray arrayWithArray: audioListArray] forKey:@"AudioList"];
7543 /* Temporarily remove subtitles from creating a new preset as it has to be converted over to use the new
7544 * subititle array code. */
7546 //[preset setObject:[fSubPopUp titleOfSelectedItem] forKey:@"Subtitles"];
7547 /* Forced Subtitles */
7548 //[preset setObject:[NSNumber numberWithInt:[fSubForcedCheck state]] forKey:@"SubtitlesForced"];
7550 [preset autorelease];
7557 [UserPresets writeToFile:UserPresetsFile atomically:YES];
7558 /* We get the default preset in case it changed */
7559 [self getDefaultPresets:nil];
7563 - (IBAction)deletePreset:(id)sender
7567 if ( [fPresetsOutlineView numberOfSelectedRows] == 0 )
7571 /* Alert user before deleting preset */
7573 status = NSRunAlertPanel(@"Warning!", @"Are you sure that you want to delete the selected preset?", @"OK", @"Cancel", nil);
7575 if ( status == NSAlertDefaultReturn )
7577 int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
7578 NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
7579 NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod];
7581 NSEnumerator *enumerator;
7582 NSMutableArray *presetsArrayToMod;
7583 NSMutableArray *tempArray;
7585 /* If we are a root level preset, we are modding the UserPresets array */
7586 if (presetToModLevel == 0)
7588 presetsArrayToMod = UserPresets;
7590 else // We have a parent preset, so we modify the chidren array object for key
7592 presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"];
7595 enumerator = [presetsArrayToMod objectEnumerator];
7596 tempArray = [NSMutableArray array];
7598 while (tempObject = [enumerator nextObject])
7600 NSDictionary *thisPresetDict = tempObject;
7601 if (thisPresetDict == presetToMod)
7603 [tempArray addObject:tempObject];
7607 [presetsArrayToMod removeObjectsInArray:tempArray];
7608 [fPresetsOutlineView reloadData];
7615 #pragma mark Import Export Preset(s)
7617 - (IBAction) browseExportPresetFile: (id) sender
7619 /* Open a panel to let the user choose where and how to save the export file */
7620 NSSavePanel * panel = [NSSavePanel savePanel];
7621 /* We get the current file name and path from the destination field here */
7622 NSString *defaultExportDirectory = [NSString stringWithFormat: @"%@/Desktop/", NSHomeDirectory()];
7624 [panel beginSheetForDirectory: defaultExportDirectory file: @"HB_Export.plist"
7625 modalForWindow: fWindow modalDelegate: self
7626 didEndSelector: @selector( browseExportPresetFileDone:returnCode:contextInfo: )
7630 - (void) browseExportPresetFileDone: (NSSavePanel *) sheet
7631 returnCode: (int) returnCode contextInfo: (void *) contextInfo
7633 if( returnCode == NSOKButton )
7635 NSString *presetExportDirectory = [[sheet filename] stringByDeletingLastPathComponent];
7636 NSString *exportPresetsFile = [sheet filename];
7637 [[NSUserDefaults standardUserDefaults] setObject:presetExportDirectory forKey:@"LastPresetExportDirectory"];
7638 /* We check for the presets.plist */
7639 if ([[NSFileManager defaultManager] fileExistsAtPath:exportPresetsFile] == 0)
7641 [[NSFileManager defaultManager] createFileAtPath:exportPresetsFile contents:nil attributes:nil];
7643 NSMutableArray * presetsToExport = [[NSMutableArray alloc] initWithContentsOfFile:exportPresetsFile];
7644 if (nil == presetsToExport)
7646 presetsToExport = [[NSMutableArray alloc] init];
7648 /* now get and add selected presets to export */
7651 if ([fPresetsOutlineView selectedRow] >= 0 && [[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] != 1)
7653 [presetsToExport addObject:[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
7654 [presetsToExport writeToFile:exportPresetsFile atomically:YES];
7662 - (IBAction) browseImportPresetFile: (id) sender
7665 NSOpenPanel * panel;
7667 panel = [NSOpenPanel openPanel];
7668 [panel setAllowsMultipleSelection: NO];
7669 [panel setCanChooseFiles: YES];
7670 [panel setCanChooseDirectories: NO ];
7671 NSString * sourceDirectory;
7672 if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastPresetImportDirectory"])
7674 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastPresetImportDirectory"];
7678 sourceDirectory = @"~/Desktop";
7679 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
7681 /* we open up the browse sources sheet here and call for browseSourcesDone after the sheet is closed
7682 * to evaluate whether we want to specify a title, we pass the sender in the contextInfo variable
7684 /* set this for allowed file types, not sure if we should allow xml or not */
7685 NSArray *fileTypes = [NSArray arrayWithObjects:@"plist", @"xml", nil];
7686 [panel beginSheetForDirectory: sourceDirectory file: nil types: fileTypes
7687 modalForWindow: fWindow modalDelegate: self
7688 didEndSelector: @selector( browseImportPresetDone:returnCode:contextInfo: )
7689 contextInfo: sender];
7692 - (void) browseImportPresetDone: (NSSavePanel *) sheet
7693 returnCode: (int) returnCode contextInfo: (void *) contextInfo
7695 if( returnCode == NSOKButton )
7697 NSString *importPresetsDirectory = [[sheet filename] stringByDeletingLastPathComponent];
7698 NSString *importPresetsFile = [sheet filename];
7699 [[NSUserDefaults standardUserDefaults] setObject:importPresetsDirectory forKey:@"LastPresetImportDirectory"];
7700 /* NOTE: here we need to do some sanity checking to verify we do not hose up our presets file */
7701 NSMutableArray * presetsToImport = [[NSMutableArray alloc] initWithContentsOfFile:importPresetsFile];
7702 /* iterate though the new array of presets to import and add them to our presets array */
7704 NSEnumerator *enumerator = [presetsToImport objectEnumerator];
7706 while (tempObject = [enumerator nextObject])
7708 /* make any changes to the incoming preset we see fit */
7709 /* make sure the incoming preset is not tagged as default */
7710 [tempObject setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
7711 /* prepend "(imported) to the name of the incoming preset for clarification since it can be changed */
7712 NSString * prependedName = [@"(import) " stringByAppendingString:[tempObject objectForKey:@"PresetName"]] ;
7713 [tempObject setObject:prependedName forKey:@"PresetName"];
7715 /* actually add the new preset to our presets array */
7716 [UserPresets addObject:tempObject];
7719 [presetsToImport autorelease];
7727 #pragma mark Manage Default Preset
7729 - (IBAction)getDefaultPresets:(id)sender
7731 presetHbDefault = nil;
7732 presetUserDefault = nil;
7733 presetUserDefaultParent = nil;
7734 presetUserDefaultParentParent = nil;
7735 NSMutableDictionary *presetHbDefaultParent = nil;
7736 NSMutableDictionary *presetHbDefaultParentParent = nil;
7739 BOOL userDefaultFound = NO;
7740 presetCurrentBuiltInCount = 0;
7741 /* First we iterate through the root UserPresets array to check for defaults */
7742 NSEnumerator *enumerator = [UserPresets objectEnumerator];
7744 while (tempObject = [enumerator nextObject])
7746 NSMutableDictionary *thisPresetDict = tempObject;
7747 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7749 presetHbDefault = thisPresetDict;
7751 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7753 presetUserDefault = thisPresetDict;
7754 userDefaultFound = YES;
7756 if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset
7758 presetCurrentBuiltInCount++; // <--increment the current number of built in presets
7762 /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */
7763 if ([thisPresetDict objectForKey:@"ChildrenArray"])
7765 NSMutableDictionary *thisPresetDictParent = thisPresetDict;
7766 NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7768 while (tempObject = [enumerator nextObject])
7770 NSMutableDictionary *thisPresetDict = tempObject;
7771 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7773 presetHbDefault = thisPresetDict;
7774 presetHbDefaultParent = thisPresetDictParent;
7776 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7778 presetUserDefault = thisPresetDict;
7779 presetUserDefaultParent = thisPresetDictParent;
7780 userDefaultFound = YES;
7783 /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */
7784 if ([thisPresetDict objectForKey:@"ChildrenArray"])
7786 NSMutableDictionary *thisPresetDictParentParent = thisPresetDict;
7787 NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7789 while (tempObject = [enumerator nextObject])
7791 NSMutableDictionary *thisPresetDict = tempObject;
7792 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7794 presetHbDefault = thisPresetDict;
7795 presetHbDefaultParent = thisPresetDictParent;
7796 presetHbDefaultParentParent = thisPresetDictParentParent;
7798 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7800 presetUserDefault = thisPresetDict;
7801 presetUserDefaultParent = thisPresetDictParent;
7802 presetUserDefaultParentParent = thisPresetDictParentParent;
7803 userDefaultFound = YES;
7812 /* check to see if a user specified preset was found, if not then assign the parents for
7813 * the presetHbDefault so that we can open the parents for the nested presets
7815 if (userDefaultFound == NO)
7817 presetUserDefaultParent = presetHbDefaultParent;
7818 presetUserDefaultParentParent = presetHbDefaultParentParent;
7822 - (IBAction)setDefaultPreset:(id)sender
7824 /* We need to determine if the item is a folder */
7825 if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] == 1)
7831 NSEnumerator *enumerator = [UserPresets objectEnumerator];
7833 /* First make sure the old user specified default preset is removed */
7834 while (tempObject = [enumerator nextObject])
7836 NSMutableDictionary *thisPresetDict = tempObject;
7837 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7839 [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
7842 /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */
7843 if ([thisPresetDict objectForKey:@"ChildrenArray"])
7845 NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7848 while (tempObject = [enumerator nextObject])
7850 NSMutableDictionary *thisPresetDict1 = tempObject;
7851 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7853 [[[thisPresetDict objectForKey:@"ChildrenArray"] objectAtIndex:ii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
7855 /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */
7856 if ([thisPresetDict1 objectForKey:@"ChildrenArray"])
7858 NSEnumerator *enumerator = [[thisPresetDict1 objectForKey:@"ChildrenArray"] objectEnumerator];
7861 while (tempObject = [enumerator nextObject])
7863 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7865 [[[thisPresetDict1 objectForKey:@"ChildrenArray"] objectAtIndex:iii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
7878 int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
7879 NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
7880 NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod];
7883 NSMutableArray *presetsArrayToMod;
7884 NSMutableArray *tempArray;
7886 /* If we are a root level preset, we are modding the UserPresets array */
7887 if (presetToModLevel == 0)
7889 presetsArrayToMod = UserPresets;
7891 else // We have a parent preset, so we modify the chidren array object for key
7893 presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"];
7896 enumerator = [presetsArrayToMod objectEnumerator];
7897 tempArray = [NSMutableArray array];
7899 while (tempObject = [enumerator nextObject])
7901 NSDictionary *thisPresetDict = tempObject;
7902 if (thisPresetDict == presetToMod)
7904 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 2
7906 [[presetsArrayToMod objectAtIndex:iiii] setObject:[NSNumber numberWithInt:2] forKey:@"Default"];
7913 /* We save all of the preset data here */
7915 /* We Reload the New Table data for presets */
7916 [fPresetsOutlineView reloadData];
7919 - (IBAction)selectDefaultPreset:(id)sender
7921 NSMutableDictionary *presetToMod;
7922 /* if there is a user specified default, we use it */
7923 if (presetUserDefault)
7925 presetToMod = presetUserDefault;
7927 else if (presetHbDefault) //else we use the built in default presetHbDefault
7929 presetToMod = presetHbDefault;
7936 if (presetUserDefaultParent != nil)
7938 [fPresetsOutlineView expandItem:presetUserDefaultParent];
7941 if (presetUserDefaultParentParent != nil)
7943 [fPresetsOutlineView expandItem:presetUserDefaultParentParent];
7947 [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[fPresetsOutlineView rowForItem: presetToMod]] byExtendingSelection:NO];
7948 [self selectPreset:nil];
7953 #pragma mark Manage Built In Presets
7956 - (IBAction)deleteFactoryPresets:(id)sender
7959 NSEnumerator *enumerator = [UserPresets objectEnumerator];
7963 NSMutableArray *tempArray;
7966 tempArray = [NSMutableArray array];
7967 /* we look here to see if the preset is we move on to the next one */
7968 while ( tempObject = [enumerator nextObject] )
7970 /* if the preset is "Factory" then we put it in the array of
7971 presets to delete */
7972 if ([[tempObject objectForKey:@"Type"] intValue] == 0)
7974 [tempArray addObject:tempObject];
7978 [UserPresets removeObjectsInArray:tempArray];
7979 [fPresetsOutlineView reloadData];
7984 /* We use this method to recreate new, updated factory presets */
7985 - (IBAction)addFactoryPresets:(id)sender
7988 /* First, we delete any existing built in presets */
7989 [self deleteFactoryPresets: sender];
7990 /* Then we generate new built in presets programmatically with fPresetsBuiltin
7991 * which is all setup in HBPresets.h and HBPresets.m*/
7992 [fPresetsBuiltin generateBuiltinPresets:UserPresets];
7993 /* update build number for built in presets */
7994 /* iterate though the new array of presets to import and add them to our presets array */
7996 NSEnumerator *enumerator = [UserPresets objectEnumerator];
7998 while (tempObject = [enumerator nextObject])
8000 /* Record the apps current build number in the PresetBuildNumber key */
8001 if ([[tempObject objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset
8003 /* Preset build number */
8004 [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:[[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]] forKey:@"PresetBuildNumber"];
8008 /* report the built in preset updating to the activity log */
8009 [self writeToActivityLog: "built in presets updated to build number: %d", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]];
8017 #pragma mark Chapter Files Import / Export
8019 - (IBAction) browseForChapterFile: (id) sender
8021 /* Open a panel to let the user choose the file */
8022 NSOpenPanel * panel = [NSOpenPanel openPanel];
8023 /* We get the current file name and path from the destination field here */
8024 [panel beginSheetForDirectory: [NSString stringWithFormat:@"%@/",
8025 [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"]]
8027 types: [NSArray arrayWithObjects:@"csv",nil]
8028 modalForWindow: fWindow modalDelegate: self
8029 didEndSelector: @selector( browseForChapterFileDone:returnCode:contextInfo: )
8033 - (void) browseForChapterFileDone: (NSOpenPanel *) sheet
8034 returnCode: (int) returnCode contextInfo: (void *) contextInfo
8036 NSArray *chaptersArray; /* temp array for chapters */
8037 NSMutableArray *chaptersMutableArray; /* temp array for chapters */
8038 NSString *chapterName; /* temp string from file */
8041 if( returnCode == NSOKButton ) /* if they click OK */
8043 chapterName = [[NSString alloc] initWithContentsOfFile:[sheet filename] encoding:NSUTF8StringEncoding error:NULL];
8044 chaptersArray = [chapterName componentsSeparatedByString:@"\n"];
8045 chaptersMutableArray= [chaptersArray mutableCopy];
8046 chapters = [fChapterTitlesDelegate numberOfRowsInTableView:fChapterTable];
8047 if ([chaptersMutableArray count] > 0)
8049 /* if last item is empty remove it */
8050 if ([[chaptersMutableArray objectAtIndex:[chaptersArray count]-1] length] == 0)
8052 [chaptersMutableArray removeLastObject];
8055 /* if chapters in table is not equal to array count */
8056 if ((unsigned int) chapters != [chaptersMutableArray count])
8059 [[NSAlert alertWithMessageText:NSLocalizedString(@"Unable to load chapter file", @"Unable to load chapter file")
8060 defaultButton:NSLocalizedString(@"OK", @"OK")
8061 alternateButton:NULL
8063 informativeTextWithFormat:NSLocalizedString(@"%d chapters expected, %d chapters found in %@", @"%d chapters expected, %d chapters found in %@"),
8064 chapters, [chaptersMutableArray count], [[sheet filename] lastPathComponent]] runModal];
8067 /* otherwise, go ahead and populate table with array */
8068 for (i=0; i<chapters; i++)
8071 if([[chaptersMutableArray objectAtIndex:i] length] > 5)
8073 /* avoid a segfault */
8074 /* Get the Range.location of the first comma in the line and then put everything after that into chapterTitle */
8075 NSRange firstCommaRange = [[chaptersMutableArray objectAtIndex:i] rangeOfString:@","];
8076 NSString *chapterTitle = [[chaptersMutableArray objectAtIndex:i] substringFromIndex:firstCommaRange.location + 1];
8077 /* Since we store our chapterTitle commas as "\," for the cli, we now need to remove the escaping "\" from the title */
8078 chapterTitle = [chapterTitle stringByReplacingOccurrencesOfString:@"\\," withString:@","];
8079 [fChapterTitlesDelegate tableView:fChapterTable
8080 setObjectValue:chapterTitle
8081 forTableColumn:fChapterTableNameColumn
8087 [[NSAlert alertWithMessageText:NSLocalizedString(@"Unable to load chapter file", @"Unable to load chapter file")
8088 defaultButton:NSLocalizedString(@"OK", @"OK")
8089 alternateButton:NULL
8091 informativeTextWithFormat:NSLocalizedString(@"%@ was not formatted as expected.", @"%@ was not formatted as expected."), [[sheet filename] lastPathComponent]] runModal];
8092 [fChapterTable reloadData];
8096 [fChapterTable reloadData];
8100 - (IBAction) browseForChapterFileSave: (id) sender
8102 NSSavePanel *panel = [NSSavePanel savePanel];
8103 /* Open a panel to let the user save to a file */
8104 [panel setAllowedFileTypes:[NSArray arrayWithObjects:@"csv",nil]];
8105 [panel beginSheetForDirectory: [[fDstFile2Field stringValue] stringByDeletingLastPathComponent]
8106 file: [[[[fDstFile2Field stringValue] lastPathComponent] stringByDeletingPathExtension]
8107 stringByAppendingString:@"-chapters.csv"]
8108 modalForWindow: fWindow
8110 didEndSelector: @selector( browseForChapterFileSaveDone:returnCode:contextInfo: )
8114 - (void) browseForChapterFileSaveDone: (NSSavePanel *) sheet
8115 returnCode: (int) returnCode contextInfo: (void *) contextInfo
8117 NSString *chapterName; /* pointer for string for later file-writing */
8118 NSString *chapterTitle;
8119 NSError *saveError = [[NSError alloc] init];
8120 int chapters, i; /* ints for the number of chapters in the table and the loop */
8122 if( returnCode == NSOKButton ) /* if they clicked OK */
8124 chapters = [fChapterTitlesDelegate numberOfRowsInTableView:fChapterTable];
8125 chapterName = [NSString string];
8126 for (i=0; i<chapters; i++)
8128 /* put each chapter title from the table into the array */
8130 { /* if i is from 0 to 8 (chapters 1 to 9) add two leading zeros */
8131 chapterName = [chapterName stringByAppendingFormat:@"00%d,",i+1];
8134 { /* if i is from 9 to 98 (chapters 10 to 99) add one leading zero */
8135 chapterName = [chapterName stringByAppendingFormat:@"0%d,",i+1];
8138 { /* in case i is from 99 to 998 (chapters 100 to 999) no leading zeros */
8139 chapterName = [chapterName stringByAppendingFormat:@"%d,",i+1];
8142 chapterTitle = [fChapterTitlesDelegate tableView:fChapterTable objectValueForTableColumn:fChapterTableNameColumn row:i];
8143 /* escape any commas in the chapter name with "\," */
8144 chapterTitle = [chapterTitle stringByReplacingOccurrencesOfString:@"," withString:@"\\,"];
8145 chapterName = [chapterName stringByAppendingString:chapterTitle];
8146 if (i+1 != chapters)
8147 { /* if not the last chapter */
8148 chapterName = [chapterName stringByAppendingString:@ "\n"];
8153 /* try to write it to where the user wanted */
8154 if (![chapterName writeToFile:[sheet filename]
8156 encoding:NSUTF8StringEncoding
8160 [[NSAlert alertWithError:saveError] runModal];
8167 /*******************************
8168 * Subclass of the HBPresetsOutlineView *
8169 *******************************/
8171 @implementation HBPresetsOutlineView
8172 - (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent*)dragEvent offset:(NSPointPointer)dragImageOffset
8176 // By default, NSTableView only drags an image of the first column. Change this to
8177 // drag an image of the queue's icon and PresetName columns.
8178 NSArray * cols = [NSArray arrayWithObjects: [self tableColumnWithIdentifier:@"PresetName"], nil];
8179 return [super dragImageForRowsWithIndexes:dragRows tableColumns:cols event:dragEvent offset:dragImageOffset];
8184 - (void) mouseDown:(NSEvent *)theEvent
8186 [super mouseDown:theEvent];
8192 - (BOOL) isDragging;