OSDN Git Service

f7253866758415a625215cb4929df19d8a95be76
[handbrake-jp/handbrake-jp-git.git] / test / test.c
1 /* $Id: test.c,v 1.82 2005/11/19 08:25:54 titer Exp $
2
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. */
6
7 #include <signal.h>
8 #include <getopt.h>
9 #include <sys/time.h>
10 #include <time.h>
11 #include <unistd.h>
12 #include <inttypes.h>
13
14 #if defined( __MINGW32__ )
15 #include <conio.h>
16 #endif
17
18 #if defined( PTW32_STATIC_LIB )
19 #include <pthread.h>
20 #endif
21
22 #include "hb.h"
23 #include "parsecsv.h"
24
25 #if defined( __APPLE_CC__ )
26 #import <CoreServices/CoreServices.h>
27 #include <IOKit/IOKitLib.h>
28 #include <IOKit/storage/IOMedia.h>
29 #include <IOKit/storage/IODVDMedia.h>
30 #endif
31
32 /* Options */
33 #if defined( __APPLE_CC__ )
34 #define EXTRA_VLC_DYLD_PATH "/Applications/VLC.app/Contents/MacOS/lib"
35 #define DEFAULT_DYLD_PATH "/usr/local/lib:/usr/lib"
36
37 static int    no_vlc_dylib = 0;
38 #endif
39 static int    debug       = HB_DEBUG_ALL;
40 static int    update      = 0;
41 static int    dvdnav      = 1;
42 static char * input       = NULL;
43 static char * output      = NULL;
44 static char * format      = NULL;
45 static int    titleindex  = 1;
46 static int    titlescan   = 0;
47 static int    main_feature = 0;
48 static char * native_language = NULL;
49 static int    native_dub  = 0;
50 static int    twoPass     = 0;
51 static int    deinterlace           = 0;
52 static char * deinterlace_opt       = 0;
53 static int    deblock               = 0;
54 static char * deblock_opt           = 0;
55 static int    denoise               = 0;
56 static char * denoise_opt           = 0;
57 static int    detelecine            = 0;
58 static char * detelecine_opt        = 0;
59 static int    decomb                = 0;
60 static char * decomb_opt            = 0;
61 static int    rotate                = 0;
62 static char * rotate_opt            = 0;
63 static int    grayscale   = 0;
64 static int    vcodec      = HB_VCODEC_FFMPEG;
65 static int    h264_13     = 0;
66 static int    h264_30     = 0;
67 static hb_list_t * audios = NULL;
68 static hb_audio_config_t * audio = NULL;
69 static int    num_audio_tracks = 0;
70 static char * mixdowns    = NULL;
71 static char * dynamic_range_compression = NULL;
72 static char * atracks     = NULL;
73 static char * arates      = NULL;
74 static char * abitrates   = NULL;
75 static char * acodecs     = NULL;
76 static char * anames      = NULL;
77 static int    default_acodec = HB_ACODEC_FAAC;
78 static int    default_abitrate = 160;
79 static int    audio_explicit = 0;
80 static char ** subtracks   = NULL;
81 static char ** subforce    = NULL;
82 static char * subburn     = NULL;
83 static char * subdefault  = NULL;
84 static char ** srtfile     = NULL;
85 static char ** srtcodeset  = NULL;
86 static char ** srtoffset   = NULL;
87 static char ** srtlang     = NULL;
88 static int     srtdefault  = -1;
89 static int    subtitle_scan = 0;
90 static int    width       = 0;
91 static int    height      = 0;
92 static int    crop[4]     = { -1,-1,-1,-1 };
93 static int    cpu         = 0;
94 static int    vrate       = 0;
95 static float  vquality    = -1.0;
96 static int    vbitrate    = 0;
97 static int    size        = 0;
98 static int    mux         = 0;
99 static int    anamorphic_mode  = 0;
100 static int    modulus       = 0;
101 static int    par_height    = 0;
102 static int    par_width     = 0;
103 static int    display_width = 0;
104 static int    keep_display_aspect = 0;
105 static int    itu_par       = 0;
106 static int    angle = 0;
107 static int    chapter_start = 0;
108 static int    chapter_end   = 0;
109 static int    chapter_markers = 0;
110 static char * marker_file   = NULL;
111 static char       *x264opts             = NULL;
112 static char       *x264opts2    = NULL;
113 static int        maxHeight             = 0;
114 static int        maxWidth              = 0;
115 static int    turbo_opts_enabled = 0;
116 static char * turbo_opts = "ref=1:subme=2:me=dia:analyse=none:trellis=0:no-fast-pskip=0:8x8dct=0:weightb=0";
117 static int    largeFileSize = 0;
118 static int    preset        = 0;
119 static char * preset_name   = 0;
120 static int    cfr           = 0;
121 static int    mp4_optimize  = 0;
122 static int    ipod_atom     = 0;
123 static int    color_matrix  = 0;
124 static int    preview_count = 10;
125 static int    store_previews = 0;
126 static int    start_at_preview = 0;
127 static int64_t start_at_pts    = 0;
128 static int    start_at_frame = 0;
129 static char * start_at_string = NULL;
130 static char * start_at_token = NULL;
131 static int64_t stop_at_pts    = 0;
132 static int    stop_at_frame = 0;
133 static char * stop_at_string = NULL;
134 static char * stop_at_token = NULL;
135 static uint64_t min_title_duration = 900000LL;
136
137 /* Exit cleanly on Ctrl-C */
138 static volatile int die = 0;
139 static void SigHandler( int );
140
141 /* Utils */
142 static void ShowCommands();
143 static void ShowHelp();
144 static void ShowPresets();
145
146 static int  ParseOptions( int argc, char ** argv );
147 static int  CheckOptions( int argc, char ** argv );
148 static int  HandleEvents( hb_handle_t * h );
149
150 static int get_acodec_for_string( char *codec );
151 static int is_sample_rate_valid(int rate);
152
153 #ifdef __APPLE_CC__
154 static char* bsd_name_for_path(char *path);
155 static int device_is_dvd(char *device);
156 static io_service_t get_iokit_service( char *device );
157 static int is_dvd_service( io_service_t service );
158 static int is_whole_media_service( io_service_t service );
159 #endif
160
161 /* Only print the "Muxing..." message once */
162 static int show_mux_warning = 1;
163
164 /****************************************************************************
165  * hb_error_handler
166  *
167  * When using the CLI just display using hb_log as we always did in the past
168  * make sure that we prefix with a nice ERROR message to catch peoples eyes.
169  ****************************************************************************/
170 static void hb_cli_error_handler ( const char *errmsg )
171 {
172     fprintf( stderr, "ERROR: %s\n", errmsg );
173 }
174
175 int main( int argc, char ** argv )
176 {
177     hb_handle_t * h;
178     int           build;
179     char        * version;
180
181 /* win32 _IOLBF (line-buffering) is the same as _IOFBF (full-buffering).
182  * force it to unbuffered otherwise informative output is not easily parsed.
183  */
184 #if defined( _WIN32 ) || defined( __MINGW32__ )
185     setvbuf( stdout, NULL, _IONBF, 0 );
186     setvbuf( stderr, NULL, _IONBF, 0 );
187 #endif
188
189     audios = hb_list_init();
190
191     /* Parse command line */
192     if( ParseOptions( argc, argv ) ||
193         CheckOptions( argc, argv ) )
194     {
195         return 1;
196     }
197
198     /* Register our error handler */
199     hb_register_error_handler(&hb_cli_error_handler);
200
201     /* Init libhb */
202     h = hb_init( debug, update );
203     hb_dvd_set_dvdnav( dvdnav );
204
205     /* Show version */
206     fprintf( stderr, "%s - %s - %s\n",
207              HB_PROJECT_TITLE, HB_PROJECT_BUILD_TITLE, HB_PROJECT_URL_WEBSITE );
208
209     /* Check for update */
210     if( update )
211     {
212         if( ( build = hb_check_update( h, &version ) ) > -1 )
213         {
214             fprintf( stderr, "You are using an old version of "
215                      "HandBrake.\nLatest is %s (build %d).\n", version,
216                      build );
217         }
218         else
219         {
220             fprintf( stderr, "Your version of HandBrake is up to "
221                      "date.\n" );
222         }
223         hb_close( &h );
224         hb_global_close();
225         return 0;
226     }
227
228     /* Geeky */
229     fprintf( stderr, "%d CPU%s detected\n", hb_get_cpu_count(),
230              hb_get_cpu_count( h ) > 1 ? "s" : "" );
231     if( cpu )
232     {
233         fprintf( stderr, "Forcing %d CPU%s\n", cpu,
234                  cpu > 1 ? "s" : "" );
235         hb_set_cpu_count( h, cpu );
236     }
237
238     /* Exit ASAP on Ctrl-C */
239     signal( SIGINT, SigHandler );
240
241     /* Feed libhb with a DVD to scan */
242     fprintf( stderr, "Opening %s...\n", input );
243
244     if (main_feature) {
245         /*
246          * We need to scan for all the titles in order to find the main feature
247          */
248         titleindex = 0;
249     }
250
251     hb_scan( h, input, titleindex, preview_count, store_previews, min_title_duration );
252
253     /* Wait... */
254     while( !die )
255     {
256 #if defined( __MINGW32__ )
257         if( _kbhit() ) {
258             switch( _getch() )
259             {
260                 case 0x03: /* ctrl-c */
261                 case 'q':
262                     fprintf( stdout, "\nEncoding Quit by user command\n" );
263                     die = 1;
264                     break;
265                 case 'p':
266                     fprintf( stdout, "\nEncoding Paused by user command, 'r' to resume\n" );
267                     hb_pause( h );
268                     break;
269                 case 'r':
270                     hb_resume( h );
271                     break;
272                 case 'h':
273                     ShowCommands();
274                     break;
275             }
276         }
277         hb_snooze( 200 );
278 #elif !defined(SYS_BEOS)
279         fd_set         fds;
280         struct timeval tv;
281         int            ret;
282         char           buf[257];
283
284         tv.tv_sec  = 0;
285         tv.tv_usec = 100000;
286
287         FD_ZERO( &fds );
288         FD_SET( STDIN_FILENO, &fds );
289         ret = select( STDIN_FILENO + 1, &fds, NULL, NULL, &tv );
290
291         if( ret > 0 )
292         {
293             int size = 0;
294
295             while( size < 256 &&
296                    read( STDIN_FILENO, &buf[size], 1 ) > 0 )
297             {
298                 if( buf[size] == '\n' )
299                 {
300                     break;
301                 }
302                 size++;
303             }
304
305             if( size >= 256 || buf[size] == '\n' )
306             {
307                 switch( buf[0] )
308                 {
309                     case 'q':
310                         fprintf( stdout, "\nEncoding Quit by user command\n" );
311                         die = 1;
312                         break;
313                     case 'p':
314                         fprintf( stdout, "\nEncoding Paused by user command, 'r' to resume\n" );
315                         hb_pause( h );
316                         break;
317                     case 'r':
318                         hb_resume( h );
319                         break;
320                     case 'h':
321                         ShowCommands();
322                         break;
323                 }
324             }
325         }
326         hb_snooze( 200 );
327 #else
328         hb_snooze( 200 );
329 #endif
330
331         HandleEvents( h );
332     }
333
334     /* Clean up */
335     hb_close( &h );
336     hb_global_close();
337     if( input )  free( input );
338     if( output ) free( output );
339     if( format ) free( format );
340     if( audios )
341     {
342         while( ( audio = hb_list_item( audios, 0 ) ) )
343         {
344             hb_list_rem( audios, audio );
345             if( audio->out.name )
346             {
347                 free( audio->out.name );
348             }
349             free( audio );
350         }
351         hb_list_close( &audios );
352     }
353     if( mixdowns ) free( mixdowns );
354     if( dynamic_range_compression ) free( dynamic_range_compression );
355     if( atracks ) free( atracks );
356     if( arates ) free( arates );
357     if( abitrates ) free( abitrates );
358     if( acodecs ) free( acodecs );
359     if( anames ) free( anames );
360     if (native_language ) free (native_language );
361         if( x264opts ) free (x264opts );
362         if( x264opts2 ) free (x264opts2 );
363     if (preset_name) free (preset_name);
364     if( stop_at_string ) free( stop_at_string );
365     if( start_at_string ) free( start_at_string );
366
367     fprintf( stderr, "HandBrake has exited.\n" );
368
369     return 0;
370 }
371
372 static void ShowCommands()
373 {
374     fprintf( stdout, "\nCommands:\n" );
375     fprintf( stdout, " [h]elp    Show this message\n" );
376     fprintf( stdout, " [q]uit    Exit HandBrakeCLI\n" );
377     fprintf( stdout, " [p]ause   Pause encoding\n" );
378     fprintf( stdout, " [r]esume  Resume encoding\n" );
379 }
380
381 static void PrintTitleInfo( hb_title_t * title )
382 {
383     hb_chapter_t  * chapter;
384     hb_subtitle_t * subtitle;
385     int i;
386
387     fprintf( stderr, "+ title %d:\n", title->index );
388     if ( title->index == title->job->feature )
389     {
390         fprintf( stderr, "  + Main Feature\n" );
391     }
392     if ( title->type == HB_STREAM_TYPE )
393     {
394         fprintf( stderr, "  + stream: %s\n", title->path );
395     }
396     else if ( title->type == HB_DVD_TYPE )
397     {
398         fprintf( stderr, "  + vts %d, ttn %d, cells %d->%d (%"PRIu64" blocks)\n",
399                 title->vts, title->ttn, title->cell_start, title->cell_end,
400                 title->block_count );
401     }
402     if (title->angle_count > 1)
403         fprintf( stderr, "  + angle(s) %d\n", title->angle_count );
404     fprintf( stderr, "  + duration: %02d:%02d:%02d\n",
405              title->hours, title->minutes, title->seconds );
406     fprintf( stderr, "  + size: %dx%d, pixel aspect: %d/%d, display aspect: %.2f, %.3f fps\n",
407              title->width, title->height,
408              title->pixel_aspect_width,
409              title->pixel_aspect_height,
410              (float) title->aspect,
411              (float) title->rate / title->rate_base );
412     fprintf( stderr, "  + autocrop: %d/%d/%d/%d\n", title->crop[0],
413              title->crop[1], title->crop[2], title->crop[3] );
414     fprintf( stderr, "  + chapters:\n" );
415     for( i = 0; i < hb_list_count( title->list_chapter ); i++ )
416     {
417         chapter = hb_list_item( title->list_chapter, i );
418         fprintf( stderr, "    + %d: cells %d->%d, %"PRIu64" blocks, duration "
419                  "%02d:%02d:%02d\n", chapter->index,
420                  chapter->cell_start, chapter->cell_end,
421                  chapter->block_count, chapter->hours, chapter->minutes,
422                  chapter->seconds );
423     }
424     fprintf( stderr, "  + audio tracks:\n" );
425     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
426     {
427         audio = hb_list_audio_config_item( title->list_audio, i );
428         if( ( audio->in.codec == HB_ACODEC_AC3 ) || ( audio->in.codec == HB_ACODEC_DCA) )
429         {
430             fprintf( stderr, "    + %d, %s (iso639-2: %s), %dHz, %dbps\n", 
431                      i + 1,
432                      audio->lang.description, 
433                      audio->lang.iso639_2,
434                      audio->in.samplerate, 
435                      audio->in.bitrate );
436         }
437         else
438         {
439             fprintf( stderr, "    + %d, %s (iso639-2: %s)\n", 
440                      i + 1, 
441                      audio->lang.description,
442                      audio->lang.iso639_2 );
443         }
444     }
445     fprintf( stderr, "  + subtitle tracks:\n" );
446     for( i = 0; i < hb_list_count( title->list_subtitle ); i++ )
447     {
448         subtitle = hb_list_item( title->list_subtitle, i );
449         fprintf( stderr, "    + %d, %s (iso639-2: %s) (%s)(%s)\n", 
450                  i + 1, subtitle->lang,
451                  subtitle->iso639_2,
452                  (subtitle->format == TEXTSUB) ? "Text" : "Bitmap",
453                  hb_subsource_name(subtitle->source));
454     }
455
456     if(title->detected_interlacing)
457     {
458         /* Interlacing was found in half or more of the preview frames */
459         fprintf( stderr, "  + combing detected, may be interlaced or telecined\n");
460     }
461
462 }
463
464 static int test_sub_list( char ** list, int pos )
465 {
466     int i;
467
468     if ( list == NULL || pos == 0 )
469         return 0;
470
471     if ( list[0] == NULL && pos == 1 )
472         return 1;
473
474     for ( i = 0; list[i] != NULL; i++ )
475     {
476         int idx = strtol( list[i], NULL, 0 );
477         if ( idx == pos )
478             return 1;
479     }
480     return 0;
481 }
482
483 static int HandleEvents( hb_handle_t * h )
484 {
485     hb_state_t s;
486     int tmp_num_audio_tracks;
487
488     hb_get_state( h, &s );
489     switch( s.state )
490     {
491         case HB_STATE_IDLE:
492             /* Nothing to do */
493             break;
494
495 #define p s.param.scanning
496         case HB_STATE_SCANNING:
497             /* Show what title is currently being scanned */
498             fprintf( stderr, "Scanning title %d", p.title_cur );
499             if( !titleindex || titlescan )
500                 fprintf( stderr, " of %d", p.title_count );
501             fprintf( stderr, "...\n" );
502             break;
503 #undef p
504
505         case HB_STATE_SCANDONE:
506         {
507             hb_list_t  * list;
508             hb_title_t * title;
509             hb_job_t   * job;
510             int i;
511             int sub_burned = 0;
512
513             /* Audio argument string parsing variables */
514             int acodec = 0;
515             int abitrate = 0;
516             int arate = 0;
517             int mixdown = HB_AMIXDOWN_DOLBYPLII;
518             double d_r_c = 0;
519             /* Audio argument string parsing variables */
520
521             list = hb_get_titles( h );
522
523             if( !hb_list_count( list ) )
524             {
525                 /* No valid title, stop right there */
526                 fprintf( stderr, "No title found.\n" );
527                 die = 1;
528                 break;
529             }
530                 if( main_feature )
531                 {
532                 int i;
533                 int main_feature_idx=0;
534                 int main_feature_pos=-1;
535                 int main_feature_time=0;
536                 int title_time;
537
538                 fprintf( stderr, "Searching for main feature title...\n" );
539
540                 for( i = 0; i < hb_list_count( list ); i++ )
541                 {
542                     title = hb_list_item( list, i );
543                     title_time = (title->hours*60*60 ) + (title->minutes *60) + (title->seconds);
544                     fprintf( stderr, " + Title (%d) index %d has length %dsec\n",
545                              i, title->index, title_time );
546                     if( main_feature_time < title_time )
547                     {
548                         main_feature_time = title_time;
549                         main_feature_pos = i;
550                         main_feature_idx = title->index;
551                     }
552                     if( title->job->feature == title->index )
553                     {
554                         main_feature_time = title_time;
555                         main_feature_pos = i;
556                         main_feature_idx = title->index;
557                         break;
558                     }
559                 }
560                 if( main_feature_pos == -1 )
561                 {
562                     fprintf( stderr, "No main feature title found.\n" );
563                     die = 1;
564                     break;
565                 }
566                 titleindex = main_feature_idx;
567                 fprintf( stderr, "Found main feature title, setting title to %d\n",
568                          main_feature_idx);
569
570                 title = hb_list_item( list, main_feature_pos);
571             } else {
572                 title = hb_list_item( list, 0 );
573             }
574
575             if( !titleindex || titlescan )
576             {
577                 /* Scan-only mode, print infos and exit */
578                 int i;
579                 for( i = 0; i < hb_list_count( list ); i++ )
580                 {
581                     title = hb_list_item( list, i );
582                     PrintTitleInfo( title );
583                 }
584                 die = 1;
585                 break;
586             }
587
588             /* Set job settings */
589             job   = title->job;
590
591             PrintTitleInfo( title );
592
593             if( chapter_start && chapter_end && !stop_at_pts && !start_at_preview && !stop_at_frame && !start_at_pts && !start_at_frame )
594             {
595                 job->chapter_start = MAX( job->chapter_start,
596                                           chapter_start );
597                 job->chapter_end   = MIN( job->chapter_end,
598                                           chapter_end );
599                 job->chapter_end   = MAX( job->chapter_start,
600                                           job->chapter_end );
601             }
602
603             if ( angle )
604             {
605                 job->angle = angle;
606             }
607
608             if (preset)
609             {
610                 fprintf( stderr, "+ Using preset: %s", preset_name);
611
612                 if (!strcmp(preset_name, "Universal"))
613                 {
614                     if( !mux )
615                     {
616                         mux = HB_MUX_MP4;
617                     }
618                     vcodec = HB_VCODEC_X264;
619                     job->vquality = 20.0;
620                     if( !atracks )
621                     {
622                         atracks = strdup("1,1");
623                     }
624                     if( !acodecs )
625                     {
626                         acodecs = strdup("faac,ac3pass");
627                     }
628                     if( !abitrates )
629                     {
630                         abitrates = strdup("160,160");
631                     }
632                     if( !mixdowns )
633                     {
634                         mixdowns = strdup("dpl2,auto");
635                     }
636                     if( !arates )
637                     {
638                         arates = strdup("Auto,Auto");
639                     }
640                     if( !dynamic_range_compression )
641                     {
642                         dynamic_range_compression = strdup("0.0,0.0");
643                     }
644                     maxWidth = 720;
645                     if( !x264opts )
646                     {
647                         x264opts = strdup("cabac=0:ref=2:me=umh:bframes=0:8x8dct=0:trellis=0:subme=6");
648                     }
649                     if( !anamorphic_mode )
650                     {
651                         anamorphic_mode = 2;
652                     }
653                     job->chapter_markers = 1;
654                     
655                 }
656
657                 if (!strcmp(preset_name, "iPod"))
658                 {
659                     if( !mux )
660                     {
661                         mux = HB_MUX_MP4;
662                     }
663                     job->ipod_atom = 1;
664                     vcodec = HB_VCODEC_X264;
665                     job->vbitrate = 700;
666                     if( !atracks )
667                     {
668                         atracks = strdup("1");
669                     }
670                     if( !acodecs )
671                     {
672                         acodecs = strdup("faac");
673                     }
674                     if( !abitrates )
675                     {
676                         abitrates = strdup("160");
677                     }
678                     if( !mixdowns )
679                     {
680                         mixdowns = strdup("dpl2");
681                     }
682                     if( !arates )
683                     {
684                         arates = strdup("Auto");
685                     }
686                     if( !dynamic_range_compression )
687                     {
688                         dynamic_range_compression = strdup("0.0");
689                     }
690                     maxWidth = 320;
691                     if( !x264opts )
692                     {
693                         x264opts = strdup("level=30:bframes=0:cabac=0:ref=1:vbv-maxrate=768:vbv-bufsize=2000:analyse=all:me=umh:no-fast-pskip=1:subme=6:8x8dct=0:trellis=0");
694                     }
695                     job->chapter_markers = 1;
696                     
697                 }
698
699                 if (!strcmp(preset_name, "iPhone & iPod Touch"))
700                 {
701                     if( !mux )
702                     {
703                         mux = HB_MUX_MP4;
704                     }
705                     vcodec = HB_VCODEC_X264;
706                     job->vquality = 20.0;
707                     if( !atracks )
708                     {
709                         atracks = strdup("1");
710                     }
711                     if( !acodecs )
712                     {
713                         acodecs = strdup("faac");
714                     }
715                     if( !abitrates )
716                     {
717                         abitrates = strdup("128");
718                     }
719                     if( !mixdowns )
720                     {
721                         mixdowns = strdup("dpl2");
722                     }
723                     if( !arates )
724                     {
725                         arates = strdup("Auto");
726                     }
727                     if( !dynamic_range_compression )
728                     {
729                         dynamic_range_compression = strdup("0.0");
730                     }
731                     maxWidth = 480;
732                     if( !x264opts )
733                     {
734                         x264opts = strdup("cabac=0:ref=2:me=umh:bframes=0:subme=6:8x8dct=0:trellis=0");
735                     }
736                     job->chapter_markers = 1;
737                     
738                 }
739
740                 if (!strcmp(preset_name, "iPad"))
741                 {
742                     if( !mux )
743                     {
744                         mux = HB_MUX_MP4;
745                     }
746                     job->largeFileSize = 1;
747                     vcodec = HB_VCODEC_X264;
748                     job->vquality = 20.0;
749                     job->vrate_base = 900900;
750                     job->cfr = 2;
751                     if( !atracks )
752                     {
753                         atracks = strdup("1");
754                     }
755                     if( !acodecs )
756                     {
757                         acodecs = strdup("faac");
758                     }
759                     if( !abitrates )
760                     {
761                         abitrates = strdup("160");
762                     }
763                     if( !mixdowns )
764                     {
765                         mixdowns = strdup("dpl2");
766                     }
767                     if( !arates )
768                     {
769                         arates = strdup("Auto");
770                     }
771                     if( !dynamic_range_compression )
772                     {
773                         dynamic_range_compression = strdup("0.0");
774                     }
775                     maxWidth = 1024;
776                     if( !anamorphic_mode )
777                     {
778                         anamorphic_mode = 2;
779                     }
780                     job->chapter_markers = 1;
781                     
782                 }
783
784                 if (!strcmp(preset_name, "AppleTV"))
785                 {
786                     if( !mux )
787                     {
788                         mux = HB_MUX_MP4;
789                     }
790                     job->largeFileSize = 1;
791                     vcodec = HB_VCODEC_X264;
792                     job->vquality = 20.0;
793                     if( !atracks )
794                     {
795                         atracks = strdup("1,1");
796                     }
797                     if( !acodecs )
798                     {
799                         acodecs = strdup("faac,ac3pass");
800                     }
801                     if( !abitrates )
802                     {
803                         abitrates = strdup("160,160");
804                     }
805                     if( !mixdowns )
806                     {
807                         mixdowns = strdup("dpl2,auto");
808                     }
809                     if( !arates )
810                     {
811                         arates = strdup("Auto,Auto");
812                     }
813                     if( !dynamic_range_compression )
814                     {
815                         dynamic_range_compression = strdup("0.0,0.0");
816                     }
817                     maxWidth = 960;
818                     if( !x264opts )
819                     {
820                         x264opts = strdup("cabac=0:ref=2:me=umh:b-pyramid=none:b-adapt=2:weightb=0:trellis=0:weightp=0:vbv-maxrate=9500:vbv-bufsize=9500");
821                     }
822                     if( !anamorphic_mode )
823                     {
824                         anamorphic_mode = 2;
825                     }
826                     job->chapter_markers = 1;
827                     
828                 }
829
830                 if (!strcmp(preset_name, "Normal"))
831                 {
832                     if( !mux )
833                     {
834                         mux = HB_MUX_MP4;
835                     }
836                     vcodec = HB_VCODEC_X264;
837                     job->vquality = 20.0;
838                     if( !atracks )
839                     {
840                         atracks = strdup("1");
841                     }
842                     if( !acodecs )
843                     {
844                         acodecs = strdup("faac");
845                     }
846                     if( !abitrates )
847                     {
848                         abitrates = strdup("160");
849                     }
850                     if( !mixdowns )
851                     {
852                         mixdowns = strdup("dpl2");
853                     }
854                     if( !arates )
855                     {
856                         arates = strdup("Auto");
857                     }
858                     if( !dynamic_range_compression )
859                     {
860                         dynamic_range_compression = strdup("0.0");
861                     }
862                     if( !x264opts )
863                     {
864                         x264opts = strdup("ref=2:bframes=2:subme=6:mixed-refs=0:weightb=0:8x8dct=0:trellis=0");
865                     }
866                     if( !anamorphic_mode )
867                     {
868                         anamorphic_mode = 1;
869                     }
870                     job->chapter_markers = 1;
871                     
872                 }
873
874                 if (!strcmp(preset_name, "High Profile"))
875                 {
876                     if( !mux )
877                     {
878                         mux = HB_MUX_MP4;
879                     }
880                     vcodec = HB_VCODEC_X264;
881                     job->vquality = 20.0;
882                     if( !atracks )
883                     {
884                         atracks = strdup("1,1");
885                     }
886                     if( !acodecs )
887                     {
888                         acodecs = strdup("faac,ac3pass");
889                     }
890                     if( !abitrates )
891                     {
892                         abitrates = strdup("160,160");
893                     }
894                     if( !mixdowns )
895                     {
896                         mixdowns = strdup("dpl2,auto");
897                     }
898                     if( !arates )
899                     {
900                         arates = strdup("Auto,Auto");
901                     }
902                     if( !dynamic_range_compression )
903                     {
904                         dynamic_range_compression = strdup("0.0,0.0");
905                     }
906                     if( !x264opts )
907                     {
908                         x264opts = strdup("b-adapt=2:rc-lookahead=50");
909                     }
910                     detelecine = 1;
911                     decomb = 1;
912                     if( !anamorphic_mode )
913                     {
914                         anamorphic_mode = 2;
915                     }
916                     job->chapter_markers = 1;
917                     
918                 }
919
920                 if (!strcmp(preset_name, "Classic"))
921                 {
922                     if( !mux )
923                     {
924                         mux = HB_MUX_MP4;
925                     }
926                     job->vbitrate = 1000;
927                     if( !atracks )
928                     {
929                         atracks = strdup("1");
930                     }
931                     if( !acodecs )
932                     {
933                         acodecs = strdup("faac");
934                     }
935                     if( !abitrates )
936                     {
937                         abitrates = strdup("160");
938                     }
939                     if( !mixdowns )
940                     {
941                         mixdowns = strdup("dpl2");
942                     }
943                     if( !arates )
944                     {
945                         arates = strdup("Auto");
946                     }
947                     if( !dynamic_range_compression )
948                     {
949                         dynamic_range_compression = strdup("0.0");
950                     }
951                     
952                 }
953
954                 if (!strcmp(preset_name, "AppleTV Legacy"))
955                 {
956                     if( !mux )
957                     {
958                         mux = HB_MUX_MP4;
959                     }
960                     job->largeFileSize = 1;
961                     vcodec = HB_VCODEC_X264;
962                     job->vbitrate = 2500;
963                     if( !atracks )
964                     {
965                         atracks = strdup("1,1");
966                     }
967                     if( !acodecs )
968                     {
969                         acodecs = strdup("faac,ac3pass");
970                     }
971                     if( !abitrates )
972                     {
973                         abitrates = strdup("160,160");
974                     }
975                     if( !mixdowns )
976                     {
977                         mixdowns = strdup("dpl2,auto");
978                     }
979                     if( !arates )
980                     {
981                         arates = strdup("Auto,Auto");
982                     }
983                     if( !dynamic_range_compression )
984                     {
985                         dynamic_range_compression = strdup("0.0,0.0");
986                     }
987                     if( !x264opts )
988                     {
989                         x264opts = strdup("ref=1:b-pyramid=none:subme=5:me=umh:no-fast-pskip=1:cabac=0:weightb=0:8x8dct=0:trellis=0");
990                     }
991                     if( !anamorphic_mode )
992                     {
993                         anamorphic_mode = 1;
994                     }
995                     job->chapter_markers = 1;
996                     
997                 }
998
999                 if (!strcmp(preset_name, "iPhone Legacy"))
1000                 {
1001                     if( !mux )
1002                     {
1003                         mux = HB_MUX_MP4;
1004                     }
1005                     job->ipod_atom = 1;
1006                     vcodec = HB_VCODEC_X264;
1007                     job->vbitrate = 960;
1008                     if( !atracks )
1009                     {
1010                         atracks = strdup("1");
1011                     }
1012                     if( !acodecs )
1013                     {
1014                         acodecs = strdup("faac");
1015                     }
1016                     if( !abitrates )
1017                     {
1018                         abitrates = strdup("128");
1019                     }
1020                     if( !mixdowns )
1021                     {
1022                         mixdowns = strdup("dpl2");
1023                     }
1024                     if( !arates )
1025                     {
1026                         arates = strdup("Auto");
1027                     }
1028                     if( !dynamic_range_compression )
1029                     {
1030                         dynamic_range_compression = strdup("0.0");
1031                     }
1032                     maxWidth = 480;
1033                     if( !x264opts )
1034                     {
1035                         x264opts = strdup("level=30:cabac=0:ref=1:analyse=all:me=umh:no-fast-pskip=1:psy-rd=0,0:bframes=0:subme=6:8x8dct=0:trellis=0");
1036                     }
1037                     job->chapter_markers = 1;
1038                     
1039                 }
1040
1041                 if (!strcmp(preset_name, "iPod Legacy"))
1042                 {
1043                     if( !mux )
1044                     {
1045                         mux = HB_MUX_MP4;
1046                     }
1047                     job->ipod_atom = 1;
1048                     vcodec = HB_VCODEC_X264;
1049                     job->vbitrate = 1500;
1050                     if( !atracks )
1051                     {
1052                         atracks = strdup("1");
1053                     }
1054                     if( !acodecs )
1055                     {
1056                         acodecs = strdup("faac");
1057                     }
1058                     if( !abitrates )
1059                     {
1060                         abitrates = strdup("160");
1061                     }
1062                     if( !mixdowns )
1063                     {
1064                         mixdowns = strdup("dpl2");
1065                     }
1066                     if( !arates )
1067                     {
1068                         arates = strdup("Auto");
1069                     }
1070                     if( !dynamic_range_compression )
1071                     {
1072                         dynamic_range_compression = strdup("0.0");
1073                     }
1074                     maxWidth = 640;
1075                     if( !x264opts )
1076                     {
1077                         x264opts = strdup("level=30:bframes=0:cabac=0:ref=1:vbv-maxrate=1500:vbv-bufsize=2000:analyse=all:me=umh:no-fast-pskip=1:psy-rd=0,0:subme=6:8x8dct=0:trellis=0");
1078                     }
1079                     job->chapter_markers = 1;
1080                     
1081                 }
1082
1083             }
1084
1085                         if ( chapter_markers )
1086                         {
1087                                 job->chapter_markers = chapter_markers;
1088
1089                 if( marker_file != NULL )
1090                 {
1091                     hb_csv_file_t * file = hb_open_csv_file( marker_file );
1092                     hb_csv_cell_t * cell;
1093                     int row = 0;
1094                     int chapter = 0;
1095
1096                     fprintf( stderr, "Reading chapter markers from file %s\n", marker_file );
1097
1098                     if( file == NULL )
1099                     {
1100                          fprintf( stderr, "Cannot open chapter marker file, using defaults\n" );
1101                     }
1102                     else
1103                     {
1104                         /* Parse the cells */
1105                         while( NULL != ( cell = hb_read_next_cell( file ) ) )
1106                         {
1107                             /* We have a chapter number */
1108                             if( cell->cell_col == 0 )
1109                             {
1110                                 row = cell->cell_row;
1111                                 chapter = atoi( cell->cell_text );
1112                             }
1113
1114                             /* We have a chapter name */
1115                             if( cell->cell_col == 1 && row == cell->cell_row )
1116                             {
1117                                 /* If we have a valid chapter, copy the string an terminate it */
1118                                 if( chapter >= job->chapter_start && chapter <= job->chapter_end )
1119                                 {
1120                                     hb_chapter_t * chapter_s;
1121
1122                                     chapter_s = hb_list_item( job->title->list_chapter, chapter - 1);
1123                                     strncpy(chapter_s->title, cell->cell_text, 1023);
1124                                     chapter_s->title[1023] = '\0';
1125                                 }
1126                             }
1127
1128
1129                             hb_dispose_cell( cell );
1130                         }
1131
1132                         hb_close_csv_file( file );
1133                     }
1134                 }
1135                 else
1136                 {
1137                     /* No marker file */
1138
1139                     int number_of_chapters = hb_list_count(job->title->list_chapter);
1140                     int chapter;
1141
1142                     for(chapter = 0; chapter <= number_of_chapters - 1 ; chapter++)
1143                     {
1144                         hb_chapter_t * chapter_s;
1145                         chapter_s = hb_list_item( job->title->list_chapter, chapter);
1146                         snprintf( chapter_s->title, 1023, "Chapter %i", chapter + 1 );
1147                         chapter_s->title[1023] = '\0';
1148                     }
1149                 }
1150                         }
1151
1152             if( crop[0] >= 0 && crop[1] >= 0 &&
1153                 crop[2] >= 0 && crop[3] >= 0 )
1154             {
1155                 memcpy( job->crop, crop, 4 * sizeof( int ) );
1156             }
1157
1158             job->deinterlace = deinterlace;
1159             job->grayscale   = grayscale;
1160             
1161             /* Add selected filters */
1162             job->filters = hb_list_init();
1163             
1164             if( rotate )
1165             {
1166                 hb_filter_rotate.settings = rotate_opt;
1167                 hb_list_add( job->filters, &hb_filter_rotate);
1168             }
1169             if( detelecine )
1170             {
1171                 hb_filter_detelecine.settings = detelecine_opt;
1172                 hb_list_add( job->filters, &hb_filter_detelecine );
1173             }
1174             if( decomb )
1175             {
1176                 hb_filter_decomb.settings = decomb_opt;
1177                 hb_list_add( job->filters, &hb_filter_decomb );
1178             }
1179             if( deinterlace )
1180             {
1181                 hb_filter_deinterlace.settings = deinterlace_opt;
1182                 hb_list_add( job->filters, &hb_filter_deinterlace );
1183             }
1184             if( deblock )
1185             {
1186                 hb_filter_deblock.settings = deblock_opt;
1187                 hb_list_add( job->filters, &hb_filter_deblock );
1188             }
1189             if( denoise )
1190             {
1191                 hb_filter_denoise.settings = denoise_opt;
1192                 hb_list_add( job->filters, &hb_filter_denoise );
1193             }
1194
1195             switch( anamorphic_mode )
1196             {
1197                 case 0: // Non-anamorphic
1198                     
1199                     if( width && height )
1200                     {
1201                         job->width  = width;
1202                         job->height = height;
1203                     }
1204                     else if( width )
1205                     {
1206                         job->width = width;
1207                         hb_fix_aspect( job, HB_KEEP_WIDTH );
1208                     }
1209                     else if( height )
1210                     {
1211                         job->height = height;
1212                         hb_fix_aspect( job, HB_KEEP_HEIGHT );
1213                     }
1214                     else if( !width && !height )
1215                     {
1216                         hb_fix_aspect( job, HB_KEEP_WIDTH );
1217                     }
1218
1219                 break;
1220                 
1221                 case 1: // Strict anammorphic
1222                     job->anamorphic.mode = anamorphic_mode;
1223                 break;
1224                 
1225                 case 2: // Loose anamorphic
1226                     job->anamorphic.mode = 2;
1227                     
1228                     if (modulus)
1229                     {
1230                         job->modulus = modulus;
1231                     }
1232                     
1233                     if( itu_par )
1234                     {
1235                         job->anamorphic.itu_par = itu_par;
1236                     }
1237                     
1238                     if( width )
1239                     {
1240                         job->width = width;
1241                     }
1242                     else if( !width && !height )
1243                     {
1244                         /* Default to full width when one isn't specified for loose anamorphic */
1245                         job->width = title->width - job->crop[2] - job->crop[3];
1246                     }
1247                     
1248                 break;
1249                 
1250                 case 3: // Custom Anamorphic 3: Power User Jamboree 
1251                     job->anamorphic.mode = 3;
1252                     
1253                     if (modulus)
1254                     {
1255                         job->modulus = modulus;
1256                     }
1257                     
1258                     if( itu_par )
1259                     {
1260                         job->anamorphic.itu_par = itu_par;
1261                     }
1262                     
1263                     if( par_width && par_height )
1264                     {
1265                         job->anamorphic.par_width = par_width;
1266                         job->anamorphic.par_height = par_height;
1267                     }
1268                     
1269                     if( keep_display_aspect )
1270                     {
1271                         job->anamorphic.keep_display_aspect = 1;
1272                         
1273                         /* First, what *is* the display aspect? */
1274                         int cropped_width = title->width - job->crop[2] - job->crop[3];
1275                         int cropped_height = title->height - job->crop[0] - job->crop[1];
1276                         
1277                         /* XXX -- I'm assuming people want to keep the source
1278                            display AR even though they might have already
1279                            asked for ITU values instead. */
1280                         float source_display_width = (float)cropped_width *
1281                             (float)title->pixel_aspect_width / (float)title->pixel_aspect_height;
1282                         float display_aspect = source_display_width / (float)cropped_height;
1283                         /* When keeping display aspect, we have to rank some values
1284                            by priority in order to consistently handle situations
1285                            when more than one might be specified by default.
1286                            
1287                            * First off, PAR gets ignored. (err make this reality)
1288                            * Height will be respected over all other settings,
1289                            * If it isn't set, display_width will be followed.
1290                            * If it isn't set, width will be followed.          */
1291                         if( height )
1292                         {
1293                             /* We scale the display width to the new height */
1294                             display_width = (int)( (double)height * display_aspect );
1295                         }
1296                         else if( display_width )
1297                         {
1298                             /* We scale the height to the new display width */
1299                             height = (int)( (double)display_width / display_aspect );
1300                         }
1301                     }
1302                     
1303                     if( display_width )
1304                     {
1305                         /* Adjust the PAR to create the new display width
1306                            from the default job width. */
1307                         job->anamorphic.dar_width = display_width;
1308                         
1309                         job->anamorphic.dar_height = height ?
1310                                                         height :
1311                                                         title->height - job->crop[0] - job->crop[1];
1312                     }
1313                     
1314                     if( width && height )
1315                     {
1316                         /* Use these storage dimensions */
1317                         job->width  = width;
1318                         job->height = height;
1319                     }
1320                     else if( width )
1321                     {
1322                         /* Use just this storage width */
1323                         job->width = width;
1324                         job->height = title->height - job->crop[0] - job->crop[1];
1325                     }
1326                     else if( height )
1327                     {
1328                         /* Use just this storage height. */
1329                         job->height = height;
1330                         job->width = title->width - job->crop[2] - job->crop[3];
1331                     }
1332                     else if( !width && !height )
1333                     {
1334                         /* Assume source dimensions after cropping. */
1335                         job->width = title->width - job->crop[2] - job->crop[3];
1336                         job->height = title->height - job->crop[0] - job->crop[1];
1337                     }
1338                     
1339                 break;
1340             }
1341
1342             if( vquality >= 0.0 && ( ( vquality <= 1.0 ) || ( vcodec == HB_VCODEC_X264 ) || (vcodec == HB_VCODEC_FFMPEG) ) )
1343             {
1344                 job->vquality = vquality;
1345                 job->vbitrate = 0;
1346             }
1347             else if( vbitrate )
1348             {
1349                 job->vquality = -1.0;
1350                 job->vbitrate = vbitrate;
1351             }
1352             if( vcodec )
1353             {
1354                 job->vcodec = vcodec;
1355             }
1356             if( h264_13 )
1357             {
1358                 job->h264_level = 13;
1359             }
1360                 if( h264_30 )
1361                 {
1362                     job->h264_level = 30;
1363             }
1364             if( vrate )
1365             {
1366                 job->cfr = cfr;
1367                 job->vrate = 27000000;
1368                 job->vrate_base = vrate;
1369             }
1370             else if ( cfr )
1371             {
1372                 // cfr or pfr flag with no rate specified implies
1373                 // use the title rate.
1374                 job->cfr = cfr;
1375                 job->vrate = title->rate;
1376                 job->vrate_base = title->rate_base;
1377             }
1378
1379             /* Grab audio tracks */
1380             if( atracks )
1381             {
1382                 char * token = strtok(atracks, ",");
1383                 if (token == NULL)
1384                     token = optarg;
1385                 int track_start, track_end;
1386                 while( token != NULL )
1387                 {
1388                     audio = calloc(1, sizeof(*audio));
1389                     hb_audio_config_init(audio);
1390                     if (strlen(token) >= 3)
1391                     {
1392                         if (sscanf(token, "%d-%d", &track_start, &track_end) == 2)
1393                         {
1394                             int i;
1395                             for (i = track_start - 1; i < track_end; i++)
1396                             {
1397                                 if (i != track_start - 1)
1398                                 {
1399                                     audio = calloc(1, sizeof(*audio));
1400                                     hb_audio_config_init(audio);
1401                                 }
1402                                 audio->in.track = i;
1403                                 audio->out.track = num_audio_tracks++;
1404                                 hb_list_add(audios, audio);
1405                             }
1406                         }
1407                         else if( !strcasecmp(token, "none" ) )
1408                         {
1409                             audio->in.track = audio->out.track = -1;
1410                             audio->out.codec = 0;
1411                             hb_list_add(audios, audio);
1412                             break;
1413                         }
1414                         else
1415                         {
1416                             fprintf(stderr, "ERROR: Unable to parse audio input \"%s\", skipping.",
1417                                     token);
1418                             free(audio);
1419                         }
1420                     }
1421                     else
1422                     {
1423                         audio->in.track = atoi(token) - 1;
1424                         audio->out.track = num_audio_tracks++;
1425                         hb_list_add(audios, audio);
1426                     }
1427                     token = strtok(NULL, ",");
1428                 }
1429             }
1430
1431             /* Parse audio tracks */
1432             if( native_language && native_dub )
1433             {
1434                 if( hb_list_count( audios ) == 0 || !audio_explicit )
1435                 {
1436                     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
1437                     {
1438                         char audio_lang[4];
1439                         int track = i;
1440                         
1441                         audio = hb_list_audio_config_item( title->list_audio, i );
1442                         
1443                         strncpy( audio_lang, audio->lang.iso639_2, sizeof( audio_lang ) );
1444                         
1445                         if( strncasecmp( native_language, audio_lang, 
1446                                          sizeof( audio_lang ) ) == 0 &&
1447                             audio->lang.type != 3 && // Directors 1
1448                             audio->lang.type != 4)   // Directors 2
1449                         {
1450                             /*
1451                              * Matched an audio to our native language - use it.
1452                              * Replace any existing audio tracks that a preset may
1453                              * have put here.
1454                              */
1455                             if( hb_list_count(audios) == 0) {
1456                                 audio = calloc(1, sizeof(*audio));
1457                                 hb_audio_config_init(audio);
1458                                 audio->in.track = track;
1459                                 audio->out.track = num_audio_tracks++;
1460                                 /* Add it to our audios */
1461                                 hb_list_add(audios, audio);
1462                             } else {
1463                                 /*
1464                                  * Update the track numbers on what is already in
1465                                  * there.
1466                                  */
1467                                 for( i=0; i < hb_list_count( audios ); i++ )
1468                                 {
1469                                     audio = hb_list_item( audios, i );
1470
1471                                     audio->in.track = track;
1472                                 }
1473                             }
1474                             break;
1475                         }
1476                     }
1477                 } else {
1478                     fprintf( stderr, "Warning: Native language (dubbing) selection ignored since an audio track has already been selected\n");
1479                 }
1480             }
1481
1482             if( hb_list_count(audios) == 0 &&
1483                 hb_list_count(job->title->list_audio) > 0 )
1484             {        
1485                 /* Create a new audio track with default settings */
1486                 audio = calloc(1, sizeof(*audio));
1487                 hb_audio_config_init(audio);
1488                 /* Add it to our audios */
1489                 hb_list_add(audios, audio);
1490             }
1491
1492             tmp_num_audio_tracks = num_audio_tracks = hb_list_count(audios);
1493             for (i = 0; i < tmp_num_audio_tracks; i++)
1494             {
1495                 audio = hb_list_item(audios, 0);
1496                 if( (audio == NULL) || (audio->in.track == -1) ||
1497                     (audio->out.track == -1) || (audio->out.codec == 0) )
1498                 {
1499                     num_audio_tracks--;
1500                 }
1501                 else
1502                 {
1503                     if( hb_audio_add( job, audio ) == 0 )
1504                     {
1505                         fprintf(stderr, "ERROR: Invalid audio input track '%u', exiting.\n", 
1506                                 audio->in.track + 1 );
1507                         num_audio_tracks--;
1508                         exit(3);
1509                     }
1510                 }
1511                 hb_list_rem(audios, audio);
1512                 if( audio != NULL)
1513                     if( audio->out.name )
1514                     {
1515                         free( audio->out.name);
1516                     }
1517                     free( audio );
1518             }
1519
1520             /* Audio Codecs */
1521             i = 0;
1522             if( acodecs )
1523             {
1524                 char * token = strtok(acodecs, ",");
1525                 if( token == NULL )
1526                     token = acodecs;
1527                 while ( token != NULL )
1528                 {
1529                     if ((acodec = get_acodec_for_string(token)) == -1)
1530                     {
1531                         fprintf(stderr, "Invalid codec %s, using default for container.\n", token);
1532                         acodec = default_acodec;
1533                     }
1534                     if( i < num_audio_tracks )
1535                     {
1536                         audio = hb_list_audio_config_item(job->list_audio, i);
1537                         audio->out.codec = acodec;
1538                     }
1539                     else
1540                     {
1541                         hb_audio_config_t * last_audio = hb_list_audio_config_item( job->list_audio, i - 1 );
1542                         hb_audio_config_t audio;
1543
1544                         if( last_audio )
1545                         {
1546                             fprintf(stderr, "More audio codecs than audio tracks, copying track %i and using encoder %s\n",
1547                                     i, token);
1548                             hb_audio_config_init(&audio);
1549                             audio.in.track = last_audio->in.track;
1550                             audio.out.track = num_audio_tracks++;
1551                             audio.out.codec = acodec;
1552                             hb_audio_add(job, &audio);
1553                         }
1554                         else
1555                         {
1556                             fprintf(stderr, "Audio codecs and no valid audio tracks, skipping codec %s\n", token);
1557                         }
1558                     }
1559                     token = strtok(NULL, ",");
1560                     i++;
1561                 }
1562             }
1563             if( i < num_audio_tracks )
1564             {
1565                 /* We have fewer inputs than audio tracks, use the default codec for
1566                  * this container for the remaining tracks. Unless we only have one input
1567                  * then use that codec instead.
1568                  */
1569                 if (i != 1)
1570                     acodec = default_acodec;
1571                 for ( ; i < num_audio_tracks; i++)
1572                 {
1573                     audio = hb_list_audio_config_item(job->list_audio, i);
1574                     audio->out.codec = acodec;
1575                 }
1576             }
1577             /* Audio Codecs */
1578
1579             /* Sample Rate */
1580             i = 0;
1581             if( arates )
1582             {
1583                 char * token = strtok(arates, ",");
1584                 if (token == NULL)
1585                     token = arates;
1586                 while ( token != NULL )
1587                 {
1588                     arate = atoi(token);
1589                     audio = hb_list_audio_config_item(job->list_audio, i);
1590                     int j;
1591
1592                     for( j = 0; j < hb_audio_rates_count; j++ )
1593                     {
1594                         if( !strcmp( token, hb_audio_rates[j].string ) )
1595                         {
1596                             arate = hb_audio_rates[j].rate;
1597                             break;
1598                         }
1599                     }
1600
1601                     if( audio != NULL )
1602                     {
1603                         if (!is_sample_rate_valid(arate))
1604                         {
1605                             fprintf(stderr, "Invalid sample rate %d, using input rate %d\n", arate, audio->in.samplerate);
1606                             arate = audio->in.samplerate;
1607                         }
1608                         
1609                         audio->out.samplerate = arate;
1610                         if( (++i) >= num_audio_tracks )
1611                             break;  /* We have more inputs than audio tracks, oops */
1612                     }
1613                     else 
1614                     {
1615                         fprintf(stderr, "Ignoring sample rate %d, no audio tracks\n", arate);
1616                     }
1617                     token = strtok(NULL, ",");
1618                 }
1619             }
1620             if (i < num_audio_tracks)
1621             {
1622                 /* We have fewer inputs than audio tracks, use default sample rate.
1623                  * Unless we only have one input, then use that for all tracks.
1624                  */
1625                 if (i != 1)
1626                     arate = audio->in.samplerate;
1627                 for ( ; i < num_audio_tracks; i++)
1628                 {
1629                     audio = hb_list_audio_config_item(job->list_audio, i);
1630                     audio->out.samplerate = arate;
1631                 }
1632             }
1633             /* Sample Rate */
1634
1635             /* Audio Bitrate */
1636             i = 0;
1637             if( abitrates )
1638             {
1639                 char * token = strtok(abitrates, ",");
1640                 if (token == NULL)
1641                     token = abitrates;
1642                 while ( token != NULL )
1643                 {
1644                     abitrate = atoi(token);
1645                     audio = hb_list_audio_config_item(job->list_audio, i);
1646
1647                     if( audio != NULL )
1648                     {
1649                         audio->out.bitrate = abitrate;
1650                         if( (++i) >= num_audio_tracks )
1651                             break;  /* We have more inputs than audio tracks, oops */
1652                     }
1653                     else 
1654                     {
1655                         fprintf(stderr, "Ignoring bitrate %d, no audio tracks\n", abitrate);
1656                     }
1657                     token = strtok(NULL, ",");
1658                 }
1659             }
1660             if (i < num_audio_tracks)
1661             {
1662                 /* We have fewer inputs than audio tracks, use the default bitrate
1663                  * for the remaining tracks. Unless we only have one input, then use
1664                  * that for all tracks.
1665                  */
1666                 if (i != 1)
1667                     abitrate = default_abitrate;
1668                 for (; i < num_audio_tracks; i++)
1669                 {
1670                     audio = hb_list_audio_config_item(job->list_audio, i);
1671                     audio->out.bitrate = abitrate;
1672                 }
1673             }
1674             /* Audio Bitrate */
1675
1676             /* Audio DRC */
1677             i = 0;
1678             if ( dynamic_range_compression )
1679             {
1680                 char * token = strtok(dynamic_range_compression, ",");
1681                 if (token == NULL)
1682                     token = dynamic_range_compression;
1683                 while ( token != NULL )
1684                 {
1685                     d_r_c = atof(token);
1686                     audio = hb_list_audio_config_item(job->list_audio, i);
1687                     if( audio != NULL )
1688                     {
1689                         audio->out.dynamic_range_compression = d_r_c;
1690                         if( (++i) >= num_audio_tracks )
1691                             break;  /* We have more inputs than audio tracks, oops */
1692                     } 
1693                     else
1694                     {
1695                         fprintf(stderr, "Ignoring drc, no audio tracks\n");
1696                     }
1697                     token = strtok(NULL, ",");
1698                 }
1699             }
1700             if (i < num_audio_tracks)
1701             {
1702                 /* We have fewer inputs than audio tracks, use no DRC for the remaining
1703                  * tracks. Unless we only have one input, then use the same DRC for all
1704                  * tracks.
1705                  */
1706                 if (i != 1)
1707                     d_r_c = 0;
1708                 for (; i < num_audio_tracks; i++)
1709                 {
1710                     audio = hb_list_audio_config_item(job->list_audio, i);
1711                     audio->out.dynamic_range_compression = d_r_c;
1712                 }
1713             }
1714             /* Audio DRC */
1715
1716             /* Audio Mixdown */
1717             i = 0;
1718             if ( mixdowns )
1719             {
1720                 char * token = strtok(mixdowns, ",");
1721                 if (token == NULL)
1722                     token = mixdowns;
1723                 while ( token != NULL )
1724                 {
1725                     mixdown = hb_mixdown_get_mixdown_from_short_name(token);
1726                     audio = hb_list_audio_config_item(job->list_audio, i);
1727                     if( audio != NULL )
1728                     {
1729                         audio->out.mixdown = mixdown;
1730                         if( (++i) >= num_audio_tracks )
1731                             break;  /* We have more inputs than audio tracks, oops */
1732                     }
1733                     else
1734                     {
1735                         fprintf(stderr, "Ignoring mixdown, no audio tracks\n");
1736                     }
1737                     token = strtok(NULL, ",");
1738                 }
1739             }
1740             if (i < num_audio_tracks)
1741             {
1742                 /* We have fewer inputs than audio tracks, use DPLII for the rest. Unless
1743                  * we only have one input, then use that.
1744                  */
1745                 if (i != 1)
1746                     mixdown = HB_AMIXDOWN_DOLBYPLII;
1747                 for (; i < num_audio_tracks; i++)
1748                 {
1749                    audio = hb_list_audio_config_item(job->list_audio, i);
1750                    audio->out.mixdown = mixdown;
1751                 }
1752             }
1753             /* Audio Mixdown */
1754
1755             /* Audio Track Names */
1756             i = 0;
1757             if ( anames )
1758             {
1759                 char * token = strtok(anames, ",");
1760                 if (token == NULL)
1761                     token = anames;
1762                 while ( token != NULL )
1763                 {
1764                     audio = hb_list_audio_config_item(job->list_audio, i);
1765                     if( audio != NULL )
1766                     {
1767                         audio->out.name = strdup(token);
1768                         if( (++i) >= num_audio_tracks )
1769                             break;  /* We have more names than audio tracks, oops */
1770                     }
1771                     else
1772                     {
1773                         fprintf(stderr, "Ignoring aname '%s', no audio track\n",
1774                                 token);
1775                     }
1776                     token = strtok(NULL, ",");
1777                 }
1778             }
1779             if( i < num_audio_tracks && i == 1 )
1780             {
1781                 /* We have exactly one name and more than one audio track. Use the same
1782                  * name for all tracks. */
1783                 for ( ; i < num_audio_tracks; i++)
1784                 {
1785                     audio = hb_list_audio_config_item(job->list_audio, i);
1786                     audio->out.name = strdup(anames);
1787                 }
1788             }
1789             /* Audio Track Names */
1790
1791             if( size )
1792             {
1793                 job->vbitrate = hb_calc_bitrate( job, size );
1794                 fprintf( stderr, "Calculated bitrate: %d kbps\n",
1795                          job->vbitrate );
1796             }
1797
1798             if( subtracks )
1799             {
1800                 char * token;
1801                 int    i;
1802                 int    burnpos = 0, defaultpos = 0;
1803
1804                 if ( subburn )
1805                     burnpos = strtol( subburn, NULL, 0 );
1806                 if ( subdefault )
1807                     defaultpos = strtol( subdefault, NULL, 0 );
1808                 for ( i = 0; subtracks[i] != NULL; i++ )
1809                 {
1810                     token = subtracks[i];
1811                     if( strcasecmp(token, "scan" ) == 0 )
1812                     {
1813                         int burn = 0, force = 0, def = 0;
1814
1815                         if ( subburn != NULL )
1816                         {
1817                             burn = ( i == 0 && subburn[0] == 0 ) ||
1818                                    ( burnpos == i+1 );
1819                         }
1820                         if ( subdefault != NULL )
1821                         {
1822                             def =  ( i == 0 && subdefault[0] == 0 ) ||
1823                                    ( defaultpos == i+1 );
1824                         }
1825                         force = test_sub_list( subforce, i+1 );
1826
1827                         if ( !burn )
1828                         {
1829                             job->select_subtitle_config.dest = PASSTHRUSUB;
1830                         }
1831                         else
1832                         {
1833                             if ( sub_burned )
1834                             {
1835                                 continue;
1836                             }
1837                             sub_burned = 1;
1838                         }
1839                         job->select_subtitle_config.force = force;
1840                         job->select_subtitle_config.default_track = def;
1841                         subtitle_scan = 1;
1842                     }
1843                     else
1844                     {
1845                         hb_subtitle_t        * subtitle;
1846                         hb_subtitle_config_t   sub_config;
1847                         int                    track;
1848                         int                    burn = 0, force = 0, def = 0;
1849
1850                         track = atoi(token) - 1;
1851                         subtitle = hb_list_item(title->list_subtitle, track);
1852                         if( subtitle == NULL ) 
1853                         {
1854                             fprintf( stderr, "Warning: Could not find subtitle track %d, skipped\n", track+1 );
1855                             continue;
1856                         }
1857                         sub_config = subtitle->config;
1858
1859                         if ( subburn != NULL )
1860                         {
1861                             burn = ( i == 0 && subburn[0] == 0 ) ||
1862                                    ( burnpos == i+1 );
1863                         }
1864                         if ( subdefault != NULL )
1865                         {
1866                             def =  ( i == 0 && subdefault[0] == 0 ) ||
1867                                    ( defaultpos == i+1 );
1868                         }
1869
1870                         force = test_sub_list(subforce, i+1);
1871                         
1872                         int supports_burn =
1873                             ( subtitle->source == VOBSUB ) ||
1874                             ( subtitle->source == SSASUB );
1875                         
1876                         if ( burn && supports_burn )
1877                         {
1878                             // Only allow one subtitle to be burned into video
1879                             if ( sub_burned )
1880                             {
1881                                 fprintf( stderr, "Warning: Skipping subtitle track %d, can't have more than one track burnt in\n", track+1 );
1882                                 continue;
1883                             }
1884                             sub_burned = 1;
1885                             
1886                             // Mark as burn-in
1887                             sub_config.dest = RENDERSUB;
1888                         }
1889                         else
1890                         {
1891                             sub_config.dest = PASSTHRUSUB;
1892                         }
1893                         sub_config.force = force;
1894                         sub_config.default_track = def;
1895                         hb_subtitle_add( job, &sub_config, track );
1896                     }
1897                 }
1898             }
1899
1900             if( srtfile )
1901             {
1902                 char * token;
1903                 int i;
1904                 hb_subtitle_config_t sub_config;
1905
1906                 for( i=0; srtfile[i] != NULL; i++ )
1907                 {
1908                     char *codeset = "L1";
1909                     int64_t offset = 0;
1910                     char *lang = "und";
1911
1912                     token = srtfile[i];
1913                     if( srtcodeset && srtcodeset[i] )
1914                     {
1915                         codeset = srtcodeset[i];
1916                     }
1917                     if( srtoffset && srtoffset[i] )
1918                     {
1919                         offset = strtoll( srtoffset[i], &srtoffset[i], 0 );
1920                     }
1921                     if ( srtlang && srtlang[i] )
1922                     {
1923                         lang = srtlang[i];
1924                     }
1925                     sub_config.default_track = 
1926                            ( srtdefault != -1 ) && ( srtdefault == i + 1 );
1927                     sub_config.force = 0;
1928                     strncpy( sub_config.src_filename, srtfile[i], 128);
1929                     strncpy( sub_config.src_codeset, codeset, 40);
1930                     sub_config.offset = offset;
1931
1932                     hb_srt_add( job, &sub_config, lang);
1933                 }
1934             }
1935
1936             if( native_language )
1937             {
1938                 char audio_lang[4];
1939                 
1940                 audio = hb_list_audio_config_item(job->list_audio, 0);
1941                 
1942                 if( audio ) 
1943                 {
1944                     strncpy( audio_lang, audio->lang.iso639_2, sizeof( audio_lang ) );
1945                     
1946                     if( strncasecmp( native_language, audio_lang, 
1947                                      sizeof( audio_lang ) ) != 0 )
1948                     {
1949                         /*
1950                          * Audio language is not the same as our native language. 
1951                          * If we have any subtitles in our native language they
1952                          * should be selected here if they haven't already been.
1953                          */
1954                         hb_subtitle_t *subtitle, *subtitle2 = NULL;
1955                         int matched_track = 0;
1956
1957                         for( i = 0; i < hb_list_count( title->list_subtitle ); i++ )
1958                         {
1959                             subtitle = hb_list_item( title->list_subtitle, i );
1960                             matched_track = i;
1961                             if( strcmp( subtitle->iso639_2, native_language ) == 0 )
1962                             {  
1963                                 /*
1964                                  * Found the first matching subtitle in our
1965                                  * native language. Is it already selected?
1966                                  */
1967                                 for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
1968                                 {
1969                                     subtitle2 =  hb_list_item( job->list_subtitle, i );
1970                                     
1971                                     if( subtitle2->track == subtitle->track) {
1972                                         /*
1973                                          * Already selected
1974                                          */
1975                                         break;
1976                                     }
1977                                     subtitle2 = NULL;
1978                                 }
1979                                 
1980                                 if( subtitle2 == NULL ) 
1981                                 {
1982                                     /*
1983                                      * Not already selected, so select it.
1984                                      */
1985                                     hb_subtitle_config_t sub_config;
1986
1987                                     if( native_dub )
1988                                     {
1989                                         fprintf( stderr, "Warning: no matching audio for native language - using subtitles instead.\n");
1990                                     }
1991                                     sub_config = subtitle->config;
1992
1993                                     if( mux == HB_MUX_MKV || subtitle->format == TEXTSUB)
1994                                     {
1995                                         sub_config.dest = PASSTHRUSUB;
1996                                     }
1997
1998                                     sub_config.force = 0;
1999                                     sub_config.default_track = 1;
2000                                     hb_subtitle_add( job, &sub_config, matched_track);
2001                                 }
2002                                 /*
2003                                  * Stop searching.
2004                                  */
2005                                 break;
2006                             }
2007                         }
2008                     }
2009                 }
2010             }
2011
2012             if( job->mux )
2013             {
2014                 job->mux = mux;
2015             }
2016
2017             if ( largeFileSize )
2018             {
2019                 job->largeFileSize = 1;
2020             }
2021             if ( mp4_optimize )
2022             {
2023                 job->mp4_optimize = 1;
2024             }
2025             if ( ipod_atom )
2026             {
2027                 job->ipod_atom = 1;
2028             }
2029
2030             job->file = strdup( output );
2031
2032             if( color_matrix )
2033             {
2034                 job->color_matrix = color_matrix;
2035             }
2036
2037             if( x264opts != NULL && *x264opts != '\0' )
2038             {
2039                 job->x264opts = x264opts;
2040             }
2041             else /*avoids a bus error crash when options aren't specified*/
2042             {
2043                 job->x264opts =  NULL;
2044             }
2045             if (maxWidth)
2046                 job->maxWidth = maxWidth;
2047             if (maxHeight)
2048                 job->maxHeight = maxHeight;
2049
2050             if( start_at_preview )
2051             {
2052                 job->start_at_preview = start_at_preview - 1;
2053                 job->seek_points = preview_count;
2054             }
2055             
2056             if( stop_at_pts )
2057             {
2058                 job->pts_to_stop = stop_at_pts;
2059                 subtitle_scan = 0;
2060             }
2061             
2062             if( stop_at_frame )
2063             {
2064                 job->frame_to_stop = stop_at_frame;
2065                 subtitle_scan = 0;
2066             }
2067             
2068             if( start_at_pts )
2069             {
2070                 job->pts_to_start = start_at_pts;
2071                 subtitle_scan = 0;
2072             }
2073             
2074             if( start_at_frame )
2075             {
2076                 job->frame_to_start = start_at_frame;
2077                 subtitle_scan = 0;
2078             }
2079             
2080             if( subtitle_scan )
2081             {
2082                 char *x264opts_tmp;
2083
2084                 /*
2085                  * When subtitle scan is enabled do a fast pre-scan job
2086                  * which will determine which subtitles to enable, if any.
2087                  */
2088                 job->pass = -1;
2089
2090                 x264opts_tmp = job->x264opts;
2091
2092                 job->x264opts = NULL;
2093
2094                 job->indepth_scan = subtitle_scan;
2095                 fprintf( stderr, "Subtitle Scan Enabled - enabling "
2096                          "subtitles if found for foreign language segments\n");
2097
2098                 /*
2099                  * Add the pre-scan job
2100                  */
2101                 hb_add( h, job );
2102
2103                 job->x264opts = x264opts_tmp;
2104             }
2105
2106             if( twoPass )
2107             {
2108                 /*
2109                  * If subtitle_scan is enabled then only turn it on
2110                  * for the first pass and then off again for the
2111                  * second.
2112                  */
2113                 job->pass = 1;
2114
2115                 job->indepth_scan = 0;
2116
2117                 if (x264opts)
2118                 {
2119                     x264opts2 = strdup(x264opts);
2120                 }
2121
2122                 /*
2123                  * If turbo options have been selected then append them
2124                  * to the x264opts now (size includes one ':' and the '\0')
2125                  */
2126                 if( turbo_opts_enabled )
2127                 {
2128                     int size = (x264opts ? strlen(x264opts) : 0) + strlen(turbo_opts) + 2;
2129                     char *tmp_x264opts;
2130
2131                     tmp_x264opts = malloc(size * sizeof(char));
2132                     if( x264opts )
2133                     {
2134                         snprintf( tmp_x264opts, size, "%s:%s",
2135                                   x264opts, turbo_opts );
2136                         free( x264opts );
2137                     } else {
2138                         /*
2139                          * No x264opts to modify, but apply the turbo options
2140                          * anyway as they may be modifying defaults
2141                          */
2142                         snprintf( tmp_x264opts, size, "%s",
2143                                   turbo_opts );
2144                     }
2145                     x264opts = tmp_x264opts;
2146
2147                     fprintf( stderr, "Modified x264 options for pass 1 to append turbo options: %s\n",
2148                              x264opts );
2149
2150                     job->x264opts = x264opts;
2151                 }
2152                 hb_add( h, job );
2153
2154                 job->pass = 2;
2155                 /*
2156                  * On the second pass we turn off subtitle scan so that we
2157                  * can actually encode using any subtitles that were auto
2158                  * selected in the first pass (using the whacky select-subtitle
2159                  * attribute of the job).
2160                  */
2161                 job->indepth_scan = 0;
2162
2163                 job->x264opts = x264opts2;
2164
2165                 hb_add( h, job );
2166             }
2167             else
2168             {
2169                 /*
2170                  * Turn on subtitle scan if requested, note that this option
2171                  * precludes encoding of any actual subtitles.
2172                  */
2173
2174                 job->indepth_scan = 0;
2175                 job->pass = 0;
2176                 hb_add( h, job );
2177             }
2178             hb_start( h );
2179             break;
2180         }
2181
2182 #define p s.param.working
2183         case HB_STATE_SEARCHING:
2184             fprintf( stdout, "\rEncoding: task %d of %d, Searching for start time, %.2f %%",
2185                      p.job_cur, p.job_count, 100.0 * p.progress );
2186             if( p.seconds > -1 )
2187             {
2188                 fprintf( stdout, " (ETA %02dh%02dm%02ds)", 
2189                          p.hours, p.minutes, p.seconds );
2190             }
2191             fflush(stdout);
2192             break;
2193
2194         case HB_STATE_WORKING:
2195             fprintf( stdout, "\rEncoding: task %d of %d, %.2f %%",
2196                      p.job_cur, p.job_count, 100.0 * p.progress );
2197             if( p.seconds > -1 )
2198             {
2199                 fprintf( stdout, " (%.2f fps, avg %.2f fps, ETA "
2200                          "%02dh%02dm%02ds)", p.rate_cur, p.rate_avg,
2201                          p.hours, p.minutes, p.seconds );
2202             }
2203             fflush(stdout);
2204             break;
2205 #undef p
2206
2207 #define p s.param.muxing
2208         case HB_STATE_MUXING:
2209         {
2210             if (show_mux_warning)
2211             {
2212                 fprintf( stdout, "\rMuxing: this may take awhile..." );
2213                 fflush(stdout);
2214                 show_mux_warning = 0;
2215             }
2216             break;
2217         }
2218 #undef p
2219
2220 #define p s.param.workdone
2221         case HB_STATE_WORKDONE:
2222             /* Print error if any, then exit */
2223             switch( p.error )
2224             {
2225                 case HB_ERROR_NONE:
2226                     fprintf( stderr, "\nRip done!\n" );
2227                     break;
2228                 case HB_ERROR_CANCELED:
2229                     fprintf( stderr, "\nRip canceled.\n" );
2230                     break;
2231                 default:
2232                     fprintf( stderr, "\nRip failed (error %x).\n",
2233                              p.error );
2234             }
2235             die = 1;
2236             break;
2237 #undef p
2238     }
2239     return 0;
2240 }
2241
2242 /****************************************************************************
2243  * SigHandler:
2244  ****************************************************************************/
2245 static volatile int64_t i_die_date = 0;
2246 void SigHandler( int i_signal )
2247 {
2248     if( die == 0 )
2249     {
2250         die = 1;
2251         i_die_date = hb_get_date();
2252         fprintf( stderr, "Signal %d received, terminating - do it "
2253                  "again in case it gets stuck\n", i_signal );
2254     }
2255     else if( i_die_date + 500 < hb_get_date() )
2256     {
2257         fprintf( stderr, "Dying badly, files might remain in your /tmp\n" );
2258         exit( 1 );
2259     }
2260 }
2261
2262 /****************************************************************************
2263  * ShowHelp:
2264  ****************************************************************************/
2265 static void ShowHelp()
2266 {
2267     int i;
2268     FILE* const out = stdout;
2269
2270     fprintf( out,
2271     "Syntax: HandBrakeCLI [options] -i <device> -o <file>\n"
2272     "\n"
2273     "### General Handbrake Options------------------------------------------------\n\n"
2274     "    -h, --help              Print help\n"
2275     "    -u, --update            Check for updates and exit\n"
2276     "    -v, --verbose <#>       Be verbose (optional argument: logging level)\n"
2277     "    -C, --cpu               Set CPU count (default: autodetected)\n"
2278     "    -Z. --preset <string>   Use a built-in preset. Capitalization matters, and\n"
2279     "                            if the preset name has spaces, surround it with\n"
2280     "                            double quotation marks\n"
2281     "    -z, --preset-list       See a list of available built-in presets\n"
2282     "        --no-dvdnav         Do not use dvdnav for reading DVDs\n"
2283     "                            (experimental, enabled by default for testing)\n"
2284     "\n"
2285
2286     "### Source Options-----------------------------------------------------------\n\n"
2287     "    -i, --input <string>    Set input device\n"
2288     "    -t, --title <number>    Select a title to encode (0 to scan all titles only,\n"
2289     "                            default: 1)\n"
2290     "        --scan              Scan selected title only.\n"
2291     "        --main-feature      Detect and select the main feature title.\n"
2292     "    -c, --chapters <string> Select chapters (e.g. \"1-3\" for chapters\n"
2293     "                            1 to 3, or \"3\" for chapter 3 only,\n"
2294     "                            default: all chapters)\n"
2295     "        --angle <number>    Select the DVD angle\n"
2296     "        --previews <#:B>    Select how many preview images are generated (max 30),\n"
2297     "                            and whether or not they're stored to disk (0 or 1).\n"
2298     "                            (default: 10:0)\n"
2299     "    --start-at-preview <#>  Start encoding at a given preview.\n"
2300     "    --start-at    <unit:#>  Start encoding at a given frame, duration (in seconds),\n"
2301     "                            or pts (on a 90kHz clock)\n"
2302     "    --stop-at     <unit:#>  Stop encoding at a given frame, duration (in seconds),\n"
2303     "                            or pts (on a 90kHz clock)"
2304     "\n"
2305
2306     "### Destination Options------------------------------------------------------\n\n"
2307     "    -o, --output <string>   Set output file name\n"
2308     "    -f, --format <string>   Set output format (mp4/mkv, default:\n"
2309     "                            autodetected from file name)\n"
2310     "    -m, --markers           Add chapter markers (mp4 and mkv output formats only)\n"
2311     "    -4, --large-file        Use 64-bit mp4 files that can hold more than\n"
2312     "                            4 GB. Note: Breaks iPod, PS3 compatibility.\n"""
2313     "    -O, --optimize          Optimize mp4 files for HTTP streaming\n"
2314     "    -I, --ipod-atom         Mark mp4 files so 5.5G iPods will accept them\n"
2315     "\n"
2316
2317
2318     "### Video Options------------------------------------------------------------\n\n"
2319     "    -e, --encoder <string>  Set video library encoder (ffmpeg,x264,theora)\n"
2320     "                            (default: ffmpeg)\n"
2321     "    -x, --x264opts <string> Specify advanced x264 options in the\n"
2322     "                            same style as mencoder:\n"
2323     "                            option1=value1:option2=value2\n"
2324     "    -q, --quality <number>  Set video quality\n"
2325     "    -S, --size <MB>         Set target size\n"
2326     "    -b, --vb <kb/s>         Set video bitrate (default: 1000)\n"
2327     "    -2, --two-pass          Use two-pass mode\n"
2328     "    -T, --turbo             When using 2-pass use the turbo options\n"
2329     "                            on the first pass to improve speed\n"
2330     "                            (only works with x264, affects PSNR by about 0.05dB,\n"
2331     "                            and increases first pass speed two to four times)\n"
2332     "    -r, --rate              Set video framerate (" );
2333     for( i = 0; i < hb_video_rates_count; i++ )
2334     {
2335         fprintf( out, hb_video_rates[i].string );
2336         if( i != hb_video_rates_count - 1 )
2337             fprintf( out, "/" );
2338     }
2339     fprintf( out, ")\n"
2340     "                            Be aware that not specifying a framerate lets\n"
2341     "                            HandBrake preserve a source's time stamps,\n"
2342     "                            potentially creating variable framerate video\n"
2343     "    --vfr, --cfr, --pfr     Select variable, constant or peak-limited\n"
2344     "                            frame rate control. VFR preserves the source\n"
2345     "                            timing. CFR makes the output constant rate at\n"
2346     "                            the rate given by the -r flag (or the source's\n"
2347     "                            average rate if no -r is given). PFR doesn't\n"
2348     "                            allow the rate to go over the rate specified\n"
2349     "                            with the -r flag but won't change the source\n"
2350     "                            timing if it's below that rate.\n"
2351     "                            If none of these flags are given, the default\n"
2352     "                            is --cfr when -r is given and --vfr otherwise\n"
2353
2354     "\n"
2355     "### Audio Options-----------------------------------------------------------\n\n"
2356     "    -a, --audio <string>    Select audio track(s), separated by commas\n"
2357     "                            More than one output track can be used for one\n"
2358     "                            input.\n"
2359     "                            (\"none\" for no audio, \"1,2,3\" for multiple\n"
2360     "                             tracks, default: first one)\n" );
2361
2362 #ifdef __APPLE_CC__
2363     fprintf( out,
2364     "    -E, --aencoder <string> Audio encoder(s)\n"
2365     "                                (ca_aac/faac/lame/vorbis/ac3/ac3pass/dtspass)\n"
2366     "                            ac3pass and dtspass meaning passthrough\n"
2367     "                            Separated by commas for more than one audio track.\n"
2368     "                            (default: guessed)\n" );
2369 #else
2370     fprintf( out,
2371     "    -E, --aencoder <string> Audio encoder(s):\n"
2372     "                                (faac/lame/vorbis/ac3/ac3pass/dtspass)\n"
2373     "                            ac3pass and dtspass meaning passthrough\n"
2374     "                            Separated by commas for more than one audio track.\n"
2375     "                            (default: guessed)\n" );
2376 #endif
2377     fprintf( out,
2378     "    -B, --ab <kb/s>         Set audio bitrate(s)  (default: 160)\n"
2379     "                            Separated by commas for more than one audio track.\n"
2380     "    -6, --mixdown <string>  Format(s) for surround sound downmixing\n"
2381     "                            Separated by commas for more than one audio track.\n"
2382     "                            (mono/stereo/dpl1/dpl2/6ch, default: dpl2)\n"
2383     "    -R, --arate             Set audio samplerate(s) (" );
2384     for( i = 0; i < hb_audio_rates_count; i++ )
2385     {
2386         fprintf( out, hb_audio_rates[i].string );
2387         if( i != hb_audio_rates_count - 1 )
2388             fprintf( out, "/" );
2389     }
2390     fprintf( out, " kHz)\n"
2391     "                            Separated by commas for more than one audio track.\n"
2392     "    -D, --drc <float>       Apply extra dynamic range compression to the audio,\n"
2393     "                            making soft sounds louder. Range is 1.0 to 4.0\n"
2394     "                            (too loud), with 1.5 - 2.5 being a useful range.\n"
2395     "                            Separated by commas for more than one audio track.\n"
2396     "    -A, --aname <string>    Audio track name(s),\n"
2397     "                            Separated by commas for more than one audio track.\n"
2398     "\n"
2399
2400     "### Picture Settings---------------------------------------------------------\n\n"
2401     "    -w, --width <number>    Set picture width\n"
2402     "    -l, --height <number>   Set picture height\n"
2403     "        --crop <T:B:L:R>    Set cropping values (default: autocrop)\n"
2404     "    -Y, --maxHeight <#>     Set maximum height\n"
2405     "    -X, --maxWidth <#>      Set maximum width\n"
2406     "    --strict-anamorphic     Store pixel aspect ratio in video stream\n"
2407     "    --loose-anamorphic      Store pixel aspect ratio with specified width\n"
2408     "    --custom-anamorphic     Store pixel aspect ratio in video stream and\n"
2409     "                            directly control all parameters.\n"
2410     "    --display-width         Set the width to scale the actual pixels to\n"
2411     "      <number>              at playback, for custom anamorphic.\n"
2412     "    --keep-display-aspect   Preserve the source's display aspect ratio\n"
2413     "                            when using custom anamorphic\n"
2414     "    --pixel-aspect          Set a custom pixel aspect for custom anamorphic\n"
2415     "      <PARX:PARY>\n"
2416     "                            (--display-width and --pixel-aspect are mutually\n"
2417     "                             exclusive and the former will override the latter)\n"
2418     "    --itu-par               Use wider, ITU pixel aspect values for loose and\n"
2419     "                            custom anamorphic, useful with underscanned sources\n"
2420     "    --modulus               Set the number you want the scaled pixel dimensions\n"
2421     "      <number>              to divide cleanly by. Does not affect strict\n"
2422     "                            anamorphic mode, which is always mod 2 (default: 16)\n"
2423     "    -M  --color-matrix      Set the color space signaled by the output\n"
2424     "          <601 or 709>      (Bt.601 is mostly for SD content, Bt.709 for HD,\n"
2425     "                             default: set by resolution)\n"
2426     "\n"
2427
2428     "### Filters---------------------------------------------------------\n\n"
2429
2430      "    -d, --deinterlace       Deinterlace video with yadif/mcdeint filter\n"
2431      "          <YM:FD:MM:QP>     (default 0:-1:-1:1)\n"
2432      "           or\n"
2433      "          <fast/slow/slower>\n"
2434      "    -5, --decomb            Selectively deinterlaces when it detects combing\n"
2435      "          <MO:ME:MT:ST:BT:BX:BY:MG:VA:LA:DI:ER:NO:MD:PP:FD>\n"
2436      "          (default: 7:2:6:9:80:16:16:10:20:20:4:2:50:24:1:-1)\n"
2437      "    -9, --detelecine        Detelecine (ivtc) video with pullup filter\n"
2438      "                            Note: this filter drops duplicate frames to\n"
2439      "                            restore the pre-telecine framerate, unless you\n"
2440      "                            specify a constant framerate (--rate 29.97)\n"
2441      "          <L:R:T:B:SB:MP:FD>   (default 1:1:4:4:0:0:-1)\n"
2442      "    -8, --denoise           Denoise video with hqdn3d filter\n"
2443      "          <SL:SC:TL:TC>     (default 4:3:6:4.5)\n"
2444      "           or\n"
2445      "          <weak/medium/strong>\n"
2446      "    -7, --deblock           Deblock video with pp7 filter\n"
2447      "          <QP:M>            (default 5:2)\n"
2448      "        --rotate            Flips images axes\n"
2449      "          <M>               (default 3)\n"
2450     "    -g, --grayscale         Grayscale encoding\n"
2451     "\n"
2452
2453     "### Subtitle Options------------------------------------------------------------\n\n"
2454     "    -s, --subtitle <string> Select subtitle track(s), separated by commas\n"
2455     "                            More than one output track can be used for one\n"
2456     "                            input.\n"
2457     "                            Example: \"1,2,3\" for multiple tracks.\n"
2458     "                            A special track name \"scan\" adds an extra 1st pass.\n"
2459     "                            This extra pass scans subtitles matching the\n"
2460     "                            language of the first audio or the language \n"
2461     "                            selected by --native-language.\n"
2462     "                            The one that's only used 10 percent of the time\n"
2463     "                            or less is selected. This should locate subtitles\n"
2464     "                            for short foreign language segments. Best used in\n"
2465     "                            conjunction with --subtitle-forced.\n"
2466     "    -F, --subtitle-forced   Only display subtitles from the selected stream if\n"
2467     "          <string>          the subtitle has the forced flag set. The values in\n"
2468     "                            \"string\" are indexes into the subtitle list\n"
2469     "                            specified with '--subtitle'.\n"
2470     "                            Separated by commas for more than one audio track.\n"
2471     "                            Example: \"1,2,3\" for multiple tracks.\n"
2472     "                            If \"string\" is omitted, the first trac is forced.\n"
2473     "        --subtitle-burn     \"Burn\" the selected subtitle into the video track\n"
2474     "          <number>          If \"number\" is omitted, the first trac is burned.\n"
2475     "                            \"number\" is an index into the subtitle list\n"
2476     "                            specified with '--subtitle'.\n"
2477     "        --subtitle-default  Flag the selected subtitle as the default subtitle\n"
2478     "          <number>          to be displayed upon playback.  Setting no default\n"
2479     "                            means no subtitle will be automatically displayed\n"
2480     "                            If \"number\" is omitted, the first trac is default.\n"
2481     "                            \"number\" is an index into the subtitle list\n"
2482     "                            specified with '--subtitle'.\n"
2483     "    -N, --native-language   Specifiy the your language preference. When the first\n"
2484     "          <string>          audio track does not match your native language then\n"
2485     "                            select the first subtitle that does. When used in\n"
2486     "                            conjunction with --native-dub the audio track is\n"
2487     "                            changed in preference to subtitles. Provide the\n"
2488     "                            language's iso639-2 code (fre, eng, spa, dut, et cetera)\n"
2489     "        --native-dub        Used in conjunction with --native-language\n"
2490     "                            requests that if no audio tracks are selected the\n"
2491     "                            default selected audio track will be the first one\n"
2492     "                            that matches the --native-language. If there are no\n"
2493     "                            matching audio tracks then the first matching\n"
2494     "                            subtitle track is used instead.\n"
2495     "        --srt-file <string> SubRip SRT filename(s), separated by commas.\n"
2496     "        --srt-codeset       Character codeset(s) that the SRT file(s) are\n"
2497     "          <string>          encoded in, separted by commas.\n"
2498     "                            Use 'iconv -l' for a list of valid\n"
2499     "                            codesets. If not specified latin1 is assumed\n"
2500     "        --srt-offset        Offset in milli-seconds to apply to the SRT file(s)\n"
2501     "          <string>          separted by commas. If not specified zero is assumed.\n"
2502     "                            Offsets may be negative.\n"
2503     "        --srt-lang <string> Language as an iso639-2 code fra, eng, spa et cetera)\n"
2504     "                            for the SRT file(s) separated by commas. If not specified\n"
2505     "                            then 'und' is used.\n"
2506     "        --srt-default       Flag the selected srt as the default subtitle\n"
2507     "          <number>          to be displayed upon playback.  Setting no default\n"
2508     "                            means no subtitle will be automatically displayed\n"
2509     "                            If \"number\" is omitted, the first srt is default.\n"
2510     "                            \"number\" is an 1 based index into the srt-file list\n"
2511     "\n"
2512
2513
2514     );
2515 }
2516
2517 /****************************************************************************
2518  * ShowPresets:
2519  ****************************************************************************/
2520 static void ShowPresets()
2521 {
2522     printf("\n< Apple\n");
2523
2524     printf("\n   + Universal:  -e x264  -q 20.0 -a 1,1 -E faac,ac3pass -B 160,160 -6 dpl2,auto -R Auto,Auto -D 0.0,0.0 -f mp4 -X 720 --loose-anamorphic -m -x cabac=0:ref=2:me=umh:bframes=0:8x8dct=0:trellis=0:subme=6\n");
2525
2526     printf("\n   + iPod:  -e x264  -b 700 -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 -f mp4 -I -X 320 -m -x level=30:bframes=0:cabac=0:ref=1:vbv-maxrate=768:vbv-bufsize=2000:analyse=all:me=umh:no-fast-pskip=1:subme=6:8x8dct=0:trellis=0\n");
2527
2528     printf("\n   + iPhone & iPod Touch:  -e x264  -q 20.0 -a 1 -E faac -B 128 -6 dpl2 -R Auto -D 0.0 -f mp4 -X 480 -m -x cabac=0:ref=2:me=umh:bframes=0:subme=6:8x8dct=0:trellis=0\n");
2529
2530     printf("\n   + iPad:  -e x264  -q 20.0 -r 29.97 --pfr  -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 -f mp4 -4 -X 1024 --loose-anamorphic -m\n");
2531
2532     printf("\n   + AppleTV:  -e x264  -q 20.0 -a 1,1 -E faac,ac3pass -B 160,160 -6 dpl2,auto -R Auto,Auto -D 0.0,0.0 -f mp4 -4 -X 960 --loose-anamorphic -m -x cabac=0:ref=2:me=umh:b-pyramid=none:b-adapt=2:weightb=0:trellis=0:weightp=0:vbv-maxrate=9500:vbv-bufsize=9500\n");
2533
2534     printf("\n>\n");
2535
2536     printf("\n< Regular\n");
2537
2538     printf("\n   + Normal:  -e x264  -q 20.0 -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 -f mp4 --strict-anamorphic -m -x ref=2:bframes=2:subme=6:mixed-refs=0:weightb=0:8x8dct=0:trellis=0\n");
2539
2540     printf("\n   + High Profile:  -e x264  -q 20.0 -a 1,1 -E faac,ac3pass -B 160,160 -6 dpl2,auto -R Auto,Auto -D 0.0,0.0 -f mp4 --detelecine --decomb --loose-anamorphic -m -x b-adapt=2:rc-lookahead=50\n");
2541
2542     printf("\n>\n");
2543
2544     printf("\n< Legacy\n");
2545
2546     printf("\n   + Classic:  -b 1000 -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 -f mp4\n");
2547
2548     printf("\n   + AppleTV Legacy:  -e x264  -b 2500 -a 1,1 -E faac,ac3pass -B 160,160 -6 dpl2,auto -R Auto,Auto -D 0.0,0.0 -f mp4 -4 --strict-anamorphic -m -x ref=1:b-pyramid=none:subme=5:me=umh:no-fast-pskip=1:cabac=0:weightb=0:8x8dct=0:trellis=0\n");
2549
2550     printf("\n   + iPhone Legacy:  -e x264  -b 960 -a 1 -E faac -B 128 -6 dpl2 -R Auto -D 0.0 -f mp4 -I -X 480 -m -x level=30:cabac=0:ref=1:analyse=all:me=umh:no-fast-pskip=1:psy-rd=0,0:bframes=0:subme=6:8x8dct=0:trellis=0\n");
2551
2552     printf("\n   + iPod Legacy:  -e x264  -b 1500 -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 -f mp4 -I -X 640 -m -x level=30:bframes=0:cabac=0:ref=1:vbv-maxrate=1500:vbv-bufsize=2000:analyse=all:me=umh:no-fast-pskip=1:psy-rd=0,0:subme=6:8x8dct=0:trellis=0\n");
2553
2554     printf("\n>\n");
2555
2556 }
2557
2558 static char * hb_strndup( char * str, int len )
2559 {
2560         char * res;
2561         int str_len = strlen( str );
2562
2563         res = malloc( len > str_len ? str_len + 1 : len + 1 );
2564         strncpy( res, str, len );
2565         res[len] = '\0';
2566         return res;
2567 }
2568
2569 static char** str_split( char *str, char delem )
2570 {
2571     char *  pos;
2572     char *  end;
2573     char ** ret;
2574     int     count, i;
2575
2576     if ( str == NULL || str[0] == 0 )
2577     {
2578         ret = malloc( sizeof(char*) );
2579         *ret = NULL;
2580         return ret;
2581     }
2582
2583     // Find number of elements in the string
2584     count = 1;
2585     pos = str;
2586     while ( ( pos = strchr( pos, delem ) ) != NULL )
2587     {
2588         count++;
2589         pos++;
2590     }
2591
2592     ret = calloc( ( count + 1 ), sizeof(char*) );
2593
2594     pos = str;
2595     for ( i = 0; i < count - 1; i++ )
2596     {
2597         end = strchr( pos, delem );
2598         ret[i] = hb_strndup(pos, end - pos);
2599         pos = end + 1;
2600     }
2601     ret[i] = strdup(pos);
2602
2603     return ret;
2604 }
2605
2606 /****************************************************************************
2607  * ParseOptions:
2608  ****************************************************************************/
2609 static int ParseOptions( int argc, char ** argv )
2610 {
2611     
2612     #define PREVIEWS            257
2613     #define START_AT_PREVIEW    258
2614     #define START_AT            259
2615     #define STOP_AT             260
2616     #define ANGLE               261
2617     #define DVDNAV              262
2618     #define DISPLAY_WIDTH       263
2619     #define PIXEL_ASPECT        264
2620     #define MODULUS             265
2621     #define KEEP_DISPLAY_ASPECT 266
2622     #define SUB_BURNED          267
2623     #define SUB_DEFAULT         268
2624     #define NATIVE_DUB          269
2625     #define SRT_FILE            270
2626     #define SRT_CODESET         271
2627     #define SRT_OFFSET          272
2628     #define SRT_LANG            273
2629     #define SRT_DEFAULT         274
2630     #define ROTATE_FILTER       275
2631     #define SCAN_ONLY           276
2632     #define MAIN_FEATURE        277
2633     #define MIN_DURATION        278
2634     
2635     for( ;; )
2636     {
2637         static struct option long_options[] =
2638           {
2639             { "help",        no_argument,       NULL,    'h' },
2640             { "update",      no_argument,       NULL,    'u' },
2641             { "verbose",     optional_argument, NULL,    'v' },
2642             { "cpu",         required_argument, NULL,    'C' },
2643             { "no-dvdnav",      no_argument,       NULL,    DVDNAV },
2644
2645             { "format",      required_argument, NULL,    'f' },
2646             { "input",       required_argument, NULL,    'i' },
2647             { "output",      required_argument, NULL,    'o' },
2648             { "large-file",  no_argument,       NULL,    '4' },
2649             { "optimize",    no_argument,       NULL,    'O' },
2650             { "ipod-atom",   no_argument,       NULL,    'I' },
2651
2652             { "title",       required_argument, NULL,    't' },
2653             { "min-duration",required_argument, NULL,    MIN_DURATION },
2654             { "scan",        no_argument,       NULL,    SCAN_ONLY },
2655             { "main-feature",no_argument,       NULL,    MAIN_FEATURE },
2656             { "chapters",    required_argument, NULL,    'c' },
2657             { "angle",       required_argument, NULL,    ANGLE },
2658             { "markers",     optional_argument, NULL,    'm' },
2659             { "audio",       required_argument, NULL,    'a' },
2660             { "mixdown",     required_argument, NULL,    '6' },
2661             { "drc",         required_argument, NULL,    'D' },
2662             { "subtitle",    required_argument, NULL,    's' },
2663             { "subtitle-forced", optional_argument,   NULL,    'F' },
2664             { "subtitle-burned", optional_argument,   NULL,    SUB_BURNED },
2665             { "subtitle-default", optional_argument,   NULL,    SUB_DEFAULT },
2666             { "srt-file",    required_argument, NULL, SRT_FILE },
2667             { "srt-codeset", required_argument, NULL, SRT_CODESET },
2668             { "srt-offset",  required_argument, NULL, SRT_OFFSET },
2669             { "srt-lang",    required_argument, NULL, SRT_LANG },
2670             { "srt-default",    optional_argument, NULL, SRT_DEFAULT },
2671             { "native-language", required_argument, NULL,'N' },
2672             { "native-dub",  no_argument,       NULL,    NATIVE_DUB },
2673             { "encoder",     required_argument, NULL,    'e' },
2674             { "aencoder",    required_argument, NULL,    'E' },
2675             { "two-pass",    no_argument,       NULL,    '2' },
2676             { "deinterlace", optional_argument, NULL,    'd' },
2677             { "deblock",     optional_argument, NULL,    '7' },
2678             { "denoise",     optional_argument, NULL,    '8' },
2679             { "detelecine",  optional_argument, NULL,    '9' },
2680             { "decomb",      optional_argument, NULL,    '5' },
2681             { "grayscale",   no_argument,       NULL,    'g' },
2682             { "rotate",      optional_argument, NULL,   ROTATE_FILTER },
2683             { "strict-anamorphic",  no_argument, &anamorphic_mode, 1 },
2684             { "loose-anamorphic", no_argument, &anamorphic_mode, 2 },
2685             { "custom-anamorphic", no_argument, &anamorphic_mode, 3 },
2686             { "display-width", required_argument, NULL, DISPLAY_WIDTH },
2687             { "keep-display-aspect", no_argument, &keep_display_aspect, 1 },
2688             { "pixel-aspect", required_argument, NULL, PIXEL_ASPECT },
2689             { "modulus",     required_argument, NULL, MODULUS },
2690             { "itu-par",     no_argument,       &itu_par, 1  },
2691             { "width",       required_argument, NULL,    'w' },
2692             { "height",      required_argument, NULL,    'l' },
2693             { "crop",        required_argument, NULL,    'n' },
2694
2695             { "vb",          required_argument, NULL,    'b' },
2696             { "quality",     required_argument, NULL,    'q' },
2697             { "size",        required_argument, NULL,    'S' },
2698             { "ab",          required_argument, NULL,    'B' },
2699             { "rate",        required_argument, NULL,    'r' },
2700             { "arate",       required_argument, NULL,    'R' },
2701             { "x264opts",    required_argument, NULL,    'x' },
2702             { "turbo",       no_argument,       NULL,    'T' },
2703             { "maxHeight",   required_argument, NULL,    'Y' },
2704             { "maxWidth",    required_argument, NULL,    'X' },
2705             { "preset",      required_argument, NULL,    'Z' },
2706             { "preset-list", no_argument,       NULL,    'z' },
2707
2708             { "aname",       required_argument, NULL,    'A' },
2709             { "color-matrix",required_argument, NULL,    'M' },
2710             { "previews",    required_argument, NULL,    PREVIEWS },
2711             { "start-at-preview", required_argument, NULL, START_AT_PREVIEW },
2712             { "start-at",    required_argument, NULL,    START_AT },
2713             { "stop-at",    required_argument, NULL,     STOP_AT },
2714             { "vfr",         no_argument,       &cfr,    0 },
2715             { "cfr",         no_argument,       &cfr,    1 },
2716             { "pfr",         no_argument,       &cfr,    2 },
2717 #if defined( __APPLE_CC__ )
2718             { "no-vlc-dylib-path", no_argument, &no_vlc_dylib,    1 },
2719 #endif
2720             { 0, 0, 0, 0 }
2721           };
2722
2723         int option_index = 0;
2724         int c;
2725         int cur_optind;
2726
2727         cur_optind = optind;
2728         c = getopt_long( argc, argv,
2729                          "hv::uC:f:4i:Io:t:c:m::M:a:A:6:s:UF::N:e:E:"
2730                          "2dD:7895gOw:l:n:b:q:S:B:r:R:x:TY:X:Z:z",
2731                          long_options, &option_index );
2732         if( c < 0 )
2733         {
2734             break;
2735         }
2736
2737         switch( c )
2738         {
2739             case 0:
2740                 /* option was handled entirely in getopt_long */
2741                 break;
2742             case 'h':
2743                 ShowHelp();
2744                 exit( 0 );
2745             case 'u':
2746                 update = 1;
2747                 break;
2748             case 'v':
2749                 if( optarg != NULL )
2750                 {
2751                     debug = atoi( optarg );
2752                 }
2753                 else
2754                 {
2755                     debug = 1;
2756                 }
2757                 break;
2758             case 'C':
2759                 cpu = atoi( optarg );
2760                 break;
2761
2762             case 'Z':
2763                 preset = 1;
2764                 preset_name = strdup(optarg);
2765                 break;
2766             case 'z':
2767                 ShowPresets();
2768                 exit ( 0 );
2769             case DVDNAV:
2770                 dvdnav = 0;
2771                 break;
2772
2773             case 'f':
2774                 format = strdup( optarg );
2775                 break;
2776             case 'i':
2777                 input = strdup( optarg );
2778 #ifdef __APPLE_CC__
2779                 char *devName = bsd_name_for_path( input ); // alloc
2780                 if( devName )
2781                 {
2782                     if( device_is_dvd( devName ))
2783                     {
2784                         free( input );
2785                         input = malloc( strlen( "/dev/" ) + strlen( devName ) + 1 );
2786                         sprintf( input, "/dev/%s", devName );
2787                     }
2788                     free( devName );
2789                 }
2790 #endif
2791                 break;
2792             case 'o':
2793                 output = strdup( optarg );
2794                 break;
2795             case '4':
2796                 largeFileSize = 1;
2797                 break;
2798             case 'O':
2799                 mp4_optimize = 1;
2800                 break;
2801             case 'I':
2802                 ipod_atom = 1;
2803                 break;
2804
2805             case 't':
2806                 titleindex = atoi( optarg );
2807                 break;
2808             case SCAN_ONLY:
2809                 titlescan = 1;
2810                 break;
2811             case MAIN_FEATURE:
2812                 main_feature = 1;
2813                 break;
2814             case 'c':
2815             {
2816                 int start, end;
2817                 if( sscanf( optarg, "%d-%d", &start, &end ) == 2 )
2818                 {
2819                     chapter_start = start;
2820                     chapter_end   = end;
2821                 }
2822                 else if( sscanf( optarg, "%d", &start ) == 1 )
2823                 {
2824                     chapter_start = start;
2825                     chapter_end   = chapter_start;
2826                 }
2827                 else
2828                 {
2829                     fprintf( stderr, "chapters: invalid syntax (%s)\n",
2830                              optarg );
2831                     return -1;
2832                 }
2833                 break;
2834             }
2835             case ANGLE:
2836                 angle = atoi( optarg );
2837                 break;
2838             case 'm':
2839                 if( optarg != NULL )
2840                 {
2841                     marker_file = strdup( optarg );
2842                 }
2843                 chapter_markers = 1;
2844                 break;
2845             case 'a':
2846                 if( optarg != NULL )
2847                 {
2848                     atracks = strdup( optarg );
2849                     audio_explicit = 1;
2850                 }
2851                 else
2852                 {
2853                     atracks = "1" ;
2854                 }
2855                 break;
2856             case '6':
2857                 if( optarg != NULL )
2858                 {
2859                     mixdowns = strdup( optarg );
2860                 }
2861                 break;
2862             case 'D':
2863                 if( optarg != NULL )
2864                 {
2865                     dynamic_range_compression = strdup( optarg );
2866                 }
2867                 break;
2868             case 's':
2869                 subtracks = str_split( optarg, ',' );
2870                 break;
2871             case 'F':
2872                 subforce = str_split( optarg, ',' );
2873                 break;
2874             case SUB_BURNED:
2875                 if( optarg != NULL )
2876                 {
2877                     subburn = strdup( optarg );
2878                 }
2879                 else
2880                 {
2881                     subburn = "" ;
2882                 }
2883                 break;
2884             case SUB_DEFAULT:
2885                 if( optarg != NULL )
2886                 {
2887                     subdefault = strdup( optarg );
2888                 }
2889                 else
2890                 {
2891                     subdefault = "" ;
2892                 }
2893                 break;
2894             case 'N':
2895                 native_language = strdup( optarg );
2896                 break;
2897             case NATIVE_DUB:
2898                 native_dub = 1;
2899                 break;
2900             case SRT_FILE:
2901                 srtfile = str_split( optarg, ',' );
2902                 break;
2903             case SRT_CODESET:
2904                 srtcodeset = str_split( optarg, ',' );
2905                 break;
2906             case SRT_OFFSET:
2907                 srtoffset = str_split( optarg, ',' );
2908                 break;
2909             case SRT_LANG:
2910                 srtlang = str_split( optarg, ',' );
2911                 break;
2912             case SRT_DEFAULT:
2913                 if( optarg != NULL )
2914                 {
2915                     srtdefault = atoi( optarg );
2916                 }
2917                 else
2918                 {
2919                     srtdefault = 1 ;
2920                 }
2921                 break;
2922             case '2':
2923                 twoPass = 1;
2924                 break;
2925             case 'd':
2926                 if( optarg != NULL )
2927                 {
2928                     if (!( strcmp( optarg, "fast" ) ))
2929                     {
2930                         deinterlace_opt = "-1";
2931                     }
2932                     else if (!( strcmp( optarg, "slow" ) ))
2933                     {
2934                         deinterlace_opt = "2";
2935                     }
2936                     else if (!( strcmp( optarg, "slower" ) ))
2937                     {
2938                         deinterlace_opt = "0";
2939                     }
2940                     else
2941                     {
2942                         deinterlace_opt = strdup( optarg );
2943                     }
2944                 }
2945                 deinterlace = 1;
2946                 break;
2947             case '7':
2948                 if( optarg != NULL )
2949                 {
2950                     deblock_opt = strdup( optarg );
2951                 }
2952                 deblock = 1;
2953                 break;
2954             case '8':
2955                 if( optarg != NULL )
2956                 {
2957                     if (!( strcmp( optarg, "weak" ) ))
2958                     {
2959                         denoise_opt = "2:1:2:3";
2960                     }
2961                     else if (!( strcmp( optarg, "medium" ) ))
2962                     {
2963                         denoise_opt = "3:2:2:3";
2964                     }
2965                     else if (!( strcmp( optarg, "strong" ) ))
2966                     {
2967                         denoise_opt = "7:7:5:5";
2968                     }
2969                     else
2970                     {
2971                         denoise_opt = strdup( optarg );
2972                     }
2973                 }
2974                 denoise = 1;
2975                 break;
2976             case '9':
2977                 if( optarg != NULL )
2978                 {
2979                     detelecine_opt = strdup( optarg );
2980                 }
2981                 detelecine = 1;
2982                 break;
2983             case '5':
2984                 if( optarg != NULL )
2985                 {
2986                     decomb_opt = strdup( optarg );
2987                 }
2988                 decomb = 1;
2989                 break;
2990             case 'g':
2991                 grayscale = 1;
2992                 break;
2993             case ROTATE_FILTER:
2994                 if( optarg != NULL )
2995                 {
2996                     rotate_opt = strdup( optarg );
2997                 }
2998                 rotate = 1;
2999                 break;
3000             case DISPLAY_WIDTH:
3001                 if( optarg != NULL )
3002                 {
3003                     sscanf( optarg, "%i", &display_width );
3004                 }
3005                 break;
3006             case PIXEL_ASPECT:
3007                 if( optarg != NULL )
3008                 {
3009                     sscanf( optarg, "%i:%i", &par_width, &par_height );
3010                 }
3011                 break;
3012             case MODULUS:
3013                 if( optarg != NULL )
3014                 {
3015                     sscanf( optarg, "%i", &modulus );
3016                 }
3017                 break;
3018             case 'e':
3019                 if( !strcasecmp( optarg, "ffmpeg" ) )
3020                 {
3021                     vcodec = HB_VCODEC_FFMPEG;
3022                 }
3023                 else if( !strcasecmp( optarg, "x264" ) )
3024                 {
3025                     vcodec = HB_VCODEC_X264;
3026                 }
3027                 else if( !strcasecmp( optarg, "x264b13" ) )
3028                 {
3029                     vcodec = HB_VCODEC_X264;
3030                     h264_13 = 1;
3031                 }
3032                 else if( !strcasecmp( optarg, "x264b30" ) )
3033                 {
3034                     vcodec = HB_VCODEC_X264;
3035                     h264_30 = 1;
3036                 }
3037                 else if( !strcasecmp( optarg, "theora" ) )
3038                 {
3039                     vcodec = HB_VCODEC_THEORA;
3040                 }
3041                 else
3042                 {
3043                     fprintf( stderr, "invalid codec (%s)\n", optarg );
3044                     return -1;
3045                 }
3046                 break;
3047             case 'E':
3048                 if( optarg != NULL )
3049                 {
3050                     acodecs = strdup( optarg );
3051                 }
3052                 break;
3053             case 'w':
3054                 width = atoi( optarg );
3055                 break;
3056             case 'l':
3057                 height = atoi( optarg );
3058                 break;
3059             case 'n':
3060             {
3061                 int    i;
3062                 char * tmp = optarg;
3063                 for( i = 0; i < 4; i++ )
3064                 {
3065                     if( !*tmp )
3066                         break;
3067                     crop[i] = strtol( tmp, &tmp, 0 );
3068                     tmp++;
3069                 }
3070                 break;
3071             }
3072             case 'r':
3073             {
3074                 int i;
3075                 vrate = 0;
3076                 for( i = 0; i < hb_video_rates_count; i++ )
3077                 {
3078                     if( !strcmp( optarg, hb_video_rates[i].string ) )
3079                     {
3080                         vrate = hb_video_rates[i].rate;
3081                         break;
3082                     }
3083                 }
3084                 if( !vrate )
3085                 {
3086                     fprintf( stderr, "invalid framerate %s\n", optarg );
3087                 }
3088                 else if ( cfr == 0 )
3089                 {
3090                     cfr = 1;
3091                 }
3092                 break;
3093             }
3094             case 'R':
3095                 if( optarg != NULL )
3096                 {
3097                     arates = strdup( optarg );
3098                 }
3099                 break;
3100             case 'b':
3101                 vbitrate = atoi( optarg );
3102                 break;
3103             case 'q':
3104                 vquality = atof( optarg );
3105                 break;
3106             case 'S':
3107                 size = atoi( optarg );
3108                 break;
3109             case 'B':
3110                 if( optarg != NULL )
3111                 {
3112                     abitrates = strdup( optarg );
3113                 }
3114                 break;
3115             case 'x':
3116                 x264opts = strdup( optarg );
3117                 break;
3118             case 'T':
3119                 turbo_opts_enabled = 1;
3120                 break;
3121             case 'Y':
3122                 maxHeight = atoi( optarg );
3123                 break;
3124             case 'X':
3125                 maxWidth = atoi (optarg );
3126                 break;
3127             case 'A':
3128                 if( optarg != NULL )
3129                 {
3130                     anames = strdup( optarg );
3131                 }
3132                 break;
3133             case PREVIEWS:
3134                 sscanf( optarg, "%i:%i", &preview_count, &store_previews );
3135                 break;
3136             case START_AT_PREVIEW:
3137                 start_at_preview = atoi( optarg );
3138                 break;
3139             case START_AT:
3140                 start_at_string = strdup( optarg );
3141                 start_at_token = strtok( start_at_string, ":");
3142                 if( !strcmp( start_at_token, "frame" ) )
3143                 {
3144                     start_at_token = strtok( NULL, ":");
3145                     start_at_frame = atoi(start_at_token);
3146                 }
3147                 else if( !strcmp( start_at_token, "pts" ) )
3148                 {
3149                     start_at_token = strtok( NULL, ":");
3150                     sscanf( start_at_token, "%"SCNd64, &start_at_pts );
3151                 }
3152                 else if( !strcmp( start_at_token, "duration" ) )
3153                 {
3154                     start_at_token = strtok( NULL, ":");
3155                     sscanf( start_at_token, "%"SCNd64, &start_at_pts );
3156                     start_at_pts *= 90000LL;
3157                 }
3158                 break;
3159             case STOP_AT:
3160                 stop_at_string = strdup( optarg );
3161                 stop_at_token = strtok( stop_at_string, ":");
3162                 if( !strcmp( stop_at_token, "frame" ) )
3163                 {
3164                     stop_at_token = strtok( NULL, ":");
3165                     stop_at_frame = atoi(stop_at_token);
3166                 }
3167                 else if( !strcmp( stop_at_token, "pts" ) )
3168                 {
3169                     stop_at_token = strtok( NULL, ":");
3170                     sscanf( stop_at_token, "%"SCNd64, &stop_at_pts );
3171                 }
3172                 else if( !strcmp( stop_at_token, "duration" ) )
3173                 {
3174                     stop_at_token = strtok( NULL, ":");
3175                     sscanf( stop_at_token, "%"SCNd64, &stop_at_pts );
3176                     stop_at_pts *= 90000LL;
3177                 }
3178                 break;
3179             case 'M':
3180                 if( atoi( optarg ) == 601 )
3181                     color_matrix = 1;
3182                 else if( atoi( optarg ) == 709 )
3183                     color_matrix = 2;
3184                 break;
3185             case MIN_DURATION:
3186                 min_title_duration = strtol( optarg, NULL, 0 );
3187                 break;
3188             default:
3189                 fprintf( stderr, "unknown option (%s)\n", argv[cur_optind] );
3190                 return -1;
3191         }
3192     }
3193
3194     return 0;
3195 }
3196
3197 char * str_printf(const char *fmt, ...)
3198 {
3199     /* Guess we need no more than 100 bytes. */
3200     int len;
3201     va_list ap;
3202     int size = 100;
3203     char *tmp, *str = NULL;
3204
3205     str = (char*)malloc(size);
3206     while (1) 
3207     {
3208         /* Try to print in the allocated space. */
3209         va_start(ap, fmt);
3210         len = vsnprintf(str, size, fmt, ap);
3211         va_end(ap);
3212
3213         /* If that worked, return the string. */
3214         if (len > -1 && len < size) {
3215             return str;
3216         }
3217
3218         /* Else try again with more space. */
3219         if (len > -1)    /* glibc 2.1 */
3220             size = len+1; /* precisely what is needed */
3221         else           /* glibc 2.0 */
3222             size *= 2;  /* twice the old size */
3223
3224         tmp = (char*)realloc(str, size);
3225         if (tmp == NULL) {
3226             return str;
3227         }
3228         str = tmp;
3229     }
3230 }
3231
3232 static int CheckOptions( int argc, char ** argv )
3233 {
3234 #if defined( __APPLE_CC__ )
3235     // If OSX, add VLC dylib path and exec to make it stick.
3236     char *dylib_path;
3237
3238     if ( !no_vlc_dylib )
3239     {
3240         dylib_path = getenv("DYLD_FALLBACK_LIBRARY_PATH");
3241         if ( dylib_path == NULL ||
3242              strstr( dylib_path, "/Applications/VLC.app/Contents/MacOS/lib" ) == NULL )
3243         {
3244             char *path = NULL;
3245             char *home;
3246             int result = -1;
3247
3248             home = getenv("HOME");
3249
3250             if ( dylib_path == NULL )
3251             {
3252                 // Set the system default of $HOME/lib:/usr/local/lib:/usr/lib
3253                 // And add our extra path
3254                 if ( home != NULL )
3255                 {
3256                     path = str_printf("%s/lib:%s:%s:%s%s", home, 
3257                                       DEFAULT_DYLD_PATH, 
3258                                       EXTRA_VLC_DYLD_PATH, 
3259                                       home, EXTRA_VLC_DYLD_PATH);
3260                 }
3261                 else
3262                 {
3263                     path = str_printf("%s:%s", DEFAULT_DYLD_PATH, EXTRA_VLC_DYLD_PATH);
3264                 }
3265                 if ( path != NULL )
3266                     result = setenv("DYLD_FALLBACK_LIBRARY_PATH", path, 1);
3267             }
3268             else
3269             {
3270                 // add our extra path
3271                 if ( home != NULL )
3272                 {
3273                     path = str_printf("%s:%s:%s%s", dylib_path, EXTRA_VLC_DYLD_PATH,
3274                                                         home, EXTRA_VLC_DYLD_PATH);
3275                 }
3276                 else
3277                 {
3278                     path = str_printf("%s:%s", dylib_path, EXTRA_VLC_DYLD_PATH);
3279                 }
3280                 if ( path != NULL )
3281                     result = setenv("DYLD_FALLBACK_LIBRARY_PATH", path, 1);
3282             }
3283             if ( result == 0 )
3284             {
3285                 const char ** new_argv;
3286                 int i;
3287
3288                 new_argv = (const char**)malloc( (argc + 2) * sizeof(char*) );
3289                 new_argv[0] = argv[0];
3290                 new_argv[1] = "--no-vlc-dylib-path";
3291                 for (i = 1; i < argc; i++)
3292                     new_argv[i+1] = argv[i];
3293                 new_argv[i+1] = NULL;
3294                 execv(new_argv[0], (char* const*)new_argv);
3295             }
3296         }
3297     }
3298 #endif
3299
3300     if( update )
3301     {
3302         return 0;
3303     }
3304
3305     if( input == NULL || *input == '\0' )
3306     {
3307         fprintf( stderr, "Missing input device. Run %s --help for "
3308                  "syntax.\n", argv[0] );
3309         return 1;
3310     }
3311
3312     /* Parse format */
3313     if( titleindex > 0 && !titlescan )
3314     {
3315         if( output == NULL || *output == '\0' )
3316         {
3317             fprintf( stderr, "Missing output file name. Run %s --help "
3318                      "for syntax.\n", argv[0] );
3319             return 1;
3320         }
3321
3322         if( !format )
3323         {
3324             char * p = strrchr( output, '.' );
3325
3326             /* autodetect */
3327             if( p && ( !strcasecmp( p, ".mp4" )  ||
3328                             !strcasecmp( p, ".m4v" ) ) )
3329             {
3330                 if ( h264_30 == 1 )
3331                     mux = HB_MUX_IPOD;
3332                 else
3333                     mux = HB_MUX_MP4;
3334                 default_acodec = HB_ACODEC_FAAC;
3335             }
3336             else if( p && !strcasecmp(p, ".mkv" ) )
3337             {
3338                 mux = HB_MUX_MKV;
3339                 default_acodec = HB_ACODEC_AC3;
3340             }
3341             else
3342             {
3343                 fprintf( stderr, "Output format couldn't be guessed "
3344                          "from file name, using default.\n" );
3345                 return 0;
3346             }
3347         }
3348         else if( !strcasecmp( format, "mp4" ) ||
3349                  !strcasecmp( format, "m4v" ) )
3350         {
3351             if ( h264_30 == 1)
3352                 mux = HB_MUX_IPOD;
3353             else
3354                 mux = HB_MUX_MP4;
3355             default_acodec = HB_ACODEC_FAAC;
3356         }
3357         else if( !strcasecmp( format, "mkv" ) )
3358         {
3359             mux = HB_MUX_MKV;
3360             default_acodec = HB_ACODEC_AC3;
3361         }
3362         else
3363         {
3364             fprintf( stderr, "Invalid output format (%s). Possible "
3365                      "choices are mp4, m4v and mkv\n.", format );
3366             return 1;
3367         }
3368     }
3369
3370     return 0;
3371 }
3372
3373 static int get_acodec_for_string( char *codec )
3374 {
3375     if( !strcasecmp( codec, "ac3" ) )
3376     {
3377         return HB_ACODEC_AC3;
3378     }
3379     else if( !strcasecmp( codec, "ac3pass" ) )
3380     {
3381         return HB_ACODEC_AC3_PASS;
3382     }
3383     else if( !strcasecmp( codec, "dtspass" ) || !strcasecmp( codec, "dcapass" ) )
3384     {
3385         return HB_ACODEC_DCA_PASS;
3386     }
3387     else if( !strcasecmp( codec, "lame" ) )
3388     {
3389         return HB_ACODEC_LAME;
3390     }
3391     else if( !strcasecmp( codec, "faac" ) )
3392     {
3393         return HB_ACODEC_FAAC;
3394     }
3395     else if( !strcasecmp( codec, "vorbis") )
3396     {
3397         return HB_ACODEC_VORBIS;
3398     }
3399 #ifdef __APPLE__
3400     else if( !strcasecmp( codec, "ca_aac") )
3401     {
3402         return HB_ACODEC_CA_AAC;
3403     }
3404 #endif
3405     else
3406     {
3407         return -1;
3408     }
3409 }
3410
3411 static int is_sample_rate_valid(int rate)
3412 {
3413     int i;
3414     for( i = 0; i < hb_audio_rates_count; i++ )
3415     {
3416             if (rate == hb_audio_rates[i].rate)
3417                 return 1;
3418     }
3419     return 0;
3420 }
3421
3422 #ifdef __APPLE_CC__
3423 /****************************************************************************
3424  * bsd_name_for_path
3425  *
3426  * Returns the BSD device name for the block device that contains the
3427  * passed-in path. Returns NULL on failure.
3428  ****************************************************************************/
3429 static char* bsd_name_for_path(char *path)
3430 {
3431     OSStatus err;
3432     FSRef ref;
3433     err = FSPathMakeRef( (const UInt8 *) input, &ref, NULL );
3434     if( err != noErr )
3435     {
3436         return NULL;
3437     }
3438
3439     // Get the volume reference number.
3440     FSCatalogInfo catalogInfo;
3441     err = FSGetCatalogInfo( &ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL,
3442                             NULL);
3443     if( err != noErr )
3444     {
3445         return NULL;
3446     }
3447     FSVolumeRefNum volRefNum = catalogInfo.volume;
3448
3449     // Now let's get the device name
3450     GetVolParmsInfoBuffer volumeParms;
3451     err = FSGetVolumeParms( volRefNum, &volumeParms, sizeof( volumeParms ) );
3452     if( err != noErr )
3453     {
3454         return NULL;
3455     }
3456
3457     // A version 4 GetVolParmsInfoBuffer contains the BSD node name in the vMDeviceID field.
3458     // It is actually a char * value. This is mentioned in the header CoreServices/CarbonCore/Files.h.
3459     if( volumeParms.vMVersion < 4 )
3460     {
3461         return NULL;
3462     }
3463
3464     // vMDeviceID might be zero as is reported with experimental ZFS (zfs-119) support in Leopard.
3465     if( !volumeParms.vMDeviceID )
3466     {
3467         return NULL;
3468     }
3469
3470     return strdup( volumeParms.vMDeviceID );
3471 }
3472
3473 /****************************************************************************
3474  * device_is_dvd
3475  *
3476  * Returns whether or not the passed in BSD device represents a DVD, or other
3477  * optical media.
3478  ****************************************************************************/
3479 static int device_is_dvd(char *device)
3480 {
3481     io_service_t service = get_iokit_service(device);
3482     if( service == IO_OBJECT_NULL )
3483     {
3484         return 0;
3485     }
3486     int result = is_dvd_service(service);
3487     IOObjectRelease(service);
3488     return result;
3489 }
3490
3491 /****************************************************************************
3492  * get_iokit_service
3493  *
3494  * Returns the IOKit service object for the passed in BSD device name.
3495  ****************************************************************************/
3496 static io_service_t get_iokit_service( char *device )
3497 {
3498     CFMutableDictionaryRef matchingDict;
3499     matchingDict = IOBSDNameMatching( kIOMasterPortDefault, 0, device );
3500     if( matchingDict == NULL )
3501     {
3502         return IO_OBJECT_NULL;
3503     }
3504     // Fetch the object with the matching BSD node name. There should only be
3505     // one match, so IOServiceGetMatchingService is used instead of
3506     // IOServiceGetMatchingServices to simplify the code.
3507     return IOServiceGetMatchingService( kIOMasterPortDefault, matchingDict );
3508 }
3509
3510 /****************************************************************************
3511  * is_dvd_service
3512  *
3513  * Returns whether or not the service passed in is a DVD.
3514  *
3515  * Searches for an IOMedia object that represents the entire (whole) media that
3516  * the volume is on. If the volume is on partitioned media, the whole media
3517  * object will be a parent of the volume's media object. If the media is not
3518  * partitioned, the volume's media object will be the whole media object.
3519  ****************************************************************************/
3520 static int is_dvd_service( io_service_t service )
3521 {
3522     kern_return_t  kernResult;
3523     io_iterator_t  iter;
3524
3525     // Create an iterator across all parents of the service object passed in.
3526     kernResult = IORegistryEntryCreateIterator( service,
3527                                                 kIOServicePlane,
3528                                                 kIORegistryIterateRecursively | kIORegistryIterateParents,
3529                                                 &iter );
3530     if( kernResult != KERN_SUCCESS )
3531     {
3532         return 0;
3533     }
3534     if( iter == IO_OBJECT_NULL )
3535     {
3536         return 0;
3537     }
3538
3539     // A reference on the initial service object is released in the do-while
3540     // loop below, so add a reference to balance.
3541     IOObjectRetain( service );
3542
3543     int result = 0;
3544     do
3545     {
3546         if( is_whole_media_service( service ) &&
3547             IOObjectConformsTo( service, kIODVDMediaClass) )
3548         {
3549             result = 1;
3550         }
3551         IOObjectRelease( service );
3552     } while( !result && (service = IOIteratorNext( iter )) );
3553     IOObjectRelease( iter );
3554
3555     return result;
3556 }
3557
3558 /****************************************************************************
3559  * is_whole_media_service
3560  *
3561  * Returns whether or not the service passed in is an IOMedia service and
3562  * represents the "whole" media instead of just a partition.
3563  *
3564  * The whole media object is indicated in the IORegistry by the presence of a
3565  * property with the key "Whole" and value "Yes".
3566  ****************************************************************************/
3567 static int is_whole_media_service( io_service_t service )
3568 {
3569     int result = 0;
3570
3571     if( IOObjectConformsTo( service, kIOMediaClass ) )
3572     {
3573         CFTypeRef wholeMedia = IORegistryEntryCreateCFProperty( service,
3574                                                                 CFSTR( kIOMediaWholeKey ),
3575                                                                 kCFAllocatorDefault,
3576                                                                 0 );
3577         if ( !wholeMedia )
3578         {
3579             return 0;
3580         }
3581         result = CFBooleanGetValue( (CFBooleanRef)wholeMedia );
3582         CFRelease( wholeMedia );
3583     }
3584
3585     return result;
3586 }
3587 #endif // __APPLE_CC__