summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libhb/common.h6
-rw-r--r--libhb/hb.c84
-rw-r--r--libhb/reader.c24
-rw-r--r--libhb/work.c119
-rw-r--r--test/test.c218
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] );