summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
authorvan <[email protected]>2008-07-26 01:20:56 +0000
committervan <[email protected]>2008-07-26 01:20:56 +0000
commitb3bc6dccde26b1b072a583bce8f5726e85ca2fd0 (patch)
tree1730301ea79410c56b39a6b0b33aa3865e7c2c06 /libhb
parente529b1dbda55f01e21fe6fb04ee7f85c2e755725 (diff)
Use PTS, not DTS, in encx264 output frames so we don't have to special-case its output in every muxer. Confine code that deals with Apple's mistakes in handling video with b-frames to muxmp4.
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@1582 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb')
-rw-r--r--libhb/encx264.c51
-rw-r--r--libhb/internal.h1
-rw-r--r--libhb/muxmkv.c9
-rw-r--r--libhb/muxmp4.c71
4 files changed, 49 insertions, 83 deletions
diff --git a/libhb/encx264.c b/libhb/encx264.c
index caeccb646..ebd9f4bb5 100644
--- a/libhb/encx264.c
+++ b/libhb/encx264.c
@@ -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
diff --git a/libhb/internal.h b/libhb/internal.h
index 7a102a505..36dca0d80 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -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
diff --git a/libhb/muxmkv.c b/libhb/muxmkv.c
index 0c2c00ada..6217820b5 100644
--- a/libhb/muxmkv.c
+++ b/libhb/muxmkv.c
@@ -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))
{
diff --git a/libhb/muxmp4.c b/libhb/muxmp4.c
index b61d579c8..fe6cc2793 100644
--- a/libhb/muxmp4.c
+++ b/libhb/muxmp4.c
@@ -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);
}
}