diff options
-rw-r--r-- | libhb/common.h | 6 | ||||
-rw-r--r-- | libhb/hb.c | 84 | ||||
-rw-r--r-- | libhb/reader.c | 24 | ||||
-rw-r--r-- | libhb/work.c | 119 | ||||
-rw-r--r-- | test/test.c | 218 |
5 files changed, 394 insertions, 57 deletions
diff --git a/libhb/common.h b/libhb/common.h index 8b09c85c9..116f99a93 100644 --- a/libhb/common.h +++ b/libhb/common.h @@ -245,6 +245,10 @@ struct hb_job_s int mux; const char * file; + int subtitle_scan; + hb_subtitle_t ** select_subtitle; + char * native_language; + #ifdef __LIBHB__ /* Internal data */ hb_handle_t * h; @@ -360,6 +364,8 @@ struct hb_subtitle_s char lang[1024]; char iso639_2[4]; + int hits; /* How many hits/occurrences of this subtitle */ + #ifdef __LIBHB__ /* Internal data */ hb_fifo_t * fifo_in; /* SPU ES */ diff --git a/libhb/hb.c b/libhb/hb.c index 467ba92ad..0550c3cac 100644 --- a/libhb/hb.c +++ b/libhb/hb.c @@ -546,13 +546,87 @@ void hb_add( hb_handle_t * h, hb_job_t * job ) } } - /* Copy the subtitle we want (or not) */ title_copy->list_subtitle = hb_list_init(); - if( ( subtitle = hb_list_item( title->list_subtitle, job->subtitle ) ) ) + + if( job->pass != 1 ) { - subtitle_copy = malloc( sizeof( hb_subtitle_t ) ); - memcpy( subtitle_copy, subtitle, sizeof( hb_subtitle_t ) ); - hb_list_add( title_copy->list_subtitle, subtitle_copy ); + /* Copy the subtitle we want (or not) + */ + if( ( subtitle = hb_list_item( title->list_subtitle, job->subtitle ) ) ) + { + subtitle_copy = malloc( sizeof( hb_subtitle_t ) ); + memcpy( subtitle_copy, subtitle, sizeof( hb_subtitle_t ) ); + hb_list_add( title_copy->list_subtitle, subtitle_copy ); + } + } else { + char audio_lang[4]; + + memset( audio_lang, 0, sizeof( audio_lang ) ); + + /* + * Pass 1, do we want to do a subtitle scan? + */ + if( job->subtitle_scan ) + { + /* + * Search for all occurances of the audio language in the + * subtitles and add them all to the + * title_copy->list_subtitle. First of all find the + * language for the audio. + */ + for( i = 0; i < 8; i++ ) + { + if( job->audios[i] < 0 ) + { + break; + } + if( ( audio = hb_list_item( title->list_audio, job->audios[i] ) ) ) + { + strncpy(audio_lang, audio->iso639_2, sizeof(audio_lang)); + } + } + } + + /* + * If we have a native language now is the time to see whether we want + * to enable subtitles for it if it differs from the audio language. + */ + if( job->native_language ) + { + if( strncasecmp( job->native_language, audio_lang, + sizeof( audio_lang ) ) != 0 ) + { + + hb_log( "Enabled subtitles in native language '%s', audio is in '%s'", + job->native_language, audio_lang); + /* + * The main audio track is not in our native language, so switch + * the subtitle scan to use our native language instead. + */ + strncpy( audio_lang, job->native_language, sizeof( audio_lang ) ); + } else { + /* + * native language is irrelevent, free it. + */ + free( job->native_language ); + job->native_language = NULL; + } + } + + for( i=0; i < hb_list_count( title->list_subtitle ); i++ ) + { + subtitle = hb_list_item( title->list_subtitle, i ); + if( strcmp( subtitle->iso639_2, audio_lang ) == 0 ) + { + /* + * Matched subtitle language with audio language, so + * add this to our list to scan. + */ + subtitle_copy = malloc( sizeof( hb_subtitle_t ) ); + memcpy( subtitle_copy, subtitle, sizeof( hb_subtitle_t ) ); + hb_list_add( title_copy->list_subtitle, subtitle_copy ); + } + } } /* Copy the job */ diff --git a/libhb/reader.c b/libhb/reader.c index 35939f96b..4767f87a7 100644 --- a/libhb/reader.c +++ b/libhb/reader.c @@ -138,12 +138,26 @@ static hb_fifo_t * GetFifoForId( hb_job_t * job, int id ) return job->fifo_mpeg2; } - if( ( subtitle = hb_list_item( title->list_subtitle, 0 ) ) && - id == subtitle->id ) - { - return subtitle->fifo_in; + if (job->subtitle_scan) { + /* + * Count the occurances of the subtitles, don't actually return any to encode. + */ + for (i=0; i < hb_list_count(title->list_subtitle); i++) { + subtitle = hb_list_item( title->list_subtitle, i); + if (id == subtitle->id) { + /* + * A hit, count it. + */ + subtitle->hits++; + } + } + } else { + if( ( subtitle = hb_list_item( title->list_subtitle, 0 ) ) && + id == subtitle->id ) + { + return subtitle->fifo_in; + } } - for( i = 0; i < hb_list_count( title->list_audio ); i++ ) { audio = hb_list_item( title->list_audio, i ); diff --git a/libhb/work.c b/libhb/work.c index aa7709719..b7d02cd5c 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -100,6 +100,11 @@ static void do_job( hb_job_t * job, int cpu_count ) hb_audio_t * audio; hb_subtitle_t * subtitle; int done; + unsigned int subtitle_highest = 0; + unsigned int subtitle_highest_id = 0; + unsigned int subtitle_lowest = -1; + unsigned int subtitle_lowest_id = 0; + unsigned int subtitle_hit = 0; title = job->title; @@ -191,17 +196,31 @@ static void do_job( hb_job_t * job, int cpu_count ) w->config = &job->config; hb_list_add( job->list_work, w ); - subtitle = hb_list_item( title->list_subtitle, 0 ); - if( subtitle ) + if( job->select_subtitle && !job->subtitle_scan ) { - hb_log( " + subtitle %x, %s", subtitle->id, subtitle->lang ); + hb_list_add( title->list_subtitle, *( job->select_subtitle ) ); + } - subtitle->fifo_in = hb_fifo_init( 8 ); - subtitle->fifo_raw = hb_fifo_init( 8 ); + for( i=0; i < hb_list_count(title->list_subtitle); i++ ) + { + subtitle = hb_list_item( title->list_subtitle, i ); - hb_list_add( job->list_work, ( w = getWork( WORK_DECSUB ) ) ); - w->fifo_in = subtitle->fifo_in; - w->fifo_out = subtitle->fifo_raw; + if( subtitle ) + { + hb_log( " + subtitle %x, %s", subtitle->id, subtitle->lang ); + + subtitle->fifo_in = hb_fifo_init( 8 ); + subtitle->fifo_raw = hb_fifo_init( 8 ); + + if (!job->subtitle_scan) { + /* + * Don't add threads for subtitles when we are scanning + */ + hb_list_add( job->list_work, ( w = getWork( WORK_DECSUB ) ) ); + w->fifo_in = subtitle->fifo_in; + w->fifo_out = subtitle->fifo_raw; + } + } } if( job->acodec & HB_ACODEC_AC3 ) @@ -499,10 +518,13 @@ static void do_job( hb_job_t * job, int cpu_count ) hb_fifo_close( &job->fifo_sync ); hb_fifo_close( &job->fifo_render ); hb_fifo_close( &job->fifo_mpeg4 ); - if( subtitle ) - { - hb_fifo_close( &subtitle->fifo_in ); - hb_fifo_close( &subtitle->fifo_raw ); + for (i=0; i < hb_list_count(title->list_subtitle); i++) { + subtitle = hb_list_item( title->list_subtitle, i); + if( subtitle ) + { + hb_fifo_close( &subtitle->fifo_in ); + hb_fifo_close( &subtitle->fifo_raw ); + } } for( i = 0; i < hb_list_count( title->list_audio ); i++ ) { @@ -513,6 +535,79 @@ static void do_job( hb_job_t * job, int cpu_count ) hb_fifo_close( &audio->fifo_out ); } + /* + * Before closing the title print out our subtitle stats if we need to + * Find the highest and lowest. + */ + for( i=0; i < hb_list_count( title->list_subtitle ); i++ ) + { + subtitle = hb_list_item( title->list_subtitle, i ); + hb_log( "Subtitle stream 0x%x '%s': %d hits", + subtitle->id, subtitle->lang, subtitle->hits ); + if( subtitle->hits > subtitle_highest ) + { + subtitle_highest = subtitle->hits; + subtitle_highest_id = subtitle->id; + } + + if( subtitle->hits < subtitle_lowest ) + { + subtitle_lowest = subtitle->hits; + subtitle_lowest_id = subtitle->id; + } + } + + if( job->native_language ) { + /* + * We still have a native_language, so the audio and subtitles are + * different, so in this case it is a foreign film and we want to + * select the first subtitle in our language. + */ + subtitle = hb_list_item( title->list_subtitle, 0 ); + subtitle_hit = subtitle->id; + hb_log( "Found a native-language subtitle id 0x%x", subtitle_hit); + } else { + if( subtitle_lowest < subtitle_highest ) + { + /* + * OK we have more than one, and the lowest is lower, but how much + * lower to qualify for turning it on by default? + * + * Let's say 20% as a default. + */ + if( subtitle_lowest < ( subtitle_highest * 0.2 ) ) + { + subtitle_hit = subtitle_lowest_id; + hb_log( "Found a subtitle candidate id 0x%x", + subtitle_hit ); + } else { + hb_log( "No candidate subtitle detected during subtitle-scan"); + } + } + } + + if( job->select_subtitle ) + { + if( job->subtitle_scan ) + { + for( i=0; i < hb_list_count( title->list_subtitle ); i++ ) + { + subtitle = hb_list_item( title->list_subtitle, i ); + if( subtitle->id = subtitle_hit ) + { + hb_list_rem( title->list_subtitle, subtitle ); + *( job->select_subtitle ) = subtitle; + } + } + } else { + /* + * Must be the second pass - we don't need this anymore. + */ + free( job->select_subtitle ); + job->select_subtitle = NULL; + } + } + hb_title_close( &job->title ); free( job ); } diff --git a/test/test.c b/test/test.c index 3ed30e4af..addee82f0 100644 --- a/test/test.c +++ b/test/test.c @@ -20,6 +20,9 @@ static char * input = NULL; static char * output = NULL; static char * format = NULL; static int titleindex = 1; +static int longest_title = 0; +static int subtitle_scan = 0; +static char * native_language = NULL; static int twoPass = 0; static int deinterlace = 0; static int grayscale = 0; @@ -51,6 +54,8 @@ static char *x264opts = NULL; static char *x264opts2 = NULL; static int maxHeight = 0; static int maxWidth = 0; +static int turbo_opts_enabled = 0; +static char * turbo_opts = "no-fast-pskip=0:subme=1:me=dia:trellis=0:analyse=none"; /* Exit cleanly on Ctrl-C */ static volatile int die = 0; @@ -116,6 +121,13 @@ int main( int argc, char ** argv ) /* Feed libhb with a DVD to scan */ fprintf( stderr, "Opening %s...\n", input ); + + if (longest_title) { + /* + * We need to scan for all the titles in order to find the longest + */ + titleindex = 0; + } hb_scan( h, input, titleindex ); /* Wait... */ @@ -181,6 +193,7 @@ int main( int argc, char ** argv ) if( output ) free( output ); if( format ) free( format ); if( audios ) free( audios ); + if (native_language ) free (native_language ); if( x264opts ) free (x264opts ); if( x264opts2 ) free (x264opts2 ); @@ -245,7 +258,8 @@ static void PrintTitleInfo( hb_title_t * title ) for( i = 0; i < hb_list_count( title->list_subtitle ); i++ ) { subtitle = hb_list_item( title->list_subtitle, i ); - fprintf( stderr, " + %d, %s\n", i + 1, subtitle->lang ); + fprintf( stderr, " + %d, %s (iso639-2: %s)\n", i + 1, subtitle->lang, + subtitle->iso639_2); } } @@ -284,6 +298,44 @@ static int HandleEvents( hb_handle_t * h ) die = 1; break; } + if( longest_title ) + { + int i; + int longest_title_idx=0; + int longest_title_pos=-1; + int longest_title_time=0; + int title_time; + + fprintf( stderr, "Searching for longest title...\n" ); + + for( i = 0; i < hb_list_count( list ); i++ ) + { + title = hb_list_item( list, i ); + title_time = (title->hours*60*60 ) + (title->minutes *60) + (title->seconds); + fprintf( stderr, " + Title (%d) index %d has length %dsec\n", + i, title->index, title_time ); + if( longest_title_time < title_time ) + { + longest_title_time = title_time; + longest_title_pos = i; + longest_title_idx = title->index; + } + } + if( longest_title_pos == -1 ) + { + fprintf( stderr, "No longest title found.\n" ); + die = 1; + break; + } + titleindex = longest_title_idx; + fprintf( stderr, "Found longest title, setting title to %d\n", + longest_title_idx); + + title = hb_list_item( list, longest_title_pos); + } else { + title = hb_list_item( list, 0 ); + } + if( !titleindex ) { /* Scan-only mode, print infos and exit */ @@ -298,7 +350,6 @@ static int HandleEvents( hb_handle_t * h ) } /* Set job settings */ - title = hb_list_item( list, 0 ); job = title->job; PrintTitleInfo( title ); @@ -479,6 +530,11 @@ static int HandleEvents( hb_handle_t * h ) job->subtitle = sub - 1; } + if( native_language ) + { + job->native_language = strdup( native_language ); + } + if( job->mux ) { job->mux = mux; @@ -490,31 +546,95 @@ static int HandleEvents( hb_handle_t * h ) job->crf = 1; } - if (x264opts != NULL && *x264opts != '\0' ) + if( x264opts != NULL && *x264opts != '\0' ) { fprintf( stderr, "Applying the following x264 options: %s\n", - x264opts); + x264opts); job->x264opts = x264opts; } else /*avoids a bus error crash when options aren't specified*/ - { - job->x264opts = NULL; - } - if (maxWidth) - job->maxWidth = maxWidth; - if (maxHeight) - job->maxHeight = maxHeight; + { + job->x264opts = NULL; + } + if (maxWidth) + job->maxWidth = maxWidth; + if (maxHeight) + job->maxHeight = maxHeight; if( twoPass ) { + /* + * If subtitle_scan is enabled then only turn it on + * for the first pass and then off again for the + * second. + */ job->pass = 1; + job->subtitle_scan = subtitle_scan; + if( subtitle_scan ) + { + fprintf( stderr, "Subtitle Scan Enabled - enabling " + "subtitles if found for foreign language segments\n"); + job->select_subtitle = malloc(sizeof(hb_subtitle_t*)); + *(job->select_subtitle) = NULL; + } + + /* + * If turbo options have been selected then append them + * to the x264opts now (size includes one ':' and the '\0') + */ + if( turbo_opts_enabled ) + { + int size = strlen(x264opts) + strlen(turbo_opts) + 2; + char *tmp_x264opts; + + tmp_x264opts = malloc(size * sizeof(char)); + if( x264opts ) + { + snprintf( tmp_x264opts, size, "%s:%s", + x264opts, turbo_opts ); + free( x264opts ); + } else { + /* + * No x264opts to modify, but apply the turbo options + * anyway as they may be modifying defaults + */ + snprintf( tmp_x264opts, size, "%s", + turbo_opts ); + } + x264opts = tmp_x264opts; + + fprintf( stderr, "Modified x264 options for pass 1 to append turbo options: %s\n", + x264opts ); + + job->x264opts = x264opts; + } hb_add( h, job ); job->pass = 2; - job->x264opts = x264opts2; + /* + * On the second pass we turn off subtitle scan so that we + * can actually encode using any subtitles that were auto + * selected in the first pass (using the whacky select-subtitle + * attribute of the job). + */ + job->subtitle_scan = 0; + + job->x264opts = x264opts2; + hb_add( h, job ); } else { + /* + * Turn on subtitle scan if requested, note that this option + * precludes encoding of any actual subtitles. + */ + job->subtitle_scan = subtitle_scan; + if ( subtitle_scan ) + { + fprintf( stderr, "Subtitle Scan Enabled, will scan all " + "subtitles matching the audio language for any\n" + "that meet our auto-selection criteria.\n"); + } job->pass = 0; hb_add( h, job ); } @@ -606,6 +726,7 @@ static void ShowHelp() " -i, --input <string> Set input device\n" " -t, --title <number> Select a title to encode (0 to scan only,\n" " default: 1)\n" + " -L, --longest Select the longest title\n" " -c, --chapters <string> Select chapters (e.g. \"1-3\" for chapters\n" " 1 to 3, or \"3\" for chapter 3 only,\n" " default: all chapters)\n" @@ -624,6 +745,13 @@ static void ShowHelp() " -Y, --maxHeight <#> Set maximum height\n" " -X, --maxWidth <#> Set maximum width\n" " -s, --subtitle <number> Select subtitle (default: none)\n" + " -U, --subtitle-scan Scan for subtitles on the first pass, and choose\n" + " the one that's only used 20 percent of the time\n" + " or less. This should locate subtitles for short\n" + " foreign language segments. Only works with 2-pass.\n" + " -N, --native-language Select subtitles with this language if it does not\n" + " <string> match the Audio language. Provide the language's\n" + " iso639-2 code (fre, eng, spa, dut, et cetera)\n" " -m, --markers Add chapter markers (mp4 output format only)\n" "\n" @@ -673,10 +801,14 @@ static void ShowHelp() "\n" - "### Advanced H264 Options----------------------------------------------------\n\n" - " -x, --x264opts <string> Specify advanced x264 options in the\n" - " same style as mencoder:\n" - " option1=value1:option2=value2\n" ); + "### Advanced H264 Options----------------------------------------------------\n\n" + " -x, --x264opts <string> Specify advanced x264 options in the\n" + " same style as mencoder:\n" + " option1=value1:option2=value2\n" + " -T, --turbo When using 2-pass use the turbo options\n" + " on the first pass to improve speed\n" + " (only works with x264, affects PSNR by about 0.05dB,\n" + " and increases first pass speed two to four times)\n"); } /**************************************************************************** @@ -698,11 +830,14 @@ static int ParseOptions( int argc, char ** argv ) { "output", required_argument, NULL, 'o' }, { "title", required_argument, NULL, 't' }, + { "longest", no_argument, NULL, 'L' }, { "chapters", required_argument, NULL, 'c' }, { "markers", optional_argument, NULL, 'm' }, { "audio", required_argument, NULL, 'a' }, { "mixdown", required_argument, NULL, '6' }, { "subtitle", required_argument, NULL, 's' }, + { "subtitle-scan", no_argument, NULL, 'U' }, + { "native-language", required_argument, NULL,'N' }, { "encoder", required_argument, NULL, 'e' }, { "aencoder", required_argument, NULL, 'E' }, @@ -720,10 +855,12 @@ static int ParseOptions( int argc, char ** argv ) { "ab", required_argument, NULL, 'B' }, { "rate", required_argument, NULL, 'r' }, { "arate", required_argument, NULL, 'R' }, - { "crf", no_argument, NULL, 'Q' }, - { "x264opts", required_argument, NULL, 'x' }, - { "maxHeight", required_argument, NULL, 'Y' }, - { "maxWidth", required_argument, NULL, 'X' }, + { "crf", no_argument, NULL, 'Q' }, + { "x264opts", required_argument, NULL, 'x' }, + { "turbo", no_argument, NULL, 'T' }, + + { "maxHeight", required_argument, NULL, 'Y' }, + { "maxWidth", required_argument, NULL, 'X' }, { 0, 0, 0, 0 } }; @@ -732,7 +869,7 @@ static int ParseOptions( int argc, char ** argv ) int c; c = getopt_long( argc, argv, - "hvuC:f:i:o:t:c:ma:6:s:e:E:2dgpw:l:n:b:q:S:B:r:R:Qx:Y:X:", + "hvuC:f:i:o:t:Lc:ma:6:s:UN:e:E:2dgpw:l:n:b:q:S:B:r:R:Qx:TY:X:", long_options, &option_index ); if( c < 0 ) { @@ -767,6 +904,9 @@ static int ParseOptions( int argc, char ** argv ) case 't': titleindex = atoi( optarg ); break; + case 'L': + longest_title = 1; + break; case 'c': { int start, end; @@ -823,7 +963,12 @@ static int ParseOptions( int argc, char ** argv ) case 's': sub = atoi( optarg ); break; - + case 'U': + subtitle_scan = 1; + break; + case 'N': + native_language = strdup( optarg ); + break; case '2': twoPass = 1; break; @@ -942,19 +1087,22 @@ static int ParseOptions( int argc, char ** argv ) case 'B': abitrate = atoi( optarg ); break; - case 'Q': - crf = 1; - break; - case 'x': - x264opts = strdup( optarg ); - x264opts2 = strdup( optarg ); - break; - case 'Y': - maxHeight = atoi( optarg ); - break; - case 'X': - maxWidth = atoi (optarg ); - break; + case 'Q': + crf = 1; + break; + case 'x': + x264opts = strdup( optarg ); + x264opts2 = strdup( optarg ); + break; + case 'T': + turbo_opts_enabled = 1; + break; + case 'Y': + maxHeight = atoi( optarg ); + break; + case 'X': + maxWidth = atoi (optarg ); + break; default: fprintf( stderr, "unknown option (%s)\n", argv[optind] ); |