Public feed #890888

pineapplecli

public text v1 · immutable
Raw Edit
·published 2008-02-04 04:31 UTC
rendered paste body
Index: libhb/encx264.c===================================================================--- libhb/encx264.c	(revision 1244)+++ libhb/encx264.c	(working copy)@@ -25,6 +25,24 @@  #define DTS_BUFFER_SIZE 32  +/*+ * The frame info struct remembers information about each frame across calls+ * to x264_encoder_encode. Since frames are uniquely identified by their+ * timestamp, we use some bits of the timestamp as an index. The LSB is+ * chosen so that two successive frames will have different values in the+ * bits over any plausible range of frame rates. (Starting with bit 9 allows+ * any frame rate slower than 175fps.) The MSB determines the size of the array.+ * It is chosen so that two frames can't use the same slot during the + * encoder's max frame delay (set by the standard as 16 frames) and so+ * that, up to some minimum frame rate, frames are guaranteed to map to+ * different slots. (An MSB of 16 which is 2^(16-9+1) = 256 slots guarantees+ * no collisions down to a rate of 1.4 fps).+ */+#define FRAME_INFO_MAX2 (9)     // 2^9 = 512; 90000/512 = 175 frames/sec+#define FRAME_INFO_MIN2 (16)    // 2^16 = 65536; 90000/65536 = 1.4 frames/sec+#define FRAME_INFO_SIZE (1 << (FRAME_INFO_MIN2 - FRAME_INFO_MAX2 + 1))+#define FRAME_INFO_MASK (FRAME_INFO_SIZE - 1)+ struct hb_work_private_s {     hb_job_t       * job;@@ -32,15 +50,15 @@     x264_picture_t   pic_in;     uint8_t         *x264_allocated_pic; -    // Internal queue of DTS start/stop values.-    int64_t        dts_start[DTS_BUFFER_SIZE];-    int64_t        dts_stop[DTS_BUFFER_SIZE];+    int64_t        dts_next;    // DTS start time value for next output frame+    int64_t        last_stop;   // Debugging - stop time of previous input frame     int64_t        init_delay;--    int64_t        dts_write_index;-    int64_t        dts_read_index;     int64_t        next_chap; +    struct {+        int64_t duration;+    } frame_info[FRAME_INFO_SIZE];+     char             filename[1024]; }; @@ -247,8 +265,7 @@      pv->x264_allocated_pic = pv->pic_in.img.plane[0]; -    pv->dts_write_index = 0;-    pv->dts_read_index = 0;+    pv->dts_next = -1;     pv->next_chap = 0;      if (job->areBframes)@@ -268,7 +285,7 @@            will use the duration of frames running at 23.976fps instead.. */         if (job->vfr)         {-            pv->init_delay = 3754;+            pv->init_delay = 7506;         }             /* The delay is 2 frames for regular b-frames, 3 for b-pyramid.@@ -296,6 +313,22 @@     /* TODO */ } +/*+ * see comments in definition of 'frame_info' in pv struct for description+ * of what these routines are doing.+ */+static void save_frame_info( hb_work_private_t * pv, hb_buffer_t * in )+{+    int i = (in->start >> FRAME_INFO_MAX2) & FRAME_INFO_MASK;+    pv->frame_info[i].duration = in->stop - in->start;+}++static int64_t get_frame_duration( hb_work_private_t * pv, int64_t pts )+{+    int i = (pts >> FRAME_INFO_MAX2) & FRAME_INFO_MASK;+    return pv->frame_info[i].duration;+}+ int encx264Work( hb_work_object_t * w, hb_buffer_t ** buf_in,                   hb_buffer_t ** buf_out ) {@@ -330,6 +363,16 @@                 job->height / 4;         } +        if( pv->dts_next == -1 )+        {+            /* we don't have a start time yet so use the first frame's +             * start. All other frame times will be determined by the+             * sum of the prior output frame durations in *DTS* order+             * (not by the order they arrive here). This timing change is+             * essential for VFR with b-frames, but a complete nop * otherwise.+             */+            pv->dts_next = in->start;+        }         if( in->new_chap && job->chapter_markers )         {             /* chapters have to start with an IDR frame so request that this@@ -351,11 +394,22 @@         }         pv->pic_in.i_qpplus1 = 0; -        // Remember current PTS value, use as DTS later-        pv->dts_start[pv->dts_write_index & (DTS_BUFFER_SIZE-1)] = in->start;-        pv->dts_stop[pv->dts_write_index & (DTS_BUFFER_SIZE-1)]  = in->stop;-        pv->dts_write_index++;+        /* XXX this is temporary debugging code to check that the upstream+         * modules (render & sync) have generated a continuous, self-consistent+         * frame stream with the current frame's start time equal to the+         * previous frame's stop time.+         */+        if( pv->last_stop != in->start )+        {+            hb_log("encx264 input continuity err: last stop %lld  start %lld",+                    pv->last_stop, in->start);+        }+        pv->last_stop = in->stop; +        // Remember info about this frame that we need to pass across +        // the x264_encoder_encode call (since it reorders frames).+        save_frame_info( pv, in );+         /* Feed the input DTS to x264 so it can figure out proper output PTS */         pv->pic_in.i_pts = in->start; @@ -386,17 +440,15 @@         /* Should be way too large */         buf        = hb_buffer_init( 3 * job->width * job->height / 2 );         buf->size  = 0;-        buf->start = in->start;-        buf->stop  = in->stop;         buf->frametype   = 0; -        int64_t dts_start, dts_stop;-         /* Get next DTS value to use */-        dts_start = pv->dts_start[pv->dts_read_index & (DTS_BUFFER_SIZE-1)];-        dts_stop  = pv->dts_stop[pv->dts_read_index & (DTS_BUFFER_SIZE-1)];-        pv->dts_read_index++;+        int64_t dts_start = pv->dts_next; +        /* compute the stop time based on the original frame's duration */+        int64_t dts_stop  = dts_start + get_frame_duration( pv, pic_out.i_pts );+        pv->dts_next = dts_stop;+         for( i = 0; i < i_nal; i++ )         {             int size, data;@@ -469,11 +521,7 @@                      /* Store the output presentation time stamp                        from x264 for use by muxmp4 in off-setting-                       b-frames with the CTTS atom.-                       For now, just add 1000000 to the offset so that the-                       value is pretty much guaranteed to be positive.  The-                       muxing code will minimize the renderOffset at the end. */-+                       b-frames with the CTTS atom. */                     buf->renderOffset = pic_out.i_pts - dts_start + pv->init_delay;                      /* Send out the next dts values */Index: libhb/render.c===================================================================--- libhb/render.c	(revision 1244)+++ libhb/render.c	(working copy)@@ -20,11 +20,15 @@     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;     uint64_t             last_start[4];     uint64_t             last_stop[4];+    uint64_t             lost_time[4];+    uint64_t             total_lost_time;+    uint64_t             total_gained_time;+    int64_t              chapter_time;+    int                  chapter_val; };  int  renderInit( hb_work_object_t *, hb_job_t * );@@ -233,7 +237,15 @@     {         hb_fifo_push( pv->subtitle_queue,  hb_buffer_init(0) );     }-    ++    /* If there's a chapter mark remember it in case we delay or drop its frame */+    if( in->new_chap )+    {+        pv->chapter_time = in->start;+        pv->chapter_val = in->new_chap;+        in->new_chap = 0;+    }+     /* Setup render buffer */     hb_buffer_t * buf_render = hb_buffer_init( 3 * job->width * job->height / 2 );       @@ -280,9 +292,22 @@             {                 if( job->vfr )                 {-                    pv->frames_to_extend += 4;+                    /* We need to compensate for the time lost by dropping this frame.+                       Spread its duration out in quarters, because usually dropped frames+                       maintain a 1-out-of-5 pattern and this spreads it out amongst the remaining ones.+                       Store these in the lost_time array, which has 4 slots in it.+                       Because not every frame duration divides evenly by 4, and we can't lose the+                       remainder, we have to go through an awkward process to preserve it in the 4th array index. */+                    uint64_t temp_duration = buf_tmp_out->stop - buf_tmp_out->start;+                    pv->lost_time[0] += (temp_duration / 4);+                    pv->lost_time[1] += (temp_duration / 4);+                    pv->lost_time[2] += (temp_duration / 4);+                    pv->lost_time[3] += ( temp_duration - (temp_duration / 4) - (temp_duration / 4) - (temp_duration / 4) );+                    +                    pv->total_lost_time += temp_duration;                     pv->dropped_frames++;-                    hb_fifo_get( pv->subtitle_queue );+                    +                    hb_fifo_get( pv->subtitle_queue );                                         buf_tmp_in = NULL;                 }                 else@@ -304,8 +329,11 @@             pv->last_start[i] = pv->last_start[i-1];             pv->last_stop[i] = pv->last_stop[i-1];         }-        pv->last_start[0] = in->start;-        pv->last_stop[0] = in->stop;+        +        /* In order to make sure we have continuous time stamps, store+           the current frame's duration as starting when the last one stopped. */+        pv->last_start[0] = pv->last_stop[1];+        pv->last_stop[0] = pv->last_start[0] + (in->stop - in->start);     }          /* Apply subtitles */@@ -385,13 +413,17 @@      if( *buf_out )     {-        if( pv->frames_to_extend )+        /* The current frame exists. That means it hasn't been dropped by a filter.+           Make it accessible as ivtc_buffer so we can edit its duration if needed. */+        ivtc_buffer = *buf_out;++        if( pv->lost_time[3] > 0 )         {             /*-             * A frame's been dropped by VFR detelecine.+             * A frame's been dropped earlier 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+             * slow down the video.+             * The dropped frame's has to be accounted for, so              * divvy it up amongst the 4 frames left behind.              * This is what the delay_queue is for;              * telecined sequences start 2 frames before@@ -399,30 +431,63 @@              * ones you need a 2 frame delay between              * reading input and writing output.              */-            ivtc_buffer = *buf_out;+                                   +            /* We want to extend the outputted frame's duration by the value+              stored in the 4th slot of the lost_time array. Because we need+              to adjust all the values in the array so they're contiguous,+              extend the duration inside the array first, before applying+              it to the current frame buffer. */+            pv->last_stop[3] += pv->lost_time[3];             -            /* The 4th cached frame will be the to use. */-            ivtc_buffer->start = pv->last_start[3];-            ivtc_buffer->stop = pv->last_stop[3];--            if (pv->frames_to_extend % 4)-                ivtc_buffer->stop += 751;-            else-                ivtc_buffer->stop += 750;+            /* Log how much time has been added back in to the video. */+            pv->total_gained_time += pv->lost_time[3];             -            /* Set the 3rd cached frame to start when this one stops,-               and to stop 3003 ticks later -- a normal 29.97fps-               length frame. If it needs to be extended as well to-               make up lost time, it'll be handled on the next-               loop through the renderer.                            */-            int temp_duration = pv->last_stop[2] - pv->last_start[2];-            pv->last_start[2] = ivtc_buffer->stop;-            pv->last_stop[2] = ivtc_buffer->stop + temp_duration;+            /* We've pulled the 4th value from the lost_time array+               and added it to the last_stop array's 4th slot. Now, rotate the+                lost_time array so the 4th slot now holds the 3rd's value, and+               so on down the line, and set the 0 index to a value of 0. */+            int i;+            for( i=2; i >=  0; i--)+            {+                pv->lost_time[i+1] = pv->lost_time[i];+            }+            pv->lost_time[0] = 0;             -            pv->frames_to_extend--;+            /* Log how many frames have had their durations extended. */             pv->extended_frames++;         }+        +        /* We can't use the given time stamps. Previous frames+           might already have been extended, throwing off the+           raw values fed to render.c. Instead, their+           stop and start times are stored in arrays.+           The 4th cached frame will be the to use.+           If it needed its duration extended to make up+           lost time, it will have happened above. */+        ivtc_buffer->start = pv->last_start[3];+        ivtc_buffer->stop = pv->last_stop[3];+        +        /* Set the 3rd cached frame to start when this one stops,+           and so on down the line. If any of them need to be+           extended as well to make up lost time, it'll be handled+           on the next loop through the renderer.  */+        int i;   +        for (i = 2; i >= 0; i--)+        {+            int temp_duration = pv->last_stop[i] - pv->last_start[i];+            pv->last_start[i] = pv->last_stop[i+1];+            pv->last_stop[i] = pv->last_start[i] + temp_duration;+        } +        /* If we have a pending chapter mark and this frame is at+           or after the time of the mark, mark this frame & clear+           our pending mark. */+        if( pv->chapter_time && pv->chapter_time <= ivtc_buffer->start )+        {+            ivtc_buffer->new_chap = pv->chapter_val;+            pv->chapter_time = 0;+        }+     }      return HB_WORK_OK;@@ -431,11 +496,12 @@ 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 ) ); +    hb_log("render: lost time: %lld (%i frames)", pv->total_lost_time, pv->dropped_frames);+    hb_log("render: gained time: %lld (%i frames) (%lld not accounted for)", pv->total_gained_time, pv->extended_frames, pv->total_lost_time - pv->total_gained_time);+    if (pv->dropped_frames)+        hb_log("render: average dropped frame duration: %lld", (pv->total_lost_time / pv->dropped_frames) );+         /* Cleanup subtitle queue */     if( pv->subtitle_queue )     {@@ -476,12 +542,23 @@     /* 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;++    /* VFR IVTC needs a bunch of time-keeping variables to track+      how many frames are dropped, how many are extended, what the+      last 4 start and stop times were (so they can be modified),+      how much time has been lost and gained overall, how much time+      the latest 4 frames should be extended by, and where chapter+      markers are (so they can be saved if their frames are dropped.) */     pv->dropped_frames = 0;     pv->extended_frames = 0;     pv->last_start[0] = 0;     pv->last_stop[0] = 0;-    +    pv->total_lost_time = 0;+    pv->total_gained_time = 0;+    pv->lost_time[0] = 0; pv->lost_time[1] = 0; pv->lost_time[2] = 0; pv->lost_time[3] = 0;+    pv->chapter_time = 0;+    pv->chapter_val  = 0;+     /* Setup filters */     /* TODO: Move to work.c? */     if( job->filters )