summaryrefslogtreecommitdiffstats
path: root/libhb/work.c
diff options
context:
space:
mode:
Diffstat (limited to 'libhb/work.c')
-rw-r--r--libhb/work.c367
1 files changed, 253 insertions, 114 deletions
diff --git a/libhb/work.c b/libhb/work.c
index 0487a07f0..7b84cd30b 100644
--- a/libhb/work.c
+++ b/libhb/work.c
@@ -21,6 +21,7 @@ typedef struct
static void work_func();
static void do_job( hb_job_t *);
static void work_loop( void * );
+static void filter_loop( void * );
#define FIFO_UNBOUNDED 65536
#define FIFO_UNBOUNDED_WAKE 65535
@@ -28,6 +29,8 @@ static void work_loop( void * );
#define FIFO_LARGE_WAKE 16
#define FIFO_SMALL 16
#define FIFO_SMALL_WAKE 15
+#define FIFO_MINI 4
+#define FIFO_MINI_WAKE 3
/**
* Allocates work object and launches work thread with work_func.
@@ -271,6 +274,29 @@ void hb_display_job_info( hb_job_t * job )
(float) job->pfr_vrate / (float) job->pfr_vrate_base );
}
+ // Filters can modify dimensions. So show them first.
+ if( hb_list_count( job->list_filter ) )
+ {
+ hb_log(" + %s", hb_list_count( job->list_filter) > 1 ? "filters" : "filter" );
+ for( i = 0; i < hb_list_count( job->list_filter ); i++ )
+ {
+ hb_filter_object_t * filter = hb_list_item( job->list_filter, i );
+ if( filter->settings )
+ hb_log(" + %s (%s)", filter->name, filter->settings);
+ else
+ hb_log(" + %s (default settings)", filter->name);
+ if( filter->info )
+ {
+ hb_filter_info_t info;
+ filter->info( filter, &info );
+ if( info.human_readable_desc[0] )
+ {
+ hb_log(" %s", info.human_readable_desc);
+ }
+ }
+ }
+ }
+
if( job->anamorphic.mode )
{
hb_log( " + %s anamorphic", job->anamorphic.mode == 1 ? "strict" : job->anamorphic.mode == 2? "loose" : "custom" );
@@ -278,9 +304,8 @@ void hb_display_job_info( hb_job_t * job )
{
hb_log( " + keeping source display aspect ratio");
}
- hb_log( " + storage dimensions: %d * %d -> %d * %d, crop %d/%d/%d/%d, mod %i",
- title->width, title->height, job->width, job->height,
- job->crop[0], job->crop[1], job->crop[2], job->crop[3], job->modulus );
+ hb_log( " + storage dimensions: %d * %d, mod %i",
+ job->width, job->height, job->modulus );
if( job->anamorphic.itu_par )
{
hb_log( " + using ITU pixel aspect ratio values");
@@ -291,27 +316,13 @@ void hb_display_job_info( hb_job_t * job )
}
else
{
- hb_log( " + dimensions: %d * %d -> %d * %d, crop %d/%d/%d/%d, mod %i",
- title->width, title->height, job->width, job->height,
- job->crop[0], job->crop[1], job->crop[2], job->crop[3], job->modulus );
+ hb_log( " + dimensions: %d * %d, mod %i",
+ job->width, job->height, job->modulus );
}
if ( job->grayscale )
hb_log( " + grayscale mode" );
- if( hb_list_count( job->filters ) )
- {
- hb_log(" + %s", hb_list_count( job->filters) > 1 ? "filters" : "filter" );
- for( i = 0; i < hb_list_count( job->filters ); i++ )
- {
- hb_filter_object_t * filter = hb_list_item( job->filters, i );
- if (filter->settings)
- hb_log(" + %s (%s)", filter->name, filter->settings);
- else
- hb_log(" + %s (default settings)", filter->name);
- }
- }
-
if( !job->indepth_scan )
{
/* Video encoder */
@@ -510,10 +521,44 @@ static void do_job( hb_job_t * job )
hb_log( "starting job" );
- if( job->anamorphic.mode )
+ // Filters have an effect on settings.
+ // So initialize the filters and update the job.
+ if( job->list_filter && hb_list_count( job->list_filter ) )
{
- hb_set_anamorphic_size(job, &job->width, &job->height, &job->anamorphic.par_width, &job->anamorphic.par_height);
+ hb_filter_init_t init;
+
+ init.job = job;
+ init.pix_fmt = PIX_FMT_YUV420P;
+ init.width = title->width;
+ init.height = title->height;
+ init.par_width = job->anamorphic.par_width;
+ init.par_height = job->anamorphic.par_height;
+ memcpy(init.crop, title->crop, sizeof(int[4]));
+ init.vrate_base = title->rate_base;
+ init.vrate = title->rate;
+ init.cfr = 0;
+ for( i = 0; i < hb_list_count( job->list_filter ); i++ )
+ {
+ hb_filter_object_t * filter = hb_list_item( job->list_filter, i );
+ if( filter->init( filter, &init ) )
+ {
+ hb_error( "Failure to initialise filter '%s'", filter->name );
+ *job->die = 1;
+ goto cleanup;
+ }
+ }
+ job->width = init.width;
+ job->height = init.height;
+ job->anamorphic.par_width = init.par_width;
+ job->anamorphic.par_height = init.par_height;
+ memcpy(title->crop, init.crop, sizeof(int[4]));
+ job->vrate_base = init.vrate_base;
+ job->vrate = init.vrate;
+ job->cfr = init.cfr;
+ }
+ if( job->anamorphic.mode )
+ {
if( job->vcodec & HB_VCODEC_FFMPEG_MASK )
{
/* Just to make working with ffmpeg even more fun,
@@ -530,47 +575,6 @@ static void do_job( hb_job_t * job )
}
}
- /* Keep width and height within these boundaries,
- but ignore for anamorphic. For "loose" anamorphic encodes,
- this stuff is covered in the pixel_ratio section above. */
- if ( job->maxHeight && ( job->height > job->maxHeight ) && ( !job->anamorphic.mode ) )
- {
- job->height = job->maxHeight;
- hb_fix_aspect( job, HB_KEEP_HEIGHT );
- hb_log( "Height out of bounds, scaling down to %i", job->maxHeight );
- hb_log( "New dimensions %i * %i", job->width, job->height );
- }
- if ( job->maxWidth && ( job->width > job->maxWidth ) && ( !job->anamorphic.mode ) )
- {
- job->width = job->maxWidth;
- hb_fix_aspect( job, HB_KEEP_WIDTH );
- hb_log( "Width out of bounds, scaling down to %i", job->maxWidth );
- hb_log( "New dimensions %i * %i", job->width, job->height );
- }
-
- if ( job->cfr == 0 )
- {
- /* Ensure we're using "Same as source" FPS */
- job->vrate = title->rate;
- job->vrate_base = title->rate_base;
- }
- else if ( job->cfr == 2 )
- {
- job->pfr_vrate = job->vrate;
- job->pfr_vrate_base = job->vrate_base;
-
- // Ensure we're using "Same as source" FPS, with peak set by pfr_vrate_*
- // For PFR, we want the framerate based on the source's actual
- // framerate, unless it's higher than the specified peak framerate.
- double source_fps = (double)job->title->rate / job->title->rate_base;
- double peak_l_fps = (double)job->vrate / job->vrate_base;
- if ( source_fps < peak_l_fps )
- {
- job->vrate_base = title->rate_base;
- job->vrate = title->rate;
- }
- }
-
job->fifo_mpeg2 = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE );
job->fifo_raw = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
job->fifo_sync = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
@@ -829,42 +833,6 @@ static void do_job( hb_job_t * job )
w->fifo_in = job->fifo_mpeg2;
w->fifo_out = job->fifo_raw;
- /* Video renderer */
- hb_list_add( job->list_work, ( w = hb_get_work( WORK_RENDER ) ) );
- w->fifo_in = job->fifo_sync;
- if( !job->indepth_scan )
- w->fifo_out = job->fifo_render;
- else
- w->fifo_out = NULL;
-
- if( !job->indepth_scan )
- {
-
- /* Video encoder */
- switch( job->vcodec )
- {
- case HB_VCODEC_FFMPEG_MPEG4:
- w = hb_get_work( WORK_ENCAVCODEC );
- w->codec_param = CODEC_ID_MPEG4;
- break;
- case HB_VCODEC_FFMPEG_MPEG2:
- w = hb_get_work( WORK_ENCAVCODEC );
- w->codec_param = CODEC_ID_MPEG2VIDEO;
- break;
- case HB_VCODEC_X264:
- w = hb_get_work( WORK_ENCX264 );
- break;
- case HB_VCODEC_THEORA:
- w = hb_get_work( WORK_ENCTHEORA );
- break;
- }
- w->fifo_in = job->fifo_render;
- w->fifo_out = job->fifo_mpeg4;
- w->config = &job->config;
-
- hb_list_add( job->list_work, w );
- }
-
/*
* Look for the scanned subtitle in the existing subtitle list
* select_subtitle implies that we did a scan.
@@ -919,7 +887,6 @@ static void do_job( hb_job_t * job )
}
}
-
for( i=0; i < hb_list_count(title->list_subtitle); i++ )
{
subtitle = hb_list_item( title->list_subtitle, i );
@@ -996,8 +963,61 @@ static void do_job( hb_job_t * job )
}
}
+ /* Set up the video filter fifo pipeline */
if( !job->indepth_scan )
{
+ if( job->list_filter )
+ {
+ int filter_count = hb_list_count( job->list_filter );
+ int i;
+ hb_fifo_t * fifo_in = job->fifo_sync;
+
+ for( i = 0; i < filter_count; i++ )
+ {
+ hb_filter_object_t * filter = hb_list_item( job->list_filter, i );
+
+ filter->fifo_in = fifo_in;
+ filter->fifo_out = hb_fifo_init( FIFO_MINI, FIFO_MINI_WAKE );
+ fifo_in = filter->fifo_out;
+ }
+ job->fifo_render = fifo_in;
+ }
+ else if ( !job->list_filter )
+ {
+ hb_log("work: Internal Error: no filters");
+ job->fifo_render = NULL;
+ }
+
+ /* Video encoder */
+ switch( job->vcodec )
+ {
+ case HB_VCODEC_FFMPEG_MPEG4:
+ w = hb_get_work( WORK_ENCAVCODEC );
+ w->codec_param = CODEC_ID_MPEG4;
+ break;
+ case HB_VCODEC_FFMPEG_MPEG2:
+ w = hb_get_work( WORK_ENCAVCODEC );
+ w->codec_param = CODEC_ID_MPEG2VIDEO;
+ break;
+ case HB_VCODEC_X264:
+ w = hb_get_work( WORK_ENCX264 );
+ break;
+ case HB_VCODEC_THEORA:
+ w = hb_get_work( WORK_ENCTHEORA );
+ break;
+ }
+ // Handle case where there are no filters.
+ // This really should never happen.
+ if ( job->fifo_render )
+ w->fifo_in = job->fifo_render;
+ else
+ w->fifo_in = job->fifo_sync;
+
+ w->fifo_out = job->fifo_mpeg4;
+ w->config = &job->config;
+
+ hb_list_add( job->list_work, w );
+
for( i = 0; i < hb_list_count( title->list_audio ); i++ )
{
audio = hb_list_item( title->list_audio, i );
@@ -1069,6 +1089,25 @@ static void do_job( hb_job_t * job )
job->done = 0;
+ if( job->list_filter && !job->indepth_scan )
+ {
+ int filter_count = hb_list_count( job->list_filter );
+ int i;
+
+ for( i = 0; i < filter_count; i++ )
+ {
+ hb_filter_object_t * filter = hb_list_item( job->list_filter, i );
+
+ if( !filter ) continue;
+
+ // Filters were initialized earlier, so we just need
+ // to start the filter's thread
+ filter->done = &job->done;
+ filter->thread = hb_thread_init( filter->name, filter_loop, filter,
+ HB_LOW_PRIORITY );
+ }
+ }
+
/* Launch processing threads */
for( i = 0; i < hb_list_count( job->list_work ); i++ )
{
@@ -1180,6 +1219,26 @@ cleanup:
/* Stop the write thread (thread_close will block until the muxer finishes) */
job->done = 1;
+ // Close render filter pipeline
+ if( job->list_filter )
+ {
+ int filter_count = hb_list_count( job->list_filter );
+ int i;
+
+ for( i = 0; i < filter_count; i++ )
+ {
+ hb_filter_object_t * filter = hb_list_item( job->list_filter, i );
+
+ if( !filter ) continue;
+
+ if( filter->thread != NULL )
+ {
+ hb_thread_close( &filter->thread );
+ }
+ filter->close( filter );
+ }
+ }
+
/* Close work objects */
while( ( w = hb_list_item( job->list_work, 0 ) ) )
{
@@ -1315,14 +1374,14 @@ cleanup:
}
}
- if( job->filters )
+ if( job->list_filter )
{
- for( i = 0; i < hb_list_count( job->filters ); i++ )
+ for( i = 0; i < hb_list_count( job->list_filter ); i++ )
{
- hb_filter_object_t * filter = hb_list_item( job->filters, i );
+ hb_filter_object_t * filter = hb_list_item( job->list_filter, i );
hb_filter_close( &filter );
}
- hb_list_close( &job->filters );
+ hb_list_close( &job->list_filter );
}
hb_buffer_pool_free();
@@ -1331,6 +1390,21 @@ cleanup:
free( job );
}
+static inline void copy_chapter( hb_buffer_t * dst, hb_buffer_t * src )
+{
+ // Propagate any chapter breaks for the worker if and only if the
+ // output frame has the same time stamp as the input frame (any
+ // worker that delays frames has to propagate the chapter marks itself
+ // and workers that move chapter marks to a different time should set
+ // 'src' to NULL so that this code won't generate spurious duplicates.)
+ if( src && dst && src->s.start == dst->s.start)
+ {
+ // restore log below to debug chapter mark propagation problems
+ //hb_log("work %s: Copying Chapter Break @ %"PRId64, w->name, src->s.start);
+ dst->s.new_chap = src->s.new_chap;
+ }
+}
+
/**
* Performs the work object's specific work function.
* Loops calling work function for associated work object. Sleeps when fifo is full.
@@ -1361,17 +1435,7 @@ static void work_loop( void * _w )
buf_out = NULL;
w->status = w->work( w, &buf_in, &buf_out );
- // Propagate any chapter breaks for the worker if and only if the
- // output frame has the same time stamp as the input frame (any
- // worker that delays frames has to propagate the chapter marks itself
- // and workers that move chapter marks to a different time should set
- // 'buf_in' to NULL so that this code won't generate spurious duplicates.)
- if( buf_in && buf_out && buf_in->new_chap && buf_in->start == buf_out->start)
- {
- // restore log below to debug chapter mark propagation problems
- //hb_log("work %s: Copying Chapter Break @ %"PRId64, w->name, buf_in->start);
- buf_out->new_chap = buf_in->new_chap;
- }
+ copy_chapter( buf_out, buf_in );
if( buf_in )
{
@@ -1408,3 +1472,78 @@ static void work_loop( void * _w )
hb_buffer_close( &buf_in );
}
}
+
+/**
+ * Performs the filter object's specific work function.
+ * Loops calling work function for associated filter object.
+ * Sleeps when fifo is full.
+ * Monitors work done indicator.
+ * Exits loop when work indiactor is set.
+ * @param _w Handle to work object.
+ */
+static void filter_loop( void * _f )
+{
+ hb_filter_object_t * f = _f;
+ hb_buffer_t * buf_in, * buf_out;
+
+ while( !*f->done && f->status != HB_FILTER_DONE )
+ {
+ buf_in = hb_fifo_get_wait( f->fifo_in );
+ if ( buf_in == NULL )
+ continue;
+
+ // Filters can drop buffers. Remember chapter information
+ // so that it can be propagated to the next buffer
+ if ( buf_in->s.new_chap )
+ {
+ f->chapter_time = buf_in->s.start;
+ f->chapter_val = buf_in->s.new_chap;
+ }
+ if ( *f->done )
+ {
+ if( buf_in )
+ {
+ hb_buffer_close( &buf_in );
+ }
+ break;
+ }
+
+ buf_out = NULL;
+ f->status = f->work( f, &buf_in, &buf_out );
+
+ if ( buf_out && f->chapter_val && f->chapter_time <= buf_out->s.start )
+ {
+ buf_out->s.new_chap = f->chapter_val;
+ f->chapter_val = 0;
+ }
+
+ if( buf_in )
+ {
+ hb_buffer_close( &buf_in );
+ }
+ if ( buf_out && f->fifo_out == NULL )
+ {
+ hb_buffer_close( &buf_out );
+ }
+ if( buf_out )
+ {
+ while ( !*f->done )
+ {
+ if ( hb_fifo_full_wait( f->fifo_out ) )
+ {
+ hb_fifo_push( f->fifo_out, buf_out );
+ break;
+ }
+ }
+ }
+ }
+ // Consume data in incoming fifo till job complete so that
+ // residual data does not stall the pipeline
+ while( !*f->done )
+ {
+ buf_in = hb_fifo_get_wait( f->fifo_in );
+ if ( buf_in != NULL )
+ hb_buffer_close( &buf_in );
+ }
+}
+