From 9c0e97cc2c369e0c720441d182bbde20210742f4 Mon Sep 17 00:00:00 2001 From: jstebbins Date: Wed, 6 May 2015 16:04:08 +0000 Subject: libhb,cli: add preset management to libhb, use it in cli This results in custom preset support in the CLI and additional command line options to fully support all preset keys. git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@7158 b64f7644-9d1e-0410-96f1-a4d463321fa5 --- test/test.c | 6811 ++++++++++++++++++++++++++++------------------------------- 1 file changed, 3260 insertions(+), 3551 deletions(-) (limited to 'test') diff --git a/test/test.c b/test/test.c index a3b18a227..d489ffd08 100644 --- a/test/test.c +++ b/test/test.c @@ -42,112 +42,117 @@ #endif /* Options */ -static int debug = HB_DEBUG_ALL; -static int update = 0; -static int dvdnav = 1; -static char * input = NULL; -static char * output = NULL; -static char * format = NULL; -static int titleindex = 1; -static int titlescan = 0; -static int main_feature = 0; -static char * native_language = NULL; -static int native_dub = 0; -static int twoPass = 0; -static int deinterlace = 0; -static char * deinterlace_opt = 0; -static int deblock = 0; -static char * deblock_opt = 0; -static int denoise = 0; -static char * denoise_opt = 0; -static int nlmeans = 0; -static char * nlmeans_opt = NULL; -static char * nlmeans_tune_opt = NULL; -static int detelecine = 0; -static char * detelecine_opt = 0; -static int decomb = 0; -static char * decomb_opt = 0; -static int rotate = 0; -static char * rotate_opt = 0; -static int rotate_val = 0; -static int grayscale = 0; -static int vcodec = HB_VCODEC_FFMPEG_MPEG4; -static hb_list_t * audios = NULL; -static hb_audio_config_t * audio = NULL; -static int num_audio_tracks = 0; -static int allowed_audio_copy = -1; -static char * mixdowns = NULL; -static char * dynamic_range_compression = NULL; -static char * audio_gain = NULL; -static char ** audio_dither = NULL; -static char ** normalize_mix_level = NULL; -static char * atracks = NULL; -static char * arates = NULL; -static char ** abitrates = NULL; -static char ** aqualities = NULL; -static char ** acompressions = NULL; -static char * acodec_fallback = NULL; -static char * acodecs = NULL; -static char ** anames = NULL; -static int audio_explicit = 0; -static char ** subtracks = NULL; -static char ** subforce = NULL; -static char * subburn = NULL; -static char * subdefault = NULL; -static char ** srtfile = NULL; -static char ** srtcodeset = NULL; -static char ** srtoffset = NULL; -static char ** srtlang = NULL; -static int srtdefault = -1; -static int srtburn = -1; -static int subtitle_scan = 0; -static int width = 0; -static int height = 0; -static int crop[4] = { -1,-1,-1,-1 }; -static int loose_crop = -1; -static int vrate = 0; -static float vquality = -1.0; -static int vbitrate = 0; -static int mux = 0; -static int anamorphic_mode = 0; -static int modulus = 0; -static int par_height = 0; -static int par_width = 0; -static int display_width = 0; -static int keep_display_aspect = 0; -static int itu_par = 0; -static int angle = 0; -static int chapter_start = 0; -static int chapter_end = 0; -static int chapter_markers = 0; -static char * marker_file = NULL; -static char * x264_preset = NULL; -static char * x264_tune = NULL; -static char * advanced_opts = NULL; -static char * h264_profile = NULL; -static char * h264_level = NULL; -static int maxHeight = 0; -static int maxWidth = 0; -static int fastfirstpass = 0; -static int preset = 0; -static char * preset_name = 0; -static int cfr = 0; -static int mp4_optimize = 0; -static int ipod_atom = 0; -static int color_matrix_code = 0; -static int preview_count = 10; -static int store_previews = 0; -static int start_at_preview = 0; -static int64_t start_at_pts = 0; -static int start_at_frame = 0; -static int64_t stop_at_pts = 0; -static int stop_at_frame = 0; +static int debug = HB_DEBUG_ALL; +static int update = 0; +static int dvdnav = 1; +static char * input = NULL; +static char * output = NULL; +static char * format = NULL; +static int titleindex = 1; +static int titlescan = 0; +static int main_feature = 0; +static char * native_language = NULL; +static int native_dub = 0; +static int twoPass = 0; +static int deinterlace_disable = 0; +static int deinterlace_custom = 0; +static char * deinterlace = NULL; +static int deblock_disable = 0; +static char * deblock = NULL; +static int hqdn3d_disable = 0; +static int hqdn3d_custom = 0; +static char * hqdn3d = NULL; +static int nlmeans_disable = 0; +static int nlmeans_custom = 0; +static char * nlmeans = NULL; +static char * nlmeans_tune = NULL; +static int detelecine_disable = 0; +static int detelecine_custom = 0; +static char * detelecine = NULL; +static int decomb_disable = 0; +static int decomb_custom = 0; +static char * decomb = NULL; +static char * rotate = NULL; +static int grayscale = -1; +static char * vcodec = NULL; +static int audio_all = -1; +static char ** audio_copy_list = NULL; +static char ** audio_lang_list = NULL; +static char ** atracks = NULL; +static char ** acodecs = NULL; +static char ** abitrates = NULL; +static char ** aqualities = NULL; +static char ** arates = NULL; +static char ** mixdowns = NULL; +static char ** normalize_mix_level = NULL; +static char ** audio_dither = NULL; +static char ** dynamic_range_compression = NULL; +static char ** audio_gain = NULL; +static char ** acompressions = NULL; +static char * acodec_fallback = NULL; +static char ** anames = NULL; +static char ** subtitle_lang_list = NULL; +static char ** subtracks = NULL; +static char ** subforce = NULL; +static int subtitle_all = -1; +static int subburn = 0; +static int subburn_native = 0; +static int subdefault = 0; +static char ** srtfile = NULL; +static char ** srtcodeset = NULL; +static char ** srtoffset = NULL; +static char ** srtlang = NULL; +static int srtdefault = -1; +static int srtburn = -1; +static int width = 0; +static int height = 0; +static int crop[4] = { -1,-1,-1,-1 }; +static int loose_crop = -1; +static char * vrate = NULL; +static float vquality = -1.0; +static int vbitrate = 0; +static int mux = 0; +static int anamorphic_mode = -1; +static int modulus = 0; +static int par_height = -1; +static int par_width = -1; +static int display_width = -1; +static int keep_display_aspect = 0; +static int itu_par = -1; +static int angle = 0; +static int chapter_start = 0; +static int chapter_end = 0; +static int chapter_markers = -1; +static char * marker_file = NULL; +static char * encoder_preset = NULL; +static char * encoder_tune = NULL; +static char * encoder_profile = NULL; +static char * encoder_level = NULL; +static char * advanced_opts = NULL; +static int maxHeight = 0; +static int maxWidth = 0; +static int fastfirstpass = 0; +static char * preset_export_name = NULL; +static char * preset_export_desc = NULL; +static char * preset_export_file = NULL; +static char * preset_name = NULL; +static int cfr = -1; +static int mp4_optimize = -1; +static int ipod_atom = -1; +static int color_matrix_code = -1; +static int preview_count = 10; +static int store_previews = 0; +static int start_at_preview = 0; +static int64_t start_at_pts = 0; +static int start_at_frame = 0; +static int64_t stop_at_pts = 0; +static int stop_at_frame = 0; static uint64_t min_title_duration = 10; -static int use_opencl = 0; -static int use_hwd = 0; +static int use_opencl = -1; +static int use_hwd = -1; #ifdef USE_QSV -static int qsv_async_depth = -1; -static int qsv_decode = 1; +static int qsv_async_depth = -1; +static int qsv_decode = -1; #endif /* Exit cleanly on Ctrl-C */ @@ -157,7 +162,6 @@ static void SigHandler( int ); /* Utils */ static void ShowHelp(); -static void ShowPresets(); static void ShowCommands() { fprintf(stdout, "\nCommands:\n"); @@ -167,10 +171,14 @@ static void ShowCommands() fprintf(stdout, " [r]esume Resume encoding\n"); } -static int ParseOptions( int argc, char ** argv ); -static int CheckOptions( int argc, char ** argv ); -static int HandleEvents( hb_handle_t * h ); +static int ParseOptions( int argc, char ** argv ); +static int CheckOptions( int argc, char ** argv ); +static int HandleEvents( hb_handle_t * h, hb_dict_t *preset_dict ); +static hb_dict_t * PreparePreset( const char *preset_name ); +static hb_dict_t * PrepareJob( hb_handle_t *h, hb_title_t *title, + hb_dict_t *preset_dict ); +static int str_vlen(char **strv); static void str_vfree( char **strv ); static char** str_split( char *str, char delem ); @@ -216,7 +224,7 @@ static int get_argv_utf8(int *argc_ptr, char ***argv_ptr) size += WideCharToMultiByte(CP_UTF8, 0, argv_utf16[i], -1, NULL, 0, NULL, NULL ); argv = malloc(size); - if (argv) + if (argv != NULL) { for (i = 0; i < argc; i++) { @@ -248,7 +256,6 @@ int main( int argc, char ** argv ) hb_global_init(); - audios = hb_list_init(); // Get utf8 command line if windows get_argv_utf8(&argc, &argv); @@ -297,6 +304,45 @@ int main( int argc, char ** argv ) /* Exit ASAP on Ctrl-C */ signal( SIGINT, SigHandler ); + // Apply all command line overrides to the preset that are possible. + // Some command line options are applied later to the job + // (e.g. chapter names, explicit audio & subtitle tracks). + hb_dict_t *preset_dict = PreparePreset(preset_name); + if (preset_dict == NULL) + { + // An appropriate error message should have already + // been spilled by PreparePreset. + return 1; + } + + if (preset_export_name != NULL) + { + hb_dict_set(preset_dict, "PresetName", + hb_value_string(preset_export_name)); + if (preset_export_desc != NULL) + { + hb_dict_set(preset_dict, "PresetDescription", + hb_value_string(preset_export_desc)); + } + if (preset_export_file != NULL) + { + hb_preset_write_json(preset_dict, preset_export_file); + } + else + { + char *json; + json = hb_preset_package_json(preset_dict); + fprintf(stdout, "%s\n", json); + } + // If the user requested to export a preset, but not to + // transcode or scan a file, exit here. + if (input == NULL || (!titlescan && titleindex != 0 && output == NULL)) + { + hb_value_free(&preset_dict); + return 0; + } + } + /* Feed libhb with a DVD to scan */ fprintf( stderr, "Opening %s...\n", input ); @@ -307,15 +353,16 @@ int main( int argc, char ** argv ) titleindex = 0; } - hb_system_sleep_prevent(h); // FIXME: When hardware decode is enabled, the scan must be performed // with hardware decode enabled because the decoder context used during // encoding phase comes from the context used during scan. This is // broken by design and I would very much like to fix this someday. - hb_hwd_set_enable(h, use_hwd); - hb_scan( h, input, titleindex, preview_count, store_previews, min_title_duration * 90000LL ); + hb_hwd_set_enable(h, hb_value_get_bool( + hb_dict_get(preset_dict, "VideoHWDecode"))); + hb_scan(h, input, titleindex, preview_count, store_previews, + min_title_duration * 90000LL); /* Wait... */ while( !die ) @@ -403,47 +450,49 @@ int main( int argc, char ** argv ) hb_snooze( 200 ); #endif - HandleEvents( h ); + HandleEvents( h, preset_dict ); } /* Clean up */ + hb_value_free(&preset_dict); hb_close(&h); hb_global_close(); - if (audios != NULL) - { - while ((audio = hb_list_item(audios, 0)) != NULL) - { - hb_list_rem(audios, audio); - if (audio->out.name != NULL) - { - free(audio->out.name); - } - free(audio); - } - hb_list_close(&audios); - } + str_vfree(audio_copy_list); str_vfree(abitrates); str_vfree(acompressions); str_vfree(aqualities); str_vfree(audio_dither); - free(acodecs); - free(arates); - free(atracks); - free(audio_gain); - free(dynamic_range_compression); - free(mixdowns); + str_vfree(acodecs); + str_vfree(arates); + str_vfree(atracks); + str_vfree(audio_lang_list); + str_vfree(audio_gain); + str_vfree(dynamic_range_compression); + str_vfree(mixdowns); + str_vfree(subtitle_lang_list); + str_vfree(subtracks); + free(acodec_fallback); free(native_language); free(format); free(input); free(output); free(preset_name); - free(x264_preset); - free(x264_tune); + free(encoder_preset); + free(encoder_tune); free(advanced_opts); - free(h264_profile); - free(h264_level); - free(nlmeans_opt); - free(nlmeans_tune_opt); + free(encoder_profile); + free(encoder_level); + free(rotate); + free(deblock); + free(detelecine); + free(deinterlace); + free(decomb); + free(hqdn3d); + free(nlmeans); + free(nlmeans_tune); + free(preset_export_name); + free(preset_export_desc); + free(preset_export_file); // write a carriage return to stdout // avoids overlap / line wrapping when stderr is redirected @@ -455,8 +504,6 @@ int main( int argc, char ** argv ) static void PrintTitleInfo( hb_title_t * title, int feature ) { - hb_chapter_t * chapter; - hb_subtitle_t * subtitle; int i; fprintf( stderr, "+ title %d:\n", title->index ); @@ -500,6 +547,7 @@ static void PrintTitleInfo( hb_title_t * title, int feature ) fprintf( stderr, " + chapters:\n" ); for( i = 0; i < hb_list_count( title->list_chapter ); i++ ) { + hb_chapter_t * chapter; chapter = hb_list_item( title->list_chapter, i ); fprintf( stderr, " + %d: cells %d->%d, %"PRIu64" blocks, duration " "%02d:%02d:%02d\n", chapter->index, @@ -510,20 +558,21 @@ static void PrintTitleInfo( hb_title_t * title, int feature ) fprintf( stderr, " + audio tracks:\n" ); for( i = 0; i < hb_list_count( title->list_audio ); i++ ) { + hb_audio_config_t *audio; audio = hb_list_audio_config_item( title->list_audio, i ); if( ( audio->in.codec == HB_ACODEC_AC3 ) || ( audio->in.codec == HB_ACODEC_DCA) ) { - fprintf( stderr, " + %d, %s (iso639-2: %s), %dHz, %dbps\n", + fprintf( stderr, " + %d, %s (iso639-2: %s), %dHz, %dbps\n", i + 1, - audio->lang.description, + audio->lang.description, audio->lang.iso639_2, - audio->in.samplerate, + audio->in.samplerate, audio->in.bitrate ); } else { - fprintf( stderr, " + %d, %s (iso639-2: %s)\n", - i + 1, + fprintf( stderr, " + %d, %s (iso639-2: %s)\n", + i + 1, audio->lang.description, audio->lang.iso639_2 ); } @@ -531,8 +580,9 @@ static void PrintTitleInfo( hb_title_t * title, int feature ) fprintf( stderr, " + subtitle tracks:\n" ); for( i = 0; i < hb_list_count( title->list_subtitle ); i++ ) { + hb_subtitle_t *subtitle; subtitle = hb_list_item( title->list_subtitle, i ); - fprintf( stderr, " + %d, %s (iso639-2: %s) (%s)(%s)\n", + fprintf( stderr, " + %d, %s (iso639-2: %s) (%s)(%s)\n", i + 1, subtitle->lang, subtitle->iso639_2, (subtitle->format == TEXTSUB) ? "Text" : "Bitmap", @@ -578,80 +628,69 @@ static int test_sub_list( char ** list, int pos ) return 0; } -static int cmp_lang( char * lang, const char * code ) +void write_chapter_names(hb_dict_t *job_dict, const char *marker_file) { - iso639_lang_t * iso639; + if (marker_file == NULL) + return; - iso639 = lang_for_code2( code ); + hb_csv_file_t * file = hb_open_csv_file(marker_file); + hb_csv_cell_t * cell; + int row = 0; - if ( iso639 == NULL ) - return 0; - if ( iso639->eng_name && !strcasecmp( lang, iso639->eng_name ) ) - return 1; - if ( iso639->native_name && !strcasecmp( lang, iso639->native_name ) ) - return 1; - if ( iso639->iso639_1 && !strcasecmp( lang, iso639->iso639_1 ) ) - return 1; - if ( iso639->iso639_2 && !strcasecmp( lang, iso639->iso639_2 ) ) - return 1; - if ( iso639->iso639_2b && !strcasecmp( lang, iso639->iso639_2b ) ) - return 1; - return 0; -} + if (file == NULL) + { + fprintf(stderr, "Cannot open chapter marker file, using defaults\n"); + return; + } + fprintf(stderr, "Reading chapter markers from file %s\n", marker_file); -static void apply_loose_crop(int total, int * v1, int * v2, int mod, int max) -{ - /* number of extra pixels which must be cropped to reach next modulus */ - int add = (total - *v1 - *v2) % mod; + hb_value_array_t *chapter_array; + chapter_array = hb_dict_get(hb_dict_get(job_dict, "Destination"), + "ChapterList"); - if (add) - { - /* number of pixels which must be uncropped to reach previous modulus */ - int sub = mod - add; + if (chapter_array == NULL) + return; - /* less than maximum (or can't reduce), increase the crop size */ - if (add <= max || sub > (*v1 + *v2)) + /* Parse the cells */ + while (NULL != (cell = hb_read_next_cell(file))) + { + /* We have a chapter number */ + if (cell->cell_col == 0) { - int add1 = add / 2; - if ((*v1 + add1) & 1) // avoid odd crop if possible - ++add1; - int add2 = (add - add1); - - *v1 += add1; - *v2 += add2; + row = cell->cell_row; } - /* more than maximum, reduce the crop size instead */ - else + /* We have a chapter name */ + if (cell->cell_col == 1 && row == cell->cell_row) { - int sub1 = sub / 2; - if (sub1 > *v1) - sub1 = *v1; - else if ((*v1 - sub1) & 1) // avoid odd crop if possible - ++sub1; - - int sub2 = sub - sub1; - if (sub2 > *v2) + /* If we have a valid chapter, add chapter entry */ + hb_dict_t *chapter_dict = hb_value_array_get(chapter_array, row); + if (chapter_dict != NULL) { - sub1 += (sub2 - *v2); - if ((*v1 - sub1) & 1) // avoid odd crop if possible - ++sub1; - sub2 = sub - sub1; + hb_dict_set(chapter_dict, "Name", + hb_value_string(cell->cell_text)); } - - *v1 -= sub1; - *v2 -= sub2; } + hb_dispose_cell( cell ); + } + hb_close_csv_file( file ); +} + +static void lang_list_remove(hb_value_array_t *list, const char *lang) +{ + int count = hb_value_array_len(list); + int ii; + for (ii = count - 1; ii >= 0; ii--) + { + const char *tmp = hb_value_get_string(hb_value_array_get(list, ii)); + if (!strncmp(lang, tmp, 4)) + hb_value_array_remove(list, ii); } } -static int HandleEvents( hb_handle_t * h ) +static int HandleEvents(hb_handle_t * h, hb_dict_t *preset_dict) { hb_state_t s; - const hb_encoder_t *encoder; - int tmp_num_audio_tracks; - int filter_cfr; - hb_rational_t filter_vrate; hb_get_state( h, &s ); switch( s.state ) @@ -681,23 +720,8 @@ static int HandleEvents( hb_handle_t * h ) { hb_title_set_t * title_set; hb_title_t * title; - hb_job_t * job; - int i; - int sub_burned = 0; - - /* Audio argument string parsing variables */ - int acodec = 0; - int abitrate = 0; - float aquality = 0; - float acompression = 0; - int arate = 0; - int mixdown = HB_AMIXDOWN_DOLBYPLII; - double d_r_c = 0; - double gain = 0; - /* Audio argument string parsing variables */ title_set = hb_get_title_set( h ); - if( !title_set || !hb_list_count( title_set->list_title ) ) { /* No valid title, stop right there */ @@ -706,7 +730,7 @@ static int HandleEvents( hb_handle_t * h ) die = 1; break; } - if( main_feature ) + if (main_feature) { int i; int main_feature_idx=0; @@ -744,15 +768,15 @@ static int HandleEvents( hb_handle_t * h ) break; } titleindex = main_feature_idx; - fprintf( stderr, "Found main feature title, setting title to %d\n", - main_feature_idx); + fprintf(stderr, "Found main feature title %d\n", + main_feature_idx); - title = hb_list_item( title_set->list_title, main_feature_pos); + title = hb_list_item(title_set->list_title, main_feature_pos); } else { - title = hb_list_item( title_set->list_title, 0 ); + title = hb_list_item(title_set->list_title, 0); } - if( !titleindex || titlescan ) + if (!titleindex || titlescan) { /* Scan-only mode, print infos and exit */ PrintTitleSetInfo( title_set ); @@ -760,3625 +784,3310 @@ static int HandleEvents( hb_handle_t * h ) break; } - PrintTitleInfo( title, title_set->feature ); + fprintf( stderr, "+ Using preset: %s\n", + hb_value_get_string(hb_dict_get(preset_dict, "PresetName"))); - /* Set job settings */ - job = hb_job_init(title); - filter_cfr = job->cfr; - filter_vrate = job->vrate; + PrintTitleInfo(title, title_set->feature); + // All overrides to the preset are complete + // Initialize the job from preset + overrides + // and apply job specific command line overrides + hb_dict_t *job_dict = PrepareJob(h, title, preset_dict); + if (job_dict == NULL) + { + die = 1; + return -1; + } - if( chapter_start && chapter_end && !stop_at_pts && !start_at_preview && !stop_at_frame && !start_at_pts && !start_at_frame ) + hb_job_t * job = NULL; + job = hb_dict_to_job(h, job_dict); + hb_value_free(&job_dict); + if (job == NULL) { - job->chapter_start = MAX( job->chapter_start, - chapter_start ); - job->chapter_end = MIN( job->chapter_end, - chapter_end ); - job->chapter_end = MAX( job->chapter_start, - job->chapter_end ); + fprintf(stderr, "Error in setting up job! Aborting.\n"); + die = 1; + return -1; } - if ( angle ) + hb_add( h, job ); + hb_job_close( &job ); + hb_start( h ); + break; + } + +#define p s.param.working + case HB_STATE_SEARCHING: + fprintf( stdout, "\rEncoding: task %d of %d, Searching for start time, %.2f %%", + p.pass, p.pass_count, 100.0 * p.progress ); + if( p.seconds > -1 ) { - job->angle = angle; + fprintf( stdout, " (ETA %02dh%02dm%02ds)", + p.hours, p.minutes, p.seconds ); } + fflush(stdout); + break; - if (preset) + case HB_STATE_WORKING: + fprintf( stdout, "\rEncoding: task %d of %d, %.2f %%", + p.pass, p.pass_count, 100.0 * p.progress ); + if( p.seconds > -1 ) { - fprintf( stderr, "+ Using preset: %s\n", preset_name); + fprintf( stdout, " (%.2f fps, avg %.2f fps, ETA " + "%02dh%02dm%02ds)", p.rate_cur, p.rate_avg, + p.hours, p.minutes, p.seconds ); + } + fflush(stdout); + break; +#undef p - if (!strcasecmp(preset_name, "Universal")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 20.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1,1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac,copy:ac3"); - } - if( !abitrates ) - { - abitrates = str_split("160,160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2,none"); - } - if( !arates ) - { - arates = strdup("Auto,Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0,0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 720; - maxHeight = 576; - if (x264_preset == NULL) - { - x264_preset = strdup("fast"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("baseline"); - } - if (h264_level == NULL) - { - h264_level = strdup("3.0"); - } - if( !anamorphic_mode ) - { - anamorphic_mode = 2; - } - modulus = 2; - job->chapter_markers = 1; - } - if (!strcasecmp(preset_name, "iPod")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - job->ipod_atom = 1; - vcodec = HB_VCODEC_X264; - job->vquality = 22.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac"); - } - if( !abitrates ) - { - abitrates = str_split("160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2"); - } - if( !arates ) - { - arates = strdup("Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 320; - maxHeight = 240; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("baseline"); - } - if (h264_level == NULL) - { - h264_level = strdup("1.3"); - } - modulus = 2; - job->chapter_markers = 1; - } - if (!strcasecmp(preset_name, "iPhone & iPod touch")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 22.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac"); - } - if( !abitrates ) - { - abitrates = str_split("160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2"); - } - if( !arates ) - { - arates = strdup("Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 960; - maxHeight = 640; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("high"); - } - if (h264_level == NULL) - { - h264_level = strdup("3.1"); - } - if( !anamorphic_mode ) - { - anamorphic_mode = 2; - } - modulus = 2; - job->chapter_markers = 1; - } - if (!strcasecmp(preset_name, "iPad")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 20.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac"); - } - if( !abitrates ) - { - abitrates = str_split("160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2"); - } - if( !arates ) - { - arates = strdup("Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 1280; - maxHeight = 720; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("high"); - } - if (h264_level == NULL) - { - h264_level = strdup("3.1"); - } - if( !anamorphic_mode ) - { - anamorphic_mode = 2; - } - modulus = 2; - job->chapter_markers = 1; - } - if (!strcasecmp(preset_name, "AppleTV")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 20.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1,1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac,copy:ac3"); - } - if( !abitrates ) - { - abitrates = str_split("160,160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2,none"); - } - if( !arates ) - { - arates = strdup("Auto,Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0,0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 960; - maxHeight = 720; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("high"); - } - if (h264_level == NULL) - { - h264_level = strdup("3.1"); - } - if (advanced_opts == NULL) - { - advanced_opts = strdup("qpmin=4:cabac=0:ref=2:b-pyramid=none:weightb=0:weightp=0:vbv-maxrate=9500:vbv-bufsize=9500"); - } - if( !anamorphic_mode ) - { - anamorphic_mode = 2; - } - modulus = 2; - job->chapter_markers = 1; - } - if (!strcasecmp(preset_name, "AppleTV 2")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 20.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1,1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac,copy:ac3"); - } - if( !abitrates ) - { - abitrates = str_split("160,160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2,none"); - } - if( !arates ) - { - arates = strdup("Auto,Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0,0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 1280; - maxHeight = 720; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("high"); - } - if (h264_level == NULL) - { - h264_level = strdup("3.1"); - } - if( !anamorphic_mode ) - { - anamorphic_mode = 2; - } - modulus = 2; - job->chapter_markers = 1; - } - if (!strcasecmp(preset_name, "AppleTV 3")) - { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 20.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1,1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac,copy:ac3"); - } - if( !abitrates ) - { - abitrates = str_split("160,160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2,none"); - } - if( !arates ) - { - arates = strdup("Auto,Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0,0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 1920; - maxHeight = 1080; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("high"); - } - if (h264_level == NULL) - { - h264_level = strdup("4.0"); - } - decomb = 1; - decomb_opt = "7:2:6:9:1:80"; - if( !anamorphic_mode ) +#define p s.param.muxing + case HB_STATE_MUXING: + { + if (show_mux_warning) + { + fprintf( stdout, "\rMuxing: this may take awhile..." ); + fflush(stdout); + show_mux_warning = 0; + } + break; + } +#undef p + +#define p s.param.workdone + case HB_STATE_WORKDONE: + /* Print error if any, then exit */ + switch( p.error ) + { + case HB_ERROR_NONE: + fprintf( stderr, "\nEncode done!\n" ); + break; + case HB_ERROR_CANCELED: + fprintf( stderr, "\nEncode canceled.\n" ); + break; + default: + fprintf( stderr, "\nEncode failed (error %x).\n", + p.error ); + } + done_error = p.error; + die = 1; + break; +#undef p + } + return 0; +} + +/**************************************************************************** + * SigHandler: + ****************************************************************************/ +static volatile int64_t i_die_date = 0; +void SigHandler( int i_signal ) +{ + done_error = HB_ERROR_CANCELED; + if( die == 0 ) + { + die = 1; + i_die_date = hb_get_date(); + fprintf( stderr, "Signal %d received, terminating - do it " + "again in case it gets stuck\n", i_signal ); + } + else if( i_die_date + 500 < hb_get_date() ) + { + fprintf( stderr, "Dying badly, files might remain in your /tmp\n" ); + exit( done_error ); + } +} + +/**************************************************************************** + * ShowHelp: + ****************************************************************************/ +static void ShowHelp() +{ + int i; + const hb_rate_t *rate; + const hb_dither_t *dither; + const hb_mixdown_t *mixdown; + const hb_encoder_t *encoder; + const hb_container_t *container; + FILE* const out = stdout; + + fprintf( out, +"Syntax: HandBrakeCLI [options] -i -o \n" +"\n" +"### General Handbrake Options---------------------------------------------\n\n" +" -h, --help Print help\n" +" -u, --update Check for updates and exit\n" +" -v, --verbose <#> Be verbose (optional argument: logging level)\n" +" -Z. --preset Use a built-in preset. Capitalization matters,\n" +" and if the preset name has spaces, surround it\n" +" with double quotation marks\n" +" -z, --preset-list See a list of available built-in presets\n" +" --preset-import-file Import presets from a json preset file.\n" +" 'filespec' may be a list of files separated\n" +" by spaces, or it may use shell wildcards.\n" +" --preset-import-gui Import presets from GUI config preset file.\n" +" --preset-export Create a new preset from command line options and\n" +" write a json representation of the preset to the\n" +" console or a file if '--preset-export-file' is\n" +" specified. The required 'name' argument will be\n" +" the new preset's name.\n" +" --preset-export-file Write new preset generated by '--preset-export'\n" +" to file 'filename'.\n" +" --preset-export-description\n" +" Add a description to the new preset created with\n" +" '--preset-export'\n" +" --no-dvdnav Do not use dvdnav for reading DVDs\n" +" --no-opencl Disable use of OpenCL\n" +"\n" +"### Source Options--------------------------------------------------------\n\n" +" -i, --input Set input device\n" +" -t, --title Select a title to encode (0 to scan all titles\n" +" only, default: 1)\n" +" --min-duration Set the minimum title duration (in seconds).\n" +" Shorter titles will be ignored (default: 10).\n" +" --scan Scan selected title only.\n" +" --main-feature Detect and select the main feature title.\n" +" -c, --chapters Select chapters (e.g. \"1-3\" for chapters\n" +" 1 to 3, or \"3\" for chapter 3 only,\n" +" default: all chapters)\n" +" --angle Select the video angle (DVD or Blu-ray only)\n" +" --previews <#:B> Select how many preview images are generated,\n" +" and whether to store to disk (0 or 1).\n" +" (default: 10:0)\n" +" --start-at-preview <#> Start encoding at a given preview.\n" +" --start-at Start encoding at a given frame, duration\n" +" (in seconds), or pts (on a 90kHz clock)\n" +" --stop-at Stop encoding at a given frame, duration\n" +" (in seconds), or pts (on a 90kHz clock)" +"\n" +"### Destination Options---------------------------------------------------\n\n" +" -o, --output Set output file name\n" +" -f, --format Set output container format ("); + container = NULL; + while ((container = hb_container_get_next(container)) != NULL) + { + fprintf(out, "%s", container->short_name); + if (hb_container_get_next(container) != NULL) + { + fprintf(out, "/"); + } + else + { + fprintf(out, ")\n"); + } + } + fprintf(out, +" (default: autodetected from file name)\n" +" -m, --markers Add chapter markers\n" +" --no-markers Disable preset chapter markers\n" +" -O, --optimize Optimize mp4 files for HTTP streaming\n" +" (\"fast start\")\n" +" --no-optimize Disable preset 'optimize'\n" +" -I, --ipod-atom Mark mp4 files so 5.5G iPods will accept them\n" +" --no-ipod-atom Disable 5.5G iPod tag\n" +" -P, --use-opencl Use OpenCL where applicable\n" +" -U, --use-hwd Use DXVA2 hardware decoding\n" +" --no-hwd Disable DXVA2 hardware decoding\n" +"\n" + + +"### Video Options------------------------------------------------------------\n\n" +" -e, --encoder Set video library encoder\n" +" Options: " ); + encoder = NULL; + while ((encoder = hb_video_encoder_get_next(encoder)) != NULL) + { + fprintf(out, "%s", encoder->short_name); + if (hb_video_encoder_get_next(encoder) != NULL) + { + fprintf(out, "/"); + } + else + { + fprintf(out, "\n"); + } + } + fprintf(out, +" --encoder-preset Adjust video encoding settings for a particular\n" +" speed/efficiency tradeoff (encoder-specific)\n" +" --encoder-preset-list List supported --encoder-preset values for the\n" +" specified video encoder\n" +" --encoder-tune Adjust video encoding settings for a particular\n" +" type of souce or situation (encoder-specific)\n" +" --encoder-tune-list List supported --encoder-tune values for the\n" +" specified video encoder\n" +" -x, --encopts Specify advanced encoding options in the same\n" +" style as mencoder (all encoders except theora):\n" +" option1=value1:option2=value2\n" +" --encoder-profile Ensures compliance with the requested codec\n" +" profile (encoder-specific)\n" +" --encoder-profile-list List supported --encoder-profile values for the\n" +" specified video encoder\n" +" --encoder-level Ensures compliance with the requested codec\n" +" level (encoder-specific)\n" +" --encoder-level-list List supported --encoder-level values for the\n" +" specified video encoder\n" +" -q, --quality Set video quality\n" +" -b, --vb Set video bitrate (default: 1000)\n" +" -2, --two-pass Use two-pass mode\n" +" -T, --turbo When using 2-pass use \"turbo\" options on the\n" +" 1st pass to improve speed\n" +" (works with x264 and x265)\n" +" -r, --rate Set video framerate\n" +" (" ); + rate = NULL; + while ((rate = hb_video_framerate_get_next(rate)) != NULL) + { + fprintf(out, "%s", rate->name); + if (hb_video_framerate_get_next(rate) != NULL) + { + fprintf(out, "/"); + } + } + fprintf( out, ")\n" +" Be aware that not specifying a framerate lets\n" +" HandBrake preserve a source's time stamps,\n" +" potentially creating variable framerate video\n" +" --vfr, --cfr, --pfr Select variable, constant or peak-limited\n" +" frame rate control. VFR preserves the source\n" +" timing. CFR makes the output constant rate at\n" +" the rate given by the -r flag (or the source's\n" +" average rate if no -r is given). PFR doesn't\n" +" allow the rate to go over the rate specified\n" +" with the -r flag but won't change the source\n" +" timing if it's below that rate.\n" +" If none of these flags are given, the default\n" +" is --cfr when -r is given and --vfr otherwise\n" +"\n" +"### Audio Options---------------------------------------------------------\n\n" +" --audio-lang-list Specifiy a comma separated list of audio\n" +" languages you would like to select from the\n" +" source title. By default, the first audio\n" +" matching each language will be added to your\n" +" output. Provide the language's iso639-2 code\n" +" (fre, eng, spa, dut, et cetera)\n" +" Use code 'und' (Unknown) to match all languages.\n" +" --all-audio Select all audio tracks matching languages in\n" +" the specified language list (--audio-lang-list).\n" +" Any language if list is not specified.\n" +" --first-audio Select first audio track matching languages in\n" +" the specified language list (--audio-lang-list).\n" +" Any language if list is not specified.\n" +" -a, --audio Select audio track(s), separated by commas\n" +" (\"none\" for no audio, \"1,2,3\" for multiple\n" +" tracks, default: first one).\n" +" Multiple output tracks can be used for one input.\n" +" -E, --aencoder Audio encoder(s):\n" ); + encoder = NULL; + while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) + { + fprintf(out, " %s\n", + encoder->short_name); + } + fprintf(out, +" copy:* will passthrough the corresponding\n" +" audio unmodified to the muxer if it is a\n" +" supported passthrough audio type.\n" +" Separate tracks by commas.\n" +" Defaults:\n"); + container = NULL; + while ((container = hb_container_get_next(container)) != NULL) + { + int audio_encoder = hb_audio_encoder_get_default(container->format); + fprintf(out, " %-8s %s\n", + container->short_name, + hb_audio_encoder_get_short_name(audio_encoder)); + } + fprintf(out, +" --audio-copy-mask Set audio codecs that are permitted when the\n" +" \"copy\" audio encoder option is specified\n" +" (" ); + i = 0; + encoder = NULL; + while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) + { + if ((encoder->codec & HB_ACODEC_PASS_FLAG) && + (encoder->codec != HB_ACODEC_AUTO_PASS)) + { + if (i) + { + fprintf(out, "/"); + } + i = 1; + // skip "copy:" + fprintf(out, "%s", encoder->short_name + 5); + } + } + fprintf(out, ")\n" +" Separated by commas for multiple allowed options.\n" +" --audio-fallback Set audio codec to use when it is not possible\n" +" to copy an audio track without re-encoding.\n" +" -B, --ab Set audio bitrate(s) (default: depends on the\n" +" selected codec, mixdown and samplerate)\n" +" Separate tracks by commas.\n" +" -Q, --aq Set audio quality metric.\n" +" Separate tracks by commas.\n" +" -C, --ac Set audio compression metric.\n" +" selected codec)\n" +" Separate tracks by commas.\n" +" -6, --mixdown Format(s) for audio downmixing/upmixing:\n"); + // skip HB_AMIXDOWN_NONE + mixdown = hb_mixdown_get_next(NULL); + while((mixdown = hb_mixdown_get_next(mixdown)) != NULL) + { + fprintf(out, " %s\n", + mixdown->short_name); + } + fprintf(out, +" Separate tracks by commas.\n" +" Defaults:\n"); + encoder = NULL; + while((encoder = hb_audio_encoder_get_next(encoder)) != NULL) + { + if (!(encoder->codec & HB_ACODEC_PASS_FLAG)) + { + // layout: UINT64_MAX (all channels) should work with any mixdown + int mixdown = hb_mixdown_get_default(encoder->codec, UINT64_MAX); + // assumes that the encoder short name is <= 16 characters long + fprintf(out, " %-16s up to %s\n", + encoder->short_name, hb_mixdown_get_short_name(mixdown)); + } + } + fprintf(out, +" --normalize-mix Normalize audio mix levels to prevent clipping.\n" +" Separate tracks by commas.\n" +" 0 = Disable Normalization (default)\n" +" 1 = Enable Normalization\n" +" -R, --arate Set audio samplerate(s)\n" +" (" ); + rate = NULL; + while ((rate = hb_audio_samplerate_get_next(rate)) != NULL) + { + fprintf(out, "%s", rate->name); + if (hb_audio_samplerate_get_next(rate) != NULL) + { + fprintf(out, "/"); + } + } + fprintf( out, " kHz)\n" +" Separate tracks by commas.\n" +" -D, --drc Apply extra dynamic range compression to the\n" +" audio, making soft sounds louder. Range is 1.0\n" +" to 4.0 (too loud), with 1.5 - 2.5 being a useful\n" +" range.\n" +" Separate tracks by commas.\n" +" --gain Amplify or attenuate audio before encoding. Does\n" +" NOT work with audio passthru (copy). Values are\n" +" in dB. Negative values attenuate, positive\n" +" values amplify. A 1 dB difference is barely\n" +" audible.\n" +" --adither Apply dithering to the audio before encoding.\n" +" Separate tracks by commas.\n" +" Only supported by some encoders\n" +" ("); + i = 0; + encoder = NULL; + while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) + { + if (hb_audio_dither_is_supported(encoder->codec)) + { + if (i) + { + fprintf(out, "/"); + } + i = 1; + fprintf(out, "%s", encoder->short_name); + } + } + fprintf(out, ").\n"); + fprintf(out, + " Options:\n"); + dither = NULL; + while ((dither = hb_audio_dither_get_next(dither)) != NULL) + { + if (dither->method == hb_audio_dither_get_default()) + { + fprintf(out, " %s (default)\n", + dither->short_name); + } + else + { + fprintf(out, " %s\n", + dither->short_name); + } + } + fprintf(out, +" -A, --aname Audio track name(s),\n" +" Separate tracks by commas.\n" +"\n" +"### Picture Settings------------------------------------------------------\n\n" +" -w, --width Set picture width\n" +" -l, --height Set picture height\n" +" --crop Set cropping values (default: autocrop)\n" +" --loose-crop Always crop to a multiple of the modulus\n" +" --no-loose-crop Disable preset 'loose-crop'\n" +" -Y, --maxHeight <#> Set maximum height\n" +" -X, --maxWidth <#> Set maximum width\n" +" --non-anamorphic Set pixel aspect ratio to 1:1\n" +" --strict-anamorphic Store pixel aspect ratio in video stream\n" +" --loose-anamorphic Store pixel aspect ratio with specified width\n" +" --custom-anamorphic Store pixel aspect ratio in video stream and\n" +" directly control all parameters.\n" +" --display-width Set the width to scale the actual pixels to\n" +" at playback, for custom anamorphic.\n" +" --keep-display-aspect Preserve the source's display aspect ratio\n" +" when using custom anamorphic\n" +" --no-keep-display-aspect Disable preset 'keep-display-aspect'\n" +" --pixel-aspect Set a custom pixel aspect for custom anamorphic\n" +" (--display-width and --pixel-aspect are mutually\n" +" exclusive.\n" +" --itu-par Use wider, ITU pixel aspect values for loose and\n" +" custom anamorphic, useful with underscanned\n" +" sources\n" +" --no-itu-par Disable preset 'itu-par'\n" +" --modulus Set the number you want the scaled pixel\n" +" dimensions\n" +" to divide cleanly by. Does not affect strict\n" +" anamorphic mode, which is always mod 2\n" +" (default: 16)\n" +" -M, --color-matrix Set the color space signaled by the output\n" +" Values: 709, pal, ntsc, 601 (same as ntsc)\n" +" (default: detected from source)\n" +"\n" +"### Filters---------------------------------------------------------------\n\n" +" -d, --deinterlace Unconditionally deinterlaces all frames\n" +" or omitted (default settings)\n" + " or\n" +" (default 0:-1)\n" +" --no-deinterlace Disable preset deinterlace filter\n" +" -5, --decomb Selectively deinterlaces when it detects combing\n" +" or omitted (default settings)\n" +" or\n" +" \n" +" (default: 7:2:6:9:80:16:16:10:20:20:4:2:50:24:1:-1)\n" +" --no-decomb Disable preset decomb filter\n" +" -9, --detelecine Detelecine (ivtc) video with pullup filter\n" +" Note: this filter drops duplicate frames to\n" +" restore the pre-telecine framerate, unless you\n" +" specify a constant framerate (--rate 29.97)\n" +" (default 1:1:4:4:0:0:-1)\n" +" --no-detelecine Disable preset detelecine filter\n" +" -8, --hqdn3d Denoise video with hqdn3d filter\n" +" or omitted (default settings)\n" +" or\n" +" \n" +" (default: 4:3:3:6:4.5:4.5)\n" +" --no-hqdn3d Disable preset hqdn3d filter\n" +" --denoise Legacy alias for '--hqdn3d'\n" +" --nlmeans Denoise video with nlmeans filter\n" +" or omitted\n" +" or\n" +" \n" +" (default 8:1:7:3:2:0)\n" +" --no-nlmeans Disable preset nlmeans filter\n" +" --nlmeans-tune Tune nlmeans filter to content type\n" +" Note: only works in conjunction with presets\n" +" ultralight/light/medium/strong.\n" +" or omitted (default none)\n" +" -7, --deblock Deblock video with pp7 filter\n" +" (default 5:2)\n" +" --no-deblock Disable preset deblock filter\n" +" --rotate Rotate image or flip its axes.\n" +" Modes: (can be combined)\n" +" 0 disable rotate\n" +" 1 vertical flip\n" +" 2 horizontal flip\n" +" 4 rotate clockwise 90 degrees\n" +" More Examples:\n" +" 3 horiz + vert (aka rotate 180')\n" +" 7 horiz + vert + 90' (aka rotate 270')\n" +" Default: 3 (vertical and horizontal flip)\n" +" -g, --grayscale Grayscale encoding\n" +" --no-grayscale Disable preset 'grayscale'\n" +"\n" +"### Subtitle Options------------------------------------------------------\n\n" +" --subtitle-lang-list Specifiy a comma separated list of subtitle\n" +" languages you would like to select from the\n" +" source title. By default, the first subtitle\n" +" matching each language will be added to your\n" +" output. Provide the language's iso639-2 code\n" +" (fre, eng, spa, dut, et cetera)\n" +" --all-subtitles Select all subtitle tracks matching languages in\n" +" the specified language list\n" +" (--subtitle-lang-list).\n" +" Any language if list is not specified.\n" +" --first-subtitle Select first subtitle track matching languages in\n" +" the specified language list\n" +" (--subtitle-lang-list).\n" +" Any language if list is not specified.\n" +" -s, --subtitle Select subtitle track(s), separated by commas\n" +" More than one output track can be used for one\n" +" input. \"none\" for no subtitles.\n" +" Example: \"1,2,3\" for multiple tracks.\n" +" A special track name \"scan\" adds an extra 1st\n" +" pass. This extra pass scans subtitles matching\n" +" the language of the first audio or the language \n" +" selected by --native-language.\n" +" The one that's only used 10 percent of the time\n" +" or less is selected. This should locate subtitles\n" +" for short foreign language segments. Best used in\n" +" conjunction with --subtitle-forced.\n" +" -F, --subtitle-forced Only display subtitles from the selected stream\n" +" if the subtitle has the forced flag set. The\n" +" values in \"string\" are indexes into the\n" +" subtitle list specified with '--subtitle'.\n" +" Separate tracks by commas.\n" +" Example: \"1,2,3\" for multiple tracks.\n" +" If \"string\" is omitted, the first track is\n" +" forced.\n" +" --subtitle-burned \"Burn\" the selected subtitle into the video\n" +" track. If \"subtitle\" is omitted, the first\n" +" track is burned. \"subtitle\" is an index into\n" +" the subtitle list specified with '--subtitle'\n" +" or \"native\" to burn the subtitle track that may\n" +" be added by the 'native-language' option.\n" +" --subtitle-default Flag the selected subtitle as the default\n" +" subtitle to be displayed upon playback. Setting\n" +" no default means no subtitle will be displayed\n" +" automatically. \"number\" is an index into the\n" +" subtitle list specified with '--subtitle'.\n" +" -N, --native-language Specifiy your language preference. When the first\n" +" audio track does not match your native language\n" +" then select the first subtitle that does. When\n" +" used in conjunction with --native-dub the audio\n" +" track is changed in preference to subtitles.\n" +" Provide the language's iso639-2 code:\n" +" (fre, eng, spa, dut, et cetera)\n" +" --native-dub Used in conjunction with --native-language\n" +" requests that if no audio tracks are selected the\n" +" default selected audio track will be the first\n" +" one that matches the --native-language. If there\n" +" are no matching audio tracks then the first\n" +" matching subtitle track is used instead.\n" +" --srt-file SubRip SRT filename(s), separated by commas.\n" +" --srt-codeset Character codeset(s) that the SRT file(s) are\n" +" encoded in, separated by commas.\n" +" Use 'iconv -l' for a list of valid\n" +" codesets. If not specified, 'latin1' is assumed\n" +" --srt-offset Offset (in milliseconds) to apply to the SRT\n" +" file(s), separated by commas. If not specified,\n" +" zero is assumed. Offsets may be negative.\n" +" --srt-lang SRT track language as an iso639-2 code:\n" +" (fre, eng, spa, dut, et cetera)\n" +" Separated by commas. If not specified, then 'und'\n" +" is used.\n" +" --srt-default Flag the selected srt as the default subtitle\n" +" to be displayed upon playback. Setting no default\n" +" means no subtitle will be automatically displayed\n" +" If \"number\" is omitted, the first SRT is the\n" +" default. \"number\" is an 1 based index into the\n" +" 'srt-file' list\n" +" --srt-burn \"Burn\" the selected SRT subtitle into the\n" +" video track. If \"number\" is omitted, the first\n" +" SRT is burned. \"number\" is an 1 based index\n" +" into the 'srt-file' list\n" +"\n" + ); + +#ifdef USE_QSV +if (hb_qsv_available()) +{ + fprintf( out, +"### Intel Quick Sync Video------------------------------------------------\n\n" +" --disable-qsv-decoding Force software decoding of the video track.\n" +" --enable-qsv-decoding Allow QSV hardware decoding of the video track.\n" +" --qsv-async-depth Specifies how many asynchronous operations\n" +" should be performed before the result is\n" +" explicitly synchronized.\n" +" Default: 4. If zero, the value is not specified.\n" +"\n" + ); +} +#endif +} + +/**************************************************************************** + * ShowPresets: + ****************************************************************************/ +static const char * +reverse_search_char(const char *front, const char *back, char delim) +{ + while (back != front && *back != delim) + back--; + return back; +} + +#if defined( __MINGW32__ ) +static char * my_strndup(const char *src, int len) +{ + int src_len = strlen(src); + int alloc = src_len < len ? src_len + 1 : len + 1; + char *result = malloc(alloc); + strncpy(result, src, alloc - 1); + result[alloc - 1] = 0; + return result; +} +#else +#define my_strndup strndup +#endif + +static char** str_width_split( const char *str, int width ) +{ + const char * pos; + const char * end; + char ** ret; + int count, ii; + int len; + char delem = ' '; + + if ( str == NULL || str[0] == 0 ) + { + ret = malloc( sizeof(char*) ); + *ret = NULL; + return ret; + } + + len = strlen(str); + + // Find number of elements in the string + count = 1; + pos = str; + end = pos + width; + while (end < str + len) + { + end = reverse_search_char(pos, end, delem); + if (end == pos) + { + // Shouldn't happen for reasonable input + break; + } + count++; + pos = end + 1; + end = pos + width; + } + count++; + ret = calloc( ( count + 1 ), sizeof(char*) ); + + pos = str; + end = pos + width; + for (ii = 0; ii < count - 1 && end < str + len; ii++) + { + end = reverse_search_char(pos, end, delem); + if (end == pos) + { + break; + } + ret[ii] = my_strndup(pos, end - pos); + pos = end + 1; + end = pos + width; + } + if (*pos != 0 && ii < count - 1) + { + ret[ii] = my_strndup(pos, width); + } + + return ret; +} + +static void Indent(FILE *f, char *whitespace, int indent) +{ + int ii; + for (ii = 0; ii < indent; ii++) + { + fprintf(f, "%s", whitespace); + } +} + +static void ShowPresets(hb_value_array_t *presets, int indent, int descriptions) +{ + if (presets == NULL) + presets = hb_presets_get(); + + int count = hb_value_array_len(presets); + int ii; + for (ii = 0; ii < count; ii++) + { + const char *name; + hb_dict_t *preset_dict = hb_value_array_get(presets, ii); + name = hb_value_get_string(hb_dict_get(preset_dict, "PresetName")); + Indent(stderr, " ", indent); + if (hb_value_get_bool(hb_dict_get(preset_dict, "Folder"))) + { + indent++; + fprintf(stderr, "%s/\n", name); + hb_value_array_t *children; + children = hb_dict_get(preset_dict, "ChildrenArray"); + if (children == NULL) + continue; + ShowPresets(children, indent, descriptions); + indent--; + } + else + { + fprintf(stderr, "%s\n", name); + if (descriptions) + { + const char *desc; + desc = hb_value_get_string(hb_dict_get(preset_dict, + "PresetDescription")); + if (desc != NULL && desc[0] != 0) + { + int ii; + char **split = str_width_split(desc, 60); + for (ii = 0; split[ii] != NULL; ii++) { - anamorphic_mode = 2; + Indent(stderr, " ", indent+1); + fprintf(stderr, "%s\n", split[ii]); } - modulus = 2; - job->chapter_markers = 1; + str_vfree(split); + } + } + } + } +} + +static char* strchr_quote(char *pos, char c, char q) +{ + if (pos == NULL) + return NULL; + + while (*pos != 0 && *pos != c) + { + if (*pos == q) + { + pos = strchr_quote(pos+1, q, 0); + if (pos == NULL) + return NULL; + pos++; + } + else if (*pos == '\\' && *(pos+1) != 0) + pos += 2; + else + pos++; + } + if (*pos != c) + return NULL; + return pos; +} + +static char *strndup_quote(char *str, char q, int len) +{ + if (str == NULL) + return NULL; + + char * res; + int str_len = strlen( str ); + int src = 0, dst = 0; + res = malloc( len > str_len ? str_len + 1 : len + 1 ); + + while (str[src] != 0 && src < len) + { + if (str[src] == q) + src++; + else if (str[src] == '\\' && str[src+1] != 0) + { + res[dst++] = str[src+1]; + src += 2; + } + else + res[dst++] = str[src++]; + } + res[dst] = '\0'; + return res; +} + +static int str_vlen(char **strv) +{ + int i; + if (strv == NULL) + return 0; + for (i = 0; strv[i]; i++); + return i; +} + +static char** str_split( char *str, char delem ) +{ + char * pos; + char * end; + char ** ret; + int count, i; + char quote = '"'; + + if (delem == '"') + { + quote = '\''; + } + if ( str == NULL || str[0] == 0 ) + { + ret = malloc( sizeof(char*) ); + *ret = NULL; + return ret; + } + + // Find number of elements in the string + count = 1; + pos = str; + while ( ( pos = strchr_quote( pos, delem, quote ) ) != NULL ) + { + count++; + pos++; + } + + ret = calloc( ( count + 1 ), sizeof(char*) ); + + pos = str; + for ( i = 0; i < count - 1; i++ ) + { + end = strchr_quote( pos, delem, quote ); + ret[i] = strndup_quote(pos, quote, end - pos); + pos = end + 1; + } + ret[i] = strndup_quote(pos, quote, strlen(pos)); + + return ret; +} + +static void str_vfree( char **strv ) +{ + int i; + + if (strv == NULL) + return; + + for ( i = 0; strv[i]; i++ ) + { + free( strv[i] ); + } + free( strv ); +} + +static double parse_hhmmss_strtok() +{ + /* Assumes strtok has already been called on a string. Intends to parse + * hh:mm:ss.ss or mm:ss.ss or ss.ss or ss into double seconds. Actually + * parses a list of doubles separated by colons, multiplying the current + * result by 60 then adding in the next value. Malformed input does not + * result in a explicit error condition but instead returns an + * intermediate result. */ + double duration = 0; + char* str; + while ((str = strtok(NULL, ":")) != NULL) + duration = 60*duration + strtod(str, NULL); + return duration; +} + +/**************************************************************************** + * ParseOptions: + ****************************************************************************/ +static int ParseOptions( int argc, char ** argv ) +{ + + #define PREVIEWS 257 + #define START_AT_PREVIEW 258 + #define START_AT 259 + #define STOP_AT 260 + #define ANGLE 261 + #define DVDNAV 262 + #define DISPLAY_WIDTH 263 + #define PIXEL_ASPECT 264 + #define MODULUS 265 + #define KEEP_DISPLAY_ASPECT 266 + #define SUB_BURNED 267 + #define SUB_DEFAULT 268 + #define NATIVE_DUB 269 + #define SRT_FILE 270 + #define SRT_CODESET 271 + #define SRT_OFFSET 272 + #define SRT_LANG 273 + #define SRT_DEFAULT 274 + #define SRT_BURN 275 + #define ROTATE_FILTER 276 + #define SCAN_ONLY 277 + #define MAIN_FEATURE 278 + #define MIN_DURATION 279 + #define AUDIO_GAIN 280 + #define ALLOWED_AUDIO_COPY 281 + #define AUDIO_FALLBACK 282 + #define LOOSE_CROP 283 + #define ENCODER_PRESET 284 + #define ENCODER_PRESET_LIST 285 + #define ENCODER_TUNE 286 + #define ENCODER_TUNE_LIST 287 + #define ENCODER_PROFILE 288 + #define ENCODER_PROFILE_LIST 289 + #define ENCODER_LEVEL 290 + #define ENCODER_LEVEL_LIST 291 + #define NORMALIZE_MIX 293 + #define AUDIO_DITHER 294 + #define QSV_BASELINE 295 + #define QSV_ASYNC_DEPTH 296 + #define QSV_IMPLEMENTATION 297 + #define FILTER_NLMEANS 298 + #define FILTER_NLMEANS_TUNE 299 + #define AUDIO_LANG_LIST 300 + #define SUBTITLE_LANG_LIST 301 + #define PRESET_EXPORT 302 + #define PRESET_EXPORT_DESC 303 + #define PRESET_EXPORT_FILE 304 + #define PRESET_IMPORT 305 + #define PRESET_IMPORT_GUI 306 + + for( ;; ) + { + static struct option long_options[] = + { + { "help", no_argument, NULL, 'h' }, + { "update", no_argument, NULL, 'u' }, + { "verbose", optional_argument, NULL, 'v' }, + { "no-dvdnav", no_argument, NULL, DVDNAV }, + { "no-opencl", no_argument, &use_opencl, 0 }, + +#ifdef USE_QSV + { "qsv-baseline", no_argument, NULL, QSV_BASELINE, }, + { "qsv-async-depth", required_argument, NULL, QSV_ASYNC_DEPTH, }, + { "qsv-implementation", required_argument, NULL, QSV_IMPLEMENTATION, }, + { "disable-qsv-decoding", no_argument, &qsv_decode, 0, }, + { "enable-qsv-decoding", no_argument, &qsv_decode, 1, }, +#endif + + { "format", required_argument, NULL, 'f' }, + { "input", required_argument, NULL, 'i' }, + { "output", required_argument, NULL, 'o' }, + { "optimize", no_argument, &mp4_optimize, 1 }, + { "no-optimize", no_argument, &mp4_optimize, 0 }, + { "ipod-atom", no_argument, &ipod_atom, 1 }, + { "no-ipod-atom",no_argument, &ipod_atom, 0 }, + { "use-opencl", no_argument, &use_opencl, 1 }, + { "use-hwd", no_argument, &use_hwd, 1 }, + { "no-hwd", no_argument, &use_hwd, 0 }, + + { "title", required_argument, NULL, 't' }, + { "min-duration",required_argument, NULL, MIN_DURATION }, + { "scan", no_argument, NULL, SCAN_ONLY }, + { "main-feature",no_argument, NULL, MAIN_FEATURE }, + { "chapters", required_argument, NULL, 'c' }, + { "angle", required_argument, NULL, ANGLE }, + { "markers", optional_argument, NULL, 'm' }, + { "no-markers", no_argument, &chapter_markers, 0 }, + { "audio-lang-list", required_argument, NULL, AUDIO_LANG_LIST }, + { "all-audio", no_argument, &audio_all, 1 }, + { "first-audio", no_argument, &audio_all, 0 }, + { "audio", required_argument, NULL, 'a' }, + { "mixdown", required_argument, NULL, '6' }, + { "normalize-mix", required_argument, NULL, NORMALIZE_MIX }, + { "drc", required_argument, NULL, 'D' }, + { "gain", required_argument, NULL, AUDIO_GAIN }, + { "adither", required_argument, NULL, AUDIO_DITHER }, + { "subtitle-lang-list", required_argument, NULL, SUBTITLE_LANG_LIST }, + { "all-subtitles", no_argument, &subtitle_all, 1 }, + { "first-subtitle", no_argument, &subtitle_all, 0 }, + { "subtitle", required_argument, NULL, 's' }, + { "subtitle-forced", optional_argument, NULL, 'F' }, + { "subtitle-burned", optional_argument, NULL, SUB_BURNED }, + { "subtitle-default", optional_argument, NULL, SUB_DEFAULT }, + { "srt-file", required_argument, NULL, SRT_FILE }, + { "srt-codeset", required_argument, NULL, SRT_CODESET }, + { "srt-offset", required_argument, NULL, SRT_OFFSET }, + { "srt-lang", required_argument, NULL, SRT_LANG }, + { "srt-default", optional_argument, NULL, SRT_DEFAULT }, + { "srt-burn", optional_argument, NULL, SRT_BURN }, + { "native-language", required_argument, NULL,'N' }, + { "native-dub", no_argument, NULL, NATIVE_DUB }, + { "encoder", required_argument, NULL, 'e' }, + { "aencoder", required_argument, NULL, 'E' }, + { "two-pass", no_argument, NULL, '2' }, + { "deinterlace", optional_argument, NULL, 'd' }, + { "no-deinterlace", no_argument, &deinterlace_disable, 1 }, + { "deblock", optional_argument, NULL, '7' }, + { "no-deblock", no_argument, &deblock_disable, 1 }, + { "denoise", optional_argument, NULL, '8' }, + { "hqdn3d", optional_argument, NULL, '8' }, + { "no-hqdn3d", no_argument, &hqdn3d_disable, 1 }, + { "nlmeans", optional_argument, NULL, FILTER_NLMEANS }, + { "no-nlmeans", no_argument, &nlmeans_disable, 1 }, + { "nlmeans-tune",required_argument, NULL, FILTER_NLMEANS_TUNE }, + { "detelecine", optional_argument, NULL, '9' }, + { "no-detelecine", no_argument, &detelecine_disable, 1 }, + { "decomb", optional_argument, NULL, '5' }, + { "no-decomb", no_argument, &decomb_disable, 1 }, + { "grayscale", no_argument, &grayscale, 1 }, + { "no-grayscale",no_argument, &grayscale, 0 }, + { "rotate", optional_argument, NULL, ROTATE_FILTER }, + { "non-anamorphic", no_argument, &anamorphic_mode, 0 }, + { "strict-anamorphic", no_argument, &anamorphic_mode, 1 }, + { "loose-anamorphic", no_argument, &anamorphic_mode, 2 }, + { "custom-anamorphic", no_argument, &anamorphic_mode, 3 }, + { "display-width", required_argument, NULL, DISPLAY_WIDTH }, + { "keep-display-aspect", optional_argument, NULL, KEEP_DISPLAY_ASPECT }, + { "no-keep-display-aspect", no_argument, &keep_display_aspect, 0 }, + { "pixel-aspect", required_argument, NULL, PIXEL_ASPECT }, + { "modulus", required_argument, NULL, MODULUS }, + { "itu-par", no_argument, &itu_par, 1 }, + { "no-itu-par", no_argument, &itu_par, 0 }, + { "width", required_argument, NULL, 'w' }, + { "height", required_argument, NULL, 'l' }, + { "crop", required_argument, NULL, 'n' }, + { "loose-crop", optional_argument, NULL, LOOSE_CROP }, + { "no-loose-crop", no_argument, &loose_crop, 0 }, + + // mapping of legacy option names for backwards compatibility + { "qsv-preset", required_argument, NULL, ENCODER_PRESET, }, + { "x264-preset", required_argument, NULL, ENCODER_PRESET, }, + { "x265-preset", required_argument, NULL, ENCODER_PRESET, }, + { "x264-tune", required_argument, NULL, ENCODER_TUNE, }, + { "x265-tune", required_argument, NULL, ENCODER_TUNE, }, + { "x264-profile", required_argument, NULL, ENCODER_PROFILE, }, + { "h264-profile", required_argument, NULL, ENCODER_PROFILE, }, + { "h265-profile", required_argument, NULL, ENCODER_PROFILE, }, + { "h264-level", required_argument, NULL, ENCODER_LEVEL, }, + { "h265-level", required_argument, NULL, ENCODER_LEVEL, }, + // encoder preset/tune/options/profile/level + { "encoder-preset", required_argument, NULL, ENCODER_PRESET, }, + { "encoder-preset-list", required_argument, NULL, ENCODER_PRESET_LIST, }, + { "encoder-tune", required_argument, NULL, ENCODER_TUNE, }, + { "encoder-tune-list", required_argument, NULL, ENCODER_TUNE_LIST, }, + { "encopts", required_argument, NULL, 'x', }, + { "encoder-profile", required_argument, NULL, ENCODER_PROFILE, }, + { "encoder-profile-list", required_argument, NULL, ENCODER_PROFILE_LIST, }, + { "encoder-level", required_argument, NULL, ENCODER_LEVEL, }, + { "encoder-level-list", required_argument, NULL, ENCODER_LEVEL_LIST, }, + + { "vb", required_argument, NULL, 'b' }, + { "quality", required_argument, NULL, 'q' }, + { "ab", required_argument, NULL, 'B' }, + { "aq", required_argument, NULL, 'Q' }, + { "ac", required_argument, NULL, 'C' }, + { "rate", required_argument, NULL, 'r' }, + { "arate", required_argument, NULL, 'R' }, + { "turbo", no_argument, NULL, 'T' }, + { "maxHeight", required_argument, NULL, 'Y' }, + { "maxWidth", required_argument, NULL, 'X' }, + { "preset", required_argument, NULL, 'Z' }, + { "preset-list", no_argument, NULL, 'z' }, + { "preset-import-file", no_argument, NULL, PRESET_IMPORT }, + { "preset-import-gui", no_argument, NULL, PRESET_IMPORT_GUI }, + { "preset-export", required_argument, NULL, PRESET_EXPORT }, + { "preset-export-file", required_argument, NULL, PRESET_EXPORT_FILE }, + { "preset-export-description", required_argument, NULL, PRESET_EXPORT_DESC }, + + { "aname", required_argument, NULL, 'A' }, + { "color-matrix",required_argument, NULL, 'M' }, + { "previews", required_argument, NULL, PREVIEWS }, + { "start-at-preview", required_argument, NULL, START_AT_PREVIEW }, + { "start-at", required_argument, NULL, START_AT }, + { "stop-at", required_argument, NULL, STOP_AT }, + { "vfr", no_argument, &cfr, 0 }, + { "cfr", no_argument, &cfr, 1 }, + { "pfr", no_argument, &cfr, 2 }, + { "audio-copy-mask", required_argument, NULL, ALLOWED_AUDIO_COPY }, + { "audio-fallback", required_argument, NULL, AUDIO_FALLBACK }, + { 0, 0, 0, 0 } + }; + + int option_index = 0; + int c; + int cur_optind; + + cur_optind = optind; + c = getopt_long( argc, argv, + "hv::uC:f:4i:Io:PUt:c:m::M:a:A:6:s:F::N:e:E:Q:C:" + "2dD:7895gOw:l:n:b:q:S:B:r:R:x:TY:X:Z:z", + long_options, &option_index ); + if( c < 0 ) + { + break; + } + + switch( c ) + { + case 0: + /* option was handled entirely in getopt_long */ + break; + case 'h': + ShowHelp(); + exit( 0 ); + case 'u': + update = 1; + break; + case 'v': + if( optarg != NULL ) + { + debug = atoi( optarg ); } - if (!strcasecmp(preset_name, "Android")) + else { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 22.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac"); - } - if( !abitrates ) - { - abitrates = str_split("128", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2"); - } - if( !arates ) - { - arates = strdup("Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 720; - maxHeight = 576; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("main"); - } - if (h264_level == NULL) - { - h264_level = strdup("3.0"); - } - if( !anamorphic_mode ) - { - anamorphic_mode = 2; - } - modulus = 2; + debug = 1; } - if (!strcasecmp(preset_name, "Android Tablet")) + break; + case 'Z': + preset_name = strdup(optarg); + break; + case 'z': + ShowPresets(NULL, 0, 1); + exit ( 0 ); + case PRESET_EXPORT: + preset_export_name = strdup(optarg); + break; + case PRESET_EXPORT_DESC: + preset_export_desc = strdup(optarg); + break; + case PRESET_EXPORT_FILE: + preset_export_file = strdup(optarg); + break; + case PRESET_IMPORT: + { + // Import list of preset files + while (optind < argc && argv[optind][0] != '-') { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 22.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac"); - } - if( !abitrates ) - { - abitrates = str_split("128", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2"); - } - if( !arates ) - { - arates = strdup("Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 1280; - maxHeight = 720; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("main"); - } - if (h264_level == NULL) - { - h264_level = strdup("3.1"); - } - if( !anamorphic_mode ) + int result = hb_presets_add_path(argv[optind]); + if (result != 0) { - anamorphic_mode = 2; + fprintf(stderr, "Preset import failed, file (%s)\n", + argv[optind]); } - modulus = 2; + optind++; } - if (!strcasecmp(preset_name, "Windows Phone 8")) + } break; + case PRESET_IMPORT_GUI: + hb_presets_gui_init(); + break; + case DVDNAV: + dvdnav = 0; + break; + + case 'f': + format = strdup( optarg ); + break; + case 'i': + input = strdup( optarg ); +#ifdef __APPLE_CC__ + char *devName = bsd_name_for_path( input ); // alloc + if( devName != NULL ) { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 22.0; - filter_vrate.den = 900000; - filter_cfr = 2; - if( !atracks ) - { - atracks = strdup("1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac"); - } - if( !abitrates ) - { - abitrates = str_split("128", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2"); - } - if( !arates ) - { - arates = strdup("Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - maxWidth = 1280; - maxHeight = 720; - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("main"); - } - if (h264_level == NULL) - { - h264_level = strdup("3.1"); - } - if( !anamorphic_mode ) + if( device_is_dvd( devName )) { - anamorphic_mode = 0; + free( input ); + input = malloc( strlen( "/dev/" ) + strlen( devName ) + 1 ); + sprintf( input, "/dev/%s", devName ); } - modulus = 2; + free( devName ); + } +#endif + break; + case 'o': + output = strdup( optarg ); + break; + case 't': + titleindex = atoi( optarg ); + break; + case SCAN_ONLY: + titlescan = 1; + break; + case MAIN_FEATURE: + main_feature = 1; + break; + case 'c': + { + int start, end; + if( sscanf( optarg, "%d-%d", &start, &end ) == 2 ) + { + chapter_start = start; + chapter_end = end; + } + else if( sscanf( optarg, "%d", &start ) == 1 ) + { + chapter_start = start; + chapter_end = chapter_start; + } + else + { + fprintf( stderr, "chapters: Invalid syntax (%s)\n", + optarg ); + return -1; + } + break; + } + case ANGLE: + angle = atoi( optarg ); + break; + case 'm': + if( optarg != NULL ) + { + marker_file = strdup( optarg ); + } + chapter_markers = 1; + break; + case AUDIO_LANG_LIST: + audio_lang_list = str_split(optarg, ','); + break; + case SUBTITLE_LANG_LIST: + subtitle_lang_list = str_split(optarg, ','); + break; + case 'a': + if( optarg != NULL ) + { + atracks = str_split(optarg, ','); + } + else + { + atracks = str_split("1", ','); + } + break; + case '6': + if( optarg != NULL ) + { + mixdowns = str_split(optarg, ','); + } + break; + case 'D': + if( optarg != NULL ) + { + dynamic_range_compression = str_split(optarg, ','); + } + break; + case AUDIO_GAIN: + if( optarg != NULL ) + { + audio_gain = str_split(optarg, ','); } - if (!strcasecmp(preset_name, "Normal")) + break; + case AUDIO_DITHER: + if (optarg != NULL) { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 20.0; - if( !atracks ) - { - atracks = strdup("1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac"); - } - if( !abitrates ) - { - abitrates = str_split("160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2"); - } - if( !arates ) - { - arates = strdup("Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - if (x264_preset == NULL) - { - x264_preset = strdup("veryfast"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("main"); - } - if (h264_level == NULL) - { - h264_level = strdup("4.0"); - } - if( !anamorphic_mode ) - { - anamorphic_mode = 2; - } - modulus = 2; - job->chapter_markers = 1; + audio_dither = str_split(optarg, ','); } - if (!strcasecmp(preset_name, "High Profile")) + break; + case NORMALIZE_MIX: + normalize_mix_level = str_split(optarg, ','); + break; + case 's': + subtracks = str_split( optarg, ',' ); + break; + case 'F': + subforce = str_split( optarg, ',' ); + break; + case SUB_BURNED: + if (optarg != NULL) { - if( !mux ) - { - mux = HB_MUX_MP4; - } - vcodec = HB_VCODEC_X264; - job->vquality = 20.0; - if( !atracks ) - { - atracks = strdup("1,1"); - } - if( !acodecs ) - { - acodecs = strdup("ffaac,copy:ac3"); - } - if( !abitrates ) - { - abitrates = str_split("160,160", ','); - } - if( !mixdowns ) - { - mixdowns = strdup("dpl2,none"); - } - if( !arates ) - { - arates = strdup("Auto,Auto"); - } - if( !dynamic_range_compression ) - { - dynamic_range_compression = strdup("0.0,0.0"); - } - if( allowed_audio_copy == -1 ) - { - allowed_audio_copy = 0; - allowed_audio_copy |= HB_ACODEC_AAC_PASS; - allowed_audio_copy |= HB_ACODEC_AC3_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; - allowed_audio_copy |= HB_ACODEC_DCA_PASS; - allowed_audio_copy |= HB_ACODEC_MP3_PASS; - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - } - if( acodec_fallback == NULL ) - { - acodec_fallback = "ffac3"; - } - if (x264_preset == NULL) - { - x264_preset = strdup("medium"); - } - if (h264_profile == NULL) - { - h264_profile = strdup("high"); - } - if (h264_level == NULL) - { - h264_level = strdup("4.1"); - } - decomb = 1; - if( !anamorphic_mode ) + if (!strcasecmp(optarg, "native") || + !strcasecmp(optarg, "scan")) + subburn_native = 1; + else { - anamorphic_mode = 2; + subburn = strtol(optarg, NULL, 0); } - modulus = 2; - job->chapter_markers = 1; } - } - - if ( chapter_markers ) - { - job->chapter_markers = chapter_markers; - - if( marker_file != NULL ) + else { - hb_csv_file_t * file = hb_open_csv_file( marker_file ); - hb_csv_cell_t * cell; - int row = 0; - int chapter = 0; - - fprintf( stderr, "Reading chapter markers from file %s\n", marker_file ); - - if( file == NULL ) - { - fprintf( stderr, "Cannot open chapter marker file, using defaults\n" ); - } - else + subburn = 1; + } + if (subburn > 0) + { + if (subtracks != NULL && str_vlen(subtracks) >= subburn && + !strcasecmp("scan", subtracks[subburn-1])) { - /* Parse the cells */ - while( NULL != ( cell = hb_read_next_cell( file ) ) ) - { - /* We have a chapter number */ - if( cell->cell_col == 0 ) - { - row = cell->cell_row; - chapter = atoi( cell->cell_text ); - } - - /* We have a chapter name */ - if( cell->cell_col == 1 && row == cell->cell_row ) - { - /* If we have a valid chapter, copy the string an terminate it */ - if( chapter >= job->chapter_start && chapter <= job->chapter_end ) - { - hb_chapter_t * chapter_s; - - chapter_s = hb_list_item( job->list_chapter, chapter - 1); - hb_chapter_set_title(chapter_s, cell->cell_text); - } - } - - - hb_dispose_cell( cell ); - } - hb_close_csv_file( file ); + subburn_native = 1; } } - } - - if (crop[0] < 0 || crop[1] < 0 || crop[2] < 0 || crop[3] < 0) - { - memcpy(crop, title->crop, sizeof(int[4])); - } - - if (loose_crop >= 0) - { - int mod = modulus > 0 ? modulus : 2; - apply_loose_crop(title->geometry.height, - &crop[0], &crop[1], mod, loose_crop); - apply_loose_crop(title->geometry.width, - &crop[2], &crop[3], mod, loose_crop); - } - - job->grayscale = grayscale; - - hb_filter_object_t * filter; - - /* Add selected filters */ - if( detelecine ) - { - filter = hb_filter_init( HB_FILTER_DETELECINE ); - hb_add_filter( job, filter, detelecine_opt ); - } - if( decomb ) - { - filter = hb_filter_init( HB_FILTER_DECOMB ); - hb_add_filter( job, filter, decomb_opt ); - } - if( deinterlace ) - { - filter = hb_filter_init( HB_FILTER_DEINTERLACE ); - hb_add_filter( job, filter, deinterlace_opt ); - } - if( deblock ) - { - filter = hb_filter_init( HB_FILTER_DEBLOCK ); - hb_add_filter( job, filter, deblock_opt ); - } - if( denoise ) - { - filter = hb_filter_init( HB_FILTER_DENOISE ); - hb_add_filter( job, filter, denoise_opt ); - } - if( nlmeans ) - { - filter = hb_filter_init( HB_FILTER_NLMEANS ); - hb_add_filter( job, filter, nlmeans_opt ); - } - if( rotate ) - { - filter = hb_filter_init( HB_FILTER_ROTATE ); - hb_add_filter( job, filter, rotate_opt); - } - - if (use_hwd) - { - job->use_hwd = use_hwd; - } - - hb_geometry_t srcGeo, resultGeo; - hb_geometry_settings_t uiGeo; - - srcGeo = title->geometry; - - keep_display_aspect |= anamorphic_mode != HB_ANAMORPHIC_CUSTOM; - uiGeo.mode = anamorphic_mode; - if (width != 0 && height != 0) - { - if (anamorphic_mode == HB_ANAMORPHIC_NONE) + break; + case SUB_DEFAULT: + if (optarg != NULL) { - keep_display_aspect = 0; + subdefault = strtol(optarg, NULL, 0); } else { - uiGeo.mode = HB_ANAMORPHIC_CUSTOM; + subdefault = 1; } - } - uiGeo.keep = !!keep_display_aspect * HB_KEEP_DISPLAY_ASPECT; - uiGeo.itu_par = itu_par; - uiGeo.modulus = modulus; - memcpy(uiGeo.crop, crop, sizeof(int[4])); - if (width == 0) - { - uiGeo.geometry.width = title->geometry.width - crop[2] - crop[3]; - } - else - { - uiGeo.keep |= HB_KEEP_WIDTH; - uiGeo.geometry.width = width; - } - if (height == 0) - { - uiGeo.geometry.height = title->geometry.height - crop[0] - crop[1]; - } - else - { - uiGeo.keep |= HB_KEEP_HEIGHT; - uiGeo.geometry.height = height; - } - uiGeo.maxWidth = maxWidth; - uiGeo.maxHeight = maxHeight; - if( par_width && par_height ) - { - uiGeo.geometry.par.num = par_width; - uiGeo.geometry.par.den = par_height; - } - else if (display_width != 0 && width != 0) + break; + case 'N': { - if (height != 0) + const iso639_lang_t *lang = lang_lookup(optarg); + if (lang != NULL) { - fprintf(stderr, "display_width (%d), width (%d), and height (%d) can not all be specified, ignoring height", display_width, width, height); + native_language = strdup(lang->iso639_2); } - uiGeo.geometry.par.num = display_width; - uiGeo.geometry.par.den = width; - } - else if (display_width != 0) - { - uiGeo.geometry.par.num = display_width; - uiGeo.geometry.par.den = uiGeo.geometry.width; - } - else - { - uiGeo.geometry.par = srcGeo.par; - } - - hb_set_anamorphic_size2(&srcGeo, &uiGeo, &resultGeo); - job->par = resultGeo.par; - - // Add filter that does cropping and scaling - char * filter_str; - filter_str = hb_strdup_printf("%d:%d:%d:%d:%d:%d", - resultGeo.width, resultGeo.height, - crop[0], crop[1], crop[2], crop[3] ); - - filter = hb_filter_init( HB_FILTER_CROP_SCALE ); - hb_add_filter( job, filter, filter_str ); - free( filter_str ); - - // Add framerate shaping filter - if (vrate) - { - filter_cfr = cfr; - filter_vrate.num = 27000000; - filter_vrate.den = vrate; - } - else if (cfr) - { - // cfr or pfr flag with no rate specified implies - // use the title rate. - filter_cfr = cfr; - filter_vrate = title->vrate; - } - filter = hb_filter_init(HB_FILTER_VFR); - filter_str = hb_strdup_printf("%d:%d:%d", filter_cfr, - filter_vrate.num, filter_vrate.den); - hb_add_filter(job, filter, filter_str); - free(filter_str); - - // hb_job_init() will set a default muxer for us - // only override it if a specific muxer has been set - // note: the muxer must be set after presets, but before encoders - if (mux) - { - job->mux = mux; - } - // then, muxer options - if (mp4_optimize) - { - job->mp4_optimize = 1; - } - if (ipod_atom) - { - job->ipod_atom = 1; - } - - if( vquality >= 0.0 ) - { - job->vquality = vquality; - job->vbitrate = 0; - } - else if( vbitrate ) - { - job->vquality = -1.0; - job->vbitrate = vbitrate; - } - - /* Set video encoder and check muxer compatibility */ - if (vcodec) - { - job->vcodec = vcodec; - } - encoder = NULL; - while ((encoder = hb_video_encoder_get_next(encoder)) != NULL) - { - if ((encoder->codec == job->vcodec) && - (encoder->muxers & job->mux) == 0) + else { - hb_error("incompatible video encoder '%s' for muxer '%s'", - hb_video_encoder_get_short_name(job->vcodec), - hb_container_get_short_name (job->mux)); - done_error = HB_ERROR_INIT; - die = 1; + fprintf(stderr, "Invalid native language (%s)\n", optarg); return -1; } - } - -#ifdef USE_QSV - if (qsv_async_depth >= 0) - { - job->qsv.async_depth = qsv_async_depth; - } - job->qsv.decode = qsv_decode; -#endif - - /* Grab audio tracks */ - if( atracks ) - { - char * token = strtok( atracks, "," ); - if( token == NULL ) - token = optarg; - int track_start, track_end; - for( ; token != NULL; token = strtok( NULL, "," ) ) - { - if( strlen( token ) >= 3 ) - { - if( sscanf( token, "%d-%d", &track_start, &track_end ) == 2 ) - { - int i; - for( i = track_start - 1; i < track_end; i++ ) - { - if( hb_list_item( title->list_audio, i ) == NULL ) - { - fprintf( stderr, "Warning: Could not find audio track %d, skipped\n", i + 1 ); - continue; - } - audio = calloc( 1, sizeof( *audio ) ); - hb_audio_config_init( audio ); - audio->in.track = i; - audio->out.track = num_audio_tracks++; - hb_list_add( audios, audio ); - } - } - else if( !strcasecmp(token, "none" ) ) - { - audio = calloc( 1, sizeof( *audio ) ); - hb_audio_config_init( audio ); - audio->in.track = audio->out.track = -1; - audio->out.codec = 0; - hb_list_add( audios, audio ); - break; - } - else - { - fprintf( stderr, "ERROR: unable to parse audio input \"%s\", skipping\n", - token); - } - } - else - { - int i = atoi( token ) - 1; - if( hb_list_item( title->list_audio, i ) == NULL ) - { - fprintf(stderr, - "Warning: Could not find audio track '%s', skipped\n", - token); - continue; - } - audio = calloc( 1, sizeof( *audio ) ); - hb_audio_config_init( audio ); - audio->in.track = i; - audio->out.track = num_audio_tracks++; - hb_list_add( audios, audio ); - } - } - } - - /* Parse audio tracks */ - if( native_language && native_dub ) - { - if( hb_list_count( audios ) == 0 || !audio_explicit ) + } break; + case NATIVE_DUB: + native_dub = 1; + break; + case SRT_FILE: + srtfile = str_split( optarg, ',' ); + break; + case SRT_CODESET: + srtcodeset = str_split( optarg, ',' ); + break; + case SRT_OFFSET: + srtoffset = str_split( optarg, ',' ); + break; + case SRT_LANG: + srtlang = str_split( optarg, ',' ); + break; + case SRT_DEFAULT: + if( optarg != NULL ) { - for( i = 0; i < hb_list_count( title->list_audio ); i++ ) - { - int track = i; - - audio = hb_list_audio_config_item( title->list_audio, i ); - - if( cmp_lang( native_language, audio->lang.iso639_2 ) && - audio->lang.type != 3 && // Directors 1 - audio->lang.type != 4) // Directors 2 - { - /* - * Matched an audio to our native language - use it. - * Replace any existing audio tracks that a preset may - * have put here. - */ - if( hb_list_count( audios ) == 0 ) - { - audio = calloc( 1, sizeof( *audio ) ); - hb_audio_config_init( audio ); - audio->in.track = track; - audio->out.track = num_audio_tracks++; - /* Add it to our audios */ - hb_list_add( audios, audio ); - } - else - { - /* - * Update the track numbers on what is already in - * there. - */ - for( i = 0; i < hb_list_count( audios ); i++ ) - { - audio = hb_list_item( audios, i ); - audio->in.track = track; - } - } - break; - } - } + srtdefault = atoi( optarg ); } else { - fprintf( stderr, "Warning: Native language (dubbing) selection ignored since an audio track has already been selected\n" ); + srtdefault = 1 ; } - } - - if( hb_list_count(audios) == 0 && - hb_list_count(title->list_audio) > 0 ) - { - /* Create a new audio track with default settings */ - audio = calloc( 1, sizeof( *audio ) ); - hb_audio_config_init( audio ); - /* Add it to our audios */ - hb_list_add( audios, audio ); - } - tmp_num_audio_tracks = num_audio_tracks = hb_list_count( audios ); - for( i = 0; i < tmp_num_audio_tracks; i++ ) - { - audio = hb_list_item( audios, 0 ); - if( audio == NULL || - audio->in.track == -1 || - audio->out.track == -1 || - audio->out.codec == 0 || - hb_audio_add( job, audio ) == 0 ) + break; + case SRT_BURN: + if( optarg != NULL ) { - num_audio_tracks--; + srtburn = atoi( optarg ); } - if( audio != NULL ) + else { - hb_list_rem( audios, audio ); - if( audio->out.name ) - { - free( audio->out.name ); - } - free( audio ); + srtburn = 1 ; } - } - - /* Audio Codecs */ - i = 0; - if( acodecs ) - { - char * token = strtok(acodecs, ","); - if( token == NULL ) - token = acodecs; - while ( token != NULL ) + break; + case '2': + twoPass = 1; + break; + case 'd': + free(deinterlace); + if (optarg != NULL) { - if ((acodec = hb_audio_encoder_get_from_name(token)) <= 0) - { - fprintf(stderr, "Invalid codec %s, using default for container.\n", token); - acodec = hb_audio_encoder_get_default(job->mux); - } - if( i < num_audio_tracks ) - { - audio = hb_list_audio_config_item(job->list_audio, i); - audio->out.codec = acodec; - } - else - { - hb_audio_config_t * last_audio = hb_list_audio_config_item( job->list_audio, i - 1 ); - hb_audio_config_t audio; - - if( last_audio ) - { - fprintf(stderr, "More audio codecs than audio tracks, copying track %i and using encoder %s\n", - i, token); - hb_audio_config_init(&audio); - audio.in.track = last_audio->in.track; - audio.out.track = num_audio_tracks++; - audio.out.codec = acodec; - hb_audio_add(job, &audio); - } - else - { - fprintf(stderr, "Audio codecs and no valid audio tracks, skipping codec %s\n", token); - } - } - token = strtok(NULL, ","); - i++; + deinterlace = strdup(optarg); } - } - if( i < num_audio_tracks ) - { - /* We have fewer inputs than audio tracks, use the default codec for - * this container for the remaining tracks. Unless we only have one input - * then use that codec instead. - */ - if (i != 1) - acodec = hb_audio_encoder_get_default(job->mux); - for ( ; i < num_audio_tracks; i++) + else { - audio = hb_list_audio_config_item(job->list_audio, i); - audio->out.codec = acodec; + deinterlace = strdup("default"); } - } - // sanity check muxer compatibility - for (i = 0; i < num_audio_tracks; i++) - { - encoder = NULL; - audio = hb_list_audio_config_item(job->list_audio, i); - if (audio != NULL) + break; + case '7': + free(deblock); + if( optarg != NULL ) { - while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) - { - if ((encoder->codec == audio->out.codec) && - (encoder->muxers & job->mux) == 0) - { - hb_error("audio track %d: incompatible encoder '%s' for muxer '%s'", i + 1, - hb_audio_encoder_get_short_name(audio->out.codec), - hb_container_get_short_name (job->mux)); - done_error = HB_ERROR_INIT; - die = 1; - return -1; - } - } + deblock = strdup(optarg); } - } - /* Audio Codecs */ - - /* Sample Rate */ - int auto_sample_rate = 0; - i = 0; - if( arates ) - { - char * token = strtok(arates, ","); - if (token == NULL) - token = arates; - while ( token != NULL ) + else { - audio = hb_list_audio_config_item(job->list_audio, i); - - if( audio != NULL ) - { - if ( !strcasecmp( token, "auto" ) ) - { - arate = audio->in.samplerate; - auto_sample_rate = 1; - } - else - { - arate = hb_audio_samplerate_get_from_name(token); - } - if (arate <= 0) - { - fprintf(stderr, - "Invalid sample rate %s, using input rate %d\n", - token, audio->in.samplerate); - arate = audio->in.samplerate; - } - - audio->out.samplerate = arate; - if( (++i) >= num_audio_tracks ) - break; /* We have more inputs than audio tracks, oops */ - } - else - { - fprintf(stderr, "Ignoring sample rate %d, no audio tracks\n", arate); - } - token = strtok(NULL, ","); + deblock = strdup("5"); } - } - if (i < num_audio_tracks) - { - /* We have fewer inputs than audio tracks, use default sample rate. - * Unless we only have one input, then use that for all tracks. - */ - int use_default = 0; - if( i != 1 || auto_sample_rate ) - use_default = 1; - - for ( ; i < num_audio_tracks; i++) + break; + case '8': + free(hqdn3d); + if (optarg != NULL) { - audio = hb_list_audio_config_item(job->list_audio, i); - if( use_default ) - arate = audio->in.samplerate; - audio->out.samplerate = arate; + hqdn3d = strdup(optarg); } - } - /* Sample Rate */ - - /* Audio Mixdown */ - i = 0; - if ( mixdowns ) - { - char * token = strtok(mixdowns, ","); - if (token == NULL) - token = mixdowns; - while ( token != NULL ) + else { - mixdown = hb_mixdown_get_from_name(token); - audio = hb_list_audio_config_item(job->list_audio, i); - if( audio != NULL ) - { - audio->out.mixdown = mixdown; - if( (++i) >= num_audio_tracks ) - break; /* We have more inputs than audio tracks, oops */ - } - else - { - fprintf(stderr, "Ignoring mixdown, no audio tracks\n"); - } - token = strtok(NULL, ","); + hqdn3d = strdup("default"); } - } - if (i < num_audio_tracks && i == 1) - { - /* We have fewer inputs than audio tracks - * and we only have one input, then use that. - */ - for (; i < num_audio_tracks; i++) + break; + case FILTER_NLMEANS: + free(nlmeans); + if (optarg != NULL) { - audio = hb_list_audio_config_item(job->list_audio, i); - audio->out.mixdown = mixdown; + nlmeans = strdup(optarg); } - } - /* Audio Mixdown */ - - /* Audio Bitrate */ - i = 0; - if( abitrates ) - { - for ( i = 0; abitrates[i] != NULL && i < num_audio_tracks; i++ ) + else { - char * token = abitrates[i]; - abitrate = atoi(token); - audio = hb_list_audio_config_item(job->list_audio, i); - - if( audio != NULL ) - { - audio->out.bitrate = abitrate; - } - else - { - fprintf(stderr, "Ignoring bitrate %d, no audio tracks\n", abitrate); - } + nlmeans = strdup("light"); } - } - if (i < num_audio_tracks && i == 1) - { - /* We have fewer inputs than audio tracks, - * and we only have one input, use - * that for all tracks. - */ - for (; i < num_audio_tracks; i++) + break; + case FILTER_NLMEANS_TUNE: + free(nlmeans_tune); + nlmeans_tune = strdup(optarg); + break; + case '9': + free(detelecine); + if (optarg != NULL) { - audio = hb_list_audio_config_item(job->list_audio, i); - audio->out.bitrate = abitrate; + detelecine = strdup(optarg); } - } - /* Audio Bitrate */ - - /* Audio Quality */ - i = 0; - if( aqualities ) - { - for ( i = 0; aqualities[i] != NULL && i < num_audio_tracks; i++ ) + else { - char * token = aqualities[i]; - audio = hb_list_audio_config_item(job->list_audio, i); - if( audio == NULL ) - { - fprintf(stderr, "Ignoring quality %.3f, no audio tracks\n", aquality); - } - else if( *token != 0 ) - { - aquality = atof(token); - - audio->out.quality = aquality; - audio->out.bitrate = -1; - } + detelecine = strdup("default"); } - } - if (i < num_audio_tracks && i == 1) - { - /* We have fewer inputs than audio tracks, - * and we only have one input, use - * that for all tracks. - */ - for (; i < num_audio_tracks; i++) + break; + case '5': + free(decomb); + if (optarg != NULL) { - audio = hb_list_audio_config_item(job->list_audio, i); - if( audio->out.bitrate <= 0 ) - audio->out.quality = aquality; + decomb = strdup(optarg); } - } - /* Audio Quality */ - - /* Audio Compression Level */ - i = 0; - if( acompressions ) - { - for ( i = 0; acompressions[i] != NULL && i < num_audio_tracks; i++ ) + else { - char * token = acompressions[i]; - audio = hb_list_audio_config_item(job->list_audio, i); - if( audio == NULL ) - { - fprintf(stderr, "Ignoring compression level %.2f, no audio tracks\n", acompression); - } - else if( *token != 0 ) - { - acompression = atof(token); - - audio->out.compression_level = acompression; - } + decomb = strdup("default"); } - } - // Compression levels are codec specific values. So don't - // try to apply to other tracks. - /* Audio Compression Level */ - - /* Audio DRC */ - i = 0; - if ( dynamic_range_compression ) - { - char * token = strtok(dynamic_range_compression, ","); - if (token == NULL) - token = dynamic_range_compression; - while ( token != NULL ) + break; + case ROTATE_FILTER: + free(rotate); + if (optarg != NULL) { - d_r_c = atof(token); - audio = hb_list_audio_config_item(job->list_audio, i); - if( audio != NULL ) - { - audio->out.dynamic_range_compression = d_r_c; - if( (++i) >= num_audio_tracks ) - break; /* We have more inputs than audio tracks, oops */ - } - else - { - fprintf(stderr, "Ignoring drc, no audio tracks\n"); - } - token = strtok(NULL, ","); + rotate = strdup(optarg); } - } - if (i < num_audio_tracks) - { - /* We have fewer inputs than audio tracks, use no DRC for the remaining - * tracks. Unless we only have one input, then use the same DRC for all - * tracks. - */ - if (i != 1) - d_r_c = 0; - for (; i < num_audio_tracks; i++) + else { - audio = hb_list_audio_config_item(job->list_audio, i); - audio->out.dynamic_range_compression = d_r_c; + rotate = strdup("3"); } - } - /* Audio DRC */ - - /* Audio Gain */ - i = 0; - if ( audio_gain ) - { - char * token = strtok(audio_gain, ","); - if (token == NULL) - token = audio_gain; - while ( token != NULL ) + break; + case KEEP_DISPLAY_ASPECT: + if( optarg != NULL ) { - gain = atof(token); - audio = hb_list_audio_config_item(job->list_audio, i); - if( audio != NULL ) - { - audio->out.gain = gain; - if( (++i) >= num_audio_tracks ) - break; /* We have more inputs than audio tracks, oops */ - } - else - { - fprintf(stderr, "Ignoring gain, no audio tracks\n"); - } - token = strtok(NULL, ","); + keep_display_aspect = atoi(optarg); } - } - if (i < num_audio_tracks) - { - /* We have fewer inputs than audio tracks, use no gain for the remaining - * tracks. Unless we only have one input, then use the same gain for all - * tracks. - */ - if (i != 1) - gain = 0; - for (; i < num_audio_tracks; i++) + else { - audio = hb_list_audio_config_item(job->list_audio, i); - audio->out.gain = gain; + keep_display_aspect = 1; } - } - /* Audio Gain */ - - /* Audio Dither */ - if (audio_dither != NULL) - { - int dither_method = hb_audio_dither_get_default(); - for (i = 0; audio_dither[i] != NULL; i++) + break; + case DISPLAY_WIDTH: + if( optarg != NULL ) { - dither_method = hb_audio_dither_get_from_name(audio_dither[i]); - audio = hb_list_audio_config_item(job->list_audio, i); - if (audio != NULL) - { - if (hb_audio_dither_is_supported(audio->out.codec)) - { - audio->out.dither_method = dither_method; - } - else if (dither_method != hb_audio_dither_get_default()) - { - fprintf(stderr, - "Ignoring dither %s, not supported by codec\n", - audio_dither[i]); - } - } - else - { - fprintf(stderr, "Ignoring dither %s, no audio tracks\n", - audio_dither[i]); - } + display_width = atoi(optarg); } - if (i < num_audio_tracks && i == 1) + break; + case PIXEL_ASPECT: + if( optarg != NULL ) { - /* - * We have fewer inputs than audio tracks, and we only have - * one input: use that for all tracks. - */ - while (i < num_audio_tracks) - { - audio = hb_list_audio_config_item(job->list_audio, i); - if (hb_audio_dither_is_supported(audio->out.codec)) - { - audio->out.dither_method = dither_method; - } - else if (dither_method != hb_audio_dither_get_default()) - { - fprintf(stderr, - "Ignoring dither %s, not supported by codec\n", - audio_dither[0]); - } - i++; - } + sscanf(optarg, "%i:%i", &par_width, &par_height); } - } - /* Audio Dither */ - - /* Audio Mix Normalization */ - i = 0; - int norm = 0; - if( normalize_mix_level ) - { - for ( i = 0; normalize_mix_level[i] != NULL && i < num_audio_tracks; i++ ) + break; + case MODULUS: + if( optarg != NULL ) { - char * token = normalize_mix_level[i]; - norm = atoi(token); - audio = hb_list_audio_config_item(job->list_audio, i); - - if( audio != NULL ) - { - audio->out.normalize_mix_level = norm; - } - else - { - fprintf(stderr, "Ignoring normalization %d, no audio tracks\n", norm); - } + modulus = atoi(optarg); } - } - if (i < num_audio_tracks && i == 1) + break; + case 'e': { - /* We have fewer inputs than audio tracks, - * and we only have one input, use - * that for all tracks. - */ - for (; i < num_audio_tracks; i++) + vcodec = strdup(optarg); + break; + } + case 'E': + if( optarg != NULL ) { - audio = hb_list_audio_config_item(job->list_audio, i); - audio->out.normalize_mix_level = norm; + acodecs = str_split(optarg, ','); } - } - /* Audio Mix Normalization */ - - /* Audio Track Names */ - if ( anames ) + break; + case 'w': + width = atoi( optarg ); + break; + case 'l': + height = atoi( optarg ); + break; + case 'n': { - char * token; - for ( i = 0; anames[i] != NULL && i < num_audio_tracks; i++ ) + int i; + char * tmp = optarg; + for( i = 0; i < 4; i++ ) { - token = anames[i]; - if ( *token ) - { - audio = hb_list_audio_config_item(job->list_audio, i); - if( audio != NULL ) - { - audio->out.name = strdup(token); - } - else - { - fprintf(stderr, "Ignoring aname '%s', no audio track\n", - token); - } - } + if( !*tmp ) + break; + crop[i] = strtol( tmp, &tmp, 0 ); + tmp++; } + break; } - if( i < num_audio_tracks && i == 1 ) + case LOOSE_CROP: + if (optarg != NULL) + loose_crop = atoi(optarg); + else + loose_crop = 1; + break; + case 'r': { - /* We have exactly one name and more than one audio track. Use the same - * name for all tracks. */ - for ( ; i < num_audio_tracks; i++) + vrate = strdup(optarg); + if (!cfr) { - audio = hb_list_audio_config_item(job->list_audio, i); - audio->out.name = strdup(anames[0]); + cfr = 1; } + break; } - /* Audio Track Names */ - - /* Sanitize passthru (drop/fallback if necessary) */ - for( i = 0; i < hb_list_count( job->list_audio ); ) - { - audio = hb_list_audio_config_item( job->list_audio, i ); - if( audio->out.codec == HB_ACODEC_AUTO_PASS ) + case 'R': + arates = str_split( optarg, ',' ); + break; + case 'b': + vbitrate = atoi( optarg ); + break; + case 'q': + vquality = atof( optarg ); + break; + case 'B': + abitrates = str_split( optarg, ',' ); + break; + case 'Q': + aqualities = str_split( optarg, ',' ); + break; + case 'C': + acompressions = str_split( optarg, ',' ); + break; + case ENCODER_PRESET: + encoder_preset = strdup( optarg ); + break; + case ENCODER_TUNE: + encoder_tune = strdup( optarg ); + break; + case 'x': + advanced_opts = strdup( optarg ); + break; + case ENCODER_PROFILE: + encoder_profile = strdup( optarg ); + break; + case ENCODER_LEVEL: + encoder_level = strdup( optarg ); + break; + case ENCODER_PRESET_LIST: + fprintf(stderr, "Available --encoder-preset values for '%s' encoder:\n", + hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg))); + print_string_list(stderr, + hb_video_encoder_get_presets(hb_video_encoder_get_from_name(optarg)), + " "); + exit(0); + case ENCODER_TUNE_LIST: + fprintf(stderr, "Available --encoder-tune values for '%s' encoder:\n", + hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg))); + print_string_list(stderr, + hb_video_encoder_get_tunes(hb_video_encoder_get_from_name(optarg)), + " "); + exit(0); + case ENCODER_PROFILE_LIST: + fprintf(stderr, "Available --encoder-profile values for '%s' encoder:\n", + hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg))); + print_string_list(stderr, + hb_video_encoder_get_profiles(hb_video_encoder_get_from_name(optarg)), + " "); + exit(0); + case ENCODER_LEVEL_LIST: + fprintf(stderr, "Available --encoder-level values for '%s' encoder:\n", + hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg))); + print_string_list(stderr, + hb_video_encoder_get_levels(hb_video_encoder_get_from_name(optarg)), + " "); + exit(0); + case 'T': + fastfirstpass = 1; + break; + case 'Y': + maxHeight = atoi( optarg ); + break; + case 'X': + maxWidth = atoi (optarg ); + break; + case 'A': + if( optarg != NULL ) { - // Auto Passthru - job->acodec_copy_mask = allowed_audio_copy == -1 ? HB_ACODEC_PASS_MASK : allowed_audio_copy; - job->acodec_fallback = hb_audio_encoder_get_from_name(acodec_fallback); + anames = str_split( optarg, ',' ); } - else if( ( audio->out.codec & HB_ACODEC_PASS_FLAG ) && - !( audio->out.codec & audio->in.codec & HB_ACODEC_PASS_MASK ) ) + break; + case PREVIEWS: + sscanf( optarg, "%i:%i", &preview_count, &store_previews ); + break; + case START_AT_PREVIEW: + start_at_preview = atoi( optarg ); + break; + case START_AT: + { + char * start_at_string = NULL; + char * start_at_token = NULL; + start_at_string = strdup( optarg ); + start_at_token = strtok( start_at_string, ":"); + if( !strcmp( start_at_token, "frame" ) ) { - // passthru fallbacks - int requested_passthru = audio->out.codec; - audio->out.codec = - hb_audio_encoder_get_fallback_for_passthru(requested_passthru); - if (!(audio->out.codec & HB_ACODEC_MASK)) - { - // Passthru not possible, drop audio. - fprintf(stderr, - "Passthru requested and input codec is not the same as output codec for track %d, dropping track\n", - audio->out.track); - hb_audio_t *item = hb_list_item(job->list_audio, i); - hb_list_rem(job->list_audio, item); - hb_audio_close(&item); - continue; - } - fprintf(stderr, - "%s requested and input codec is not compatible for track %d, using %s encoder\n", - hb_audio_encoder_get_name(requested_passthru), audio->out.track, - hb_audio_encoder_get_name(audio->out.codec)); + start_at_token = strtok( NULL, ":"); + start_at_frame = atoi(start_at_token); } - // we didn't drop the track - i++; - } - - if( subtracks ) - { - char * token; - int i; - int burnpos = 0, defaultpos = 0; - - if ( subburn ) - burnpos = strtol( subburn, NULL, 0 ); - if ( subdefault ) - defaultpos = strtol( subdefault, NULL, 0 ); - for ( i = 0; subtracks[i] != NULL; i++ ) + else if( !strcmp( start_at_token, "pts" ) ) { - token = subtracks[i]; - if( strcasecmp(token, "scan" ) == 0 ) - { - int burn = 0, force = 0, def = 0; - - if ( subburn != NULL ) - { - burn = ( i == 0 && subburn[0] == 0 ) || - ( burnpos == i+1 ); - } - if ( subdefault != NULL ) - { - def = ( i == 0 && subdefault[0] == 0 ) || - ( defaultpos == i+1 ); - } - force = test_sub_list( subforce, i+1 ); - - if ( !burn ) - { - job->select_subtitle_config.dest = PASSTHRUSUB; - } - else - { - if ( sub_burned ) - { - continue; - } - sub_burned = 1; - } - job->select_subtitle_config.force = force; - job->select_subtitle_config.default_track = def; - subtitle_scan = 1; - } - else - { - hb_subtitle_t * subtitle; - hb_subtitle_config_t sub_config; - int track; - int burn = 0, force = 0, def = 0; - - track = atoi(token) - 1; - subtitle = hb_list_item(title->list_subtitle, track); - if( subtitle == NULL ) - { - fprintf(stderr, - "Warning: Could not find subtitle track '%s', skipped\n", - token); - continue; - } - sub_config = subtitle->config; - - if ( subburn != NULL ) - { - burn = ( i == 0 && subburn[0] == 0 ) || - ( burnpos == i+1 ); - } - if ( subdefault != NULL ) - { - def = ( i == 0 && subdefault[0] == 0 ) || - ( defaultpos == i+1 ); - } - - force = test_sub_list(subforce, i+1); - - int supports_burn = hb_subtitle_can_burn( subtitle->source ); - - if ( ( burn && supports_burn ) || - !hb_subtitle_can_pass( subtitle->source, mux ) ) - { - // Only allow one subtitle to be burned into video - if ( sub_burned ) - { - fprintf( stderr, "Warning: Skipping subtitle track %d, can't have more than one track burnt in\n", track+1 ); - continue; - } - sub_burned = 1; - - // Mark as burn-in - sub_config.dest = RENDERSUB; - } - else - { - sub_config.dest = PASSTHRUSUB; - } - sub_config.force = force; - sub_config.default_track = def; - hb_subtitle_add( job, &sub_config, track ); - } + start_at_token = strtok( NULL, ":"); + sscanf( start_at_token, "%"SCNd64, &start_at_pts ); } - } - - if( srtfile ) - { - int i; - hb_subtitle_config_t sub_config; - - for( i=0; srtfile[i] != NULL; i++ ) + else if( !strcmp( start_at_token, "duration" ) ) { - char *codeset = "L1"; - int64_t offset = 0; - char *lang = "und"; - - if (srtburn == i + 1 && hb_subtitle_can_burn(SRTSUB)) - { - // Only allow one subtitle to be burned into video - if ( sub_burned ) - { - fprintf( stderr, "Warning: Skipping SRT track %d, can't have more than one track burnt in\n", i+1 ); - continue; - } - sub_burned = 1; - - // Mark as burn-in - sub_config.dest = RENDERSUB; - } - else - { - sub_config.dest = PASSTHRUSUB; - } - if( srtcodeset && srtcodeset[i] ) - { - codeset = srtcodeset[i]; - } - if( srtoffset && srtoffset[i] ) - { - offset = strtoll( srtoffset[i], &srtoffset[i], 0 ); - } - if ( srtlang && srtlang[i] ) - { - lang = srtlang[i]; - } - sub_config.default_track = srtdefault == i + 1; - sub_config.force = 0; - strncpy( sub_config.src_filename, srtfile[i], 255); - sub_config.src_filename[255] = 0; - strncpy( sub_config.src_codeset, codeset, 39); - sub_config.src_codeset[39] = 0; - sub_config.offset = offset; - - hb_srt_add( job, &sub_config, lang); + double duration_seconds = parse_hhmmss_strtok(); + start_at_pts = (int64_t)(duration_seconds * 90e3); } + free( start_at_string ); + break; } - - if ( sub_burned ) - { - char * filter_str; - filter_str = hb_strdup_printf("%d:%d:%d:%d", - crop[0], crop[1], crop[2], crop[3] ); - filter = hb_filter_init( HB_FILTER_RENDER_SUB ); - hb_add_filter( job, filter, filter_str); - free( filter_str ); - } - - if( native_language ) + case STOP_AT: { - audio = hb_list_audio_config_item(job->list_audio, 0); - - if( audio ) + char * stop_at_string = NULL; + char * stop_at_token = NULL; + stop_at_string = strdup( optarg ); + stop_at_token = strtok( stop_at_string, ":"); + if( !strcmp( stop_at_token, "frame" ) ) { - if( !cmp_lang( native_language, audio->lang.iso639_2 ) ) - { - /* - * Audio language is not the same as our native language. - * If we have any subtitles in our native language they - * should be selected here if they haven't already been. - */ - hb_subtitle_t *subtitle, *subtitle2 = NULL; - int matched_track = 0; - - for( i = 0; i < hb_list_count( title->list_subtitle ); i++ ) - { - subtitle = hb_list_item( title->list_subtitle, i ); - matched_track = i; - if (cmp_lang(native_language, subtitle->iso639_2)) - { - /* - * Found the first matching subtitle in our - * native language. Is it already selected? - */ - for( i = 0; i < hb_list_count( job->list_subtitle ); i++ ) - { - subtitle2 = hb_list_item( job->list_subtitle, i ); - - if( subtitle2->track == subtitle->track) { - /* - * Already selected - */ - break; - } - subtitle2 = NULL; - } - - if( subtitle2 == NULL ) - { - /* - * Not already selected, so select it. - */ - hb_subtitle_config_t sub_config; - - if( native_dub ) - { - fprintf( stderr, "Warning: no matching audio for native language - using subtitles instead.\n"); - } - sub_config = subtitle->config; - - if ((mux & HB_MUX_MASK_MKV) || subtitle->format == TEXTSUB) - { - sub_config.dest = PASSTHRUSUB; - } - - sub_config.force = 0; - sub_config.default_track = 1; - hb_subtitle_add( job, &sub_config, matched_track); - } - /* - * Stop searching. - */ - break; - } - } - } + stop_at_token = strtok( NULL, ":"); + stop_at_frame = atoi(stop_at_token); } + else if( !strcmp( stop_at_token, "pts" ) ) + { + stop_at_token = strtok( NULL, ":"); + sscanf( stop_at_token, "%"SCNd64, &stop_at_pts ); + } + else if( !strcmp( stop_at_token, "duration" ) ) + { + double duration_seconds = parse_hhmmss_strtok(); + stop_at_pts = (int64_t)(duration_seconds * 90e3); + } + free( stop_at_string ); + break; } - - hb_job_set_file( job, output ); - - if( color_matrix_code ) - { - job->color_matrix_code = color_matrix_code; - } - - hb_job_set_encoder_preset (job, x264_preset); - hb_job_set_encoder_tune (job, x264_tune); - hb_job_set_encoder_profile(job, h264_profile); - hb_job_set_encoder_level (job, h264_level); - - if( start_at_preview ) - { - job->start_at_preview = start_at_preview - 1; - job->seek_points = preview_count; - } - - if( stop_at_pts ) - { - job->pts_to_stop = stop_at_pts; - subtitle_scan = 0; - } - - if( stop_at_frame ) - { - job->frame_to_stop = stop_at_frame; - subtitle_scan = 0; - } - - if( start_at_pts ) - { - job->pts_to_start = start_at_pts; - subtitle_scan = 0; - } - - if( start_at_frame ) - { - job->frame_to_start = start_at_frame; - subtitle_scan = 0; - } - - /* OpenCL */ - job->use_opencl = use_opencl; - - job->indepth_scan = subtitle_scan; - job->twopass = twoPass; - job->fastfirstpass = fastfirstpass; - hb_job_set_encoder_options(job, advanced_opts); - - hb_add( h, job ); - hb_job_close( &job ); - hb_start( h ); - break; - } - -#define p s.param.working - case HB_STATE_SEARCHING: - fprintf( stdout, "\rEncoding: task %d of %d, Searching for start time, %.2f %%", - p.pass, p.pass_count, 100.0 * p.progress ); - if( p.seconds > -1 ) - { - fprintf( stdout, " (ETA %02dh%02dm%02ds)", - p.hours, p.minutes, p.seconds ); - } - fflush(stdout); - break; - - case HB_STATE_WORKING: - fprintf( stdout, "\rEncoding: task %d of %d, %.2f %%", - p.pass, p.pass_count, 100.0 * p.progress ); - if( p.seconds > -1 ) - { - fprintf( stdout, " (%.2f fps, avg %.2f fps, ETA " - "%02dh%02dm%02ds)", p.rate_cur, p.rate_avg, - p.hours, p.minutes, p.seconds ); - } - fflush(stdout); - break; -#undef p - -#define p s.param.muxing - case HB_STATE_MUXING: - { - if (show_mux_warning) + case ALLOWED_AUDIO_COPY: { - fprintf( stdout, "\rMuxing: this may take awhile..." ); - fflush(stdout); - show_mux_warning = 0; + audio_copy_list = str_split(optarg, ','); + break; } - break; + case AUDIO_FALLBACK: + acodec_fallback = strdup( optarg ); + break; + case 'M': + if( optarg != NULL ) + { + if( !strcmp( optarg, "601" ) || + !strcmp( optarg, "ntsc" ) ) + color_matrix_code = 1; + else if( !strcmp( optarg, "pal" ) ) + color_matrix_code = 2; + else if( !strcmp( optarg, "709" ) ) + color_matrix_code = 3; + } break; + case MIN_DURATION: + min_title_duration = strtol( optarg, NULL, 0 ); + break; +#ifdef USE_QSV + case QSV_BASELINE: + hb_qsv_force_workarounds(); + break; + case QSV_ASYNC_DEPTH: + qsv_async_depth = atoi(optarg); + break; + case QSV_IMPLEMENTATION: + hb_qsv_impl_set_preferred(optarg); + break; +#endif + default: + fprintf( stderr, "unknown option (%s)\n", argv[cur_optind] ); + return -1; } -#undef p -#define p s.param.workdone - case HB_STATE_WORKDONE: - /* Print error if any, then exit */ - switch( p.error ) - { - case HB_ERROR_NONE: - fprintf( stderr, "\nEncode done!\n" ); - break; - case HB_ERROR_CANCELED: - fprintf( stderr, "\nEncode canceled.\n" ); - break; - default: - fprintf( stderr, "\nEncode failed (error %x).\n", - p.error ); - } - done_error = p.error; - die = 1; - break; -#undef p } - return 0; -} -/**************************************************************************** - * SigHandler: - ****************************************************************************/ -static volatile int64_t i_die_date = 0; -void SigHandler( int i_signal ) -{ - done_error = HB_ERROR_CANCELED; - if( die == 0 ) + if (deblock != NULL) { - die = 1; - i_die_date = hb_get_date(); - fprintf( stderr, "Signal %d received, terminating - do it " - "again in case it gets stuck\n", i_signal ); - } - else if( i_die_date + 500 < hb_get_date() ) - { - fprintf( stderr, "Dying badly, files might remain in your /tmp\n" ); - exit( done_error ); + if (deblock_disable) + { + fprintf(stderr, + "Incompatible options --deblock and --no-deblock\n"); + return -1; + } + if (hb_validate_filter_settings(HB_FILTER_DEBLOCK, deblock)) + { + fprintf(stderr, "Invalid deblock option %s\n", deblock); + return -1; + } } -} -/**************************************************************************** - * ShowHelp: - ****************************************************************************/ -static void ShowHelp() -{ - int i; - const char *name; - const hb_rate_t *rate; - const hb_dither_t *dither; - const hb_mixdown_t *mixdown; - const hb_encoder_t *encoder; - const hb_container_t *container; - FILE* const out = stdout; - - fprintf( out, - "Syntax: HandBrakeCLI [options] -i -o \n" - "\n" - "### General Handbrake Options------------------------------------------------\n\n" - " -h, --help Print help\n" - " -u, --update Check for updates and exit\n" - " -v, --verbose <#> Be verbose (optional argument: logging level)\n" - " -Z. --preset Use a built-in preset. Capitalization matters, and\n" - " if the preset name has spaces, surround it with\n" - " double quotation marks\n" - " -z, --preset-list See a list of available built-in presets\n" - " --no-dvdnav Do not use dvdnav for reading DVDs\n" - " --no-opencl Disable use of OpenCL\n" - "\n" - - "### Source Options-----------------------------------------------------------\n\n" - " -i, --input Set input device\n" - " -t, --title Select a title to encode (0 to scan all titles only,\n" - " default: 1)\n" - " --min-duration Set the minimum title duration (in seconds). Shorter\n" - " titles will not be scanned (default: 10).\n" - " --scan Scan selected title only.\n" - " --main-feature Detect and select the main feature title.\n" - " -c, --chapters Select chapters (e.g. \"1-3\" for chapters\n" - " 1 to 3, or \"3\" for chapter 3 only,\n" - " default: all chapters)\n" - " --angle Select the video angle (DVD or Blu-ray only)\n" - " --previews <#:B> Select how many preview images are generated,\n" - " and whether or not they're stored to disk (0 or 1).\n" - " (default: 10:0)\n" - " --start-at-preview <#> Start encoding at a given preview.\n" - " --start-at Start encoding at a given frame, duration (in seconds),\n" - " or pts (on a 90kHz clock)\n" - " --stop-at Stop encoding at a given frame, duration (in seconds),\n" - " or pts (on a 90kHz clock)" - "\n" - - "### Destination Options------------------------------------------------------\n\n" - " -o, --output Set output file name\n" - " -f, --format Set output container format ("); - container = NULL; - while ((container = hb_container_get_next(container)) != NULL) + if (detelecine != NULL) { - fprintf(out, "%s", container->short_name); - if (hb_container_get_next(container) != NULL) + if (detelecine_disable) { - fprintf(out, "/"); + fprintf(stderr, + "Incompatible options --detelecine and --no-detelecine\n"); + return -1; + } + if (!hb_validate_filter_preset(HB_FILTER_DETELECINE, + detelecine, NULL)) + { + // Nothing to do, but must validate preset before + // attempting to validate custom settings to prevent potential + // false positive + } + else if (!hb_validate_filter_settings(HB_FILTER_DETELECINE, detelecine)) + { + detelecine_custom = 1; } else { - fprintf(out, ")\n"); + fprintf(stderr, "Invalid detelecine option %s\n", detelecine); + return -1; } } - fprintf(out, - " (default: autodetected from file name)\n" - " -m, --markers Add chapter markers\n" - " -O, --optimize Optimize mp4 files for HTTP streaming (\"fast start\")\n" - " -I, --ipod-atom Mark mp4 files so 5.5G iPods will accept them\n" - " -P, --use-opencl Use OpenCL where applicable\n" - " -U, --use-hwd Use DXVA2 hardware decoding\n" - "\n" - - - "### Video Options------------------------------------------------------------\n\n" - " -e, --encoder Set video library encoder\n" - " Options: " ); - name = NULL; - encoder = NULL; - while ((encoder = hb_video_encoder_get_next(encoder)) != NULL) + + if (deinterlace != NULL) { - fprintf(out, "%s", encoder->short_name); - if (hb_video_encoder_get_next(encoder) != NULL) + if (deinterlace_disable) { - fprintf(out, "/"); + fprintf(stderr, + "Incompatible options --deinterlace and --no-deinterlace\n"); + return -1; } - else + if (!hb_validate_filter_preset(HB_FILTER_DEINTERLACE, + deinterlace, NULL)) { - fprintf(out, "\n"); + // Nothing to do, but must validate preset before + // attempting to validate custom settings to prevent potential + // false positive } - if (encoder->codec == vcodec) + else if (!hb_validate_filter_settings(HB_FILTER_HQDN3D, deinterlace)) { - name = encoder->short_name; + deinterlace_custom = 1; } - } - fprintf(out, " (default: %s)\n", name); - fprintf(out, - " --encoder-preset Adjust video encoding settings for a particular\n" - " speed/efficiency tradeoff (encoder-specific)\n" - " --encoder-preset-list List supported --encoder-preset values for the\n" - " specified video encoder\n" - " --encoder-tune Adjust video encoding settings for a particular\n" - " type of souce or situation (encoder-specific)\n" - " --encoder-tune-list List supported --encoder-tune values for the\n" - " specified video encoder\n" - " -x, --encopts Specify advanced encoding options in the same\n" - " style as mencoder (all encoders except theora):\n" - " option1=value1:option2=value2\n" - " --encoder-profile Ensures compliance with the requested codec\n" - " profile (encoder-specific)\n" - " --encoder-profile-list List supported --encoder-profile values for the\n" - " specified video encoder\n" - " --encoder-level Ensures compliance with the requested codec\n" - " level (encoder-specific)\n" - " --encoder-level-list List supported --encoder-level values for the\n" - " specified video encoder\n" - " -q, --quality Set video quality\n" - " -b, --vb Set video bitrate (default: 1000)\n" - " -2, --two-pass Use two-pass mode\n" - " -T, --turbo When using 2-pass use \"turbo\" options on the\n" - " 1st pass to improve speed (only works with x264)\n" - " -r, --rate Set video framerate (" ); - rate = NULL; - while ((rate = hb_video_framerate_get_next(rate)) != NULL) - { - fprintf(out, "%s", rate->name); - if (hb_video_framerate_get_next(rate) != NULL) + else { - fprintf(out, "/"); + fprintf(stderr, "Invalid deinterlace option %s\n", deinterlace); + return -1; } } - fprintf( out, ")\n" - " Be aware that not specifying a framerate lets\n" - " HandBrake preserve a source's time stamps,\n" - " potentially creating variable framerate video\n" - " --vfr, --cfr, --pfr Select variable, constant or peak-limited\n" - " frame rate control. VFR preserves the source\n" - " timing. CFR makes the output constant rate at\n" - " the rate given by the -r flag (or the source's\n" - " average rate if no -r is given). PFR doesn't\n" - " allow the rate to go over the rate specified\n" - " with the -r flag but won't change the source\n" - " timing if it's below that rate.\n" - " If none of these flags are given, the default\n" - " is --cfr when -r is given and --vfr otherwise\n" - - "\n" - "### Audio Options-----------------------------------------------------------\n\n" - " -a, --audio Select audio track(s), separated by commas\n" - " (\"none\" for no audio, \"1,2,3\" for multiple\n" - " tracks, default: first one).\n" - " Multiple output tracks can be used for one input.\n" - " -E, --aencoder Audio encoder(s):\n" ); - encoder = NULL; - while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) - { - fprintf(out, " %s\n", - encoder->short_name); - } - fprintf(out, - " copy:* will passthrough the corresponding\n" - " audio unmodified to the muxer if it is a\n" - " supported passthrough audio type.\n" - " Separated by commas for more than one audio track.\n" - " Defaults:\n"); - container = NULL; - while ((container = hb_container_get_next(container)) != NULL) - { - int audio_encoder = hb_audio_encoder_get_default(container->format); - fprintf(out, " %-8s %s\n", - container->short_name, - hb_audio_encoder_get_short_name(audio_encoder)); - } - fprintf(out, - " --audio-copy-mask Set audio codecs that are permitted when the\n" - " \"copy\" audio encoder option is specified\n" - " (" ); - i = 0; - encoder = NULL; - while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) + + if (decomb != NULL) { - if ((encoder->codec & HB_ACODEC_PASS_FLAG) && - (encoder->codec != HB_ACODEC_AUTO_PASS)) + if (decomb_disable) { - if (i) - { - fprintf(out, "/"); - } - i = 1; - // skip "copy:" - fprintf(out, "%s", encoder->short_name + 5); + fprintf(stderr, + "Incompatible options --decomb and --no-decomb\n"); + return -1; } - } - fprintf(out, ", default: all).\n" - " Separated by commas for multiple allowed options.\n" - " --audio-fallback Set audio codec to use when it is not possible\n" - " to copy an audio track without re-encoding.\n" - " -B, --ab Set audio bitrate(s) (default: depends on the\n" - " selected codec, mixdown and samplerate)\n" - " Separated by commas for more than one audio track.\n" - " -Q, --aq Set audio quality metric (default: depends on the\n" - " selected codec)\n" - " Separated by commas for more than one audio track.\n" - " -C, --ac Set audio compression metric (default: depends on the\n" - " selected codec)\n" - " Separated by commas for more than one audio track.\n" - " -6, --mixdown Format(s) for audio downmixing/upmixing:\n"); - // skip HB_AMIXDOWN_NONE - mixdown = hb_mixdown_get_next(NULL); - while((mixdown = hb_mixdown_get_next(mixdown)) != NULL) - { - fprintf(out, " %s\n", - mixdown->short_name); - } - fprintf(out, - " Separated by commas for more than one audio track.\n" - " Defaults:\n"); - encoder = NULL; - while((encoder = hb_audio_encoder_get_next(encoder)) != NULL) - { - if (!(encoder->codec & HB_ACODEC_PASS_FLAG)) + if (!hb_validate_filter_preset(HB_FILTER_DECOMB, decomb, NULL)) { - // layout: UINT64_MAX (all channels) should work with any mixdown - int mixdown = hb_mixdown_get_default(encoder->codec, UINT64_MAX); - // assumes that the encoder short name is <= 16 characters long - fprintf(out, " %-16s up to %s\n", - encoder->short_name, hb_mixdown_get_short_name(mixdown)); + // Nothing to do, but must validate preset before + // attempting to validate custom settings to prevent potential + // false positive + } + else if (!hb_validate_filter_settings(HB_FILTER_DECOMB, decomb)) + { + decomb_custom = 1; } - } - fprintf(out, - " --normalize-mix Normalize audio mix levels to prevent clipping.\n" - " Separated by commas for more than one audio track.\n" - " 0 = Disable Normalization (default)\n" - " 1 = Enable Normalization\n" - " -R, --arate Set audio samplerate(s) (" ); - rate = NULL; - while ((rate = hb_audio_samplerate_get_next(rate)) != NULL) - { - fprintf(out, "%s", rate->name); - if (hb_audio_samplerate_get_next(rate) != NULL) + else { - fprintf(out, "/"); + fprintf(stderr, "Invalid decomb option %s\n", decomb); + return -1; } } - fprintf( out, " kHz)\n" - " Separated by commas for more than one audio track.\n" - " -D, --drc Apply extra dynamic range compression to the audio,\n" - " making soft sounds louder. Range is 1.0 to 4.0\n" - " (too loud), with 1.5 - 2.5 being a useful range.\n" - " Separated by commas for more than one audio track.\n" - " --gain Amplify or attenuate audio before encoding. Does\n" - " NOT work with audio passthru (copy). Values are in\n" - " dB. Negative values attenuate, positive values\n" - " amplify. A 1 dB difference is barely audible.\n" - " --adither Apply dithering to the audio before encoding.\n" - " Separated by commas for more than one audio track.\n" - " Only supported by some encoders ("); - i = 0; - encoder = NULL; - while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) + + if (hqdn3d != NULL) { - if (hb_audio_dither_is_supported(encoder->codec)) + if (hqdn3d_disable) { - if (i) - { - fprintf(out, "/"); - } - i = 1; - fprintf(out, "%s", encoder->short_name); + fprintf(stderr, + "Incompatible options --hqdn3d and --no-hqdn3d\n"); + return -1; + } + if (!hb_validate_filter_preset(HB_FILTER_HQDN3D, hqdn3d, NULL)) + { + // Nothing to do, but must validate preset before + // attempting to validate custom settings to prevent potential + // false positive + } + else if (!hb_validate_filter_settings(HB_FILTER_HQDN3D, hqdn3d)) + { + hqdn3d_custom = 1; + } + else + { + fprintf(stderr, "Invalid hqdn3d option %s\n", hqdn3d); + return -1; } } - fprintf(out, ").\n"); - fprintf(out, - " Options:\n"); - dither = NULL; - while ((dither = hb_audio_dither_get_next(dither)) != NULL) + + if (nlmeans != NULL) { - if (dither->method == hb_audio_dither_get_default()) + if (nlmeans_disable) { - fprintf(out, " %s (default)\n", - dither->short_name); + fprintf(stderr, + "Incompatible options --nlmeans and --no-nlmeans\n"); + return -1; + } + if (!hb_validate_filter_preset(HB_FILTER_HQDN3D, nlmeans, nlmeans_tune)) + { + // Nothing to do, but must validate preset before + // attempting to validate custom settings to prevent potential + // false positive + } + else if (!hb_validate_filter_settings(HB_FILTER_HQDN3D, nlmeans)) + { + nlmeans_custom = 1; } else { - fprintf(out, " %s\n", - dither->short_name); + fprintf(stderr, "Invalid hqdn3d option %s\n", nlmeans); + return -1; } } - fprintf(out, - " -A, --aname Audio track name(s),\n" - " Separated by commas for more than one audio track.\n" - "\n" - - "### Picture Settings---------------------------------------------------------\n\n" - " -w, --width Set picture width\n" - " -l, --height Set picture height\n" - " --crop Set cropping values (default: autocrop)\n" - " --loose-crop <#> Always crop to a multiple of the modulus\n" - " Specifies the maximum number of extra pixels\n" - " which may be cropped (default: 15)\n" - " -Y, --maxHeight <#> Set maximum height\n" - " -X, --maxWidth <#> Set maximum width\n" - " --strict-anamorphic Store pixel aspect ratio in video stream\n" - " --loose-anamorphic Store pixel aspect ratio with specified width\n" - " --custom-anamorphic Store pixel aspect ratio in video stream and\n" - " directly control all parameters.\n" - " --display-width Set the width to scale the actual pixels to\n" - " at playback, for custom anamorphic.\n" - " --keep-display-aspect Preserve the source's display aspect ratio\n" - " when using custom anamorphic\n" - " --pixel-aspect Set a custom pixel aspect for custom anamorphic\n" - " \n" - " (--display-width and --pixel-aspect are mutually\n" - " exclusive and the former will override the latter)\n" - " --itu-par Use wider, ITU pixel aspect values for loose and\n" - " custom anamorphic, useful with underscanned sources\n" - " --modulus Set the number you want the scaled pixel dimensions\n" - " to divide cleanly by. Does not affect strict\n" - " anamorphic mode, which is always mod 2 (default: 16)\n" - " -M, --color-matrix Set the color space signaled by the output\n" - " Values: 709, pal, ntsc, 601 (same as ntsc)\n" - " (default: detected from source)\n" - "\n" - - "### Filters---------------------------------------------------------\n\n" - - " -d, --deinterlace Unconditionally deinterlaces all frames\n" - " or omitted (default settings)\n" - " or\n" - " (default 0:-1)\n" - " -5, --decomb Selectively deinterlaces when it detects combing\n" - " or omitted (default settings)\n" - " or\n" - " \n" - " (default: 7:2:6:9:80:16:16:10:20:20:4:2:50:24:1:-1)\n" - " -9, --detelecine Detelecine (ivtc) video with pullup filter\n" - " Note: this filter drops duplicate frames to\n" - " restore the pre-telecine framerate, unless you\n" - " specify a constant framerate (--rate 29.97)\n" - " (default 1:1:4:4:0:0:-1)\n" - " -8, --denoise Denoise video with hqdn3d filter\n" - " or omitted (default settings)\n" - " or\n" - " \n" - " (default: 4:3:3:6:4.5:4.5)\n" - " --nlmeans Denoise video with nlmeans filter\n" - " or omitted\n" - " or\n" - " \n" - " (default 8:1:7:3:2:0)\n" - " --nlmeans-tune Tune nlmeans filter to content type\n" - " Note: only works in conjunction with presets\n" - " ultralight/light/medium/strong.\n" - " or omitted (default none)\n" - " -7, --deblock Deblock video with pp7 filter\n" - " (default 5:2)\n" - " --rotate Rotate image or flip its axes.\n" - " Modes: (can be combined)\n" - " 1 vertical flip\n" - " 2 horizontal flip\n" - " 4 rotate clockwise 90 degrees\n" - " Default: 3 (vertical and horizontal flip)\n" - " -g, --grayscale Grayscale encoding\n" - "\n" - - "### Subtitle Options------------------------------------------------------------\n\n" - " -s, --subtitle Select subtitle track(s), separated by commas\n" - " More than one output track can be used for one\n" - " input.\n" - " Example: \"1,2,3\" for multiple tracks.\n" - " A special track name \"scan\" adds an extra 1st pass.\n" - " This extra pass scans subtitles matching the\n" - " language of the first audio or the language \n" - " selected by --native-language.\n" - " The one that's only used 10 percent of the time\n" - " or less is selected. This should locate subtitles\n" - " for short foreign language segments. Best used in\n" - " conjunction with --subtitle-forced.\n" - " -F, --subtitle-forced Only display subtitles from the selected stream if\n" - " the subtitle has the forced flag set. The values in\n" - " \"string\" are indexes into the subtitle list\n" - " specified with '--subtitle'.\n" - " Separated by commas for more than one subtitle track.\n" - " Example: \"1,2,3\" for multiple tracks.\n" - " If \"string\" is omitted, the first track is forced.\n" - " --subtitle-burned \"Burn\" the selected subtitle into the video track\n" - " If \"number\" is omitted, the first track is burned.\n" - " \"number\" is an index into the subtitle list\n" - " specified with '--subtitle'.\n" - " --subtitle-default Flag the selected subtitle as the default subtitle\n" - " to be displayed upon playback. Setting no default\n" - " means no subtitle will be automatically displayed\n" - " If \"number\" is omitted, the first track is default.\n" - " \"number\" is an index into the subtitle list\n" - " specified with '--subtitle'.\n" - " -N, --native-language Specifiy your language preference. When the first\n" - " audio track does not match your native language then\n" - " select the first subtitle that does. When used in\n" - " conjunction with --native-dub the audio track is\n" - " changed in preference to subtitles. Provide the\n" - " language's iso639-2 code (fre, eng, spa, dut, et cetera)\n" - " --native-dub Used in conjunction with --native-language\n" - " requests that if no audio tracks are selected the\n" - " default selected audio track will be the first one\n" - " that matches the --native-language. If there are no\n" - " matching audio tracks then the first matching\n" - " subtitle track is used instead.\n" - " --srt-file SubRip SRT filename(s), separated by commas.\n" - " --srt-codeset Character codeset(s) that the SRT file(s) are\n" - " encoded in, separated by commas.\n" - " Use 'iconv -l' for a list of valid\n" - " codesets. If not specified, 'latin1' is assumed\n" - " --srt-offset Offset (in milliseconds) to apply to the SRT file(s),\n" - " separated by commas. If not specified, zero is assumed.\n" - " Offsets may be negative.\n" - " --srt-lang Language as an iso639-2 code fra, eng, spa et cetera)\n" - " for the SRT file(s), separated by commas. If not specified,\n" - " then 'und' is used.\n" - " --srt-default Flag the selected srt as the default subtitle\n" - " to be displayed upon playback. Setting no default\n" - " means no subtitle will be automatically displayed\n" - " If \"number\" is omitted, the first srt is default.\n" - " \"number\" is an 1 based index into the srt-file list\n" - " --srt-burn \"Burn\" the selected srt subtitle into the video track\n" - " If \"number\" is omitted, the first srt is burned.\n" - " \"number\" is an 1 based index into the srt-file list\n" - "\n" - ); - -#ifdef USE_QSV -if (hb_qsv_available()) -{ - fprintf( out, - "### Intel Quick Sync Video------------------------------------------------------\n\n" - " --disable-qsv-decoding Force software decoding of the video track.\n" - " --qsv-async-depth Specifies how many asynchronous operations should be\n" - " performed before the result is explicitly synchronized.\n" - " Default: 4. If zero, the value is not specified.\n" - "\n" - ); -} -#endif -} -/**************************************************************************** - * ShowPresets: - ****************************************************************************/ -static void ShowPresets() -{ - fprintf( stderr, "%s - %s - %s\n", HB_PROJECT_TITLE, HB_PROJECT_BUILD_TITLE, HB_PROJECT_URL_WEBSITE ); - - printf("\n< Devices\n"); - printf("\n + Universal: -e x264 -q 20.0 -r 30 --pfr -a 1,1 -E ffaac,copy:ac3 -B 160,160 -6 dpl2,none -R Auto,Auto -D 0.0,0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 720 -Y 576 --loose-anamorphic --modulus 2 -m --x264-preset fast --h264-profile baseline --h264-level 3.0\n"); - printf("\n + iPod: -e x264 -q 22.0 -r 30 --pfr -a 1 -E ffaac -B 160 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -I -X 320 -Y 240 --modulus 2 -m --x264-preset medium --h264-profile baseline --h264-level 1.3\n"); - printf("\n + iPhone & iPod touch: -e x264 -q 22.0 -r 30 --pfr -a 1 -E ffaac -B 160 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 960 -Y 640 --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 3.1\n"); - printf("\n + iPad: -e x264 -q 20.0 -r 30 --pfr -a 1 -E ffaac -B 160 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 1280 -Y 720 --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 3.1\n"); - printf("\n + AppleTV: -e x264 -q 20.0 -r 30 --pfr -a 1,1 -E ffaac,copy:ac3 -B 160,160 -6 dpl2,none -R Auto,Auto -D 0.0,0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 960 -Y 720 --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 3.1 -x qpmin=4:cabac=0:ref=2:b-pyramid=none:weightb=0:weightp=0:vbv-maxrate=9500:vbv-bufsize=9500\n"); - printf("\n + AppleTV 2: -e x264 -q 20.0 -r 30 --pfr -a 1,1 -E ffaac,copy:ac3 -B 160,160 -6 dpl2,none -R Auto,Auto -D 0.0,0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 1280 -Y 720 --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 3.1\n"); - printf("\n + AppleTV 3: -e x264 -q 20.0 -r 30 --pfr -a 1,1 -E ffaac,copy:ac3 -B 160,160 -6 dpl2,none -R Auto,Auto -D 0.0,0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 1920 -Y 1080 --decomb=fast --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 4.0\n"); - printf("\n + Android: -e x264 -q 22.0 -r 30 --pfr -a 1 -E ffaac -B 128 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 720 -Y 576 --loose-anamorphic --modulus 2 --x264-preset medium --h264-profile main --h264-level 3.0\n"); - printf("\n + Android Tablet: -e x264 -q 22.0 -r 30 --pfr -a 1 -E ffaac -B 128 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 1280 -Y 720 --loose-anamorphic --modulus 2 --x264-preset medium --h264-profile main --h264-level 3.1\n"); - printf("\n + Windows Phone 8: -e x264 -q 22.0 -r 30 --pfr -a 1 -E ffaac -B 128 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 1280 -Y 720 --modulus 2 --x264-preset medium --h264-profile main --h264-level 3.1\n"); - printf("\n>\n"); - printf("\n< Regular\n"); - printf("\n + Normal: -e x264 -q 20.0 -a 1 -E ffaac -B 160 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 --loose-anamorphic --modulus 2 -m --x264-preset veryfast --h264-profile main --h264-level 4.0\n"); - printf("\n + High Profile: -e x264 -q 20.0 -a 1,1 -E ffaac,copy:ac3 -B 160,160 -6 dpl2,none -R Auto,Auto -D 0.0,0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 --decomb --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 4.1\n"); - printf("\n>\n"); + return 0; } -static char* strchr_quote(char *pos, char c, char q) +static int foreign_audio_scan(char **subtracks) { - if (pos == NULL) - return NULL; - - while (*pos != 0 && *pos != c) + if (subtracks != NULL) { - if (*pos == q) + int count = str_vlen(subtracks); + int ii; + for (ii = 0; ii < count; ii++) { - pos = strchr_quote(pos+1, q, 0); - if (pos == NULL) - return NULL; - pos++; + if (!strcasecmp(subtracks[0], "scan")) + { + return 1; + } } - else if (*pos == '\\' && *(pos+1) != 0) - pos += 2; - else - pos++; } - if (*pos != c) - return NULL; - return pos; + return 0; } -static char *strndup_quote(char *str, char q, int len) +static int count_subtitles(char **subtracks) { - if (str == NULL) - return NULL; - - char * res; - int str_len = strlen( str ); - int src = 0, dst = 0; - res = malloc( len > str_len ? str_len + 1 : len + 1 ); - - while (str[src] != 0 && src < len) + int subtitle_track_count = 0; + if (subtracks != NULL) { - if (str[src] == q) - src++; - else if (str[src] == '\\' && str[src+1] != 0) + int count = str_vlen(subtracks); + int ii; + for (ii = 0; ii < count; ii++) { - res[dst++] = str[src+1]; - src += 2; + if (strcasecmp(subtracks[0], "scan") && + strcasecmp(subtracks[0], "none")) + { + subtitle_track_count++; + } } - else - res[dst++] = str[src++]; } - res[dst] = '\0'; - return res; + return subtitle_track_count; } -static char** str_split( char *str, char delem ) +static int CheckOptions( int argc, char ** argv ) { - char * pos; - char * end; - char ** ret; - int count, i; - char quote = '"'; - - if (delem == '"') + if( update ) { - quote = '\''; + return 0; } - if ( str == NULL || str[0] == 0 ) + + if (preset_export_name == NULL && (input == NULL || *input == '\0')) { - ret = malloc( sizeof(char*) ); - *ret = NULL; - return ret; + fprintf( stderr, "Missing input device. Run %s --help for " + "syntax.\n", argv[0] ); + return 1; } - // Find number of elements in the string - count = 1; - pos = str; - while ( ( pos = strchr_quote( pos, delem, quote ) ) != NULL ) + /* Parse format */ + if (titleindex > 0 && !titlescan) { - count++; - pos++; - } + if (preset_export_name == NULL && (output == NULL || *output == '\0')) + { + fprintf( stderr, "Missing output file name. Run %s --help " + "for syntax.\n", argv[0] ); + return 1; + } - ret = calloc( ( count + 1 ), sizeof(char*) ); + if (format == NULL && output != NULL) + { + /* autodetect */ + const char *extension = strrchr(output, '.'); + if (extension != NULL) + { + // skip '.' + mux = hb_container_get_from_extension(extension + 1); + } + hb_container_t * c = hb_container_get_from_format(mux); + if (c != NULL) + format = strdup(c->short_name); + } + } - pos = str; - for ( i = 0; i < count - 1; i++ ) + int subtitle_track_count = count_subtitles(subtracks); + if (subtitle_track_count > 0 && subtitle_lang_list != NULL) { - end = strchr_quote( pos, delem, quote ); - ret[i] = strndup_quote(pos, quote, end - pos); - pos = end + 1; + fprintf(stderr, + "Incompatible options: --subtitle-lang-list and --subtitle\n"); + return 1; } - ret[i] = strndup_quote(pos, quote, strlen(pos)); - - return ret; -} - -static void str_vfree( char **strv ) -{ - int i; - if (strv == NULL) - return; - - for ( i = 0; strv[i]; i++ ) + if (subtitle_track_count > 0 && subtitle_all != -1) { - free( strv[i] ); + fprintf(stderr, + "Incompatible options: --all-subtitles/--first-subtitle and --subtitle\n"); + return 1; } - free( strv ); -} - -static double parse_hhmmss_strtok() -{ - /* Assumes strtok has already been called on a string. Intends to parse - * hh:mm:ss.ss or mm:ss.ss or ss.ss or ss into double seconds. Actually - * parses a list of doubles separated by colons, multiplying the current - * result by 60 then adding in the next value. Malformed input does not - * result in a explicit error condition but instead returns an - * intermediate result. */ - double duration = 0; - char* str; - while ((str = strtok(NULL, ":")) != NULL) - duration = 60*duration + strtod(str, NULL); - return duration; -} -/**************************************************************************** - * ParseOptions: - ****************************************************************************/ -static int ParseOptions( int argc, char ** argv ) -{ - - #define PREVIEWS 257 - #define START_AT_PREVIEW 258 - #define START_AT 259 - #define STOP_AT 260 - #define ANGLE 261 - #define DVDNAV 262 - #define DISPLAY_WIDTH 263 - #define PIXEL_ASPECT 264 - #define MODULUS 265 - #define KEEP_DISPLAY_ASPECT 266 - #define SUB_BURNED 267 - #define SUB_DEFAULT 268 - #define NATIVE_DUB 269 - #define SRT_FILE 270 - #define SRT_CODESET 271 - #define SRT_OFFSET 272 - #define SRT_LANG 273 - #define SRT_DEFAULT 274 - #define SRT_BURN 275 - #define ROTATE_FILTER 276 - #define SCAN_ONLY 277 - #define MAIN_FEATURE 278 - #define MIN_DURATION 279 - #define AUDIO_GAIN 280 - #define ALLOWED_AUDIO_COPY 281 - #define AUDIO_FALLBACK 282 - #define LOOSE_CROP 283 - #define ENCODER_PRESET 284 - #define ENCODER_PRESET_LIST 285 - #define ENCODER_TUNE 286 - #define ENCODER_TUNE_LIST 287 - #define ENCODER_PROFILE 288 - #define ENCODER_PROFILE_LIST 289 - #define ENCODER_LEVEL 290 - #define ENCODER_LEVEL_LIST 291 - #define NO_OPENCL 292 - #define NORMALIZE_MIX 293 - #define AUDIO_DITHER 294 - #define QSV_BASELINE 295 - #define QSV_ASYNC_DEPTH 296 - #define QSV_IMPLEMENTATION 297 - #define FILTER_NLMEANS 298 - #define FILTER_NLMEANS_TUNE 299 + if (atracks != NULL && audio_lang_list != NULL) + { + fprintf(stderr, + "Incompatible options: --audio-lang-list and --audio\n"); + return 1; + } - for( ;; ) + if (atracks != NULL && audio_all != -1) { - static struct option long_options[] = - { - { "help", no_argument, NULL, 'h' }, - { "update", no_argument, NULL, 'u' }, - { "verbose", optional_argument, NULL, 'v' }, - { "no-dvdnav", no_argument, NULL, DVDNAV }, - { "no-opencl", no_argument, NULL, NO_OPENCL }, + fprintf(stderr, + "Incompatible options: --all-audio/--first-audio and --audio\n"); + return 1; + } -#ifdef USE_QSV - { "qsv-baseline", no_argument, NULL, QSV_BASELINE, }, - { "qsv-async-depth", required_argument, NULL, QSV_ASYNC_DEPTH, }, - { "qsv-implementation", required_argument, NULL, QSV_IMPLEMENTATION, }, - { "disable-qsv-decoding", no_argument, &qsv_decode, 0, }, -#endif + if ((par_width > 0 && par_height > 0) && display_width > 0) + { + fprintf(stderr, + "Incompatible options: --display-width and --pixel-aspect\n"); + return 1; + } - { "format", required_argument, NULL, 'f' }, - { "input", required_argument, NULL, 'i' }, - { "output", required_argument, NULL, 'o' }, - { "optimize", no_argument, NULL, 'O' }, - { "ipod-atom", no_argument, NULL, 'I' }, - { "use-opencl", no_argument, NULL, 'P' }, - { "use-hwd", no_argument, NULL, 'U' }, + if (preset_export_file != NULL && preset_export_name == NULL) + { + fprintf(stderr, + "Error: --preset-export-file requires option --preset-export\n"); + return 1; + } - { "title", required_argument, NULL, 't' }, - { "min-duration",required_argument, NULL, MIN_DURATION }, - { "scan", no_argument, NULL, SCAN_ONLY }, - { "main-feature",no_argument, NULL, MAIN_FEATURE }, - { "chapters", required_argument, NULL, 'c' }, - { "angle", required_argument, NULL, ANGLE }, - { "markers", optional_argument, NULL, 'm' }, - { "audio", required_argument, NULL, 'a' }, - { "mixdown", required_argument, NULL, '6' }, - { "normalize-mix", required_argument, NULL, NORMALIZE_MIX }, - { "drc", required_argument, NULL, 'D' }, - { "gain", required_argument, NULL, AUDIO_GAIN }, - { "adither", required_argument, NULL, AUDIO_DITHER }, - { "subtitle", required_argument, NULL, 's' }, - { "subtitle-forced", optional_argument, NULL, 'F' }, - { "subtitle-burned", optional_argument, NULL, SUB_BURNED }, - { "subtitle-default", optional_argument, NULL, SUB_DEFAULT }, - { "srt-file", required_argument, NULL, SRT_FILE }, - { "srt-codeset", required_argument, NULL, SRT_CODESET }, - { "srt-offset", required_argument, NULL, SRT_OFFSET }, - { "srt-lang", required_argument, NULL, SRT_LANG }, - { "srt-default", optional_argument, NULL, SRT_DEFAULT }, - { "srt-burn", optional_argument, NULL, SRT_BURN }, - { "native-language", required_argument, NULL,'N' }, - { "native-dub", no_argument, NULL, NATIVE_DUB }, - { "encoder", required_argument, NULL, 'e' }, - { "aencoder", required_argument, NULL, 'E' }, - { "two-pass", no_argument, NULL, '2' }, - { "deinterlace", optional_argument, NULL, 'd' }, - { "deblock", optional_argument, NULL, '7' }, - { "denoise", optional_argument, NULL, '8' }, - { "nlmeans", optional_argument, NULL, FILTER_NLMEANS }, - { "nlmeans-tune",required_argument, NULL, FILTER_NLMEANS_TUNE }, - { "detelecine", optional_argument, NULL, '9' }, - { "decomb", optional_argument, NULL, '5' }, - { "grayscale", no_argument, NULL, 'g' }, - { "rotate", optional_argument, NULL, ROTATE_FILTER }, - { "strict-anamorphic", no_argument, &anamorphic_mode, 1 }, - { "loose-anamorphic", no_argument, &anamorphic_mode, 2 }, - { "custom-anamorphic", no_argument, &anamorphic_mode, 3 }, - { "display-width", required_argument, NULL, DISPLAY_WIDTH }, - { "keep-display-aspect", no_argument, &keep_display_aspect, 1 }, - { "pixel-aspect", required_argument, NULL, PIXEL_ASPECT }, - { "modulus", required_argument, NULL, MODULUS }, - { "itu-par", no_argument, &itu_par, 1 }, - { "width", required_argument, NULL, 'w' }, - { "height", required_argument, NULL, 'l' }, - { "crop", required_argument, NULL, 'n' }, - { "loose-crop", optional_argument, NULL, LOOSE_CROP }, + if (preset_export_desc != NULL && preset_export_name == NULL) + { + fprintf(stderr, + "Error: --preset-export-desc requires option --preset-export\n"); + return 1; + } - // mapping of legacy option names for backwards compatibility - { "qsv-preset", required_argument, NULL, ENCODER_PRESET, }, - { "x264-preset", required_argument, NULL, ENCODER_PRESET, }, - { "x265-preset", required_argument, NULL, ENCODER_PRESET, }, - { "x264-tune", required_argument, NULL, ENCODER_TUNE, }, - { "x265-tune", required_argument, NULL, ENCODER_TUNE, }, - { "x264-profile", required_argument, NULL, ENCODER_PROFILE, }, - { "h264-profile", required_argument, NULL, ENCODER_PROFILE, }, - { "h265-profile", required_argument, NULL, ENCODER_PROFILE, }, - { "h264-level", required_argument, NULL, ENCODER_LEVEL, }, - { "h265-level", required_argument, NULL, ENCODER_LEVEL, }, - // encoder preset/tune/options/profile/level - { "encoder-preset", required_argument, NULL, ENCODER_PRESET, }, - { "encoder-preset-list", required_argument, NULL, ENCODER_PRESET_LIST, }, - { "encoder-tune", required_argument, NULL, ENCODER_TUNE, }, - { "encoder-tune-list", required_argument, NULL, ENCODER_TUNE_LIST, }, - { "encopts", required_argument, NULL, 'x', }, - { "encoder-profile", required_argument, NULL, ENCODER_PROFILE, }, - { "encoder-profile-list", required_argument, NULL, ENCODER_PROFILE_LIST, }, - { "encoder-level", required_argument, NULL, ENCODER_LEVEL, }, - { "encoder-level-list", required_argument, NULL, ENCODER_LEVEL_LIST, }, + return 0; +} - { "vb", required_argument, NULL, 'b' }, - { "quality", required_argument, NULL, 'q' }, - { "ab", required_argument, NULL, 'B' }, - { "aq", required_argument, NULL, 'Q' }, - { "ac", required_argument, NULL, 'C' }, - { "rate", required_argument, NULL, 'r' }, - { "arate", required_argument, NULL, 'R' }, - { "turbo", no_argument, NULL, 'T' }, - { "maxHeight", required_argument, NULL, 'Y' }, - { "maxWidth", required_argument, NULL, 'X' }, - { "preset", required_argument, NULL, 'Z' }, - { "preset-list", no_argument, NULL, 'z' }, +static hb_dict_t * PreparePreset(const char *preset_name) +{ + int ii; + hb_dict_t *preset; - { "aname", required_argument, NULL, 'A' }, - { "color-matrix",required_argument, NULL, 'M' }, - { "previews", required_argument, NULL, PREVIEWS }, - { "start-at-preview", required_argument, NULL, START_AT_PREVIEW }, - { "start-at", required_argument, NULL, START_AT }, - { "stop-at", required_argument, NULL, STOP_AT }, - { "vfr", no_argument, &cfr, 0 }, - { "cfr", no_argument, &cfr, 1 }, - { "pfr", no_argument, &cfr, 2 }, - { "audio-copy-mask", required_argument, NULL, ALLOWED_AUDIO_COPY }, - { "audio-fallback", required_argument, NULL, AUDIO_FALLBACK }, - { 0, 0, 0, 0 } - }; + if (preset_name != NULL) + { + preset = hb_preset_get(preset_name, 1 /*recurse*/); + if (preset == NULL) + { + fprintf(stderr, "Invalid preset %s\n" + "Valid presets are:\n", preset_name); + ShowPresets(NULL, 1, 1); + return NULL; + } + } + else + { + preset = hb_presets_get_default(); + } + if (preset == NULL) + { + fprintf(stderr, "Error loading presets! Aborting.\n"); + return NULL; + } - int option_index = 0; - int c; - int cur_optind; + int subtitle_track_count = count_subtitles(subtracks); + // Apply any overrides that can be made directly to the preset + if (format != NULL) + { + hb_dict_set(preset, "FileFormat", hb_value_string(format)); + } + if (mp4_optimize != -1) + { + hb_dict_set(preset, "Mp4HttpOptimize", hb_value_bool(mp4_optimize)); + } + if (ipod_atom != -1) + { + hb_dict_set(preset, "Mp4iPodCompatible", hb_value_bool(ipod_atom)); + } + if (chapter_markers != -1) + { + hb_dict_set(preset, "ChapterMarkers", hb_value_bool(chapter_markers)); + } + hb_value_array_t *subtitle_lang_array; + subtitle_lang_array = hb_dict_get(preset, "SubtitleLanguageList"); + if (subtitle_lang_array == NULL) + { + subtitle_lang_array = hb_value_array_init(); + hb_dict_set(preset, "SubtitleLanguageList", subtitle_lang_array); + } + if (subtitle_lang_list != NULL) + { + hb_value_array_clear(subtitle_lang_array); + int count = str_vlen(subtitle_lang_list); + for (ii = 0; ii < count; ii++) + { + const iso639_lang_t *lang = lang_lookup(subtitle_lang_list[ii]); + if (lang != NULL) + { + hb_value_array_append(subtitle_lang_array, + hb_value_string(lang->iso639_2)); + } + else + { + fprintf(stderr, "Warning: Invalid subtitle language (%s)\n", + subtitle_lang_list[ii]); + return NULL; + } + } + hb_dict_set(preset, "SubtitleTrackSelectionBehavior", + hb_value_string("first")); + } + if (native_language != NULL) + { + // Add native language subtitles if audio is not native + lang_list_remove(subtitle_lang_array, native_language); + hb_value_array_insert(subtitle_lang_array, 0, + hb_value_string(native_language)); + hb_dict_set(preset, "SubtitleAddForeignAudioSubtitle", + hb_value_bool(1)); + } + if (foreign_audio_scan(subtracks)) + { + // Add foreign audio search + hb_dict_set(preset, "SubtitleAddForeignAudioSearch", hb_value_bool(1)); + } + // Subtitle burn behavior + const char *burn = "none"; + if (subtitle_track_count == 0) + { + if (subburn_native && subburn == 1) + { + burn = "foreign_first"; + } + else if (subburn_native) + { + burn = "foreign"; + } + else if (subburn == 1) + { + burn = "first"; + } + } + else + { + if (subburn_native) + { + burn = "foreign"; + } + } + hb_dict_set(preset, "SubtitleBurnBehavior", hb_value_string(burn)); + const char *selection = NULL; + if (subtitle_track_count == 0 && subtitle_all != -1) + { + selection = subtitle_all == 1 ? "all" : "first"; + } + else + { + selection = "none"; + } + if (selection != NULL) + { + hb_dict_set(preset, "SubtitleTrackSelectionBehavior", + hb_value_string(selection)); + } - cur_optind = optind; - c = getopt_long( argc, argv, - "hv::uC:f:4i:Io:PUt:c:m::M:a:A:6:s:F::N:e:E:Q:C:" - "2dD:7895gOw:l:n:b:q:S:B:r:R:x:TY:X:Z:z", - long_options, &option_index ); - if( c < 0 ) + if (audio_copy_list != NULL) + { + // Create autopassthru copy mask + hb_value_array_t *array = hb_value_array_init(); + for (ii = 0; audio_copy_list[ii] != NULL; ii++) { - break; + hb_value_array_append(array, hb_value_string(audio_copy_list[ii])); } + hb_dict_set(preset, "AudioCopyMask", array); + } + if (acodec_fallback != NULL) + { + hb_dict_set(preset, "AudioEncoderFallback", + hb_value_string(acodec_fallback)); + } - switch( c ) + hb_value_array_t *audio_lang_array; + audio_lang_array = hb_dict_get(preset, "AudioLanguageList"); + if (audio_lang_array == NULL) + { + audio_lang_array = hb_value_array_init(); + hb_dict_set(preset, "AudioLanguageList", audio_lang_array); + } + if (audio_lang_list != NULL) + { + hb_value_array_clear(audio_lang_array); + int count = str_vlen(audio_lang_list); + for (ii = 0; ii < count; ii++) { - case 0: - /* option was handled entirely in getopt_long */ - break; - case 'h': - ShowHelp(); - exit( 0 ); - case 'u': - update = 1; - break; - case 'v': - if( optarg != NULL ) - { - debug = atoi( optarg ); - } - else - { - debug = 1; - } - break; - case 'Z': - preset = 1; - preset_name = strdup(optarg); - break; - case 'z': - ShowPresets(); - exit ( 0 ); - case DVDNAV: - dvdnav = 0; - break; + const iso639_lang_t *lang = lang_lookup(audio_lang_list[ii]); + if (lang != NULL) + { + hb_value_array_append(audio_lang_array, + hb_value_string(lang->iso639_2)); + } + else + { + fprintf(stderr, "Warning: Invalid audio language (%s)\n", + audio_lang_list[ii]); + return NULL; + } + } + hb_dict_set(preset, "AudioTrackSelectionBehavior", + hb_value_string("first")); + } + if (native_dub && native_language) + { + // Add native language audio + lang_list_remove(audio_lang_array, native_language); + hb_value_array_insert(audio_lang_array, 0, + hb_value_string(native_language)); + } + if (audio_all != -1) + { + hb_dict_set(preset, "AudioTrackSelectionBehavior", + hb_value_string(audio_all == 1 ? "all" : "first")); + } - case 'f': - format = strdup( optarg ); - break; - case 'i': - input = strdup( optarg ); -#ifdef __APPLE_CC__ - char *devName = bsd_name_for_path( input ); // alloc - if( devName ) - { - if( device_is_dvd( devName )) - { - free( input ); - input = malloc( strlen( "/dev/" ) + strlen( devName ) + 1 ); - sprintf( input, "/dev/%s", devName ); - } - free( devName ); - } -#endif - break; - case 'o': - output = strdup( optarg ); - break; - case 'O': - mp4_optimize = 1; - break; - case 'I': - ipod_atom = 1; - break; - case 'P': - use_opencl = 1; - break; - case 'U': - use_hwd = 1; - break; + // Audio overrides + if (atracks == NULL && ( + acodecs != NULL || + abitrates != NULL || + arates != NULL || + mixdowns != NULL || + normalize_mix_level != NULL || + audio_dither != NULL || + dynamic_range_compression != NULL || + audio_gain != NULL || + aqualities != NULL || + acompressions != NULL || + anames != NULL)) + { + // No explicit audio tracks, but track settings modified. + // Modify the presets audio settings. + hb_value_array_t *list; + list = hb_dict_get(preset, "AudioList"); + if (list == NULL) + { + list = hb_value_array_init(); + hb_dict_set(preset, "AudioList", list); + } + int count = MAX(str_vlen(mixdowns), + MAX(str_vlen(dynamic_range_compression), + MAX(str_vlen(audio_gain), + MAX(str_vlen(audio_dither), + MAX(str_vlen(normalize_mix_level), + MAX(str_vlen(arates), + MAX(str_vlen(abitrates), + MAX(str_vlen(aqualities), + MAX(str_vlen(acompressions), + MAX(str_vlen(acodecs), + str_vlen(anames))))))))))); + + hb_dict_t *audio_dict; + // Add audio dict entries to list if needed + for (ii = 0; ii < count; ii++) + { + audio_dict = hb_value_array_get(list, ii); + if (audio_dict == NULL) + { + audio_dict = hb_dict_init(); + hb_value_array_append(list, audio_dict); + } + } - case 't': - titleindex = atoi( optarg ); - break; - case SCAN_ONLY: - titlescan = 1; - break; - case MAIN_FEATURE: - main_feature = 1; - break; - case 'c': + // Update codecs + if (str_vlen(acodecs) > 0) + { + for (ii = 0; acodecs[ii] != NULL; ii++) { - int start, end; - if( sscanf( optarg, "%d-%d", &start, &end ) == 2 ) - { - chapter_start = start; - chapter_end = end; - } - else if( sscanf( optarg, "%d", &start ) == 1 ) - { - chapter_start = start; - chapter_end = chapter_start; - } - else - { - fprintf( stderr, "chapters: invalid syntax (%s)\n", - optarg ); - return -1; - } - break; + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioEncoder", + hb_value_string(acodecs[ii])); } - case NO_OPENCL: - use_opencl = 0; - break; - case ANGLE: - angle = atoi( optarg ); - break; - case 'm': - if( optarg != NULL ) - { - marker_file = strdup( optarg ); - } - chapter_markers = 1; - break; - case 'a': - if( optarg != NULL ) - { - atracks = strdup( optarg ); - audio_explicit = 1; - } - else - { - atracks = "1" ; - } - break; - case '6': - if( optarg != NULL ) - { - mixdowns = strdup( optarg ); - } - break; - case 'D': - if( optarg != NULL ) - { - dynamic_range_compression = strdup( optarg ); - } - break; - case AUDIO_GAIN: - if( optarg != NULL ) - { - audio_gain = strdup( optarg ); - } - break; - case AUDIO_DITHER: - if (optarg != NULL) - { - audio_dither = str_split(optarg, ','); - } - break; - case NORMALIZE_MIX: - if( optarg != NULL ) - { - normalize_mix_level = str_split( optarg, ',' ); - } - break; - case 's': - subtracks = str_split( optarg, ',' ); - break; - case 'F': - subforce = str_split( optarg, ',' ); - break; - case SUB_BURNED: - if( optarg != NULL ) - { - subburn = strdup( optarg ); - } - else - { - subburn = "" ; - } - break; - case SUB_DEFAULT: - if( optarg != NULL ) - { - subdefault = strdup( optarg ); - } - else - { - subdefault = "" ; - } - break; - case 'N': - native_language = strdup( optarg ); - break; - case NATIVE_DUB: - native_dub = 1; - break; - case SRT_FILE: - srtfile = str_split( optarg, ',' ); - break; - case SRT_CODESET: - srtcodeset = str_split( optarg, ',' ); - break; - case SRT_OFFSET: - srtoffset = str_split( optarg, ',' ); - break; - case SRT_LANG: - srtlang = str_split( optarg, ',' ); - break; - case SRT_DEFAULT: - if( optarg != NULL ) - { - srtdefault = atoi( optarg ); - } - else - { - srtdefault = 1 ; - } - break; - case SRT_BURN: - if( optarg != NULL ) - { - srtburn = atoi( optarg ); - } - else - { - srtburn = 1 ; - } - break; - case '2': - twoPass = 1; - break; - case 'd': - if( optarg != NULL ) - { - if (!( strcmp( optarg, "fast" ) )) - { - deinterlace_opt = "0"; - } - else if (!( strcmp( optarg, "slow" ) )) - { - deinterlace_opt = "1"; - } - else if (!( strcmp( optarg, "slower" ) )) - { - deinterlace_opt = "3"; - } - else if (!( strcmp( optarg, "bob" ) )) - { - deinterlace_opt = "15"; - } - else - { - deinterlace_opt = strdup( optarg ); - } - } - deinterlace = 1; - break; - case '7': - if( optarg != NULL ) - { - deblock_opt = strdup( optarg ); - } - deblock = 1; - break; - case '8': - if( optarg != NULL ) - { - free(denoise_opt); - denoise_opt = strdup( optarg ); - } - denoise = 1; - break; - case FILTER_NLMEANS: - if (optarg != NULL) - { - free(nlmeans_opt); - nlmeans_opt = strdup(optarg); - } - nlmeans = 1; - break; - case FILTER_NLMEANS_TUNE: - if (optarg != NULL) - { - free(nlmeans_tune_opt); - nlmeans_tune_opt = strdup(optarg); - } - break; - case '9': - if( optarg != NULL ) + // Apply last codec in list to all other entries + for (; ii < count; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioEncoder", + hb_value_string(acodecs[ii-1])); + } + } + + // Update qualities + if (str_vlen(aqualities) > 0) + { + for (ii = 0; aqualities[ii] != NULL && + aqualities[ii][0] != 0; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioTrackQualityEnable", + hb_value_bool(1)); + hb_dict_set(audio_dict, "AudioTrackQuality", + hb_value_double(strtod(aqualities[ii], NULL))); + } + } + + // Update bitrates + if (str_vlen(abitrates) > 0) + { + for (ii = 0; abitrates[ii] != NULL && + abitrates[ii][0] != 0; ii++) + { + audio_dict = hb_value_array_get(list, ii); + if (hb_value_get_bool(hb_dict_get(audio_dict, + "AudioTrackQualityEnable"))) { - detelecine_opt = strdup( optarg ); + continue; } - detelecine = 1; - break; - case '5': - if( optarg != NULL ) + hb_dict_set(audio_dict, "AudioBitrate", + hb_value_int(atoi(abitrates[ii]))); + } + // Apply last bitrate in list to all other entries + if (abitrates[ii-1][0] != 0) + for (; ii < count; ii++) { - if (!( strcmp( optarg, "fast" ) )) - { - decomb_opt = "7:2:6:9:1:80"; - } - else if (!( strcmp( optarg, "bob" ) )) - { - decomb_opt = "455"; - } - else + audio_dict = hb_value_array_get(list, ii); + if (hb_value_get_bool(hb_dict_get(audio_dict, + "AudioTrackQualityEnable"))) { - decomb_opt = strdup( optarg ); + continue; } + hb_dict_set(audio_dict, "AudioBitrate", + hb_value_int(atoi(abitrates[ii-1]))); } - decomb = 1; - break; - case 'g': - grayscale = 1; - break; - case ROTATE_FILTER: - if( optarg != NULL ) - { - rotate_opt = strdup( optarg ); - rotate_val = atoi( optarg ); - } - rotate = 1; - break; - case DISPLAY_WIDTH: - if( optarg != NULL ) - { - sscanf( optarg, "%i", &display_width ); - } - break; - case PIXEL_ASPECT: - if( optarg != NULL ) - { - sscanf( optarg, "%i:%i", &par_width, &par_height ); - } - break; - case MODULUS: - if( optarg != NULL ) + } + + // Update samplerates + if (str_vlen(arates) > 0) + { + for (ii = 0; arates[ii] != NULL && + arates[ii][0] != 0; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioSamplerate", + hb_value_string(arates[ii])); + } + // Apply last samplerate in list to all other entries + if (arates[ii-1][0] != 0) + for (; ii < count; ii++) { - sscanf( optarg, "%i", &modulus ); + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioSamplerate", + hb_value_string(arates[ii-1])); } - break; - case 'e': + } + + // Update mixdowns + if (str_vlen(mixdowns) > 0) + { + for (ii = 0; mixdowns[ii] != NULL && + mixdowns[ii][0] != 0; ii++) { - vcodec = hb_video_encoder_get_from_name(optarg); - if (vcodec <= 0) + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioMixdown", + hb_value_string(mixdowns[ii])); + } + // Apply last codec in list to all other entries + if (mixdowns[ii-1][0] != 0) + for (; ii < count; ii++) { - fprintf(stderr, "invalid codec (%s)\n", optarg); - return -1; + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioMixdown", + hb_value_string(mixdowns[ii-1])); } - break; + } + + // Update mixdowns normalization + if (str_vlen(normalize_mix_level) > 0) + { + for (ii = 0; normalize_mix_level[ii] != NULL && + normalize_mix_level[ii][0] != 0; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioNormalizeMixLevel", + hb_value_bool(atoi(normalize_mix_level[ii]))); } - case 'E': - if( optarg != NULL ) + // Apply last mix norm in list to all other entries + if (normalize_mix_level[ii-1][0] != 0) + for (; ii < count; ii++) { - acodecs = strdup( optarg ); + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, + "AudioNormalizeMixLevel", + hb_value_bool( + atoi(normalize_mix_level[ii-1]))); } - break; - case 'w': - width = atoi( optarg ); - break; - case 'l': - height = atoi( optarg ); - break; - case 'n': + } + + // Update DRC + if (str_vlen(dynamic_range_compression) > 0) + { + for (ii = 0;dynamic_range_compression[ii] != NULL && + dynamic_range_compression[ii][0] != 0; ii++) { - int i; - char * tmp = optarg; - for( i = 0; i < 4; i++ ) + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioTrackDRCSlider", + hb_value_double( + strtod(dynamic_range_compression[ii], NULL))); + } + // Apply last DRC in list to all other entries + if (dynamic_range_compression[ii-1][0] != 0) + for (; ii < count; ii++) { - if( !*tmp ) - break; - crop[i] = strtol( tmp, &tmp, 0 ); - tmp++; + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioTrackDRCSlider", + hb_value_double( + strtod(dynamic_range_compression[ii-1], + NULL))); } - break; - } - case LOOSE_CROP: - loose_crop = optarg ? atoi(optarg) : 15; - break; - case 'r': + } + + // Update Gain + if (str_vlen(audio_gain) > 0) + { + for (ii = 0; audio_gain[ii] != NULL && + audio_gain[ii][0] != 0; ii++) { - vrate = hb_video_framerate_get_from_name(optarg); - if (vrate <= 0) + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioTrackGainSlider", + hb_value_double( + strtod(audio_gain[ii], NULL))); + } + // Apply last gain in list to all other entries + if (audio_gain[ii-1][0] != 0) + for (; ii < count; ii++) { - vrate = 0; - fprintf(stderr, "invalid framerate %s\n", optarg); + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioTrackGainSlider", + hb_value_double( + strtod(audio_gain[ii-1], NULL))); } - else if (!cfr) + } + + // Update dither method + if (str_vlen(audio_dither) > 0) + { + for (ii = 0; audio_dither[ii] != NULL && + audio_dither[ii][0] != 0; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioDitherMethod", + hb_value_string(audio_dither[ii])); + } + // Apply last dither in list to all other entries + if (audio_dither[ii-1][0] != 0) + for (; ii < count; ii++) { - cfr = 1; + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioDitherMethod", + hb_value_string(audio_dither[ii-1])); } - break; + } + + // Update compression + if (str_vlen(acompressions) > 0) + { + for (ii = 0; acompressions[ii] != NULL && + acompressions[ii][0] != 0; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioCompressionLevel", + hb_value_double( + strtod(acompressions[ii], NULL))); } - case 'R': - if( optarg != NULL ) + // Apply last compression in list to all other entries + if (acompressions[ii-1][0] != 0) + for (; ii < count; ii++) { - arates = strdup( optarg ); + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioCompressionLevel", + hb_value_double( + strtod(acompressions[ii-1], NULL))); } - break; - case 'b': - vbitrate = atoi( optarg ); - break; - case 'q': - vquality = atof( optarg ); - break; - case 'B': - abitrates = str_split( optarg, ',' ); - break; - case 'Q': - aqualities = str_split( optarg, ',' ); - break; - case 'C': - acompressions = str_split( optarg, ',' ); - break; - case ENCODER_PRESET: - x264_preset = strdup( optarg ); - break; - case ENCODER_TUNE: - x264_tune = strdup( optarg ); - break; - case 'x': - advanced_opts = strdup( optarg ); - break; - case ENCODER_PROFILE: - h264_profile = strdup( optarg ); - break; - case ENCODER_LEVEL: - h264_level = strdup( optarg ); - break; - case ENCODER_PRESET_LIST: - fprintf(stderr, "Available --encoder-preset values for '%s' encoder:\n", - hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg))); - print_string_list(stderr, - hb_video_encoder_get_presets(hb_video_encoder_get_from_name(optarg)), - " "); - exit(0); - case ENCODER_TUNE_LIST: - fprintf(stderr, "Available --encoder-tune values for '%s' encoder:\n", - hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg))); - print_string_list(stderr, - hb_video_encoder_get_tunes(hb_video_encoder_get_from_name(optarg)), - " "); - exit(0); - case ENCODER_PROFILE_LIST: - fprintf(stderr, "Available --encoder-profile values for '%s' encoder:\n", - hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg))); - print_string_list(stderr, - hb_video_encoder_get_profiles(hb_video_encoder_get_from_name(optarg)), - " "); - exit(0); - case ENCODER_LEVEL_LIST: - fprintf(stderr, "Available --encoder-level values for '%s' encoder:\n", - hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg))); - print_string_list(stderr, - hb_video_encoder_get_levels(hb_video_encoder_get_from_name(optarg)), - " "); - exit(0); - case 'T': - fastfirstpass = 1; - break; - case 'Y': - maxHeight = atoi( optarg ); - break; - case 'X': - maxWidth = atoi (optarg ); - break; - case 'A': - if( optarg != NULL ) + } + + // Update track names + if (str_vlen(anames) > 0) + { + for (ii = 0; anames[ii] != NULL && + anames[ii][0] != 0; ii++) + { + audio_dict = hb_value_array_get(list, ii); + hb_dict_set(audio_dict, "AudioTrackName", + hb_value_string(acodecs[ii])); + } + } + } + + if (atracks != NULL) + { + // User has specified explicit audio tracks + // Disable preset's audio track selection + hb_dict_set(preset, "AudioTrackSelectionBehavior", + hb_value_string("none")); + } + + if (vcodec != NULL) + { + hb_dict_set(preset, "VideoEncoder", hb_value_string(vcodec)); + } + if (encoder_preset != NULL) + { + hb_dict_set(preset, "VideoPreset", hb_value_string(encoder_preset)); + } + if (encoder_tune != NULL) + { + hb_dict_set(preset, "VideoTune", hb_value_string(encoder_tune)); + } + if (encoder_profile != NULL) + { + hb_dict_set(preset, "VideoProfile", hb_value_string(encoder_profile)); + } + if (encoder_level != NULL) + { + hb_dict_set(preset, "VideoLevel", hb_value_string(encoder_level)); + } + if (advanced_opts != NULL) + { + hb_dict_set(preset, "VideoOptionExtra", hb_value_string(advanced_opts)); + } + if (vquality >= 0) + { + hb_dict_set(preset, "VideoQualityType", hb_value_int(2)); + hb_dict_set(preset, "VideoQualitySlider", hb_value_double(vquality)); + } + else if (vbitrate != 0) + { + hb_dict_set(preset, "VideoQualityType", hb_value_int(1)); + hb_dict_set(preset, "VideoAvgBitrate", hb_value_int(vbitrate)); + if (twoPass) + { + hb_dict_set(preset, "VideoTwoPass", hb_value_bool(1)); + } + if (fastfirstpass) + { + hb_dict_set(preset, "VideoTurboTwoPass", hb_value_bool(1)); + } + } + if (vrate != NULL) + { + hb_dict_set(preset, "VideoFramerate", hb_value_string(vrate)); + } + else + { + hb_dict_set(preset, "VideoFramerate", hb_value_string("auto")); + } + if (cfr != -1) + { + hb_dict_set(preset, "VideoFramerateMode", + hb_value_string(cfr == 0 ? "vfr" : + cfr == 1 ? "cfr" : "pfr")); + } + if (color_matrix_code > 0) + { + hb_dict_set(preset, "VideoColorMatrixCode", + hb_value_int(color_matrix_code)); + } +#ifdef USE_QSV + if (qsv_async_depth >= 0) + { + hb_dict_set(preset, "VideoQSVAsyncDepth", + hb_value_int(qsv_async_depth)); + } + if (qsv_decode != -1) + { + hb_dict_set(preset, "VideoQSVDecode", hb_value_int(qsv_decode)); + } +#endif + if (use_hwd != -1) + { + hb_dict_set(preset, "VideoHWDecode", hb_value_bool(use_hwd)); + } + if (use_opencl != -1) + { + hb_dict_set(preset, "VideoScaler", + hb_value_string(use_opencl ? "opencl" : "swscale")); + } + if (maxWidth > 0) + { + hb_dict_set(preset, "PictureWidth", hb_value_int(maxWidth)); + } + if (maxHeight > 0) + { + hb_dict_set(preset, "PictureHeight", hb_value_int(maxHeight)); + } + if (width > 0) + { + hb_dict_set(preset, "PictureForceWidth", hb_value_int(width)); + } + if (height > 0) + { + hb_dict_set(preset, "PictureForceHeight", hb_value_int(height)); + } + if (crop[0] >= 0) + { + hb_dict_set(preset, "PictureTopCrop", hb_value_int(crop[0])); + } + if (crop[1] >= 0) + { + hb_dict_set(preset, "PictureBottomCrop", hb_value_int(crop[1])); + } + if (crop[2] >= 0) + { + hb_dict_set(preset, "PictureLeftCrop", hb_value_int(crop[2])); + } + if (crop[3] >= 0) + { + hb_dict_set(preset, "PictureRightCrop", hb_value_int(crop[3])); + } + if (loose_crop != -1) + { + hb_dict_set(preset, "PictureLooseCrop", hb_value_bool(loose_crop)); + } + if (display_width > 0) + { + keep_display_aspect = 0; + anamorphic_mode = 3; + hb_dict_set(preset, "PictureDARWidth", hb_value_int(display_width)); + } + else if (par_width > 0 && par_height > 0) + { + keep_display_aspect = 0; + anamorphic_mode = 3; + hb_dict_set(preset, "PicturePARWidth", hb_value_int(par_width)); + hb_dict_set(preset, "PicturePARHeight", hb_value_int(par_height)); + } + if (anamorphic_mode != -1) + { + hb_dict_set(preset, "PicturePAR", hb_value_int(anamorphic_mode)); + } + if (keep_display_aspect != -1) + { + hb_dict_set(preset, "PictureKeepRatio", + hb_value_bool(keep_display_aspect)); + } + if (itu_par != -1) + { + hb_dict_set(preset, "PictureItuPAR", hb_value_bool(itu_par)); + } + if (modulus > 0) + { + hb_dict_set(preset, "PictureModulus", hb_value_int(modulus)); + } + if (grayscale != -1) + { + hb_dict_set(preset, "VideoGrayScale", hb_value_bool(grayscale)); + } + if (deinterlace_disable) + { + hb_dict_set(preset, "PictureDeinterlace", hb_value_string("off")); + } + if (deinterlace != NULL) + { + hb_dict_set(preset, "PictureDecombDeinterlace", hb_value_int(0)); + if (!deinterlace_custom) + { + hb_dict_set(preset, "PictureDeinterlace", + hb_value_string(deinterlace)); + } + else + { + hb_dict_set(preset, "PictureDeinterlace", + hb_value_string("custom")); + hb_dict_set(preset, "PictureDeinterlaceCustom", + hb_value_string(deinterlace)); + } + } + if (decomb_disable) + { + hb_dict_set(preset, "PictureDecomb", hb_value_string("off")); + } + if (decomb != NULL) + { + hb_dict_set(preset, "PictureDecombDeinterlace", hb_value_int(1)); + if (!decomb_custom) + { + hb_dict_set(preset, "PictureDecomb", hb_value_string(decomb)); + } + else + { + hb_dict_set(preset, "PictureDecomb", hb_value_string("custom")); + hb_dict_set(preset, "PictureDecombCustom", hb_value_string(decomb)); + } + } + if (detelecine_disable) + { + hb_dict_set(preset, "PictureDetelecine", hb_value_string("off")); + } + if (detelecine != NULL) + { + if (!detelecine_custom) + { + hb_dict_set(preset, "PictureDetelecine", + hb_value_string(detelecine)); + } + else + { + hb_dict_set(preset, "PictureDetelecine", hb_value_string("custom")); + hb_dict_set(preset, "PictureDetelecineCustom", + hb_value_string(detelecine)); + } + } + const char *s; + s = hb_value_get_string(hb_dict_get(preset, "PictureDenoiseFilter")); + if (hqdn3d_disable && !strcasecmp(s, "hqdn3d")) + { + hb_dict_set(preset, "PictureDenoiseFilter", hb_value_string("off")); + } + if (hqdn3d != NULL) + { + hb_dict_set(preset, "PictureDenoiseFilter", hb_value_string("hqdn3d")); + if (!hqdn3d_custom) + { + hb_dict_set(preset, "PictureDenoisePreset", + hb_value_string(hqdn3d)); + } + else + { + hb_dict_set(preset, "PictureDenoisePreset", + hb_value_string("custom")); + hb_dict_set(preset, "PictureDenoiseCustom", + hb_value_string(hqdn3d)); + } + } + if (nlmeans_disable && !strcasecmp(s, "nlmeans")) + { + hb_dict_set(preset, "PictureDenoiseFilter", hb_value_string("off")); + } + if (nlmeans != NULL) + { + hb_dict_set(preset, "PictureDenoiseFilter", hb_value_string("nlmeans")); + if (!nlmeans_custom) + { + hb_dict_set(preset, "PictureDenoisePreset", + hb_value_string(nlmeans)); + if (nlmeans_tune != NULL) + { + hb_dict_set(preset, "PictureDenoisePreset", + hb_value_string(nlmeans_tune)); + } + } + else + { + hb_dict_set(preset, "PictureDenoisePreset", + hb_value_string("custom")); + hb_dict_set(preset, "PictureDenoiseCustom", + hb_value_string(nlmeans)); + } + } + if (deblock_disable) + { + hb_dict_set(preset, "PictureDeblock", hb_value_string("0")); + } + if (deblock != NULL) + { + hb_dict_set(preset, "PictureDeblock", hb_value_string(deblock)); + } + if (rotate != NULL) + { + hb_dict_set(preset, "PictureRotate", hb_value_string(rotate)); + } + + return preset; +} + + +static int add_sub(hb_value_array_t *list, hb_title_t *title, int track, int *one_burned) +{ + hb_subtitle_t *subtitle; + // Check that the track exists + subtitle = hb_list_item(title->list_subtitle, track); + if (subtitle == NULL) + { + fprintf(stderr, "Warning: Could not find subtitle track %d, skipped\n", + track + 1); + return -1; + } + + int burn = !*one_burned && subburn == track + 1 && + hb_subtitle_can_burn(subtitle->source); + *one_burned |= burn; + int def = subdefault == track + 1; + int force = test_sub_list(subforce, track + 1); + + if (!burn && + !hb_subtitle_can_pass(subtitle->source, mux)) + { + // Only allow one subtitle to be burned into video + if (*one_burned) + { + fprintf(stderr, "Warning: Skipping subtitle track %d, can't have more than one track burnt in\n", track + 1); + return -1; + } + *one_burned = 1; + } + hb_dict_t *subtitle_dict = hb_dict_init(); + hb_dict_set(subtitle_dict, "Track", hb_value_int(track)); + hb_dict_set(subtitle_dict, "Default", hb_value_bool(def)); + hb_dict_set(subtitle_dict, "Forced", hb_value_bool(force)); + hb_dict_set(subtitle_dict, "Burn", hb_value_bool(burn)); + hb_value_array_append(list, subtitle_dict); + return 0; +} + +static int add_srt(hb_value_array_t *list, int track, int *one_burned) +{ + char *codeset = "ISO-8859-1"; + int64_t offset = 0; + char *iso639_2 = "und"; + int burn = !*one_burned && srtburn == track + 1 && + hb_subtitle_can_burn(SRTSUB); + *one_burned |= burn; + int def = srtdefault == track + 1; + + if (srtcodeset && track < str_vlen(srtcodeset) && srtcodeset[track]) + codeset = srtcodeset[track]; + if (srtoffset && track < str_vlen(srtoffset) && srtoffset[track]) + offset = strtoll(srtoffset[track], NULL, 0); + if (srtlang && track < str_vlen(srtlang) && srtlang[track]) + { + const iso639_lang_t *lang = lang_lookup(srtlang[track]); + if (lang != NULL) + { + iso639_2 = lang->iso639_2; + } + else + { + fprintf(stderr, "Warning: Invalid SRT language (%s)\n", + srtlang[track]); + } + } + + hb_dict_t *subtitle_dict = hb_dict_init(); + hb_dict_t *srt_dict = hb_dict_init(); + hb_dict_set(subtitle_dict, "SRT", srt_dict); + hb_dict_set(subtitle_dict, "Default", hb_value_bool(def)); + hb_dict_set(subtitle_dict, "Burn", hb_value_bool(burn)); + hb_dict_set(subtitle_dict, "Offset", hb_value_int(offset)); + hb_dict_set(srt_dict, "Filename", hb_value_string(srtfile[track])); + hb_dict_set(srt_dict, "Language", hb_value_string(iso639_2)); + hb_dict_set(srt_dict, "Codeset", hb_value_string(codeset)); + hb_value_array_append(list, subtitle_dict); + return 0; +} + +static int add_audio(hb_value_array_t *list, hb_title_t *title, int track) +{ + // Check that the track exists + if (hb_list_item(title->list_audio, track) == NULL) + { + fprintf(stderr, "Warning: Could not find audio track %d, skipped\n", + track + 1); + return -1; + } + hb_dict_t *audio_dict = hb_dict_init(); + hb_dict_set(audio_dict, "Track", hb_value_int(track)); + hb_value_array_append(list, audio_dict); + + return 0; +} + +static hb_dict_t* +PrepareJob(hb_handle_t *h, hb_title_t *title, hb_dict_t *preset_dict) +{ + hb_dict_t *job_dict; + job_dict = hb_preset_job_init(h, title->index, preset_dict); + if (job_dict == NULL) + { + fprintf(stderr, "Failed to initialize job\n"); + return NULL; + } + + if (hb_value_get_bool(hb_dict_get(job_dict, "ChapterMarkers"))) + { + write_chapter_names(job_dict, marker_file); + } + + hb_dict_t *dest_dict = hb_dict_get(job_dict, "Destination"); + hb_dict_set(dest_dict, "File", hb_value_string(output)); + + // Now that the job is initialized, we need to find out + // what muxer is being used. + mux = hb_container_get_from_name( + hb_value_get_string(hb_dict_get(dest_dict, "Mux"))); + + // Now set non-preset settings in the job dict + int range_start = 0, range_end = 0, range_seek_points = 0; + const char *range_type = "chapter"; + if (chapter_start && chapter_end && + !start_at_pts && !stop_at_pts && + !start_at_frame && !stop_at_frame && + !start_at_preview ) + { + range_type = "chapter"; + int chapter_count = hb_list_count(title->list_chapter); + range_start = MAX(1, chapter_start); + range_end = MIN(chapter_count, chapter_end); + range_end = MAX(chapter_start, chapter_end); + } + else if (start_at_preview) + { + range_type = "preview"; + range_start = start_at_preview -1; + range_end = stop_at_pts; + range_seek_points = preview_count; + } + else if (start_at_pts || stop_at_pts) + { + range_type = "time"; + range_start = start_at_pts; + range_end = stop_at_pts; + } + else if (start_at_frame || stop_at_frame) + { + range_type = "frame"; + range_start = start_at_frame; + range_end = stop_at_frame; + } + if (range_start || range_end) + { + hb_dict_t *range_dict = hb_dict_get( + hb_dict_get(job_dict, "Source"), "Range"); + hb_dict_set(range_dict, "Type", hb_value_string(range_type)); + if (range_start) + hb_dict_set(range_dict, "Start", hb_value_int(range_start)); + if (range_end) + hb_dict_set(range_dict, "End", hb_value_int(range_end)); + if (range_seek_points) + hb_dict_set(range_dict, "SeekPoints", + hb_value_int(range_seek_points)); + } + + if (angle) + { + hb_dict_t *source_dict = hb_dict_get(job_dict, "Source"); + hb_dict_set(source_dict, "Angle", hb_value_int(angle)); + } + + hb_dict_t *subtitles_dict = hb_dict_get(job_dict, "Subtitle"); + hb_value_array_t * subtitle_array; + subtitle_array = hb_dict_get(subtitles_dict, "SubtitleList"); + + hb_dict_t *audios_dict = hb_dict_get(job_dict, "Audio"); + hb_value_array_t * audio_array = hb_dict_get(audios_dict, "AudioList"); + hb_dict_t *audio_dict; + int track_count = hb_value_array_len(audio_array); + + /* Grab audio tracks */ + if (atracks != NULL) + { + int ii; + if (atracks[0] != NULL && strcasecmp("none", atracks[0])) + { + // First "track" is not "none", add tracks + for (ii = 0; atracks[ii] != NULL; ii++) + { + int first, last, track; + if (sscanf(atracks[ii], "%d-%d", &first, &last ) == 2) { - anames = str_split( optarg, ',' ); + for (track = first - 1; track < last; track++) + { + add_audio(audio_array, title, track); + } } - break; - case PREVIEWS: - sscanf( optarg, "%i:%i", &preview_count, &store_previews ); - break; - case START_AT_PREVIEW: - start_at_preview = atoi( optarg ); - break; - case START_AT: - { - char * start_at_string = NULL; - char * start_at_token = NULL; - start_at_string = strdup( optarg ); - start_at_token = strtok( start_at_string, ":"); - if( !strcmp( start_at_token, "frame" ) ) + else if (sscanf(atracks[ii], "%d", &track) == 1) { - start_at_token = strtok( NULL, ":"); - start_at_frame = atoi(start_at_token); + add_audio(audio_array, title, track); } - else if( !strcmp( start_at_token, "pts" ) ) + else { - start_at_token = strtok( NULL, ":"); - sscanf( start_at_token, "%"SCNd64, &start_at_pts ); + fprintf(stderr, "ERROR: unable to parse audio input \"%s\", skipping\n", atracks[ii]); } - else if( !strcmp( start_at_token, "duration" ) ) + } + } + track_count = hb_value_array_len(audio_array); + + // Now we need to take care of initializing subtitle selection + // for foreign audio since this could not be done by the preset + // due to disabling of preset audio selection. + + // Determine the language of the first audio track + if (track_count > 0) + { + audio_dict = hb_value_array_get(audio_array, 0); + int track = hb_value_get_int(hb_dict_get(audio_dict, "Track")); + + hb_audio_config_t *audio; + audio = hb_list_audio_config_item(title->list_audio, track); + if (audio != NULL) + { + hb_preset_job_add_subtitles(h, title->index, + preset_dict, job_dict); + } + } + + /* Audio Codecs */ + int acodec; + ii = 0; + if (acodecs != NULL) + { + for (; acodecs[ii] != NULL && ii < track_count; ii++) + { + audio_dict = hb_value_array_get(audio_array, ii); + acodec = hb_audio_encoder_get_from_name(acodecs[ii]); + if (acodec <= 0) { - double duration_seconds = parse_hhmmss_strtok(); - start_at_pts = (int64_t)(duration_seconds * 90e3); + fprintf(stderr, + "Invalid codec %s, using default for container.\n", + acodecs[ii]); + acodec = hb_audio_encoder_get_default(mux); } - free( start_at_string ); - break; + hb_dict_set(audio_dict, "Encoder", hb_value_int(acodec)); } - case STOP_AT: + if (acodecs[ii] != NULL) { - char * stop_at_string = NULL; - char * stop_at_token = NULL; - stop_at_string = strdup( optarg ); - stop_at_token = strtok( stop_at_string, ":"); - if( !strcmp( stop_at_token, "frame" ) ) + fprintf(stderr, "Dropping excess audio encoders\n"); + } + } + acodec = hb_audio_encoder_get_default(mux); + for (; ii < track_count; ii++) + { + // We have fewer inputs than audio tracks, use the + // default codec for this container for the remaining + // tracks. Unless we only have one input then use that + // codec instead. + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Encoder", hb_value_int(acodec)); + } + + /* Sample Rate */ + int arate; + ii = 0; + if (arates != NULL) + { + for (; arates[ii] != NULL && ii < track_count; ii++) + { + if (!strcasecmp(arates[ii], "auto")) { - stop_at_token = strtok( NULL, ":"); - stop_at_frame = atoi(stop_at_token); + arate = 0; } - else if( !strcmp( stop_at_token, "pts" ) ) + else { - stop_at_token = strtok( NULL, ":"); - sscanf( stop_at_token, "%"SCNd64, &stop_at_pts ); + arate = hb_audio_samplerate_get_from_name(arates[ii]); } - else if( !strcmp( stop_at_token, "duration" ) ) + if (arate <= 0) { - double duration_seconds = parse_hhmmss_strtok(); - stop_at_pts = (int64_t)(duration_seconds * 90e3); + fprintf(stderr, "Invalid sample rate %s, using input rate\n", + arates[ii]); + arate = 0; } - free( stop_at_string ); - break; + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Samplerate", hb_value_int(arate)); } - case ALLOWED_AUDIO_COPY: + if (arates[ii] != NULL) + { + fprintf(stderr, "Dropping excess audio sample rates\n"); + } + } + // If exactly one samplerate was specified, apply it to the reset + // of the tracks. + // + // For any tracks that we do not set the rate for, libhb will + // assign the source audio track's rate + if (ii == 1) for (; ii < track_count; ii++) + { + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Samplerate", hb_value_int(arate)); + } + + /* Audio Mixdown */ + int mix; + ii = 0; + if (mixdowns != NULL) + { + for (; mixdowns[ii] != NULL && ii < track_count; ii++) + { + mix = hb_mixdown_get_from_name(mixdowns[ii]); + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Mixdown", hb_value_int(mix)); + } + if (mixdowns[ii] != NULL) { - allowed_audio_copy = 0; - const hb_encoder_t *audio_encoder = NULL; - char **allowed = str_split(optarg, ','); + fprintf(stderr, "Dropping excess audio mixdowns\n"); + } + } + // If exactly one mix was specified, apply it to the reset + // of the tracks + if (ii == 1) for (; ii < track_count; ii++) + { + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Mixdown", hb_value_int(mix)); + } - while ((audio_encoder = hb_audio_encoder_get_next(audio_encoder)) != NULL) + /* Audio Bitrate */ + int abitrate; + ii = 0; + if (abitrates != NULL) + { + for (; abitrates[ii] != NULL && ii < track_count; ii++) + { + if (*abitrates[ii]) { - if ((audio_encoder->codec & HB_ACODEC_PASS_FLAG) && - (audio_encoder->codec != HB_ACODEC_AUTO_PASS)) - { - int i = -1; - while(allowed[++i] != NULL) - { - // skip "copy:" - if (!strcasecmp(allowed[i], - audio_encoder->short_name + 5)) - { - allowed_audio_copy |= audio_encoder->codec; - break; - } - } - } + abitrate = atoi(abitrates[ii]); + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Bitrate", hb_value_int(abitrate)); } - - allowed_audio_copy &= HB_ACODEC_PASS_MASK; - str_vfree(allowed); - break; } - case AUDIO_FALLBACK: - acodec_fallback = strdup( optarg ); - break; - case 'M': - if( optarg != NULL ) - { - if( !strcmp( optarg, "601" ) || - !strcmp( optarg, "ntsc" ) ) - color_matrix_code = 1; - else if( !strcmp( optarg, "pal" ) ) - color_matrix_code = 2; - else if( !strcmp( optarg, "709" ) ) - color_matrix_code = 3; - } break; - case MIN_DURATION: - min_title_duration = strtol( optarg, NULL, 0 ); - break; -#ifdef USE_QSV - case QSV_BASELINE: - hb_qsv_force_workarounds(); - break; - case QSV_ASYNC_DEPTH: - qsv_async_depth = atoi(optarg); - break; - case QSV_IMPLEMENTATION: - hb_qsv_impl_set_preferred(optarg); - break; -#endif - default: - fprintf( stderr, "unknown option (%s)\n", argv[cur_optind] ); - return -1; + if (abitrates[ii] != NULL) + { + fprintf(stderr, "Dropping excess audio bitrates\n"); + } + } + // If exactly one bitrate was specified, apply it to the reset + // of the tracks + if (ii == 1) for (; ii < track_count; ii++) + { + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Bitrate", hb_value_int(abitrate)); } - } - - if (nlmeans) - { - char *opt = hb_generate_filter_settings(HB_FILTER_NLMEANS, - nlmeans_opt, nlmeans_tune_opt); - if (opt != NULL) + /* Audio Quality */ + double aquality; + ii = 0; + if (aqualities != NULL) { - free(nlmeans_opt); - nlmeans_opt = opt; + for (; aqualities[ii] != NULL && ii < track_count; ii++) + { + if (*aqualities[ii]) + { + aquality = atof(aqualities[ii]); + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Quality", + hb_value_double(aquality)); + hb_dict_set(audio_dict, "Bitrate", hb_value_int(-1)); + } + } + if (aqualities[ii] != NULL) + { + fprintf(stderr, "Dropping excess audio qualities\n"); + } } - else if (nlmeans_opt != NULL) + // If exactly one quality was specified, apply it to the reset + // of the tracks that do not already have the bitrate set. + if (ii == 1) for (; ii < track_count; ii++) { - fprintf(stderr, "Invalid parameters for nlmeans (%s).", nlmeans_opt); - return -1; + audio_dict = hb_value_array_get(audio_array, ii); + abitrate = hb_value_get_int(hb_dict_get(audio_dict, "Bitrate")); + if (abitrate <= 0) + { + hb_dict_set(audio_dict, "Quality", hb_value_double(aquality)); + hb_dict_set(audio_dict, "Bitrate", hb_value_int(-1)); + } } - else if (nlmeans_tune_opt != NULL) + + /* Audio Compression Level */ + double acompression; + ii = 0; + if (acompressions != NULL) { - fprintf(stdout, "Default nlmeans parameters specified; ignoring nlmeans tune (%s).\n", nlmeans_tune_opt); + for (; acompressions[ii] != NULL && ii < track_count; ii++) + { + if (*acompressions[ii]) + { + acompression = atof(acompressions[ii]); + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "CompressionLevel", + hb_value_double(acompression)); + } + } + if (acompressions[ii] != NULL) + { + fprintf(stderr, + "Dropping excess audio compression levels\n"); + } } - } - if (denoise) - { - char *opt = hb_generate_filter_settings(HB_FILTER_DENOISE, - denoise_opt, NULL); - if (opt != NULL) + // Compression levels are codec specific values. So don't + // try to apply to other tracks. + + /* Audio DRC */ + ii = 0; + double drc; + if (dynamic_range_compression) { - free(denoise_opt); - denoise_opt = opt; + char **drcs = dynamic_range_compression; + for (; drcs[ii] != NULL && ii < track_count; ii++) + { + drc = atof(drcs[ii]); + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "DRC", hb_value_double(drc)); + } + if (drcs[ii] != NULL) + { + fprintf(stderr, "Dropping excess audio dynamic range controls\n"); + } } - else if (denoise_opt != NULL) + // If exactly one DRC was specified, apply it to the reset + // of the tracks + if (ii == 1) for (; ii < track_count; ii++) { - fprintf(stderr, "Invalid parameters for hqdn3d (%s).", denoise_opt); - return -1; + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "DRC", hb_value_double(drc)); } - } - - return 0; -} -static int CheckOptions( int argc, char ** argv ) -{ - if( update ) - { - return 0; - } - - if( input == NULL || *input == '\0' ) - { - fprintf( stderr, "Missing input device. Run %s --help for " - "syntax.\n", argv[0] ); - return 1; - } + /* Audio Gain */ + ii = 0; + double gain; + if (audio_gain) + { + for (; audio_gain[ii] != NULL && ii < track_count; ii++) + { + gain = atof(audio_gain[ii]); + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Gain", hb_value_double(gain)); + } + if (audio_gain[ii] != NULL) + { + fprintf(stderr, "Dropping excess audio gains\n"); + } + } + // If exactly one gain was specified, apply it to the reset + // of the tracks + if (ii == 1) for (; ii < track_count; ii++) + { + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Gain", hb_value_double(gain)); + } - /* Parse format */ - if( titleindex > 0 && !titlescan ) - { - if( output == NULL || *output == '\0' ) + /* Audio Dither */ + int dither; + ii = 0; + if (audio_dither != NULL) { - fprintf( stderr, "Missing output file name. Run %s --help " - "for syntax.\n", argv[0] ); - return 1; + for (; audio_dither[ii] != NULL && ii < track_count; ii++) + { + if (*audio_dither[ii]) + { + dither = hb_audio_dither_get_from_name(audio_dither[ii]); + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "DitherMethod", + hb_value_int(dither)); + } + } + if (audio_dither[ii] != NULL) + { + fprintf(stderr, "Dropping excess audio dither methods\n"); + } + } + // If exactly one dither was specified, apply it to the reset + // of the tracks + if (ii == 1) for (; ii < track_count; ii++) + { + int codec; + audio_dict = hb_value_array_get(audio_array, ii); + codec = hb_value_get_int(hb_dict_get(audio_dict, "Encoder")); + if (hb_audio_dither_is_supported(codec)) + { + hb_dict_set(audio_dict, "DitherMethod", + hb_value_double(dither)); + } } - if (format == NULL) + /* Audio Mix Normalization */ + int norm = 0; + ii = 0; + if (normalize_mix_level) { - /* autodetect */ - const char *extension = strrchr(output, '.'); - if (extension != NULL) + char **nmls = normalize_mix_level; + for (; nmls[ii] != NULL && ii < track_count; ii++) { - // skip '.' - mux = hb_container_get_from_extension(extension + 1); + norm = atoi(nmls[ii]); + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "NormalizeMixLevel", + hb_value_int(norm)); } - if (mux <= 0) + if (nmls[ii] != NULL) { fprintf(stderr, - "Output format can't be guessed from file name (%s), " - "using default.\n", output); - // reset the muxer (use default) - mux = 0; - return 0; + "Dropping excess audio mixdown normalizations\n"); } } - else + // If exactly one norm was specified, apply it to the reset + // of the tracks + if (ii == 1) for (; ii < track_count; ii++) + { + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "NormalizeMixLevel", hb_value_int(norm)); + } + + /* Audio Track Names */ + ii = 0; + if (anames != NULL) { - mux = hb_container_get_from_name(format); - if (mux <= 0) + for (; anames[ii] != NULL && ii < track_count; ii++) { - fprintf(stderr, "Invalid output format (%s).", format); - fprintf(stderr, "Possible choices are: "); - const hb_container_t *container = NULL; - while ((container = hb_container_get_next(container)) != NULL) + if (*anames[ii]) { - fprintf(stderr, "%s", container->short_name); - if (hb_container_get_next(container) != NULL) - { - fprintf(stderr, ", "); - } - else - { - fprintf(stderr, "\n"); - } + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Name", + hb_value_string(anames[ii])); } - return 1; + } + if (anames[ii] != NULL) + { + fprintf(stderr, "Dropping excess audio track names\n"); } } + // If exactly one name was specified, apply it to the reset + // of the tracks + if (ii == 1 && *anames[0]) for (; ii < track_count; ii++) + { + audio_dict = hb_value_array_get(audio_array, ii); + hb_dict_set(audio_dict, "Name", hb_value_string(anames[0])); + } } - return 0; + int one_burned = 0; + if (subtracks != NULL) + { + int ii; + for (ii = 0; subtracks[ii] != NULL; ii++) + { + if (strcasecmp(subtracks[ii], "none" ) == 0) + { + // Taken care of already when initializing the job + // from a preset + continue; + } + if (strcasecmp(subtracks[ii], "scan" ) == 0) + { + // Taken care of already when initializing the job + // from a preset + continue; + } + + int first, last, track; + if (sscanf(subtracks[ii], "%d-%d", &first, &last ) == 2) + { + for (track = first - 1; track < last; track++) + { + add_sub(subtitle_array, title, track, &one_burned); + } + } + else if (sscanf(subtracks[ii], "%d", &track) == 1) + { + add_sub(subtitle_array, title, track, &one_burned); + } + else + { + fprintf(stderr, "ERROR: unable to parse subtitle input \"%s\", skipping\n", subtracks[ii]); + } + } + } + + if (srtfile != NULL) + { + int ii; + for (ii = 0; srtfile[ii] != NULL; ii++) + { + add_srt(subtitle_array, ii, &one_burned); + } + } + + return job_dict; } + static void print_string_list(FILE *out, const char* const *list, const char *prefix) { if (out != NULL && prefix != NULL) -- cgit v1.2.3