OSDN Git Service

use DTS generated by x264 when computing duration and offset in muxmp4
authorjstebbins <jstebbins@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Sat, 6 Feb 2010 16:30:31 +0000 (16:30 +0000)
committerjstebbins <jstebbins@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Sat, 6 Feb 2010 16:30:31 +0000 (16:30 +0000)
also fix an issue where ffmpeg generated negative PTS for initial
frames of video.

git-svn-id: svn://localhost/HandBrake/trunk@3097 b64f7644-9d1e-0410-96f1-a4d463321fa5

libhb/encx264.c
libhb/muxcommon.c
libhb/muxmkv.c
libhb/muxmp4.c
libhb/stream.c

index 84cbac8..47f3b5a 100644 (file)
@@ -55,7 +55,6 @@ struct hb_work_private_s
     uint32_t       frames_split; // number of frames we had to split
     int            chap_mark;   // saved chap mark when we're propagating it
     int64_t        last_stop;   // Debugging - stop time of previous input frame
-    int64_t        init_delay;
     int64_t        next_chap;
 
     struct {
@@ -133,6 +132,8 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
     param.i_height     = job->height;
     param.i_fps_num    = job->vrate;
     param.i_fps_den    = job->vrate_base;
+    param.i_timebase_num   = 1;
+    param.i_timebase_den   = 90000;
 
     /* Disable annexb. Inserts size into nal header instead of start code */
     param.b_annexb     = 0;
@@ -356,44 +357,6 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
     pv->pic_in.img.i_stride[2] = pv->pic_in.img.i_stride[1] = ( ( job->width + 1 ) >> 1 );
     pv->x264_allocated_pic = pv->pic_in.img.plane[0];
 
-    if (job->areBframes)
-    {
-        /* Basic initDelay value is the clockrate divided by the FPS
-           -- the length of one frame in clockticks.                  */
-        pv->init_delay = 90000. / ((double)job->vrate / (double)job->vrate_base);
-
-        /* 23.976-length frames are 3753.75 ticks long on average but the DVD
-           creates that average rate by repeating 59.95 fields so the max
-           frame size is actually 4504.5 (3 field times). The field durations
-           are computed based on quantized times (see below) so we need an extra
-           two ticks to account for the rounding. */
-        if (pv->init_delay == 3753)
-            pv->init_delay = 4507;
-
-        /* frame rates are not exact in the DVD 90KHz PTS clock (they are
-           exact in the DVD 27MHz system clock but we never see that) so the
-           rates computed above are all +-1 due to quantization. Worst case
-           is when a clock-rounded-down frame is adjacent to a rounded-up frame
-           which makes one of the frames 2 ticks longer than the nominal
-           frame time. */
-        pv->init_delay += 2;
-
-        /* For VFR, libhb sees the FPS as 29.97, but the longest frames
-           will use the duration of frames running at 23.976fps instead.
-           Since detelecine occasionally makes mistakes and since we have
-           to deal with some really horrible timing jitter from mkvs and
-           mp4s encoded with low resolution clocks, make the delay very
-           conservative if we're not doing CFR. */
-        if ( job->cfr != 1 )
-        {
-            pv->init_delay *= 2;
-        }
-
-        /* The delay is 1 frames for regular b-frames, 2 for b-pyramid. */
-        pv->init_delay *= job->areBframes;
-    }
-    w->config->h264.init_delay = pv->init_delay;
-
     return 0;
 }
 
@@ -451,6 +414,11 @@ static hb_buffer_t *nal_encode( hb_work_object_t *w, x264_picture_t *pic_out,
     int64_t duration  = get_frame_duration( pv, pic_out->i_pts );
     buf->start = pic_out->i_pts;
     buf->stop  = pic_out->i_pts + duration;
+    buf->renderOffset = pic_out->i_dts;
+    if ( !w->config->h264.init_delay && pic_out->i_dts < 0 )
+    {
+        w->config->h264.init_delay = -pic_out->i_dts;
+    }
 
     /* Encode all the NALs we were given into buf.
        NOTE: This code assumes one video frame per NAL (but there can
@@ -677,62 +645,7 @@ int encx264Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
 
     // Not EOF - encode the packet & wrap it in a NAL
     ++pv->frames_in;
-
-    // if we're re-ordering frames, check if this frame is too large to reorder
-    if ( pv->init_delay && in->stop - in->start > pv->init_delay )
-    {
-        // This frame's duration is larger than the time allotted for b-frame
-        // reordering. That means that if it's used as a reference the decoder
-        // won't be able to move it early enough to render it in correct
-        // sequence & the playback will have odd jumps & twitches. To make
-        // sure this doesn't happen we pretend this frame is multiple
-        // frames, each with duration <= init_delay. Since each of these
-        // new frames contains the same image the visual effect is identical
-        // to the original but the resulting stream can now be coded without
-        // error. We take advantage of the fact that x264 buffers frame
-        // data internally to feed the same image into the encoder multiple
-        // times, just changing its start & stop times each time.
-        ++pv->frames_split;
-        int64_t orig_stop = in->stop;
-        int64_t new_stop = in->start;
-        hb_buffer_t *last_buf = NULL;
-
-        // We want to spread the new frames uniformly over the total time
-        // so that we don't end up with a very short frame at the end.
-        // In the number of pieces calculation we add in init_delay-1 to
-        // round up but not add an extra piece if the frame duration is
-        // a multiple of init_delay. The final increment of frame_dur is
-        // to restore the bits that got truncated by the divide on the
-        // previous line. If we don't do this we end up with an extra tiny
-        // frame at the end whose duration is npieces-1.
-        int64_t frame_dur = orig_stop - new_stop;
-        int64_t npieces = ( frame_dur + pv->init_delay - 1 ) / pv->init_delay;
-        frame_dur /= npieces;
-        ++frame_dur;
-
-        while ( in->start < orig_stop )
-        {
-            new_stop += frame_dur;
-            if ( new_stop > orig_stop )
-                new_stop = orig_stop;
-            in->stop = new_stop;
-            hb_buffer_t *buf = x264_encode( w, in );
-            if ( buf )
-            {
-                ++pv->frames_out;
-                if ( last_buf == NULL )
-                    *buf_out = buf;
-                else
-                    last_buf->next = buf;
-                last_buf = buf;
-            }
-            in->start = new_stop;
-        }
-    }
-    else
-    {
-        ++pv->frames_out;
-        *buf_out = x264_encode( w, in );
-    }
+    ++pv->frames_out;
+    *buf_out = x264_encode( w, in );
     return HB_WORK_OK;
 }
index 95e1ab1..43b9710 100644 (file)
@@ -181,7 +181,6 @@ static void OutputTrackChunk( hb_mux_t *mux, hb_track_t *track, hb_mux_object_t
         m->mux( m, track->mux_data, mf_pull( track ) );
         track->frames += 1;
         track->bytes  += buf->size;
-        hb_buffer_close( &buf );
     }
 }
 
index f179cb7..d30e7c0 100644 (file)
@@ -399,6 +399,7 @@ static int MKVMux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
             }
             mk_addFrameData(m->file, mux_data->track, op->packet, op->bytes);
             mk_setFrameFlags(m->file, mux_data->track, timecode, 1, 0);
+            hb_buffer_close( &buf );
             return 0;
         }
     }
@@ -424,6 +425,7 @@ static int MKVMux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
             mk_setFrameFlags(m->file, mux_data->track, timecode, 1, duration);
         }
         mk_flushFrame(m->file, mux_data->track);
+        hb_buffer_close( &buf );
         return 0;
     }
     else
@@ -442,6 +444,7 @@ static int MKVMux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
             }
             mk_addFrameData(m->file, mux_data->track, op->packet, op->bytes);
             mk_setFrameFlags(m->file, mux_data->track, timecode, 1, 0);
+            hb_buffer_close( &buf );
             return 0;
         }
     }
@@ -457,6 +460,7 @@ static int MKVMux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
                        mux_data == job->mux_data) ? 
                             (buf->frametype == HB_FRAME_IDR) : 
                             ((buf->frametype & HB_FRAME_KEY) != 0)), 0 );
+    hb_buffer_close( &buf );
     return 0;
 }
 
index 1264f9b..dcab2e5 100644 (file)
@@ -20,8 +20,7 @@ struct hb_mux_object_s
 
     int64_t sum_dur;    // sum of video frame durations so far
 
-    // bias to keep render offsets in ctts atom positive (set up by encx264)
-    int64_t init_delay;
+    hb_buffer_t *delay_buf;
 
     /* Chapter state information for muxing */
     MP4TrackId chapter_track;
@@ -146,8 +145,6 @@ static int MP4Init( hb_mux_object_t * m )
                        hb_deep_log( 2, "muxmp4: adding iPod atom");
                        MP4AddIPodUUID(m->file, mux_data->track);
                }
-
-        m->init_delay = job->config.h264.init_delay;
     }
     else /* FFmpeg or XviD */
     {
@@ -774,25 +771,37 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
     hb_job_t * job = m->job;
     int64_t duration;
     int64_t offset = 0;
+    hb_buffer_t *tmp;
 
     if( mux_data == job->mux_data )
     {
         /* Video */
 
-        // if there are b-frames compute the render offset
-        // (we'll need it for both the video frame & the chapter track)
-        if ( m->init_delay )
+        if( job->vcodec == HB_VCODEC_X264 )
         {
-            offset = buf->start + m->init_delay - m->sum_dur;
-            if ( offset < 0 )
+            if ( buf && buf->start < buf->renderOffset )
             {
-                hb_log("MP4Mux: illegal render offset %"PRId64", start %"PRId64","
-                       "stop %"PRId64", sum_dur %"PRId64,
-                       offset, buf->start, buf->stop, m->sum_dur );
-                offset = 0;
+                hb_log("MP4Mux: PTS %"PRId64" < DTS %"PRId64,
+                       buf->start, buf->renderOffset );
+                buf->renderOffset = buf->start;
             }
         }
 
+        // We delay muxing video by one frame so that we can calculate
+        // the dts to dts duration of the frames.
+        tmp = buf;
+        buf = m->delay_buf;
+        m->delay_buf = tmp;
+
+        if ( !buf )
+            return 0;
+
+        if( job->vcodec == HB_VCODEC_X264 )
+        {
+            // x264 supplies us with DTS, so offset is PTS - DTS
+            offset = buf->start - buf->renderOffset;
+        }
+
         /* Add the sample before the new frame.
            It is important that this be calculated prior to the duration
            of the new video sample, as we want to sync to right after it.
@@ -824,10 +833,54 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
             }
         }
 
-        // We're getting the frames in decode order but the timestamps are
-        // for presentation so we have to use durations and effectively
-        // compute a DTS.
-        duration = buf->stop - buf->start;
+        if( job->vcodec == HB_VCODEC_X264 )
+        {
+            // x264 supplies us with DTS
+            if ( m->delay_buf )
+            {
+                duration = m->delay_buf->renderOffset - buf->renderOffset;
+            }
+            else
+            {
+                duration = buf->stop - m->sum_dur;
+                // Due to how libx264 generates DTS, it's possible for the
+                // above calculation to be negative. 
+                //
+                // x264 generates DTS by rearranging PTS in this sequence:
+                // pts0 - delay, pts1 - delay, pts2 - delay, pts1, pts2, pts3...
+                //
+                // where delay == pts2.  This guarantees that DTS <= PTS for
+                // any frame, but also generates this sequence of durations:
+                // d0 + d1 + d0 + d1 + d2 + d3 ... + d(N-2)
+                // 
+                // so the sum up to the last frame is:
+                // sum_dur = d0 + d1 + d0 + d1 + d2 + d3 ... + d(N-3)
+                //
+                // while the original total duration of the video was:
+                // duration = d0 + d1 + d2 + d3 ... + d(N)
+                //
+                // Note that if d0 + d1 != d(N-1) + d(N), the total
+                // length of the video changes since d(N-1) and d(N) are
+                // replaced by d0 and d1 in the final duration sum.
+                //
+                // To keep the total length of the video the same as the source
+                // we try to make 
+                // d(N-2) = duration - sum_dur
+                //
+                // But if d0 + d1 >= d(N-1) + d(N), the above calculation
+                // results in a nagative value and we need to fix it.
+                if ( duration <= 0 )
+                    duration = 90000. / ((double)job->vrate / (double)job->vrate_base);
+            }
+        }
+        else
+        {
+            // We're getting the frames in decode order but the timestamps are
+            // for presentation so we have to use durations and effectively
+            // compute a DTS.
+            duration = buf->stop - buf->start;
+        }
+
         if ( duration <= 0 )
         {
             /* We got an illegal mp4/h264 duration. This shouldn't
@@ -986,7 +1039,7 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
             *job->die = 1;
         }
     }
-
+    hb_buffer_close( &buf );
 
     return 0;
 }
@@ -996,6 +1049,10 @@ static int MP4End( hb_mux_object_t * m )
     hb_job_t   * job   = m->job;
     hb_title_t * title = job->title;
 
+    // Flush the delayed frame
+    if ( m->delay_buf )
+        MP4Mux( m, job->mux_data, NULL );
+
     /* Write our final chapter marker */
     if( m->job->chapter_markers )
     {
@@ -1016,11 +1073,11 @@ static int MP4End( hb_mux_object_t * m )
         }
     }
 
-    if (job->areBframes)
+    if ( job->config.h264.init_delay )
     {
            // Insert track edit to get A/V back in sync.  The edit amount is
            // the init_delay.
-           int64_t edit_amt = m->init_delay;
+           int64_t edit_amt = job->config.h264.init_delay;
            MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, edit_amt,
                            MP4GetTrackDuration(m->file, 1), 0);
             if ( m->job->chapter_markers )
index b347971..07f07c7 100644 (file)
@@ -1274,6 +1274,17 @@ int hb_stream_seek_chapter( hb_stream_t * stream, int chapter_num )
     {
         av_seek_frame( stream->ffmpeg_ic, -1, pos, 0);
     }
+    else
+    {
+        // ffmpeg has a bug that causes the first PTS after
+        // av_find_stream_info() is called to be incorrect.
+        // av_find_stream_info is called whenever opening a file
+        // with ffmpeg.  av_seek_frame clears the condition
+        // that causes the problem. since hb_stream_seek_chapter
+        // is called before we start reading, make sure
+        // we do a seek here.
+        av_seek_frame( stream->ffmpeg_ic, -1, 0LL, AVSEEK_FLAG_BACKWARD );
+    }
     return 1;
 }