OSDN Git Service

First attempt at variable frame rate detelecining for NTSC video sources.
authorjbrjake <jbrjake@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Sat, 10 Nov 2007 01:51:36 +0000 (01:51 +0000)
committerjbrjake <jbrjake@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Sat, 10 Nov 2007 01:51:36 +0000 (01:51 +0000)
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://localhost/HandBrake/trunk@1051 b64f7644-9d1e-0410-96f1-a4d463321fa5

libhb/common.h
libhb/detelecine.c
libhb/muxmp4.c
libhb/render.c
libhb/work.c
test/test.c

index 1cf6cdb..8a45d7e 100644 (file)
@@ -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
index 93b153c..368341d 100644 (file)
@@ -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;
+
 }
 
 
index 2f375e8..d279667 100644 (file)
@@ -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
index edbd18a..07f3889 100644 (file)
@@ -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? */
index 22e0815..fdeace0 100644 (file)
@@ -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");
index 4ddefdf..e3f7274 100644 (file)
@@ -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 <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");
+    "                            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] );