OSDN Git Service

Use PTS, not DTS, in encx264 output frames so we don't have to special-case its outpu...
authorvan <van@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Sat, 26 Jul 2008 01:20:56 +0000 (01:20 +0000)
committervan <van@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Sat, 26 Jul 2008 01:20:56 +0000 (01:20 +0000)
git-svn-id: svn://localhost/HandBrake/trunk@1582 b64f7644-9d1e-0410-96f1-a4d463321fa5

libhb/encx264.c
libhb/internal.h
libhb/muxmkv.c
libhb/muxmp4.c

index caeccb6..ebd9f4b 100644 (file)
@@ -51,10 +51,8 @@ struct hb_work_private_s
     uint8_t         *x264_allocated_pic;
 
     int            chap_mark;   // saved chap mark when we're propagating it
-    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        max_delay;   // if init_delay too small, delay really needed
     int64_t        next_chap;
 
     struct {
@@ -306,9 +304,6 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
 
     pv->x264_allocated_pic = pv->pic_in.img.plane[0];
 
-    pv->dts_next = -1;
-    pv->next_chap = 0;
-
     if (job->areBframes)
     {
         /* Basic initDelay value is the clockrate divided by the FPS
@@ -341,6 +336,7 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
         /* 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;
 }
@@ -384,34 +380,15 @@ static hb_buffer_t *nal_encode( hb_work_object_t *w, x264_picture_t *pic_out,
     hb_work_private_t *pv = w->private_data;
     hb_job_t *job = pv->job;
 
-    /* Get next DTS value to use */
-    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;
-
     /* Should be way too large */
     buf = hb_buffer_init( 3 * job->width * job->height / 2 );
     buf->size = 0;
     buf->frametype = 0;
-    buf->start = dts_start;
-    buf->stop  = dts_stop;
 
-    /* Store the output presentation time stamp from x264 for use by muxmp4
-       in off-setting b-frames with the CTTS atom. */
-    buf->renderOffset = pic_out->i_pts - dts_start + pv->init_delay;
-    if ( buf->renderOffset < 0 )
-    {
-        if ( dts_start - pic_out->i_pts > pv->max_delay )
-        {
-            pv->max_delay = dts_start - pic_out->i_pts;
-            hb_log( "encx264: init_delay too small: "
-                    "is %lld need %lld", pv->init_delay,
-                    pv->max_delay );
-        }
-        buf->renderOffset = 0;
-    }
+    // use the pts to get the original frame's duration.
+    int64_t duration  = get_frame_duration( pv, pic_out->i_pts );
+    buf->start = pic_out->i_pts;
+    buf->stop  = pic_out->i_pts + duration;
 
     /* Encode all the NALs we were given into buf.
        NOTE: This code assumes one video frame per NAL (but there can
@@ -504,8 +481,7 @@ static hb_buffer_t *nal_encode( hb_work_object_t *w, x264_picture_t *pic_out,
     // make sure we found at least one video frame
     if ( buf->size <= 0 )
     {
-        // no video: back up the output time stamp then free the buf
-        pv->dts_next = buf->start;
+        // no video - discard the buf
         hb_buffer_close( &buf );
     }
     return buf;
@@ -531,17 +507,6 @@ static hb_buffer_t *x264_encode( hb_work_object_t *w, hb_buffer_t *in )
         pv->pic_in.img.plane[1] = in->data + job->width * job->height;
         pv->pic_in.img.plane[2] = in->data + 5 * job->width * 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
@@ -580,7 +545,7 @@ static hb_buffer_t *x264_encode( hb_work_object_t *w, hb_buffer_t *in )
     // 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 */
+    /* Feed the input PTS to x264 so it can figure out proper output PTS */
     pv->pic_in.i_pts = in->start;
 
     x264_picture_t pic_out;
@@ -634,6 +599,8 @@ int encx264Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
     }
 
     // Not EOF - encode the packet & wrap it in a NAL
+
+    // 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
index 7a102a5..36dca0d 100644 (file)
@@ -157,6 +157,7 @@ union hb_esconfig_u
            int       sps_length;
            uint8_t  pps[HB_CONFIG_MAX_SIZE];
            int       pps_length;
+        uint32_t init_delay;
        } h264;
 
     struct
index 0c2c00a..6217820 100644 (file)
@@ -251,14 +251,7 @@ static int MKVMux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
     if (mux_data == job->mux_data)
     {
         /* Video */
-        if ((job->vcodec == HB_VCODEC_X264) && (job->areBframes))
-        {
-            timecode = (buf->start + (buf->renderOffset)) * TIMECODE_SCALE;
-        }
-        else
-        {
-            timecode = buf->start * TIMECODE_SCALE;
-        }
+        timecode = buf->start * TIMECODE_SCALE;
 
         if (job->chapter_markers && (buf->new_chap || timecode == 0))
         {
index b61d579..fe6cc27 100644 (file)
@@ -20,8 +20,12 @@ struct hb_mux_object_s
     /* libmp4v2 handle */
     MP4FileHandle file;
 
-    /* Cumulated durations so far, in timescale units (see MP4Mux) */
-    uint64_t sum_dur;
+    /* Cumulated durations so far, in output & input timescale units (see MP4Mux) */
+    int64_t sum_dur;        // duration in output timescale units
+    int64_t sum_dur_in;     // duration in input 90KHz timescale units
+
+    // bias to keep render offsets in ctts atom positive (set up by encx264)
+    int64_t init_delay;
 
     /* Chapter state information for muxing */
     MP4TrackId chapter_track;
@@ -223,6 +227,7 @@ static int MP4Init( hb_mux_object_t * m )
                        AddIPodUUID(m->file, mux_data->track);
                }
 
+        m->init_delay = job->config.h264.init_delay;
     }
     else /* FFmpeg or XviD */
     {
@@ -383,11 +388,20 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
                    hb_buffer_t * buf )
 {
     hb_job_t * job = m->job;
-
     int64_t duration;
+    int64_t offset = 0;
 
     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 )
+        {
+            offset = ( buf->start + m->init_delay ) * m->samplerate / 90000 -
+                     m->sum_dur;
+        }
         /* 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.
@@ -396,17 +410,11 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
         {
             struct hb_text_sample_s *sample;
 
-            /* If this is an x264 encode with bframes the IDR frame we're
-               trying to mark will be displayed offset by its renderOffset
-               so we need to offset the chapter by the same amount.
-               MP4 render offsets don't seem to work for text tracks so
-               we have to fudge the duration instead. */
-            duration = m->sum_dur - m->chapter_duration;
-
-            if ( job->areBframes )
-            {
-                duration += buf->renderOffset * m->samplerate / 90000;
-            }
+            // this chapter is postioned by writing out the previous chapter.
+            // the duration of the previous chapter is the duration up to but
+            // not including the current frame minus the duration of all
+            // chapters up to the previous.
+            duration = m->sum_dur - m->chapter_duration + offset;
             if ( duration <= 0 )
             {
                 /* The initial & final chapters can have very short durations
@@ -434,12 +442,14 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
             m->chapter_duration += duration;
         }
 
-        /* Video */
-        /* 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 */
-        int64_t bias = ( buf->start * m->samplerate / 90000 ) - m->sum_dur;
-        duration = ( buf->stop - buf->start ) * m->samplerate / 90000 + bias;
+        // since we're changing the sample rate we need to keep track of
+        // the truncation bias so that the audio and video don't go out
+        // of sync. m->sum_dur_in is the sum of the input durations so far.
+        // m->sum_dur is the sum of the output durations. Their difference
+        // (in output sample rate units) is the accumulated truncation bias.
+        int64_t bias = ( m->sum_dur_in * m->samplerate / 90000 ) - m->sum_dur;
+        int64_t dur_in = buf->stop - buf->start;
+        duration = dur_in * m->samplerate / 90000 + bias;
         if ( duration <= 0 )
         {
             /* We got an illegal mp4/h264 duration. This shouldn't
@@ -458,6 +468,7 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
             duration = 1000 * m->samplerate / 90000;
         }
         m->sum_dur += duration;
+        m->sum_dur_in += dur_in;
     }
     else
     {
@@ -465,20 +476,13 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
         duration = MP4_INVALID_DURATION;
     }
 
-    /* Here's where the sample actually gets muxed.
-       If it's an audio sample, don't offset the sample's playback.
-       If it's a video sample and there are no b-frames, ditto.
-       If there are b-frames, offset by the initDelay plus the
-       difference between the presentation time stamp x264 gives
-       and the decoding time stamp from the buffer data. */
+    // Here's where the sample actually gets muxed.
     if( !MP4WriteSample( m->file,
                          mux_data->track,
                          buf->data,
                          buf->size,
                          duration,
-                         ((mux_data->track != 1) ||
-                          (job->areBframes==0) ||
-                          (job->vcodec != HB_VCODEC_X264)) ? 0 : (  buf->renderOffset * m->samplerate / 90000),
+                         offset,
                          ((buf->frametype & HB_FRAME_KEY) != 0) ) )
     {
         hb_error("Failed to write to output file, disk full?");
@@ -516,14 +520,15 @@ static int MP4End( hb_mux_object_t * m )
     if (job->areBframes)
     {
            // Insert track edit to get A/V back in sync.  The edit amount is
-           // the rendering offset of the first sample.
-           MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, MP4GetSampleRenderingOffset(m->file,1,1),
-               MP4GetTrackDuration(m->file, 1), 0);
+           // the init_delay.
+           int64_t edit_amt = m->init_delay * m->samplerate / 90000;
+           MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, edit_amt,
+                           MP4GetTrackDuration(m->file, 1), 0);
             if ( m->job->chapter_markers )
             {
                 // apply same edit to chapter track to keep it in sync with video
                 MP4AddTrackEdit(m->file, m->chapter_track, MP4_INVALID_EDIT_ID,
-                                MP4GetSampleRenderingOffset(m->file,1,1),
+                                edit_amt,
                                 MP4GetTrackDuration(m->file, m->chapter_track), 0);
             }
      }