From d95e8d52105a29a03750232c47949b37dc3075dc Mon Sep 17 00:00:00 2001 From: jbrjake Date: Sat, 10 Nov 2007 01:51:36 +0000 Subject: First attempt at variable frame rate detelecining for NTSC video sources. This check-in includes the library code as well as the CLI implementation. Only works with MP4 and MKV, untested with high profile, results may vary with mixed content, consult a physician if condition persists for longer than four hours. git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@1051 b64f7644-9d1e-0410-96f1-a4d463321fa5 --- libhb/common.h | 3 ++- libhb/detelecine.c | 19 +++++++++++---- libhb/muxmp4.c | 9 ++++++- libhb/render.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++- libhb/work.c | 21 ++++++++++++++++ test/test.c | 16 +++++++++--- 6 files changed, 128 insertions(+), 11 deletions(-) diff --git a/libhb/common.h b/libhb/common.h index 1cf6cdb59..8a45d7eb7 100644 --- a/libhb/common.h +++ b/libhb/common.h @@ -172,7 +172,8 @@ struct hb_job_s int crf; char *x264opts; int areBframes; - + int vfr; + /* Audio tracks: audios: Indexes in hb_title_t's audios list, starting from 0. -1 indicates the end of the list diff --git a/libhb/detelecine.c b/libhb/detelecine.c index 93b153c8d..368341dd7 100644 --- a/libhb/detelecine.c +++ b/libhb/detelecine.c @@ -975,7 +975,7 @@ int hb_detelecine_work( const hb_buffer_t * buf_in, } else { - goto output_frame; + goto discard_frame; } } @@ -987,7 +987,7 @@ int hb_detelecine_work( const hb_buffer_t * buf_in, if (!frame) { - goto output_frame; + goto discard_frame; } if( frame->length < 2 ) { @@ -995,19 +995,19 @@ int hb_detelecine_work( const hb_buffer_t * buf_in, if( !(buf_in->flags & PIC_FLAG_REPEAT_FIRST_FIELD) ) { - goto output_frame; + goto discard_frame; } frame = pullup_get_frame( ctx ); if( !frame ) { - goto output_frame; + goto discard_frame; } if( frame->length < 2 ) { pullup_release_frame( frame ); - goto output_frame; + goto discard_frame; } } } @@ -1034,6 +1034,15 @@ int hb_detelecine_work( const hb_buffer_t * buf_in, output_frame: *buf_out = pv->buf_out; return FILTER_OK; + +/* This and all discard_frame calls shown above are + the result of me restoring the functionality in + pullup that huevos_rancheros disabled because + HB couldn't handle it. */ +discard_frame: + *buf_out = pv->buf_out; + return FILTER_DROP; + } diff --git a/libhb/muxmp4.c b/libhb/muxmp4.c index 2f375e832..d279667ee 100644 --- a/libhb/muxmp4.c +++ b/libhb/muxmp4.c @@ -393,7 +393,14 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data, /* Because we use the audio samplerate as the timescale, we have to use potentially variable durations so the video doesn't go out of sync */ - duration = ( buf->stop * job->arate / 90000 ) - m->sum_dur; + if ( job->vfr ) + { + duration = ( ( buf->stop * job->arate / 90000 ) - ( buf->start * job->arate / 90000 ) ); + } + else + { + duration = ( buf->stop * job->arate / 90000 ) - m->sum_dur; + } m->sum_dur += duration; } else diff --git a/libhb/render.c b/libhb/render.c index edbd18a80..07f38897e 100644 --- a/libhb/render.c +++ b/libhb/render.c @@ -19,6 +19,10 @@ struct hb_work_private_s AVPicture pic_tmp_out; hb_buffer_t * buf_scale; hb_fifo_t * subtitle_queue; + hb_fifo_t * delay_queue; + int frames_to_extend; + int dropped_frames; + int extended_frames; }; int renderInit( hb_work_object_t *, hb_job_t * ); @@ -153,10 +157,14 @@ int renderWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_job_t * job = pv->job; hb_title_t * title = job->title; hb_buffer_t * in = *buf_in, * buf_tmp_in = *buf_in; + hb_buffer_t * ivtc_buffer = NULL; if(!in->data) { - /* If the input buffer is end of stream, send out an empty one to the next stage as well. */ + /* If the input buffer is end of stream, send out an empty one + * to the next stage as well. Note that this will result in us + * losing the current contents of the delay queue. + */ *buf_out = hb_buffer_init(0); return HB_WORK_OK; } @@ -227,6 +235,11 @@ int renderWork( hb_work_object_t * w, hb_buffer_t ** buf_in, { hb_fifo_get( pv->subtitle_queue ); buf_tmp_in = NULL; + if( job->vfr ) + { + pv->frames_to_extend += 4; + pv->dropped_frames++; + } break; } } @@ -291,6 +304,49 @@ int renderWork( hb_work_object_t * w, hb_buffer_t ** buf_in, memcpy( buf_render->data, buf_tmp_in->data, buf_render->size ); hb_buffer_copy_settings( buf_render, buf_tmp_in ); } + + if (*buf_out) + { + hb_fifo_push( pv->delay_queue, *buf_out ); + *buf_out = NULL; + } + + /* + * Keep the last three frames in our queue, this ensures that we have the last + * two always in there should we need to rewrite the durations on them. + */ + if( hb_fifo_size( pv->delay_queue ) >= 3 ) + { + *buf_out = hb_fifo_get( pv->delay_queue ); + } + + if( *buf_out ) + { + if( pv->frames_to_extend ) + { + /* + * A frame's been dropped by VFR detelecine. + * Gotta make up the lost time. This will also + * slow down the video to 23.976fps. + * The dropped frame ran for 3003 ticks, so + * divvy it up amongst the 4 frames left behind. + * This is what the delay_queue is for; + * telecined sequences start 2 frames before + * the dropped frame, so to slow down the right + * ones you need a 2 frame delay between + * reading input and writing output. + */ + ivtc_buffer = *buf_out; + + if (pv->frames_to_extend % 4) + ivtc_buffer->stop += 751; + else + ivtc_buffer->stop += 750; + + pv->frames_to_extend--; + pv->extended_frames++; + } + } return HB_WORK_OK; } @@ -299,12 +355,21 @@ void renderClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; + hb_log("render: dropped frames: %i (%i ticks)", pv->dropped_frames, (pv->dropped_frames * 3003) ); + hb_log("render: extended frames: %i (%i ticks)", pv->extended_frames, ( ( pv->extended_frames / 4 ) * 3003 ) ); + hb_log("render: Lost time: %i frames (%i ticks)", (pv->dropped_frames * 4) - (pv->extended_frames), (pv->dropped_frames * 3003) - ( ( pv->extended_frames / 4 ) * 3003 ) ); + /* Cleanup subtitle queue */ if( pv->subtitle_queue ) { hb_fifo_close( &pv->subtitle_queue ); } + if( pv->delay_queue ) + { + hb_fifo_close( &pv->delay_queue ); + } + /* Cleanup filters */ /* TODO: Move to work.c? */ if( pv->job->filters ) @@ -352,6 +417,10 @@ int renderInit( hb_work_object_t * w, hb_job_t * job ) /* Setup FIFO queue for subtitle cache */ pv->subtitle_queue = hb_fifo_init( 8 ); + pv->delay_queue = hb_fifo_init( 8 ); + pv->frames_to_extend = 0; + pv->dropped_frames = 0; + pv->extended_frames = 0; /* Setup filters */ /* TODO: Move to work.c? */ diff --git a/libhb/work.c b/libhb/work.c index 22e081532..fdeace025 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -160,6 +160,27 @@ static void do_job( hb_job_t * job, int cpu_count ) job->crop[0], job->crop[1], job->crop[2], job->crop[3] ); hb_log( " + grayscale %s", job->grayscale ? "on" : "off" ); + if ( job->vfr ) + { + job->vrate_base = 900900; + + int detelecine_present = 0; + if ( job->filters ) + { + for( i = 0; i < hb_list_count( job->filters ); i++ ) + { + hb_filter_object_t * filter = hb_list_item( job->filters, i ); + if (filter->id == FILTER_DETELECINE) + detelecine_present = 1; + } + } + + if (!detelecine_present) + hb_list_add( job->filters, &hb_filter_detelecine ); + + hb_log("work: VFR mode -- Switching FPS to 29.97 and detelecining."); + } + if( job->filters ) { hb_log(" + filters"); diff --git a/test/test.c b/test/test.c index 4ddefdfdb..e3f7274c2 100644 --- a/test/test.c +++ b/test/test.c @@ -69,6 +69,7 @@ static char * turbo_opts = "ref=1:subme=1:me=dia:analyse=none:trellis=0:no-fast- static int largeFileSize = 0; static int preset = 0; static char * preset_name = 0; +static int vfr = 0; /* Exit cleanly on Ctrl-C */ static volatile int die = 0; @@ -877,6 +878,9 @@ static int HandleEvents( hb_handle_t * h ) job->maxWidth = maxWidth; if (maxHeight) job->maxHeight = maxHeight; + + if (vfr) + job->vfr = 1; if( subtitle_force ) { @@ -1177,14 +1181,16 @@ static void ShowHelp() "\n" - "### Advanced H264 Options----------------------------------------------------\n\n" + "### Advanced Options---------------------------------------------------------\n\n" " -x, --x264opts 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"); + " and increases first pass speed two to four times)\n" + " -V, --vfr Perform variable framerate detelecine on NTSC content\n" + ); } /**************************************************************************** @@ -1285,6 +1291,7 @@ static int ParseOptions( int argc, char ** argv ) { "maxWidth", required_argument, NULL, 'X' }, { "preset", required_argument, NULL, 'Z' }, { "preset-list", no_argument, NULL, 'z' }, + { "vfr", no_argument, NULL, 'V' }, { 0, 0, 0, 0 } }; @@ -1293,7 +1300,7 @@ static int ParseOptions( int argc, char ** argv ) int c; c = getopt_long( argc, argv, - "hvuC:f:4i:o:t:Lc:ma:6:s:UFN:e:E:2d789gpP::w:l:n:b:q:S:B:r:R:Qx:TY:X:Z:z", + "hvuC:f:4i:o:t:Lc:ma:6:s:UFN:e:E:2d789gpP::w:l:n:b:q:S:B:r:R:Qx:TY:X:VZ:z", long_options, &option_index ); if( c < 0 ) { @@ -1613,6 +1620,9 @@ static int ParseOptions( int argc, char ** argv ) case 'X': maxWidth = atoi (optarg ); break; + case 'V': + vfr = 1; + break; default: fprintf( stderr, "unknown option (%s)\n", argv[optind] ); -- cgit v1.2.3