OSDN Git Service

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