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.m0k.org/>.
5 It may be used under the terms of the GNU General Public License. */
7 #include "Controller.h"
8 #include "a52dec/a52.h"
10 #define _(a) NSLocalizedString(a,NULL)
12 static int FormatSettings[3][4] =
13 { { HB_MUX_MP4 | HB_VCODEC_FFMPEG | HB_ACODEC_FAAC,
14 HB_MUX_MP4 | HB_VCODEC_X264 | HB_ACODEC_FAAC,
17 { HB_MUX_AVI | HB_VCODEC_FFMPEG | HB_ACODEC_LAME,
18 HB_MUX_AVI | HB_VCODEC_FFMPEG | HB_ACODEC_AC3,
19 HB_MUX_AVI | HB_VCODEC_X264 | HB_ACODEC_LAME,
20 HB_MUX_AVI | HB_VCODEC_X264 | HB_ACODEC_AC3 },
21 { HB_MUX_OGM | HB_VCODEC_FFMPEG | HB_ACODEC_VORBIS,
22 HB_MUX_OGM | HB_VCODEC_FFMPEG | HB_ACODEC_LAME,
26 /*******************************
27 * HBController implementation *
28 *******************************/
29 @implementation HBController
38 - (void) applicationDidFinishLaunching: (NSNotification *) notification
44 fHandle = hb_init( HB_DEBUG_NONE, [[NSUserDefaults
45 standardUserDefaults] boolForKey:@"CheckForUpdates"] );
47 /* Init others controllers */
48 [fScanController SetHandle: fHandle];
49 [fPictureController SetHandle: fHandle];
50 [fQueueController SetHandle: fHandle];
53 /* Call UpdateUI every 2/10 sec */
54 [[NSRunLoop currentRunLoop] addTimer: [NSTimer
55 scheduledTimerWithTimeInterval: 0.2 target: self
56 selector: @selector( UpdateUI: ) userInfo: NULL repeats: FALSE]
57 forMode: NSModalPanelRunLoopMode];
59 if( ( build = hb_check_update( fHandle, &version ) ) > -1 )
61 /* Update available - tell the user */
63 NSBeginInformationalAlertSheet( _( @"Update is available" ),
64 _( @"Go get it!" ), _( @"Discard" ), NULL, fWindow, self,
65 @selector( UpdateAlertDone:returnCode:contextInfo: ),
66 NULL, NULL, [NSString stringWithFormat:
67 _( @"HandBrake %s (build %d) is now available for download." ),
73 /* Show scan panel ASAP */
74 [self performSelectorOnMainThread: @selector(ShowScanPanel:)
75 withObject: NULL waitUntilDone: NO];
78 - (NSApplicationTerminateReply) applicationShouldTerminate:
81 if( [[fRipButton title] isEqualToString: _( @"Cancel" )] )
84 return NSTerminateCancel;
89 return NSTerminateNow;
96 [self TranslateStrings];
98 /* Init User Presets .plist */
99 /* We declare the default NSFileManager into fileManager */
100 NSFileManager * fileManager = [NSFileManager defaultManager];
101 //presetPrefs = [[NSUserDefaults standardUserDefaults] retain];
102 /* we set the files and support paths here */
103 AppSupportDirectory = @"~/Library/Application Support/HandBrake";
104 AppSupportDirectory = [AppSupportDirectory stringByExpandingTildeInPath];
106 UserPresetsFile = @"~/Library/Application Support/HandBrake/UserPresets.plist";
107 UserPresetsFile = [UserPresetsFile stringByExpandingTildeInPath];
109 x264ProfilesFile = @"~/Library/Application Support/HandBrake/x264Profiles.plist";
110 x264ProfilesFile = [x264ProfilesFile stringByExpandingTildeInPath];
111 /* We check for the app support directory for media fork */
112 if ([fileManager fileExistsAtPath:AppSupportDirectory] == 0)
114 // If it doesnt exist yet, we create it here
115 [fileManager createDirectoryAtPath:AppSupportDirectory attributes:nil];
117 // We check for the presets.plist here
119 if ([fileManager fileExistsAtPath:UserPresetsFile] == 0)
122 [fileManager createFileAtPath:UserPresetsFile contents:nil attributes:nil];
125 // We check for the x264profiles.plist here
127 if ([fileManager fileExistsAtPath:x264ProfilesFile] == 0)
130 [fileManager createFileAtPath:x264ProfilesFile contents:nil attributes:nil];
134 UserPresetsFile = @"~/Library/Application Support/HandBrake/UserPresets.plist";
135 UserPresetsFile = [[UserPresetsFile stringByExpandingTildeInPath]retain];
137 UserPresets = [[NSMutableArray alloc] initWithContentsOfFile:UserPresetsFile];
138 if (nil == UserPresets)
140 UserPresets = [[NSMutableArray alloc] init];
141 [self AddFactoryPresets:NULL];
143 /* Show/Dont Show Presets drawer upon launch based
144 on user preference DefaultPresetsDrawerShow*/
145 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultPresetsDrawerShow"] > 0)
147 [fPresetDrawer open];
153 [fDstFormatPopUp removeAllItems];
154 [fDstFormatPopUp addItemWithTitle: _( @"MP4 file" )];
155 [fDstFormatPopUp addItemWithTitle: _( @"AVI file" )];
156 [fDstFormatPopUp addItemWithTitle: _( @"OGM file" )];
157 [fDstFormatPopUp selectItemAtIndex: 0];
159 [self FormatPopUpChanged: NULL];
160 /* We enable the create chapters checkbox here since we are .mp4 */
161 [fCreateChapterMarkers setEnabled: YES];
162 if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultChapterMarkers"] > 0)
164 [fCreateChapterMarkers setState: NSOnState];
166 [fDstFile2Field setStringValue: [NSString stringWithFormat:
167 @"%@/Desktop/Movie.mp4", NSHomeDirectory()]];
170 [fVidEncoderPopUp removeAllItems];
171 [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"];
172 [fVidEncoderPopUp addItemWithTitle: @"XviD"];
175 [fVidTargetSizeField setIntValue: 700];
176 [fVidBitrateField setIntValue: 1000];
178 [fVidQualityMatrix selectCell: fVidBitrateCell];
179 [self VideoMatrixChanged: NULL];
181 /* Video framerate */
182 [fVidRatePopUp removeAllItems];
183 [fVidRatePopUp addItemWithTitle: _( @"Same as source" )];
184 for( int i = 0; i < hb_video_rates_count; i++ )
186 [fVidRatePopUp addItemWithTitle:
187 [NSString stringWithCString: hb_video_rates[i].string]];
189 [fVidRatePopUp selectItemAtIndex: 0];
191 /* Picture Settings */
192 [fPicLabelPAROutp setStringValue: @""];
193 [fPicLabelPAROutputX setStringValue: @""];
194 [fPicSettingPARWidth setStringValue: @""];
195 [fPicSettingPARHeight setStringValue: @""];
198 [fAudBitratePopUp removeAllItems];
199 for( int i = 0; i < hb_audio_bitrates_count; i++ )
201 [fAudBitratePopUp addItemWithTitle:
202 [NSString stringWithCString: hb_audio_bitrates[i].string]];
204 [fAudBitratePopUp selectItemAtIndex: hb_audio_bitrates_default];
206 /* Audio samplerate */
207 [fAudRatePopUp removeAllItems];
208 for( int i = 0; i < hb_audio_rates_count; i++ )
210 [fAudRatePopUp addItemWithTitle:
211 [NSString stringWithCString: hb_audio_rates[i].string]];
213 [fAudRatePopUp selectItemAtIndex: hb_audio_rates_default];
216 [fStatusField setStringValue: @""];
219 [fPauseButton setEnabled: NO];
220 [fRipButton setEnabled: NO];
227 - (void) TranslateStrings
229 [fSrcDVD1Field setStringValue: _( @"DVD:" )];
230 [fSrcTitleField setStringValue: _( @"Title:" )];
231 [fSrcChapterField setStringValue: _( @"Chapters:" )];
232 [fSrcChapterToField setStringValue: _( @"to" )];
233 [fSrcDuration1Field setStringValue: _( @"Duration:" )];
235 [fDstFormatField setStringValue: _( @"File format:" )];
236 [fDstCodecsField setStringValue: _( @"Codecs:" )];
237 [fDstFile1Field setStringValue: _( @"File:" )];
238 [fDstBrowseButton setTitle: _( @"Browse" )];
240 [fVidRateField setStringValue: _( @"Framerate (fps):" )];
241 [fVidEncoderField setStringValue: _( @"Encoder:" )];
242 [fVidQualityField setStringValue: _( @"Quality:" )];
245 /***********************************************************************
247 ***********************************************************************
248 * Shows a progression bar on the dock icon, filled according to
249 * 'progress' (0.0 <= progress <= 1.0).
250 * Called with progress < 0.0 or progress > 1.0, restores the original
252 **********************************************************************/
253 - (void) UpdateDockIcon: (float) progress
257 NSBitmapImageRep * bmp;
259 uint32_t black = htonl( 0x000000FF );
260 uint32_t red = htonl( 0xFF0000FF );
261 uint32_t white = htonl( 0xFFFFFFFF );
262 int row_start, row_end;
265 /* Get application original icon */
266 icon = [NSImage imageNamed: @"NSApplicationIcon"];
268 if( progress < 0.0 || progress > 1.0 )
270 [NSApp setApplicationIconImage: icon];
274 /* Get it in a raw bitmap form */
275 tiff = [icon TIFFRepresentationUsingCompression:
276 NSTIFFCompressionNone factor: 1.0];
277 bmp = [NSBitmapImageRep imageRepWithData: tiff];
279 /* Draw the progression bar */
280 /* It's pretty simple (ugly?) now, but I'm no designer */
282 row_start = 3 * (int) [bmp size].height / 4;
283 row_end = 7 * (int) [bmp size].height / 8;
285 for( i = row_start; i < row_start + 2; i++ )
287 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
288 for( j = 0; j < (int) [bmp size].width; j++ )
293 for( i = row_start + 2; i < row_end - 2; i++ )
295 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
298 for( j = 2; j < (int) [bmp size].width - 2; j++ )
300 if( j < 2 + (int) ( ( [bmp size].width - 4.0 ) * progress ) )
312 for( i = row_end - 2; i < row_end; i++ )
314 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
315 for( j = 0; j < (int) [bmp size].width; j++ )
321 /* Now update the dock icon */
322 tiff = [bmp TIFFRepresentationUsingCompression:
323 NSTIFFCompressionNone factor: 1.0];
324 icon = [[NSImage alloc] initWithData: tiff];
325 [NSApp setApplicationIconImage: icon];
329 - (void) UpdateUI: (NSTimer *) timer
333 hb_get_state( fHandle, &s );
340 case HB_STATE_SCANNING:
341 [fScanController UpdateUI: &s];
344 #define p s.param.scandone
345 case HB_STATE_SCANDONE:
349 int indxpri=0; // Used to search the longuest title (default in combobox)
350 int longuestpri=0; // Used to search the longuest title (default in combobox)
352 [fScanController UpdateUI: &s];
354 list = hb_get_titles( fHandle );
356 if( !hb_list_count( list ) )
362 [fSrcTitlePopUp removeAllItems];
363 for( int i = 0; i < hb_list_count( list ); i++ )
365 title = (hb_title_t *) hb_list_item( list, i );
366 /*Set DVD Name at top of window*/
367 [fSrcDVD2Field setStringValue: [NSString
368 stringWithUTF8String: title->name]];
370 /* Use the dvd name in the default output field here
371 May want to add code to remove blank spaces for some dvd names*/
372 /* Check to see if the last destination has been set,use if so, if not, use Desktop */
373 if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"])
375 [fDstFile2Field setStringValue: [NSString stringWithFormat:
376 @"%@/%@.mp4", [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"],[NSString
377 stringWithUTF8String: title->name]]];
381 [fDstFile2Field setStringValue: [NSString stringWithFormat:
382 @"%@/Desktop/%@.mp4", NSHomeDirectory(),[NSString
383 stringWithUTF8String: title->name]]];
387 if (longuestpri < title->hours*60*60 + title->minutes *60 + title->seconds)
389 longuestpri=title->hours*60*60 + title->minutes *60 + title->seconds;
394 int format = [fDstFormatPopUp indexOfSelectedItem];
400 /*Get Default MP4 File Extension for mpeg4 (.mp4 or .m4v) from prefs*/
401 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0)
419 NSString * string = [fDstFile2Field stringValue];
420 /* Add/replace File Output name to the correct extension*/
421 if( [string characterAtIndex: [string length] - 4] == '.' )
423 [fDstFile2Field setStringValue: [NSString stringWithFormat:
424 @"%@.%s", [string substringToIndex: [string length] - 4],
429 [fDstFile2Field setStringValue: [NSString stringWithFormat:
430 @"%@.%s", string, ext]];
434 [fSrcTitlePopUp addItemWithTitle: [NSString
435 stringWithFormat: @"%d - %02dh%02dm%02ds",
436 title->index, title->hours, title->minutes,
440 // Select the longuest title
441 [fSrcTitlePopUp selectItemAtIndex: indxpri];
442 /* We set the Settings Display to "Default" here
443 until we get default presets implemented */
444 [fPresetSelectedDisplay setStringValue: @"Default"];
446 [self TitlePopUpChanged: NULL];
447 [self EnableUI: YES];
448 [fPauseButton setEnabled: NO];
449 [fRipButton setEnabled: YES];
454 #define p s.param.working
455 case HB_STATE_WORKING:
457 float progress_total;
458 NSMutableString * string;
460 /* Update text field */
461 string = [NSMutableString stringWithFormat:
462 _( @"Encoding: task %d of %d, %.2f %%" ),
463 p.job_cur, p.job_count, 100.0 * p.progress];
466 [string appendFormat:
467 _( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" ),
468 p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];
470 [fStatusField setStringValue: string];
473 progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
474 [fRipIndicator setIndeterminate: NO];
475 [fRipIndicator setDoubleValue: 100.0 * progress_total];
477 /* Update dock icon */
478 [self UpdateDockIcon: progress_total];
480 [fPauseButton setEnabled: YES];
481 [fPauseButton setTitle: _( @"Pause" )];
482 [fRipButton setEnabled: YES];
483 [fRipButton setTitle: _( @"Cancel" )];
488 #define p s.param.muxing
489 case HB_STATE_MUXING:
491 NSMutableString * string;
493 /* Update text field */
494 string = [NSMutableString stringWithFormat:
496 [fStatusField setStringValue: string];
499 [fRipIndicator setIndeterminate: YES];
500 [fRipIndicator startAnimation: nil];
502 /* Update dock icon */
503 [self UpdateDockIcon: 1.0];
505 [fPauseButton setEnabled: YES];
506 [fPauseButton setTitle: _( @"Pause" )];
507 [fRipButton setEnabled: YES];
508 [fRipButton setTitle: _( @"Cancel" )];
513 case HB_STATE_PAUSED:
514 [fStatusField setStringValue: _( @"Paused" )];
515 [fPauseButton setEnabled: YES];
516 [fPauseButton setTitle: _( @"Resume" )];
517 [fRipButton setEnabled: YES];
518 [fRipButton setTitle: _( @"Cancel" )];
521 case HB_STATE_WORKDONE:
523 [self EnableUI: YES];
524 [fStatusField setStringValue: _( @"Done." )];
525 [fRipIndicator setIndeterminate: NO];
526 [fRipIndicator setDoubleValue: 0.0];
527 [fRipButton setTitle: _( @"Rip" )];
529 /* Restore dock icon */
530 [self UpdateDockIcon: -1.0];
532 [fPauseButton setEnabled: NO];
533 [fPauseButton setTitle: _( @"Pause" )];
534 [fRipButton setEnabled: YES];
535 [fRipButton setTitle: _( @"Rip" )];
539 while( ( job = hb_job( fHandle, 0 ) ) )
541 hb_rem( fHandle, job );
547 /* FIXME: we should only do that when necessary */
548 if( [fQueueCheck state] == NSOnState )
550 int count = hb_count( fHandle );
553 [fQueueCheck setTitle: [NSString stringWithFormat:
554 @"Enable queue (%d task%s in queue)",
555 count, ( count > 1 ) ? "s" : ""]];
559 [fQueueCheck setTitle: @"Enable queue (no task in queue)"];
563 [[NSRunLoop currentRunLoop] addTimer: [NSTimer
564 scheduledTimerWithTimeInterval: 0.2 target: self
565 selector: @selector( UpdateUI: ) userInfo: NULL repeats: FALSE]
566 forMode: NSModalPanelRunLoopMode];
569 - (void) EnableUI: (bool) b
571 NSControl * controls[] =
572 { fSrcDVD1Field, fSrcDVD2Field, fSrcTitleField, fSrcTitlePopUp,
573 fSrcChapterField, fSrcChapterStartPopUp, fSrcChapterToField,
574 fSrcChapterEndPopUp, fSrcDuration1Field, fSrcDuration2Field,
575 fDstFormatField, fDstFormatPopUp, fDstCodecsField,
576 fDstCodecsPopUp, fDstFile1Field, fDstFile2Field,
577 fDstBrowseButton, fVidRateField, fVidRatePopUp,
578 fVidEncoderField, fVidEncoderPopUp, fVidQualityField,
579 fVidQualityMatrix, fVidGrayscaleCheck, fSubField, fSubPopUp,
580 fAudLang1Field, fAudLang1PopUp, fAudLang2Field, fAudLang2PopUp,
581 fAudTrack1MixLabel, fAudTrack1MixPopUp, fAudTrack2MixLabel, fAudTrack2MixPopUp,
582 fAudRateField, fAudRatePopUp, fAudBitrateField,
583 fAudBitratePopUp, fPictureButton, fQueueCheck,
584 fPicSrcWidth,fPicSrcHeight,fPicSettingWidth,fPicSettingHeight,
585 fPicSettingARkeep,fPicSettingDeinterlace,fPicSettingARkeepDsply,
586 fPicSettingDeinterlaceDsply,fPicLabelSettings,fPicLabelSrc,fPicLabelOutp,
587 fPicLabelAr,fPicLabelDeinter,fPicLabelSrcX,fPicLabelOutputX,
588 fPicLabelPAROutp,fPicLabelPAROutputX,fPicSettingPARWidth,fPicSettingPARHeight,
589 fPicSettingPARDsply,fPicLabelAnamorphic,tableView,fPresetsAdd,fPresetsDelete,
590 fCreateChapterMarkers,fPresetNewX264OptLabel,fDisplayX264Options};
593 i < sizeof( controls ) / sizeof( NSControl * ); i++ )
595 if( [[controls[i] className] isEqualToString: @"NSTextField"] )
597 NSTextField * tf = (NSTextField *) controls[i];
598 if( ![tf isBezeled] )
600 [tf setTextColor: b ? [NSColor controlTextColor] :
601 [NSColor disabledControlTextColor]];
605 [controls[i] setEnabled: b];
611 /* if we're enabling the interface, check if the audio mixdown controls need to be enabled or not */
612 /* these will have been enabled by the mass control enablement above anyway, so we're sense-checking it here */
613 [self SetEnabledStateOfAudioMixdownControls: NULL];
617 [tableView setEnabled: NO];
621 [self VideoMatrixChanged: NULL];
624 - (IBAction) ShowScanPanel: (id) sender
626 [fScanController Show];
629 - (BOOL) windowShouldClose: (id) sender
631 /* Stop the application when the user closes the window */
632 [NSApp terminate: self];
636 - (IBAction) VideoMatrixChanged: (id) sender;
638 bool target, bitrate, quality;
640 target = bitrate = quality = false;
641 if( [fVidQualityMatrix isEnabled] )
643 switch( [fVidQualityMatrix selectedRow] )
656 [fVidTargetSizeField setEnabled: target];
657 [fVidBitrateField setEnabled: bitrate];
658 [fVidQualitySlider setEnabled: quality];
659 [fVidTwoPassCheck setEnabled: !quality &&
660 [fVidQualityMatrix isEnabled]];
663 [fVidTwoPassCheck setState: NSOffState];
666 [self QualitySliderChanged: sender];
667 [self CalculateBitrate: sender];
668 [self CustomSettingUsed: sender];
671 - (IBAction) QualitySliderChanged: (id) sender
673 [fVidConstantCell setTitle: [NSString stringWithFormat:
674 _( @"Constant quality: %.0f %%" ), 100.0 *
675 [fVidQualitySlider floatValue]]];
676 [self CustomSettingUsed: sender];
679 - (IBAction) BrowseFile: (id) sender
681 /* Open a panel to let the user choose and update the text field */
682 NSSavePanel * panel = [NSSavePanel savePanel];
683 /* We get the current file name and path from the destination field here */
684 [panel beginSheetForDirectory: [[fDstFile2Field stringValue] stringByDeletingLastPathComponent] file: [[fDstFile2Field stringValue] lastPathComponent]
685 modalForWindow: fWindow modalDelegate: self
686 didEndSelector: @selector( BrowseFileDone:returnCode:contextInfo: )
690 - (void) BrowseFileDone: (NSSavePanel *) sheet
691 returnCode: (int) returnCode contextInfo: (void *) contextInfo
693 if( returnCode == NSOKButton )
695 [fDstFile2Field setStringValue: [sheet filename]];
700 - (IBAction) ShowPicturePanel: (id) sender
702 hb_list_t * list = hb_get_titles( fHandle );
703 hb_title_t * title = (hb_title_t *) hb_list_item( list,
704 [fSrcTitlePopUp indexOfSelectedItem] );
706 /* Resize the panel */
708 newSize.width = 246 + title->width;
709 newSize.height = 80 + title->height;
710 [fPicturePanel setContentSize: newSize];
712 [fPictureController SetTitle: title];
714 [NSApp beginSheet: fPicturePanel modalForWindow: fWindow
715 modalDelegate: NULL didEndSelector: NULL contextInfo: NULL];
716 [NSApp runModalForWindow: fPicturePanel];
717 [NSApp endSheet: fPicturePanel];
718 [fPicturePanel orderOut: self];
719 [self CalculatePictureSizing: sender];
722 - (IBAction) ShowQueuePanel: (id) sender
724 /* Update the OutlineView */
725 [fQueueController Update: sender];
728 [NSApp beginSheet: fQueuePanel modalForWindow: fWindow
729 modalDelegate: NULL didEndSelector: NULL contextInfo: NULL];
730 [NSApp runModalForWindow: fQueuePanel];
731 [NSApp endSheet: fQueuePanel];
732 [fQueuePanel orderOut: self];
737 hb_list_t * list = hb_get_titles( fHandle );
738 hb_title_t * title = (hb_title_t *) hb_list_item( list,
739 [fSrcTitlePopUp indexOfSelectedItem] );
740 hb_job_t * job = title->job;
742 /* Chapter selection */
743 job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
744 job->chapter_end = [fSrcChapterEndPopUp indexOfSelectedItem] + 1;
748 /* Format and codecs */
749 int format = [fDstFormatPopUp indexOfSelectedItem];
750 int codecs = [fDstCodecsPopUp indexOfSelectedItem];
751 job->mux = FormatSettings[format][codecs] & HB_MUX_MASK;
752 job->vcodec = FormatSettings[format][codecs] & HB_VCODEC_MASK;
753 job->acodec = FormatSettings[format][codecs] & HB_ACODEC_MASK;
754 /* We set the chapter marker extraction here based on the format being
755 mpeg4 and the checkbox being checked */
756 if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [fCreateChapterMarkers state] == NSOnState)
758 job->chapter_markers = 1;
762 job->chapter_markers = 0;
764 if( ( job->vcodec & HB_VCODEC_FFMPEG ) &&
765 [fVidEncoderPopUp indexOfSelectedItem] > 0 )
767 job->vcodec = HB_VCODEC_XVID;
769 if( job->vcodec & HB_VCODEC_X264 )
771 if ([fVidEncoderPopUp indexOfSelectedItem] > 0 )
773 /* Just use new Baseline Level 3.0
774 Lets Deprecate Baseline Level 1.3*/
775 job->h264_level = 30;
776 job->mux = HB_MUX_IPOD;
777 /* move sanity check for iPod Encoding here */
778 job->pixel_ratio = 0 ;
782 /* Set this flag to switch from Constant Quantizer(default) to Constant Rate Factor Thanks jbrjake
783 Currently only used with Constant Quality setting*/
784 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultCrf"] > 0 && [fVidQualityMatrix selectedRow] == 2)
786 /* Can only be used with svn rev >= 89 */
790 /* Below Sends x264 options to the core library if x264 is selected*/
791 /* First we look to see if a user preset has been selected that contains a x264 optional string CurUserPresetChosenNum = nil */
792 if (curUserPresetChosenNum != nil)
795 /* Lets use this as per Nyx, Thanks Nyx! */
796 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
797 strcpy(job->x264opts, [[chosenPreset valueForKey:@"x264Option"] UTF8String]);
798 //job->x264opts = [[chosenPreset valueForKey:@"x264Option"] cString];
802 /* if not, then we check to see if there is a x264 opt in the preferences and use that if we want */
803 //job->x264opts = [[[NSUserDefaults standardUserDefaults] stringForKey:@"DefAdvancedx264Flags"] UTF8String];
804 /* Lets use this as per Nyx, Thanks Nyx! */
805 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
806 strcpy(job->x264opts, [[[NSUserDefaults standardUserDefaults] stringForKey:@"DefAdvancedx264Flags"] UTF8String]);
811 job->h264_13 = [fVidEncoderPopUp indexOfSelectedItem];
815 if( [fVidRatePopUp indexOfSelectedItem] > 0 )
817 job->vrate = 27000000;
818 job->vrate_base = hb_video_rates[[fVidRatePopUp
819 indexOfSelectedItem]-1].rate;
823 job->vrate = title->rate;
824 job->vrate_base = title->rate_base;
827 switch( [fVidQualityMatrix selectedRow] )
831 Bitrate should already have been calculated and displayed
832 in fVidBitrateField, so let's just use it */
834 job->vquality = -1.0;
835 job->vbitrate = [fVidBitrateField intValue];
838 job->vquality = [fVidQualitySlider floatValue];
843 job->grayscale = ( [fVidGrayscaleCheck state] == NSOnState );
847 /* Subtitle settings */
848 job->subtitle = [fSubPopUp indexOfSelectedItem] - 1;
850 /* Audio tracks and mixdowns */
851 /* check for the condition where track 2 has an audio selected, but track 1 does not */
852 /* we will use track 2 as track 1 in this scenario */
853 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
855 job->audios[0] = [fAudLang1PopUp indexOfSelectedItem] - 1;
856 job->audios[1] = [fAudLang2PopUp indexOfSelectedItem] - 1; /* will be -1 if "none" is selected */
858 job->audio_mixdowns[0] = [[fAudTrack1MixPopUp selectedItem] tag];
859 job->audio_mixdowns[1] = [[fAudTrack2MixPopUp selectedItem] tag];
861 else if ([fAudLang2PopUp indexOfSelectedItem] > 0)
863 job->audios[0] = [fAudLang2PopUp indexOfSelectedItem] - 1;
864 job->audio_mixdowns[0] = [[fAudTrack2MixPopUp selectedItem] tag];
873 job->arate = hb_audio_rates[[fAudRatePopUp
874 indexOfSelectedItem]].rate;
875 job->abitrate = hb_audio_bitrates[[fAudBitratePopUp
876 indexOfSelectedItem]].rate;
880 - (IBAction) EnableQueue: (id) sender
882 bool e = ( [fQueueCheck state] == NSOnState );
883 [fQueueAddButton setHidden: !e];
884 [fQueueShowButton setHidden: !e];
885 [fRipButton setTitle: e ? @"Start" : @"Rip"];
888 - (IBAction) AddToQueue: (id) sender
890 /* We get the destination directory from the destingation field here */
891 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
892 /* We check for a valid destination here */
893 if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0)
895 NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
900 hb_list_t * list = hb_get_titles( fHandle );
901 hb_title_t * title = (hb_title_t *) hb_list_item( list,
902 [fSrcTitlePopUp indexOfSelectedItem] );
903 hb_job_t * job = title->job;
907 /* Destination file */
908 job->file = [[fDstFile2Field stringValue] UTF8String];
910 if( [fVidTwoPassCheck state] == NSOnState )
913 hb_add( fHandle, job );
915 /* First we look to see if a user preset has been selected that contains a x264 optional string CurUserPresetChosenNum = nil */
916 if (curUserPresetChosenNum != nil)
919 /* Lets use this as per Nyx, Thanks Nyx! */
920 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
921 strcpy(job->x264opts, [[chosenPreset valueForKey:@"x264Option"] UTF8String]);
922 //job->x264opts = [[chosenPreset valueForKey:@"x264Option"] cString];
926 //job->x264opts = [[[NSUserDefaults standardUserDefaults] stringForKey:@"DefAdvancedx264Flags"] UTF8String];
927 /* Lets use this as per Nyx, Thanks Nyx! */
928 job->x264opts = (char *)calloc(1024,1); /* Fixme, this just leaks */
929 strcpy(job->x264opts, [[[NSUserDefaults standardUserDefaults]stringForKey:@"DefAdvancedx264Flags"] UTF8String]);
931 hb_add( fHandle, job );
936 hb_add( fHandle, job );
939 [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
943 - (IBAction) Rip: (id) sender
947 /* Rip or Cancel ? */
948 if( [[fRipButton title] isEqualToString: _( @"Cancel" )] )
950 [self Cancel: sender];
954 if( [fQueueCheck state] == NSOffState )
957 [self AddToQueue: sender];
963 /* We check for duplicate name here */
964 if( [[NSFileManager defaultManager] fileExistsAtPath:
965 [fDstFile2Field stringValue]] )
967 NSBeginCriticalAlertSheet( _( @"File already exists" ),
968 _( @"Cancel" ), _( @"Overwrite" ), NULL, fWindow, self,
969 @selector( OverwriteAlertDone:returnCode:contextInfo: ),
970 NULL, NULL, [NSString stringWithFormat:
971 _( @"Do you want to overwrite %@?" ),
972 [fDstFile2Field stringValue]] );
975 /* We get the destination directory from the destination field here */
976 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
977 /* We check for a valid destination here */
978 if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0)
980 NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
984 [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
992 - (void) OverwriteAlertDone: (NSWindow *) sheet
993 returnCode: (int) returnCode contextInfo: (void *) contextInfo
995 if( returnCode == NSAlertAlternateReturn )
1001 - (void) UpdateAlertDone: (NSWindow *) sheet
1002 returnCode: (int) returnCode contextInfo: (void *) contextInfo
1004 if( returnCode == NSAlertAlternateReturn )
1006 /* Show scan panel */
1007 [self performSelectorOnMainThread: @selector(ShowScanPanel:)
1008 withObject: NULL waitUntilDone: NO];
1012 /* Go to HandBrake homepage and exit */
1013 [self OpenHomepage: NULL];
1014 [NSApp terminate: self];
1019 /* Let libhb do the job */
1020 hb_start( fHandle );
1022 /* Disable interface */
1023 [self EnableUI: NO];
1024 [fPauseButton setEnabled: NO];
1025 [fRipButton setEnabled: NO];
1028 - (IBAction) Cancel: (id) sender
1030 NSBeginCriticalAlertSheet( _( @"Cancel - Are you sure?" ),
1031 _( @"Keep working" ), _( @"Cancel encoding" ), NULL, fWindow, self,
1032 @selector( _Cancel:returnCode:contextInfo: ), NULL, NULL,
1033 _( @"Encoding won't be recoverable." ) );
1036 - (void) _Cancel: (NSWindow *) sheet
1037 returnCode: (int) returnCode contextInfo: (void *) contextInfo
1039 if( returnCode == NSAlertAlternateReturn )
1042 [fPauseButton setEnabled: NO];
1043 [fRipButton setEnabled: NO];
1047 - (IBAction) Pause: (id) sender
1049 [fPauseButton setEnabled: NO];
1050 [fRipButton setEnabled: NO];
1052 if( [[fPauseButton title] isEqualToString: _( @"Resume" )] )
1054 hb_resume( fHandle );
1058 hb_pause( fHandle );
1062 - (IBAction) TitlePopUpChanged: (id) sender
1064 hb_list_t * list = hb_get_titles( fHandle );
1065 hb_title_t * title = (hb_title_t*)
1066 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
1069 /* If Auto Naming is on. We create an output filename of dvd name - title number */
1070 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultAutoNaming"] > 0)
1072 [fDstFile2Field setStringValue: [NSString stringWithFormat:
1073 @"%@/%@-%d.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent],
1074 [NSString stringWithUTF8String: title->name],
1075 [fSrcTitlePopUp indexOfSelectedItem] + 1,
1076 [[fDstFile2Field stringValue] pathExtension]]];
1079 /* Update chapter popups */
1080 [fSrcChapterStartPopUp removeAllItems];
1081 [fSrcChapterEndPopUp removeAllItems];
1082 for( int i = 0; i < hb_list_count( title->list_chapter ); i++ )
1084 [fSrcChapterStartPopUp addItemWithTitle: [NSString
1085 stringWithFormat: @"%d", i + 1]];
1086 [fSrcChapterEndPopUp addItemWithTitle: [NSString
1087 stringWithFormat: @"%d", i + 1]];
1089 [fSrcChapterStartPopUp selectItemAtIndex: 0];
1090 [fSrcChapterEndPopUp selectItemAtIndex:
1091 hb_list_count( title->list_chapter ) - 1];
1092 [self ChapterPopUpChanged: NULL];
1094 /* Start Get and set the initial pic size for display */
1095 hb_job_t * job = title->job;
1097 /* Turn Deinterlace on/off depending on the preference */
1098 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultDeinterlaceOn"] > 0)
1100 job->deinterlace = 1;
1104 job->deinterlace = 0;
1107 /* Pixel Ratio Setting */
1108 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PixelRatio"])
1111 job->pixel_ratio = 1 ;
1115 job->pixel_ratio = 0 ;
1117 /*Set Source Size Fields Here */
1118 [fPicSrcWidth setStringValue: [NSString stringWithFormat:
1119 @"%d", fTitle->width]];
1120 [fPicSrcHeight setStringValue: [NSString stringWithFormat:
1121 @"%d", fTitle->height]];
1122 /* We get the originial output picture width and height and put them
1123 in variables for use with some presets later on */
1124 PicOrigOutputWidth = job->width;
1125 PicOrigOutputHeight = job->height;
1126 /* we test getting the max output value for pic sizing here to be used later*/
1127 [fPicSettingWidth setStringValue: [NSString stringWithFormat:
1128 @"%d", PicOrigOutputWidth]];
1129 [fPicSettingHeight setStringValue: [NSString stringWithFormat:
1130 @"%d", PicOrigOutputHeight]];
1131 /* we run the picture size values through
1132 CalculatePictureSizing to get all picture size
1134 [self CalculatePictureSizing: NULL];
1135 /* Run Through EncoderPopUpChanged to see if there
1136 needs to be any pic value modifications based on encoder settings */
1137 //[self EncoderPopUpChanged: NULL];
1138 /* END Get and set the initial pic size for display */
1141 /* Update subtitle popups */
1142 hb_subtitle_t * subtitle;
1143 [fSubPopUp removeAllItems];
1144 [fSubPopUp addItemWithTitle: @"None"];
1145 for( int i = 0; i < hb_list_count( title->list_subtitle ); i++ )
1147 subtitle = (hb_subtitle_t *) hb_list_item( title->list_subtitle, i );
1149 /* We cannot use NSPopUpButton's addItemWithTitle because
1150 it checks for duplicate entries */
1151 [[fSubPopUp menu] addItemWithTitle: [NSString stringWithCString:
1152 subtitle->lang] action: NULL keyEquivalent: @""];
1154 [fSubPopUp selectItemAtIndex: 0];
1156 /* Update audio popups */
1157 [self AddAllAudioTracksToPopUp: fAudLang1PopUp];
1158 [self AddAllAudioTracksToPopUp: fAudLang2PopUp];
1159 /* search for the first instance of our prefs default language for track 1, and set track 2 to "none" */
1160 NSString * audioSearchPrefix = [[NSUserDefaults standardUserDefaults] stringForKey:@"DefaultLanguage"];
1161 [self SelectAudioTrackInPopUp: fAudLang1PopUp searchPrefixString: audioSearchPrefix selectIndexIfNotFound: 1];
1162 [self SelectAudioTrackInPopUp: fAudLang2PopUp searchPrefixString: NULL selectIndexIfNotFound: 0];
1164 /* changing the title may have changed the audio channels on offer, */
1165 /* so call AudioTrackPopUpChanged for both audio tracks to update the mixdown popups */
1166 [self AudioTrackPopUpChanged: fAudLang1PopUp];
1167 [self AudioTrackPopUpChanged: fAudLang2PopUp];
1171 - (IBAction) ChapterPopUpChanged: (id) sender
1173 hb_list_t * list = hb_get_titles( fHandle );
1174 hb_title_t * title = (hb_title_t *)
1175 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
1177 hb_chapter_t * chapter;
1178 int64_t duration = 0;
1179 for( int i = [fSrcChapterStartPopUp indexOfSelectedItem];
1180 i <= [fSrcChapterEndPopUp indexOfSelectedItem]; i++ )
1182 chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
1183 duration += chapter->duration;
1186 duration /= 90000; /* pts -> seconds */
1187 [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
1188 @"%02lld:%02lld:%02lld", duration / 3600, ( duration / 60 ) % 60,
1191 [self CalculateBitrate: sender];
1194 - (IBAction) FormatPopUpChanged: (id) sender
1196 NSString * string = [fDstFile2Field stringValue];
1197 int format = [fDstFormatPopUp indexOfSelectedItem];
1200 /* Update the codecs popup */
1201 [fDstCodecsPopUp removeAllItems];
1205 /*Get Default MP4 File Extension*/
1206 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0)
1214 [fDstCodecsPopUp addItemWithTitle:
1215 _( @"MPEG-4 Video / AAC Audio" )];
1216 [fDstCodecsPopUp addItemWithTitle:
1217 _( @"AVC/H.264 Video / AAC Audio" )];
1218 /* We enable the create chapters checkbox here since we are .mp4*/
1219 [fCreateChapterMarkers setEnabled: YES];
1223 [fDstCodecsPopUp addItemWithTitle:
1224 _( @"MPEG-4 Video / MP3 Audio" )];
1225 [fDstCodecsPopUp addItemWithTitle:
1226 _( @"MPEG-4 Video / AC-3 Audio" )];
1227 [fDstCodecsPopUp addItemWithTitle:
1228 _( @"AVC/H.264 Video / MP3 Audio" )];
1229 [fDstCodecsPopUp addItemWithTitle:
1230 _( @"AVC/H.264 Video / AC-3 Audio" )];
1231 /* We disable the create chapters checkbox here since we are NOT .mp4
1232 and make sure it is unchecked*/
1233 [fCreateChapterMarkers setEnabled: NO];
1234 [fCreateChapterMarkers setState: NSOffState];
1238 [fDstCodecsPopUp addItemWithTitle:
1239 _( @"MPEG-4 Video / Vorbis Audio" )];
1240 [fDstCodecsPopUp addItemWithTitle:
1241 _( @"MPEG-4 Video / MP3 Audio" )];
1242 /* We disable the create chapters checkbox here since we are NOT .mp4
1243 and make sure it is unchecked*/
1244 [fCreateChapterMarkers setEnabled: NO];
1245 [fCreateChapterMarkers setState: NSOffState];
1248 [self CodecsPopUpChanged: NULL];
1250 /* Add/replace to the correct extension */
1251 if( [string characterAtIndex: [string length] - 4] == '.' )
1253 [fDstFile2Field setStringValue: [NSString stringWithFormat:
1254 @"%@.%s", [string substringToIndex: [string length] - 4],
1259 [fDstFile2Field setStringValue: [NSString stringWithFormat:
1260 @"%@.%s", string, ext]];
1263 /* changing the format may mean that we can / can't offer mono or 6ch, */
1264 /* so call AudioTrackPopUpChanged for both audio tracks to update the mixdown popups */
1265 [self AudioTrackPopUpChanged: fAudLang1PopUp];
1266 [self AudioTrackPopUpChanged: fAudLang2PopUp];
1268 /* We call method method to change UI to reflect whether a preset is used or not*/
1269 [self CustomSettingUsed: sender];
1273 - (IBAction) CodecsPopUpChanged: (id) sender
1275 int format = [fDstFormatPopUp indexOfSelectedItem];
1276 int codecs = [fDstCodecsPopUp indexOfSelectedItem];
1278 /* Update the encoder popup */
1279 if( ( FormatSettings[format][codecs] & HB_VCODEC_X264 ) )
1281 /* MPEG-4 -> H.264 */
1282 [fVidEncoderPopUp removeAllItems];
1283 [fVidEncoderPopUp addItemWithTitle: @"x264 (h.264 Main)"];
1284 [fVidEncoderPopUp addItemWithTitle: @"x264 (h.264 iPod)"];
1288 else if( ( FormatSettings[format][codecs] & HB_VCODEC_FFMPEG ) )
1290 /* H.264 -> MPEG-4 */
1291 [fVidEncoderPopUp removeAllItems];
1292 [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"];
1293 [fVidEncoderPopUp addItemWithTitle: @"XviD"];
1294 [fVidEncoderPopUp selectItemAtIndex: 0];
1297 if( FormatSettings[format][codecs] & HB_ACODEC_AC3 )
1299 /* AC-3 pass-through: disable samplerate and bitrate */
1300 [fAudRatePopUp setEnabled: NO];
1301 [fAudBitratePopUp setEnabled: NO];
1305 [fAudRatePopUp setEnabled: YES];
1306 [fAudBitratePopUp setEnabled: YES];
1309 /* changing the codecs on offer may mean that we can / can't offer mono or 6ch, */
1310 /* so call AudioTrackPopUpChanged for both audio tracks to update the mixdown popups */
1311 [self AudioTrackPopUpChanged: fAudLang1PopUp];
1312 [self AudioTrackPopUpChanged: fAudLang2PopUp];
1314 [self CalculateBitrate: sender];
1315 /* We call method method to change UI to reflect whether a preset is used or not*/
1316 [self CustomSettingUsed: sender];
1319 - (IBAction) EncoderPopUpChanged: (id) sender
1322 /* Check to see if we need to modify the job pic values based on x264 (iPod) encoder selection */
1323 if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [fDstCodecsPopUp indexOfSelectedItem] == 1 && [fVidEncoderPopUp indexOfSelectedItem] == 1)
1325 hb_job_t * job = fTitle->job;
1326 job->pixel_ratio = 0 ;
1328 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultPicSizeAutoiPod"] > 0)
1331 if (job->width > 640)
1335 job->keep_ratio = 1;
1336 hb_fix_aspect( job, HB_KEEP_WIDTH );
1342 [self CalculatePictureSizing: sender];
1343 /* We call method method to change UI to reflect whether a preset is used or not*/
1344 [self CustomSettingUsed: sender];
1347 - (IBAction) SetEnabledStateOfAudioMixdownControls: (id) sender
1350 /* enable/disable the mixdown text and popupbutton for audio track 1 */
1351 [fAudTrack1MixPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
1352 [fAudTrack1MixLabel setTextColor: ([fAudLang1PopUp indexOfSelectedItem] == 0) ?
1353 [NSColor disabledControlTextColor] : [NSColor controlTextColor]];
1355 /* enable/disable the mixdown text and popupbutton for audio track 2 */
1356 [fAudTrack2MixPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
1357 [fAudTrack2MixLabel setTextColor: ([fAudLang2PopUp indexOfSelectedItem] == 0) ?
1358 [NSColor disabledControlTextColor] : [NSColor controlTextColor]];
1362 - (IBAction) AddAllAudioTracksToPopUp: (id) sender
1365 hb_list_t * list = hb_get_titles( fHandle );
1366 hb_title_t * title = (hb_title_t*)
1367 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
1371 [sender removeAllItems];
1372 [sender addItemWithTitle: _( @"None" )];
1373 for( int i = 0; i < hb_list_count( title->list_audio ); i++ )
1375 audio = (hb_audio_t *) hb_list_item( title->list_audio, i );
1376 [[sender menu] addItemWithTitle:
1377 [NSString stringWithCString: audio->lang]
1378 action: NULL keyEquivalent: @""];
1380 [sender selectItemAtIndex: 0];
1384 - (IBAction) SelectAudioTrackInPopUp: (id) sender searchPrefixString: (NSString *) searchPrefixString selectIndexIfNotFound: (int) selectIndexIfNotFound
1387 /* this method can be used to find a language, or a language-and-source-format combination, by passing in the appropriate string */
1388 /* e.g. to find the first French track, pass in an NSString * of "Francais" */
1389 /* e.g. to find the first English 5.1 AC3 track, pass in an NSString * of "English (AC3) (5.1 ch)" */
1390 /* if no matching track is found, then selectIndexIfNotFound is used to choose which track to select instead */
1392 if (searchPrefixString != NULL)
1395 for( int i = 0; i < [sender numberOfItems]; i++ )
1397 /* Try to find the desired search string */
1398 if ([[[sender itemAtIndex: i] title] hasPrefix:searchPrefixString])
1400 [sender selectItemAtIndex: i];
1404 /* couldn't find the string, so select the requested "search string not found" item */
1405 /* index of 0 means select the "none" item */
1406 /* index of 1 means select the first audio track */
1407 [sender selectItemAtIndex: selectIndexIfNotFound];
1411 /* if no search string is provided, then select the selectIndexIfNotFound item */
1412 [sender selectItemAtIndex: selectIndexIfNotFound];
1417 - (IBAction) AudioTrackPopUpChanged: (id) sender
1419 /* utility function to call AudioTrackPopUpChanged without passing in a mixdown-to-use */
1420 [self AudioTrackPopUpChanged: sender mixdownToUse: 0];
1423 - (IBAction) AudioTrackPopUpChanged: (id) sender mixdownToUse: (int) mixdownToUse
1426 /* make sure we have a selected title before continuing */
1427 if (fTitle == NULL) return;
1429 /* find out if audio track 1 or 2 was changed - this is passed to us in the tag of the sender */
1430 /* the sender will have been either fAudLang1PopUp (tag = 0) or fAudLang2PopUp (tag = 1) */
1431 int thisAudio = [sender tag];
1433 /* get the index of the selected audio */
1434 int thisAudioIndex = [sender indexOfSelectedItem] - 1;
1436 /* Handbrake can't currently cope with ripping the same source track twice */
1437 /* So, if this audio is also selected in the other audio track popup, set that popup's selection to "none" */
1438 /* get a reference to the two audio track popups */
1439 NSPopUpButton * thisAudioPopUp = (thisAudio == 1 ? fAudLang2PopUp : fAudLang1PopUp);
1440 NSPopUpButton * otherAudioPopUp = (thisAudio == 1 ? fAudLang1PopUp : fAudLang2PopUp);
1441 /* if the same track is selected in the other audio popup, then select "none" in that popup */
1442 /* unless, of course, both are selected as "none!" */
1443 if ([thisAudioPopUp indexOfSelectedItem] != 0 && [thisAudioPopUp indexOfSelectedItem] == [otherAudioPopUp indexOfSelectedItem]) {
1444 [otherAudioPopUp selectItemAtIndex: 0];
1445 [self AudioTrackPopUpChanged: otherAudioPopUp];
1448 /* pointer for the hb_audio_s struct we will use later on */
1451 /* find out what the currently-selected output audio codec is */
1452 int format = [fDstFormatPopUp indexOfSelectedItem];
1453 int codecs = [fDstCodecsPopUp indexOfSelectedItem];
1454 int acodec = FormatSettings[format][codecs] & HB_ACODEC_MASK;
1456 /* pointer to this track's mixdown NSPopUpButton */
1457 NSTextField * mixdownTextField;
1458 NSPopUpButton * mixdownPopUp;
1460 /* find our mixdown NSTextField and NSPopUpButton */
1463 mixdownTextField = fAudTrack1MixLabel;
1464 mixdownPopUp = fAudTrack1MixPopUp;
1468 mixdownTextField = fAudTrack2MixLabel;
1469 mixdownPopUp = fAudTrack2MixPopUp;
1472 /* delete the previous audio mixdown options */
1473 [mixdownPopUp removeAllItems];
1475 /* check if the audio mixdown controls need their enabled state changing */
1476 [self SetEnabledStateOfAudioMixdownControls: NULL];
1478 if (thisAudioIndex != -1)
1482 audio = (hb_audio_t *) hb_list_item( fTitle->list_audio, thisAudioIndex );
1486 int audioCodecsSupportMono = (audio->codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_FAAC);
1487 int audioCodecsSupport6Ch = (audio->codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_FAAC);
1489 /* check for AC-3 passthru */
1490 if (audio->codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3)
1492 [[mixdownPopUp menu] addItemWithTitle:
1493 [NSString stringWithCString: "AC3 Passthru"]
1494 action: NULL keyEquivalent: @""];
1499 /* find out if our selected output audio codec supports mono and / or 6ch */
1500 /* we also check for an input codec of AC3,
1501 as it is the only library able to do the mixdown to mono / conversion to 6-ch */
1502 /* audioCodecsSupportMono and audioCodecsSupport6Ch are the same for now,
1503 but this may change in the future, so they are separated for flexibility */
1505 /* find out the audio channel layout for our input audio */
1506 /* we'll cheat and use the liba52 layouts, even if the source isn't AC3 */
1509 if (audio->codec == HB_ACODEC_AC3)
1511 channel_layout = (audio->ac3flags & A52_CHANNEL_MASK);
1512 audio_has_lfe = (audio->ac3flags & A52_LFE);
1516 /* assume a stereo input for all other input codecs */
1517 channel_layout = A52_STEREO;
1521 /* add the appropriate audio mixdown menuitems to the popupbutton */
1522 /* in each case, we set the new menuitem's tag to be the amixdown value for that mixdown,
1523 so that we can reference the mixdown later */
1525 /* keep a track of the min and max mixdowns we used, so we can select the best match later */
1526 int minMixdownUsed = 0;
1527 int maxMixdownUsed = 0;
1529 /* do we want to add a mono option? */
1530 if (audioCodecsSupportMono == 1) {
1531 id<NSMenuItem> menuItem = [[mixdownPopUp menu] addItemWithTitle:
1532 [NSString stringWithCString: hb_audio_mixdowns[0].human_readable_name]
1533 action: NULL keyEquivalent: @""];
1534 [menuItem setTag: hb_audio_mixdowns[0].amixdown];
1535 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[0].amixdown;
1536 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[0].amixdown);
1539 /* do we want to add a stereo option? */
1540 if (channel_layout >= A52_STEREO && channel_layout != A52_CHANNEL1 && channel_layout != A52_CHANNEL2) {
1541 id<NSMenuItem> menuItem = [[mixdownPopUp menu] addItemWithTitle:
1542 [NSString stringWithCString: hb_audio_mixdowns[1].human_readable_name]
1543 action: NULL keyEquivalent: @""];
1544 [menuItem setTag: hb_audio_mixdowns[1].amixdown];
1545 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[1].amixdown;
1546 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[1].amixdown);
1549 /* do we want to add a dolby surround (DPL1) option? */
1550 if (channel_layout == A52_3F1R || channel_layout == A52_3F2R || channel_layout == A52_DOLBY) {
1551 id<NSMenuItem> menuItem = [[mixdownPopUp menu] addItemWithTitle:
1552 [NSString stringWithCString: hb_audio_mixdowns[2].human_readable_name]
1553 action: NULL keyEquivalent: @""];
1554 [menuItem setTag: hb_audio_mixdowns[2].amixdown];
1555 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[2].amixdown;
1556 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[2].amixdown);
1559 /* do we want to add a dolby pro logic 2 (DPL2) option? */
1560 if (channel_layout == A52_3F2R) {
1561 id<NSMenuItem> menuItem = [[mixdownPopUp menu] addItemWithTitle:
1562 [NSString stringWithCString: hb_audio_mixdowns[3].human_readable_name]
1563 action: NULL keyEquivalent: @""];
1564 [menuItem setTag: hb_audio_mixdowns[3].amixdown];
1565 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[3].amixdown;
1566 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[3].amixdown);
1569 /* do we want to add a 6-channel discrete option? */
1570 if (audioCodecsSupport6Ch == 1 && channel_layout == A52_3F2R && audio_has_lfe == A52_LFE) {
1571 id<NSMenuItem> menuItem = [[mixdownPopUp menu] addItemWithTitle:
1572 [NSString stringWithCString: hb_audio_mixdowns[4].human_readable_name]
1573 action: NULL keyEquivalent: @""];
1574 [menuItem setTag: hb_audio_mixdowns[4].amixdown];
1575 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[4].amixdown;
1576 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[4].amixdown);
1579 /* auto-select the best mixdown based on our saved mixdown preference */
1581 /* for now, this is hard-coded to a "best" mixdown of HB_AMIXDOWN_DOLBYPLII */
1582 /* ultimately this should be a prefs option */
1585 /* if we passed in a mixdown to use - in order to load a preset - then try and use it */
1586 if (mixdownToUse > 0)
1588 useMixdown = mixdownToUse;
1592 useMixdown = HB_AMIXDOWN_DOLBYPLII;
1595 /* if useMixdown > maxMixdownUsed, then use maxMixdownUsed */
1596 if (useMixdown > maxMixdownUsed) useMixdown = maxMixdownUsed;
1598 /* if useMixdown < minMixdownUsed, then use minMixdownUsed */
1599 if (useMixdown < minMixdownUsed) useMixdown = minMixdownUsed;
1601 /* select the (possibly-amended) preferred mixdown */
1602 [mixdownPopUp selectItemWithTag: useMixdown];
1610 /* see if the new audio track choice will change the bitrate we need */
1611 [self CalculateBitrate: sender];
1615 /* lets set the picture size back to the max from right after title scan
1616 Lets use an IBAction here as down the road we could always use a checkbox
1617 in the gui to easily take the user back to max. Remember, the compiler
1618 resolves IBActions down to -(void) during compile anyway */
1619 - (IBAction) RevertPictureSizeToMax: (id) sender
1621 hb_job_t * job = fTitle->job;
1622 /* We use the output picture width and height
1623 as calculated from libhb right after title is set
1624 in TitlePopUpChanged */
1625 job->width = PicOrigOutputWidth;
1626 job->height = PicOrigOutputHeight;
1630 [self CalculatePictureSizing: sender];
1631 /* We call method method to change UI to reflect whether a preset is used or not*/
1632 [self CustomSettingUsed: sender];
1636 /* Get and Display Current Pic Settings in main window */
1637 - (IBAction) CalculatePictureSizing: (id) sender
1641 [fPicSettingWidth setStringValue: [NSString stringWithFormat:
1642 @"%d", fTitle->job->width]];
1643 [fPicSettingHeight setStringValue: [NSString stringWithFormat:
1644 @"%d", fTitle->job->height]];
1645 [fPicSettingARkeep setStringValue: [NSString stringWithFormat:
1646 @"%d", fTitle->job->keep_ratio]];
1647 [fPicSettingDeinterlace setStringValue: [NSString stringWithFormat:
1648 @"%d", fTitle->job->deinterlace]];
1649 [fPicSettingPAR setStringValue: [NSString stringWithFormat:
1650 @"%d", fTitle->job->pixel_ratio]];
1652 if (fTitle->job->pixel_ratio == 1)
1654 int titlewidth = fTitle->width-fTitle->job->crop[2]-fTitle->job->crop[3];
1655 int arpwidth = fTitle->job->pixel_aspect_width;
1656 int arpheight = fTitle->job->pixel_aspect_height;
1657 int displayparwidth = titlewidth * arpwidth / arpheight;
1658 int displayparheight = fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1];
1659 [fPicSettingHeight setStringValue: [NSString stringWithFormat:
1660 @"%d", displayparheight]];
1661 [fPicLabelPAROutp setStringValue: @"Anamorphic Output:"];
1662 [fPicLabelPAROutputX setStringValue: @"x"];
1663 [fPicSettingPARWidth setStringValue: [NSString stringWithFormat:
1664 @"%d", displayparwidth]];
1665 [fPicSettingPARHeight setStringValue: [NSString stringWithFormat:
1666 @"%d", displayparheight]];
1668 fTitle->job->keep_ratio = 0;
1672 [fPicLabelPAROutp setStringValue: @""];
1673 [fPicLabelPAROutputX setStringValue: @""];
1674 [fPicSettingPARWidth setStringValue: @""];
1675 [fPicSettingPARHeight setStringValue: @""];
1678 /* Set ON/Off values for the deinterlace/keep aspect ratio according to boolean */
1679 if (fTitle->job->keep_ratio > 0)
1681 [fPicSettingARkeepDsply setStringValue: @"On"];
1685 [fPicSettingARkeepDsply setStringValue: @"Off"];
1687 if (fTitle->job->deinterlace > 0)
1689 [fPicSettingDeinterlaceDsply setStringValue: @"On"];
1693 [fPicSettingDeinterlaceDsply setStringValue: @"Off"];
1695 if (fTitle->job->pixel_ratio > 0)
1697 [fPicSettingPARDsply setStringValue: @"On"];
1701 [fPicSettingPARDsply setStringValue: @"Off"];
1703 /* below will trigger the preset, if selected, to be
1704 changed to "Custom". Lets comment out for now until
1705 we figure out a way to determine if the picture values
1706 changed modify the preset values */
1707 //[self CustomSettingUsed: sender];
1710 - (IBAction) CalculateBitrate: (id) sender
1712 if( !fHandle || [fVidQualityMatrix selectedRow] != 0 )
1717 hb_list_t * list = hb_get_titles( fHandle );
1718 hb_title_t * title = (hb_title_t *) hb_list_item( list,
1719 [fSrcTitlePopUp indexOfSelectedItem] );
1720 hb_job_t * job = title->job;
1724 [fVidBitrateField setIntValue: hb_calc_bitrate( job,
1725 [fVidTargetSizeField intValue] )];
1730 /* Method to determine if we should change the UI
1731 To reflect whether or not a Preset is being used or if
1732 the user is using "Custom" settings by determining the sender*/
1733 - (IBAction) CustomSettingUsed: (id) sender
1735 if ([sender stringValue] != NULL)
1737 /* Deselect the currently selected Preset if there is one*/
1738 [tableView deselectRow:[tableView selectedRow]];
1739 /* Change UI to show "Custom" settings are being used */
1740 [fPresetSelectedDisplay setStringValue: @"Custom"];
1741 /* Empty the field to display custom x264 preset options*/
1742 [fDisplayX264Options setStringValue: @""];
1743 curUserPresetChosenNum = nil;
1744 /* If we have MP4, AVC H.264 and x264 Main then we look to see
1745 if there are any x264 options from the preferences to use */
1746 if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [fDstCodecsPopUp indexOfSelectedItem] == 1)
1748 /* Lets check to see if the user wants them displayed from the preferences */
1749 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefAdvancedx264FlagsShow"] > 0)
1751 [fDisplayX264Options setStringValue: [NSString stringWithFormat:[[NSUserDefaults standardUserDefaults] stringForKey:@"DefAdvancedx264Flags"]]];
1759 /* We use this method to recreate new, updated factory
1761 - (IBAction)AddFactoryPresets:(id)sender
1763 /* First, we delete any existing built in presets */
1764 [self DeleteFactoryPresets: sender];
1765 /* Then, we re-create new built in presets programmatically CreatePS3Preset*/
1766 [UserPresets addObject:[self CreateIpodPreset]];
1767 [UserPresets addObject:[self CreateAppleTVPreset]];
1768 [UserPresets addObject:[self CreatePSThreePreset]];
1771 - (IBAction)DeleteFactoryPresets:(id)sender
1774 NSEnumerator *enumerator = [UserPresets objectEnumerator];
1778 NSMutableArray *tempArray;
1781 tempArray = [NSMutableArray array];
1782 /* we look here to see if the preset is we move on to the next one */
1783 while ( tempObject = [enumerator nextObject] )
1785 /* if the preset is "Factory" then we put it in the array of
1786 presets to delete */
1787 if ([[tempObject objectForKey:@"Type"] intValue] == 0)
1789 [tempArray addObject:tempObject];
1793 [UserPresets removeObjectsInArray:tempArray];
1794 [tableView reloadData];
1799 - (IBAction) ShowAddPresetPanel: (id) sender
1801 /* Deselect the currently selected Preset if there is one*/
1802 [tableView deselectRow:[tableView selectedRow]];
1803 /* If we have MP4, AVC H.264 and Main then we enable the x264 Options field for the
1804 Add Preset window we are about to open. We do this before we actually open the panel,
1805 as doing it after causes it to stick from the last selection for some reason. */
1806 if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [fDstCodecsPopUp indexOfSelectedItem] == 1)
1808 [fPresetNewX264Opt setEditable: YES];
1809 [fPresetNewX264OptLabel setEnabled: YES];
1813 [fPresetNewX264Opt setEditable: NO];
1814 [fPresetNewX264OptLabel setEnabled: NO];
1816 /* Erase info from the input fields */
1817 [fPresetNewName setStringValue: @""];
1818 [fPresetNewX264Opt setStringValue:@""];
1819 /* Show the panel */
1820 [NSApp beginSheet: fAddPresetPanel modalForWindow: fWindow
1821 modalDelegate: NULL didEndSelector: NULL contextInfo: NULL];
1822 [NSApp runModalForWindow: fAddPresetPanel];
1823 [NSApp endSheet: fAddPresetPanel];
1824 [fAddPresetPanel orderOut: self];
1828 - (IBAction) CloseAddPresetPanel: (id) sender
1834 - (IBAction)AddUserPreset:(id)sender
1837 /* Here we create a custom user preset */
1838 [UserPresets addObject:[self CreatePreset]];
1839 /* Erase info from the input fields */
1840 [fPresetNewName setStringValue: @""];
1841 [fPresetNewX264Opt setStringValue:@""];
1842 /* We stop the modal window for the new preset */
1852 /* We Sort the Presets By Factory or Custom */
1853 NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type"
1854 ascending:YES] autorelease];
1855 /* We Sort the Presets Alphabetically by name */
1856 NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName"
1857 ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease];
1858 NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil];
1859 NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors];
1860 [UserPresets setArray:sortedArray];
1863 /* We Reload the New Table data for presets */
1864 [tableView reloadData];
1865 /* We save all of the preset data here */
1869 - (IBAction)InsertPreset:(id)sender
1871 int index = [tableView selectedRow];
1872 [UserPresets insertObject:[self CreatePreset] atIndex:index];
1873 [tableView reloadData];
1877 - (NSDictionary *)CreatePreset
1879 NSMutableDictionary *preset = [[NSMutableDictionary alloc] init];
1880 /* Get the New Preset Name from the field in the AddPresetPanel */
1881 [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
1882 /*Set whether or not this is a user preset or factory 0 is factory, 1 is user*/
1883 [preset setObject:[NSNumber numberWithInt:1] forKey:@"Type"];
1884 /*Set whether or not this is default, at creation set to 0*/
1885 [preset setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
1886 /*Get the whether or not to apply pic settings in the AddPresetPanel*/
1887 [preset setObject:[NSNumber numberWithInt:[fPresetNewPicSettingsApply state]] forKey:@"UsesPictureSettings"];
1889 [preset setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"];
1890 /* Chapter Markers fCreateChapterMarkers*/
1891 [preset setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"];
1893 [preset setObject:[fDstCodecsPopUp titleOfSelectedItem] forKey:@"FileCodecs"];
1895 [preset setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"];
1896 /* x264 Option String */
1897 [preset setObject:[fPresetNewX264Opt stringValue] forKey:@"x264Option"];
1899 [preset setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"];
1900 [preset setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
1901 [preset setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"];
1902 [preset setObject:[NSNumber numberWithFloat:[fVidQualitySlider floatValue]] forKey:@"VideoQualitySlider"];
1904 /* Video framerate */
1905 [preset setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"];
1907 [preset setObject:[NSNumber numberWithInt:[fVidGrayscaleCheck state]] forKey:@"VideoGrayScale"];
1908 /* 2 Pass Encoding */
1909 [preset setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"];
1911 /*Picture Settings*/
1912 hb_job_t * job = fTitle->job;
1913 /* Basic Picture Settings */
1914 /* Use Max Picture settings for whatever the dvd is.*/
1915 [preset setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
1916 [preset setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
1917 [preset setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
1918 [preset setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
1919 [preset setObject:[NSNumber numberWithInt:fTitle->job->deinterlace] forKey:@"PictureDeinterlace"];
1920 [preset setObject:[NSNumber numberWithInt:fTitle->job->pixel_ratio] forKey:@"PicturePAR"];
1921 /* Set crop settings here */
1922 /* The Auto Crop Matrix in the Picture Window autodetects differences in crop settings */
1923 [preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
1924 [preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
1925 [preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
1926 [preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
1929 /* Audio Language One*/
1930 [preset setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"AudioLang1"];
1931 /* Audio Track one mixdown */
1932 if ([fAudLang1PopUp indexOfSelectedItem] > 0) {
1933 [preset setObject:[NSString stringWithCString:hb_mixdown_get_short_name_from_mixdown([[fAudTrack1MixPopUp selectedItem] tag])] forKey:@"AudioLang1Mixdown"];
1937 [preset setObject:[NSString stringWithCString:""] forKey:@"AudioLang1Mixdown"];
1939 /* Audio Language Two*/
1940 [preset setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"AudioLang2"];
1941 /* Audio Track Two mixdown */
1942 if ([fAudLang2PopUp indexOfSelectedItem] > 0) {
1943 [preset setObject:[NSString stringWithCString:hb_mixdown_get_short_name_from_mixdown([[fAudTrack2MixPopUp selectedItem] tag])] forKey:@"AudioLang2Mixdown"];
1947 [preset setObject:[NSString stringWithCString:""] forKey:@"AudioLang2Mixdown"];
1949 /* Audio Sample Rate*/
1950 [preset setObject:[fAudRatePopUp titleOfSelectedItem] forKey:@"AudioSampleRate"];
1951 /* Audio Bitrate Rate*/
1952 [preset setObject:[fAudBitratePopUp titleOfSelectedItem] forKey:@"AudioBitRate"];
1954 [preset setObject:[fSubPopUp titleOfSelectedItem] forKey:@"Subtitles"];
1957 [preset autorelease];
1962 - (NSDictionary *)CreateIpodPreset
1964 NSMutableDictionary *preset = [[NSMutableDictionary alloc] init];
1965 /* Get the New Preset Name from the field in the AddPresetPanel */
1966 [preset setObject:@"HB-iPod" forKey:@"PresetName"];
1967 /*Set whether or not this is a user preset or factory 0 is factory, 1 is user*/
1968 [preset setObject:[NSNumber numberWithInt:0] forKey:@"Type"];
1969 /*Set whether or not this is default, at creation set to 0*/
1970 [preset setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
1971 /*Get the whether or not to apply pic settings in the AddPresetPanel*/
1972 [preset setObject:[NSNumber numberWithInt:0] forKey:@"UsesPictureSettings"];
1974 [preset setObject:@"MP4 file" forKey:@"FileFormat"];
1975 /* Chapter Markers*/
1976 [preset setObject:[NSNumber numberWithInt:1] forKey:@"ChapterMarkers"];
1978 [preset setObject:@"AVC/H.264 Video / AAC Audio" forKey:@"FileCodecs"];
1980 [preset setObject:@"x264 (h.264 iPod)" forKey:@"VideoEncoder"];
1981 /* x264 Option String */
1982 [preset setObject:@"frameref=1:bframes=0:nofast_pskip:subq=6:partitions=p8x8,p8x4,p4x8,i4x4:qcomp=0:me=umh:nodct_decimate" forKey:@"x264Option"];
1984 [preset setObject:[NSNumber numberWithInt:1] forKey:@"VideoQualityType"];
1985 [preset setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
1986 [preset setObject:@"1500" forKey:@"VideoAvgBitrate"];
1987 [preset setObject:[NSNumber numberWithFloat:[fVidQualitySlider floatValue]] forKey:@"VideoQualitySlider"];
1989 /* Video framerate */
1990 [preset setObject:@"Same as source" forKey:@"VideoFramerate"];
1992 [preset setObject:[NSNumber numberWithInt:0] forKey:@"VideoGrayScale"];
1993 /* 2 Pass Encoding */
1994 [preset setObject:[NSNumber numberWithInt:0] forKey:@"VideoTwoPass"];
1996 /*Picture Settings*/
1997 //hb_job_t * job = fTitle->job;
1998 /* Basic Picture Settings */
1999 /* Use Max Picture settings for whatever the dvd is.*/
2000 [preset setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
2001 //[preset setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
2002 //[preset setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
2003 //[preset setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
2004 //[preset setObject:[NSNumber numberWithInt:fTitle->job->deinterlace] forKey:@"PictureDeinterlace"];
2005 //[preset setObject:[NSNumber numberWithInt:fTitle->job->pixel_ratio] forKey:@"PicturePAR"];
2006 /* Set crop settings here */
2007 /* The Auto Crop Matrix in the Picture Window autodetects differences in crop settings */
2008 //[preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
2009 //[preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
2010 //[preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
2011 //[preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
2014 /* Audio track one*/
2015 [preset setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"AudioLang1"];
2016 /* Track one mixdown dpl2*/
2017 [preset setObject:[NSString stringWithCString:"dpl2"] forKey:@"AudioLang1Mixdown"];
2018 /* Audio track two */
2019 [preset setObject:[NSString stringWithFormat:@"None"] forKey:@"AudioLang2"];
2020 /* Track two mixdown */
2021 [preset setObject:[NSString stringWithCString:""] forKey:@"AudioLang2Mixdown"];
2022 /* Audio Sample Rate*/
2023 [preset setObject:@"48" forKey:@"AudioSampleRate"];
2024 /* Audio Bitrate Rate*/
2025 [preset setObject:@"160" forKey:@"AudioBitRate"];
2027 [preset setObject:@"None" forKey:@"Subtitles"];
2030 [preset autorelease];
2035 - (NSDictionary *)CreateAppleTVPreset
2037 NSMutableDictionary *preset = [[NSMutableDictionary alloc] init];
2038 /* Get the New Preset Name from the field in the AddPresetPanel */
2039 [preset setObject:@"HB-AppleTV" forKey:@"PresetName"];
2040 /*Set whether or not this is a user preset where 0 is factory, 1 is user*/
2041 [preset setObject:[NSNumber numberWithInt:0] forKey:@"Type"];
2042 /*Set whether or not this is default, at creation set to 0*/
2043 [preset setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
2044 /*Get the whether or not to apply pic settings in the AddPresetPanel*/
2045 [preset setObject:[NSNumber numberWithInt:1] forKey:@"UsesPictureSettings"];
2047 [preset setObject:@"MP4 file" forKey:@"FileFormat"];
2048 /* Chapter Markers*/
2049 [preset setObject:[NSNumber numberWithInt:1] forKey:@"ChapterMarkers"];
2051 [preset setObject:@"AVC/H.264 Video / AAC Audio" forKey:@"FileCodecs"];
2053 [preset setObject:@"x264 (h.264 Main)" forKey:@"VideoEncoder"];
2054 /* x264 Option String (We can use this to tweak the appleTV output)*/
2055 [preset setObject:@"bframes=3:ref=1:subme=5:me=umh:no-fast-pskip=1:no-dct-decimate=1:trellis=2" forKey:@"x264Option"];
2057 [preset setObject:[NSNumber numberWithInt:1] forKey:@"VideoQualityType"];
2058 [preset setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
2059 [preset setObject:@"2500" forKey:@"VideoAvgBitrate"];
2060 [preset setObject:[NSNumber numberWithFloat:[fVidQualitySlider floatValue]] forKey:@"VideoQualitySlider"];
2062 /* Video framerate */
2063 [preset setObject:@"Same as source" forKey:@"VideoFramerate"];
2065 [preset setObject:[NSNumber numberWithInt:0] forKey:@"VideoGrayScale"];
2066 /* 2 Pass Encoding */
2067 [preset setObject:[NSNumber numberWithInt:0] forKey:@"VideoTwoPass"];
2069 /*Picture Settings*/
2070 /* For AppleTV we only want to retain UsesMaxPictureSettings
2071 which depend on the source dvd picture settings, so we don't
2072 record the current dvd's picture info since it will vary from
2074 //hb_job_t * job = fTitle->job;
2075 //hb_job_t * job = title->job;
2076 /* Basic Picture Settings */
2077 /* Use Max Picture settings for whatever the dvd is.*/
2078 [preset setObject:[NSNumber numberWithInt:1] forKey:@"UsesMaxPictureSettings"];
2079 //[preset setObject:[NSNumber numberWithInt:PicOrigOutputWidth] forKey:@"PictureWidth"];
2080 //[preset setObject:[NSNumber numberWithInt:PicOrigOutputHeight] forKey:@"PictureHeight"];
2081 //[preset setObject:[NSNumber numberWithInt:0] forKey:@"PictureKeepRatio"];
2082 //[preset setObject:[NSNumber numberWithInt:0] forKey:@"PictureDeinterlace"];
2083 [preset setObject:[NSNumber numberWithInt:1] forKey:@"PicturePAR"];
2084 /* Set crop settings here */
2085 /* The Auto Crop Matrix in the Picture Window autodetects differences in crop settings */
2086 //[preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
2087 //[preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
2088 //[preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
2089 //[preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
2092 /* Audio track one*/
2093 [preset setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"AudioLang1"];
2094 /* Track one mixdown dpl2*/
2095 [preset setObject:[NSString stringWithCString:"dpl2"] forKey:@"AudioLang1Mixdown"];
2096 /* Audio track two */
2097 [preset setObject:[NSString stringWithFormat:@"None"] forKey:@"AudioLang2"];
2098 /* Track two mixdown */
2099 [preset setObject:[NSString stringWithCString:""] forKey:@"AudioLang2Mixdown"];
2100 /* Audio Sample Rate*/
2101 [preset setObject:@"44.1" forKey:@"AudioSampleRate"];
2102 /* Audio Bitrate Rate*/
2103 [preset setObject:@"160" forKey:@"AudioBitRate"];
2105 [preset setObject:@"None" forKey:@"Subtitles"];
2108 [preset autorelease];
2113 - (NSDictionary *)CreatePSThreePreset
2115 NSMutableDictionary *preset = [[NSMutableDictionary alloc] init];
2116 /* Get the New Preset Name from the field in the AddPresetPanel */
2117 [preset setObject:@"HB-PS3" forKey:@"PresetName"];
2118 /*Set whether or not this is a user preset where 0 is factory, 1 is user*/
2119 [preset setObject:[NSNumber numberWithInt:0] forKey:@"Type"];
2120 /*Set whether or not this is default, at creation set to 0*/
2121 [preset setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
2122 /*Get the whether or not to apply pic settings in the AddPresetPanel*/
2123 [preset setObject:[NSNumber numberWithInt:1] forKey:@"UsesPictureSettings"];
2125 [preset setObject:@"MP4 file" forKey:@"FileFormat"];
2126 /* Chapter Markers*/
2127 [preset setObject:[NSNumber numberWithInt:1] forKey:@"ChapterMarkers"];
2129 [preset setObject:@"AVC/H.264 Video / AAC Audio" forKey:@"FileCodecs"];
2131 [preset setObject:@"x264 (h.264 Main)" forKey:@"VideoEncoder"];
2132 /* x264 Option String (We can use this to tweak the appleTV output)*/
2133 [preset setObject:@"level=41" forKey:@"x264Option"];
2135 [preset setObject:[NSNumber numberWithInt:1] forKey:@"VideoQualityType"];
2136 [preset setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
2137 [preset setObject:@"2500" forKey:@"VideoAvgBitrate"];
2138 [preset setObject:[NSNumber numberWithFloat:[fVidQualitySlider floatValue]] forKey:@"VideoQualitySlider"];
2140 /* Video framerate */
2141 [preset setObject:@"Same as source" forKey:@"VideoFramerate"];
2143 [preset setObject:[NSNumber numberWithInt:0] forKey:@"VideoGrayScale"];
2144 /* 2 Pass Encoding */
2145 [preset setObject:[NSNumber numberWithInt:0] forKey:@"VideoTwoPass"];
2147 /*Picture Settings*/
2148 /* For PS3 we only want to retain UsesMaxPictureSettings
2149 which depend on the source dvd picture settings, so we don't
2150 record the current dvd's picture info since it will vary from
2152 /* Use Max Picture settings for whatever the dvd is.*/
2153 [preset setObject:[NSNumber numberWithInt:1] forKey:@"UsesMaxPictureSettings"];
2154 //[preset setObject:[NSNumber numberWithInt:PicOrigOutputWidth] forKey:@"PictureWidth"];
2155 //[preset setObject:[NSNumber numberWithInt:PicOrigOutputHeight] forKey:@"PictureHeight"];
2156 //[preset setObject:[NSNumber numberWithInt:0] forKey:@"PictureKeepRatio"];
2157 //[preset setObject:[NSNumber numberWithInt:0] forKey:@"PictureDeinterlace"];
2158 [preset setObject:[NSNumber numberWithInt:1] forKey:@"PicturePAR"];
2159 /* Set crop settings here */
2160 /* The Auto Crop Matrix in the Picture Window autodetects differences in crop settings */
2161 //[preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
2162 //[preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
2163 //[preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
2164 //[preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
2167 /* Audio track one*/
2168 [preset setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"AudioLang1"];
2169 /* Track one mixdown dpl2*/
2170 [preset setObject:[NSString stringWithCString:"dpl2"] forKey:@"AudioLang1Mixdown"];
2171 /* Audio track two */
2172 [preset setObject:[NSString stringWithFormat:@"None"] forKey:@"AudioLang2"];
2173 /* Track two mixdown */
2174 [preset setObject:[NSString stringWithCString:""] forKey:@"AudioLang2Mixdown"];
2175 /* Audio Sample Rate*/
2176 [preset setObject:@"44.1" forKey:@"AudioSampleRate"];
2177 /* Audio Bitrate Rate*/
2178 [preset setObject:@"160" forKey:@"AudioBitRate"];
2180 [preset setObject:@"None" forKey:@"Subtitles"];
2183 [preset autorelease];
2189 - (IBAction)DeletePreset:(id)sender
2192 NSEnumerator *enumerator;
2194 NSMutableArray *tempArray;
2197 if ( [tableView numberOfSelectedRows] == 0 )
2199 /* Alert user before deleting preset */
2200 /* Comment out for now, tie to user pref eventually */
2202 status = NSRunAlertPanel(@"Warning!", @"Are you sure that you want to delete the selected preset?", @"OK", @"Cancel", nil);
2204 if ( status == NSAlertDefaultReturn ) {
2205 enumerator = [tableView selectedRowEnumerator];
2206 tempArray = [NSMutableArray array];
2208 while ( (index = [enumerator nextObject]) ) {
2209 tempObject = [UserPresets objectAtIndex:[index intValue]];
2210 [tempArray addObject:tempObject];
2213 [UserPresets removeObjectsInArray:tempArray];
2214 [tableView reloadData];
2218 - (IBAction)tableViewSelected:(id)sender
2220 /* Since we cannot disable the presets tableView in terms of clickability
2221 we will use the enabled state of the add presets button to determine whether
2222 or not clicking on a preset will do anything */
2223 if ([fPresetsAdd isEnabled])
2226 /* we get the chosen preset from the UserPresets array */
2227 chosenPreset = [UserPresets objectAtIndex:[sender selectedRow]];
2228 curUserPresetChosenNum = [sender selectedRow];
2229 /* we set the preset display field in main window here */
2230 [fPresetSelectedDisplay setStringValue: [NSString stringWithFormat: @"%@",[chosenPreset valueForKey:@"PresetName"]]];
2232 [fDstFormatPopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"FileFormat"]]];
2233 [self FormatPopUpChanged: NULL];
2234 /* Chapter Markers*/
2235 [fCreateChapterMarkers setState:[[chosenPreset objectForKey:@"ChapterMarkers"] intValue]];
2237 [fDstCodecsPopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"FileCodecs"]]];
2238 [self CodecsPopUpChanged: NULL];
2240 [fVidEncoderPopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"VideoEncoder"]]];
2242 /* We can show the preset options here in the gui if we want to
2243 so we check to see it the user has specified it in the prefs */
2244 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefAdvancedx264FlagsShow"] > 0)
2246 [fDisplayX264Options setStringValue: [NSString stringWithFormat:[chosenPreset valueForKey:@"x264Option"]]];
2249 /* Lets run through the following functions to get variables set there */
2250 [self EncoderPopUpChanged: NULL];
2251 [self CalculateBitrate: NULL];
2254 [fVidQualityMatrix selectCellAtRow:[[chosenPreset objectForKey:@"VideoQualityType"] intValue] column:0];
2256 [fVidTargetSizeField setStringValue: [NSString stringWithFormat:[chosenPreset valueForKey:@"VideoTargetSize"]]];
2257 [fVidBitrateField setStringValue: [NSString stringWithFormat:[chosenPreset valueForKey:@"VideoAvgBitrate"]]];
2259 [fVidQualitySlider setFloatValue: [[chosenPreset valueForKey:@"VideoQualitySlider"] floatValue]];
2260 [self VideoMatrixChanged: NULL];
2262 /* Video framerate */
2263 [fVidRatePopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"VideoFramerate"]]];
2266 [fVidGrayscaleCheck setState:[[chosenPreset objectForKey:@"VideoGrayScale"] intValue]];
2268 /* 2 Pass Encoding */
2269 [fVidTwoPassCheck setState:[[chosenPreset objectForKey:@"VideoTwoPass"] intValue]];
2273 /* Audio Language One*/
2274 /* select our preferred audio language / mixdown format in the audio track popup */
2275 /* SelectAudioTrackInPopUp will default to the first audio if the requested format can't be found */
2276 [self SelectAudioTrackInPopUp: fAudLang1PopUp searchPrefixString: [NSString stringWithFormat:[chosenPreset valueForKey:@"AudioLang1"]]
2277 selectIndexIfNotFound: 1];
2278 /* if the preset contains a mixdown value for track 1, then try and load it */
2279 /* if the preset contains the empty string for this value, then we'll get
2280 a mixdown of 0 from hb_mixdown_get_mixdown_from_short_name,
2281 which will be correctly ignored by AudioTrackPopUpChanged */
2282 /* if the mixdown is unavailable, AudioTrackPopUpChanged will choose the next best mixdown */
2284 NSString * short_name1 = [NSString stringWithFormat:[chosenPreset valueForKey:@"AudioLang1Mixdown"]];
2285 [short_name1 getCString:cBuffer1];
2286 int mixdown1 = hb_mixdown_get_mixdown_from_short_name(cBuffer1);
2287 [self AudioTrackPopUpChanged: fAudLang1PopUp mixdownToUse: mixdown1];
2289 /* Audio Language Two*/
2290 /* select our preferred audio language / mixdown format in the audio track popup */
2291 /* SelectAudioTrackInPopUp will default to "none" if the requested format can't be found */
2292 [self SelectAudioTrackInPopUp: fAudLang2PopUp searchPrefixString: [NSString stringWithFormat:[chosenPreset valueForKey:@"AudioLang2"]]
2293 selectIndexIfNotFound: 0];
2294 /* if the preset contains a mixdown value for track 2, then try and load it */
2295 /* if the preset contains the empty string for this value, then we'll get
2296 a mixdown of 0 from hb_mixdown_get_mixdown_from_short_name,
2297 which will be correctly ignored by AudioTrackPopUpChanged */
2298 /* if the mixdown is unavailable, AudioTrackPopUpChanged will choose the next best mixdown */
2300 NSString * short_name2 = [NSString stringWithFormat:[chosenPreset valueForKey:@"AudioLang2Mixdown"]];
2301 [short_name2 getCString:cBuffer2];
2302 int mixdown2 = hb_mixdown_get_mixdown_from_short_name(cBuffer2);
2303 [self AudioTrackPopUpChanged: fAudLang2PopUp mixdownToUse: mixdown2];
2306 /* Audio Sample Rate*/
2307 [fAudRatePopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"AudioSampleRate"]]];
2308 /* Audio Bitrate Rate*/
2309 [fAudBitratePopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"AudioBitRate"]]];
2311 [fSubPopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"Subtitles"]]];
2313 /* Picture Settings */
2314 /* Look to see if we apply these here in objectForKey:@"UsesPictureSettings"] */
2315 if ([[chosenPreset objectForKey:@"UsesPictureSettings"] intValue] == 1)
2317 hb_job_t * job = fTitle->job;
2318 /* Check to see if we should use the max picture setting for the current title*/
2319 if ([[chosenPreset objectForKey:@"UsesMaxPictureSettings"] intValue] == 1)
2321 /* Use Max Picture settings for whatever the dvd is.*/
2322 [self RevertPictureSizeToMax: NULL];
2323 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"] intValue];
2324 if (job->keep_ratio == 1)
2326 hb_fix_aspect( job, HB_KEEP_WIDTH );
2328 job->pixel_ratio = [[chosenPreset objectForKey:@"PicturePAR"] intValue];
2332 job->width = [[chosenPreset objectForKey:@"PictureWidth"] intValue];
2333 job->height = [[chosenPreset objectForKey:@"PictureHeight"] intValue];
2334 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"] intValue];
2335 if (job->keep_ratio == 1)
2337 hb_fix_aspect( job, HB_KEEP_WIDTH );
2339 job->pixel_ratio = [[chosenPreset objectForKey:@"PicturePAR"] intValue];
2340 job->crop[0] = [[chosenPreset objectForKey:@"PictureTopCrop"] intValue];
2341 job->crop[1] = [[chosenPreset objectForKey:@"PictureBottomCrop"] intValue];
2342 job->crop[2] = [[chosenPreset objectForKey:@"PictureLeftCrop"] intValue];
2343 job->crop[3] = [[chosenPreset objectForKey:@"PictureRightCrop"] intValue];
2345 [self CalculatePictureSizing: NULL];
2356 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
2358 return [UserPresets count];
2361 /* we use this to determine display characteristics for
2362 each table cell based on content currently only used to
2363 show the built in presets in a blue font. */
2364 - (void)tableView:(NSTableView *)aTableView
2365 willDisplayCell:(id)aCell
2366 forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
2368 NSDictionary *userPresetDict = [UserPresets objectAtIndex:rowIndex];
2369 if ([[userPresetDict objectForKey:@"Type"] intValue] == 0)
2371 [aCell setTextColor:[NSColor blueColor]];
2375 [aCell setTextColor:[NSColor blackColor]];
2380 - (id)tableView:(NSTableView *)aTableView
2381 objectValueForTableColumn:(NSTableColumn *)aTableColumn
2384 id theRecord, theValue;
2386 theRecord = [UserPresets objectAtIndex:rowIndex];
2387 theValue = [theRecord objectForKey:[aTableColumn identifier]];
2391 // NSTableDataSource method that we implement to edit values directly in the table...
2392 - (void)tableView:(NSTableView *)aTableView
2393 setObjectValue:(id)anObject
2394 forTableColumn:(NSTableColumn *)aTableColumn
2399 theRecord = [UserPresets objectAtIndex:rowIndex];
2400 [theRecord setObject:anObject forKey:@"PresetName"];
2401 /* We Sort the Presets By Factory or Custom */
2402 NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type"
2403 ascending:YES] autorelease];
2404 /* We Sort the Presets Alphabetically by name */
2405 NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName"
2406 ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease];
2407 NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil];
2408 NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors];
2409 [UserPresets setArray:sortedArray];
2410 /* We Reload the New Table data for presets */
2411 [tableView reloadData];
2412 /* We save all of the preset data here */
2419 [UserPresets writeToFile:UserPresetsFile atomically:YES];
2425 - (void) controlTextDidBeginEditing: (NSNotification *) notification
2427 [self CalculateBitrate: NULL];
2430 - (void) controlTextDidEndEditing: (NSNotification *) notification
2432 [self CalculateBitrate: NULL];
2435 - (void) controlTextDidChange: (NSNotification *) notification
2437 [self CalculateBitrate: NULL];
2440 - (IBAction) OpenHomepage: (id) sender
2442 [[NSWorkspace sharedWorkspace] openURL: [NSURL
2443 URLWithString:@"http://handbrake.m0k.org/"]];
2446 - (IBAction) OpenForums: (id) sender
2448 [[NSWorkspace sharedWorkspace] openURL: [NSURL
2449 URLWithString:@"http://handbrake.m0k.org/forum/"]];