diff options
-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; } |