diff options
author | jbrjake <jb.rubin@gmail.com> | 2007-03-30 00:50:20 +0000 |
---|---|---|
committer | jbrjake <jb.rubin@gmail.com> | 2007-03-30 00:50:20 +0000 |
commit | 91948dd02f961c7dad3a855cf7edbf0f860af9a0 (patch) | |
tree | 35c0745066fce77fbaf7b1eb666177de0e49ae07 /libhb | |
parent | f2ef21afdeff9ebfd8042176235e6888a7f9398e (diff) |
H.264 B-frame muxing for MP4, including B-pyramids. The latter are QuickTime incompatible.
"Most software today is very much like an Egyptian pyramid with millions of bricks piled on top of each other, with no structural integrity, but just done by brute force and thousands of slaves." -- Alan Kay
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@464 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb')
-rw-r--r-- | libhb/encx264.c | 49 | ||||
-rw-r--r-- | libhb/internal.h | 3 | ||||
-rw-r--r-- | libhb/muxmp4.c | 38 |
3 files changed, 82 insertions, 8 deletions
diff --git a/libhb/encx264.c b/libhb/encx264.c index 69739635b..747bae9f2 100644 --- a/libhb/encx264.c +++ b/libhb/encx264.c @@ -121,7 +121,16 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job ) } } - /* Here's where the strings are passed to libx264 for parsing. */ + /* Note b-pyramid here, so the initial delay can be doubled */ + if (!(strcmp(name, "b-pyramid"))) + { + if (atoi(value) > 0) + { + job->areBframes = 2; + } + } + + /* Here's where the strings are passed to libx264 for parsing. */ ret = x264_param_parse(¶m, name, value); /* Let x264 sanity check the options for us*/ @@ -240,6 +249,9 @@ int encx264Work( hb_work_object_t * w, hb_buffer_t ** buf_in, pv->pic_in.i_type = X264_TYPE_AUTO; pv->pic_in.i_qpplus1 = 0; + /* Feed the input DTS to x264 so it can figure out proper output PTS */ + pv->pic_in.i_pts = in->start; + x264_encoder_encode( pv->x264, &nal, &i_nal, &pv->pic_in, &pv->pic_out ); @@ -285,10 +297,37 @@ int encx264Work( hb_work_object_t * w, hb_buffer_t ** buf_in, buf->data[buf->size+1] = ( ( size - 4 ) >> 16 ) & 0xFF; buf->data[buf->size+2] = ( ( size - 4 ) >> 8 ) & 0xFF; buf->data[buf->size+3] = ( ( size - 4 ) >> 0 ) & 0xFF; - if( nal[i].i_ref_idc == NAL_PRIORITY_HIGHEST ) - { - buf->key = 1; - } + + /* For IDR (key frames), buf->key = 1, + and the same for regular I-frames. */ + if( (pv->pic_out.i_type == X264_TYPE_IDR) || (pv->pic_out.i_type == X264_TYPE_I) ) + { + buf->key = 1; + } + /* For B-frames, buf->key = 2 */ + else if( (pv->pic_out.i_type == X264_TYPE_B) ) + { + buf->key = 2; + } + /* This is for b-pyramid, which has reference b-frames + However, it doesn't seem to ever be used... + They just show up as buf->key == 2 like + regular b-frames. */ + else if( (pv->pic_out.i_type == X264_TYPE_BREF) ) + { + buf->key = 3; + } + /* For P-frames, buf->key = 0 */ + else + { + buf->key = 0; + } + + /* Store the output presentation time stamp + from x264 for use by muxmp4 in off-setting + b-frames with the CTTS atom. */ + buf->encodedPTS = pv->pic_out.i_pts; + buf->size += size; } } diff --git a/libhb/internal.h b/libhb/internal.h index fb5731881..577ad3032 100644 --- a/libhb/internal.h +++ b/libhb/internal.h @@ -39,6 +39,9 @@ struct hb_buffer_s int64_t stop; int key; + /* Holds the output PTS from x264, for use by b-frame offsets in muxmp4.c */ + int64_t encodedPTS; + int x; int y; int width; diff --git a/libhb/muxmp4.c b/libhb/muxmp4.c index f06527295..1d599fbb5 100644 --- a/libhb/muxmp4.c +++ b/libhb/muxmp4.c @@ -11,6 +11,9 @@ void AddIPodUUID(MP4FileHandle, MP4TrackId); +/* B-frame muxing variables */ +MP4SampleId thisSample = 0; +uint64_t initDelay; struct hb_mux_object_s { @@ -64,7 +67,7 @@ static int MP4Init( hb_mux_object_t * m ) /* Stolen from mp4creator */ MP4SetVideoProfileLevel( m->file, 0x7F ); - if (job->areBframes == 1) + if (job->areBframes >= 1) { hb_log("muxmp4: Adjusting duration for B-frames"); mux_data->track = MP4AddH264VideoTrack( m->file, job->arate, @@ -238,8 +241,37 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data, duration = MP4_INVALID_DURATION; } - MP4WriteSample( m->file, mux_data->track, buf->data, buf->size, - duration, 0, buf->key ); + /* If for some reason the first frame muxmp4 gets isn't a key-frame, + drop frames until we get one. (Yes, very bad. Quick and dirty.) + This is for QuickTime--when it sees a non-IDR frame first, it + displays a white box instead of video until the second GOP. + Also, you've got to save the skipped duration to keep from + throwing off the offset values. */ + if((mux_data->track == 1) && (thisSample == 0) && (buf->key != 1)) + { + initDelay +=duration; + return 0; + } + /* When we do get the first keyframe, use its duration as the + initial delay added to the frame order offset for b-frames. + Because of b-pyramid, double this duration when there are + b-pyramids, as denoted by job->areBframes equalling 2. */ + if ((mux_data->track == 1) && (thisSample == 0) && (buf->key == 1)) + { + initDelay += duration * job->areBframes; + thisSample++; + } + + /* 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. */ + MP4WriteSample( m->file, mux_data->track, buf->data, buf->size, + duration, ((mux_data->track != 1) || (job->areBframes==0)) ? 0 : ( initDelay + ((buf->encodedPTS - buf->start) * job->arate / 90000)), + (buf->key == 1) ); + return 0; } |