From 68143bcb611820145a390f480de18b4060220f97 Mon Sep 17 00:00:00 2001 From: Rodeo Date: Wed, 5 Jun 2013 15:31:00 +0000 Subject: [PATCH] QSV: muxmkv: fix writing of SPS/PPS with the QSV encoder. muxmp4: uniform writing of SPS/PPS with muxmkv. git-svn-id: svn://svn.handbrake.fr/HandBrake/branches/qsv@5557 b64f7644-9d1e-0410-96f1-a4d463321fa5 --- libhb/muxmkv.c | 137 ++++++++++++++++++++++++++++++++++++++----------- libhb/muxmp4.c | 72 +++++++++++++++++--------- 2 files changed, 153 insertions(+), 56 deletions(-) diff --git a/libhb/muxmkv.c b/libhb/muxmkv.c index b5abae96a..ccaacbfe1 100644 --- a/libhb/muxmkv.c +++ b/libhb/muxmkv.c @@ -26,6 +26,8 @@ struct hb_mux_object_s hb_job_t * job; mk_Writer * file; + + int wait_for_sps_pps; }; struct hb_mux_data_s @@ -51,6 +53,49 @@ static uint8_t * create_flac_header( uint8_t *data, int size ) return out; } +static uint8_t* create_h264_header(hb_job_t *job, int *size) +{ + /* Taken from x264's muxers.c */ + int avcC_len = (5 + + 1 + 2 + job->config.h264.sps_length + + 1 + 2 + job->config.h264.pps_length); +#define MAX_AVCC_LEN 5 + 1 + 2 + 1024 + 1 + 2 + 1024 // FIXME + if (avcC_len > MAX_AVCC_LEN) + { + hb_log("create_h264_header: H.264 header too long (%d, max: %d)", + avcC_len, MAX_AVCC_LEN); + return NULL; + } + uint8_t *avcC = malloc(avcC_len); + if (avcC == NULL) + { + return NULL; + } + + avcC[0] = 1; + avcC[1] = job->config.h264.sps[1]; /* AVCProfileIndication */ + avcC[2] = job->config.h264.sps[2]; /* profile_compat */ + avcC[3] = job->config.h264.sps[3]; /* AVCLevelIndication */ + avcC[4] = 0xff; // nalu size length is four bytes + avcC[5] = 0xe1; // one sps + + avcC[6] = job->config.h264.sps_length >> 8; + avcC[7] = job->config.h264.sps_length; + memcpy(avcC + 8, job->config.h264.sps, job->config.h264.sps_length); + + avcC[8 + job->config.h264.sps_length] = 1; // one pps + avcC[9 + job->config.h264.sps_length] = job->config.h264.pps_length >> 8; + avcC[10 + job->config.h264.sps_length] = job->config.h264.pps_length; + memcpy(avcC + 11 + job->config.h264.sps_length, + job->config.h264.pps, job->config.h264.pps_length); + + if (size != NULL) + { + *size = avcC_len; + } + return avcC; +} + /********************************************************************** * MKVInit ********************************************************************** @@ -92,39 +137,36 @@ static int MKVInit( hb_mux_object_t * m ) track->flagEnabled = 1; switch (job->vcodec) { - case HB_VCODEC_QSV_H264: case HB_VCODEC_X264: - track->codecID = MK_VCODEC_MP4AVC; - /* Taken from x264 muxers.c */ - avcC_len = 5 + 1 + 2 + job->config.h264.sps_length + 1 + 2 + job->config.h264.pps_length; - avcC = malloc(avcC_len); - if (avcC == NULL) { + // we already have the SPS/PPS, write it now + avcC = create_h264_header(job, &avcC_len); + if (avcC == NULL) + { free(track); return -1; } - - avcC[0] = 1; - avcC[1] = job->config.h264.sps[1]; /* AVCProfileIndication */ - avcC[2] = job->config.h264.sps[2]; /* profile_compat */ - avcC[3] = job->config.h264.sps[3]; /* AVCLevelIndication */ - avcC[4] = 0xff; // nalu size length is four bytes - avcC[5] = 0xe1; // one sps - - avcC[6] = job->config.h264.sps_length >> 8; - avcC[7] = job->config.h264.sps_length; - - memcpy(avcC+8, job->config.h264.sps, job->config.h264.sps_length); - - avcC[8+job->config.h264.sps_length] = 1; // one pps - avcC[9+job->config.h264.sps_length] = job->config.h264.pps_length >> 8; - avcC[10+job->config.h264.sps_length] = job->config.h264.pps_length; - - memcpy( avcC+11+job->config.h264.sps_length, job->config.h264.pps, job->config.h264.pps_length ); - track->codecPrivate = avcC; + track->codecID = MK_VCODEC_MP4AVC; + track->codecPrivate = avcC; track->codecPrivateSize = avcC_len; if (job->areBframes) track->minCache = 1; break; + case HB_VCODEC_QSV_H264: + // we don't have the SPS/PPS yet + avcC = calloc(1, MAX_AVCC_LEN); // allocate here, write later + avcC_len = MAX_AVCC_LEN; + if (avcC == NULL) + { + free(track); + return -1; + } + track->codecID = MK_VCODEC_MP4AVC; + track->codecPrivate = avcC; + track->codecPrivateSize = avcC_len; + m->wait_for_sps_pps = 1; + if (job->areBframes) + track->minCache = 1; + break; case HB_VCODEC_FFMPEG_MPEG4: track->codecID = MK_VCODEC_MP4ASP; track->codecPrivate = job->config.mpeg4.bytes; @@ -444,6 +486,33 @@ static int MKVMux(hb_mux_object_t *m, hb_mux_data_t *mux_data, hb_buffer_t *buf) ogg_packet *op = NULL; hb_job_t *job = m->job; + if (m->wait_for_sps_pps && (job->vcodec & HB_VCODEC_H264_MASK)) + { +#ifdef USE_QSV + if (job->vcodec != HB_VCODEC_QSV_H264 || (job->qsv != NULL && + job->qsv->enc_space != NULL && + job->qsv->enc_space->is_init_done)) +#endif + { + // add the SPS/PPS now that we know it + int avcC_len; + uint8_t *avcC = create_h264_header(job, &avcC_len); + if (avcC != NULL) + { + if (mk_updateTrackPrivateData(m->file, job->mux_data->track, + avcC, avcC_len) < 0) + { + hb_log("MKVMux: failed to update SPS/PPS (H.264)"); + } + } + else + { + hb_log("MKVMux: create_h264_header() failed"); + } + m->wait_for_sps_pps = 0; + } + } + if (mux_data == job->mux_data) { /* Video */ @@ -544,12 +613,12 @@ static int MKVMux(hb_mux_object_t *m, hb_mux_data_t *mux_data, hb_buffer_t *buf) } mk_addFrameData(m->file, mux_data->track, buf->data, buf->size); mk_setFrameFlags(m->file, mux_data->track, timecode, - (((job->vcodec == HB_VCODEC_X264 || job->vcodec == HB_VCODEC_QSV_H264 || - (job->vcodec & HB_VCODEC_FFMPEG_MASK)) && - mux_data == job->mux_data) ? - (buf->s.frametype == HB_FRAME_IDR) : - ((buf->s.frametype & HB_FRAME_KEY) != 0)), 0 ); - hb_buffer_close( &buf ); + ((mux_data == job->mux_data && + ((job->vcodec & HB_VCODEC_H264_MASK) || + (job->vcodec & HB_VCODEC_FFMPEG_MASK))) ? + (buf->s.frametype == HB_FRAME_IDR) : + (buf->s.frametype & HB_FRAME_KEY) != 0), 0); + hb_buffer_close(&buf); return 0; } @@ -677,6 +746,11 @@ static int MKVEnd(hb_mux_object_t *m) *job->die = 1; } + if (m->wait_for_sps_pps && (job->vcodec & HB_VCODEC_H264_MASK)) + { + hb_log("MKVEnd: warning: we didn't get any SPS/PPS (H.264)"); + } + // TODO: Free what we alloc'd return 0; @@ -689,5 +763,6 @@ hb_mux_object_t * hb_mux_mkv_init( hb_job_t * job ) m->mux = MKVMux; m->end = MKVEnd; m->job = job; + m->wait_for_sps_pps = 0; return m; } diff --git a/libhb/muxmp4.c b/libhb/muxmp4.c index 227f77ad1..8af9c9988 100644 --- a/libhb/muxmp4.c +++ b/libhb/muxmp4.c @@ -30,7 +30,7 @@ struct hb_mux_object_s int current_chapter; uint64_t chapter_duration; - int qsv_delay; + int wait_for_sps_pps; }; struct hb_mux_data_s @@ -129,7 +129,7 @@ static int MP4Init( hb_mux_object_t * m ) return 0; } - if( job->vcodec == HB_VCODEC_X264 || job->vcodec == HB_VCODEC_QSV_H264 ) + if (job->vcodec & HB_VCODEC_H264_MASK) { /* Stolen from mp4creator */ MP4SetVideoProfileLevel( m->file, 0x7F ); @@ -151,12 +151,21 @@ static int MP4Init( hb_mux_object_t * m ) { return 0; } - // will be added later, after first encode starts - if(job->vcodec != HB_VCODEC_QSV_H264){ - MP4AddH264SequenceParameterSet( m->file, mux_data->track, - job->config.h264.sps, job->config.h264.sps_length ); - MP4AddH264PictureParameterSet( m->file, mux_data->track, - job->config.h264.pps, job->config.h264.pps_length ); + + if (job->vcodec == HB_VCODEC_QSV_H264) + { + // we don't have the SPS/PPS yet + m->wait_for_sps_pps = 1; + } + else + { + // we already have the SPS/PPS, write it now + MP4AddH264SequenceParameterSet(m->file, mux_data->track, + job->config.h264.sps, + job->config.h264.sps_length); + MP4AddH264PictureParameterSet(m->file, mux_data->track, + job->config.h264.pps, + job->config.h264.pps_length); } if( job->ipod_atom ) @@ -913,23 +922,31 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data, int64_t offset = 0; hb_buffer_t *tmp; - if( mux_data == job->mux_data ) + if (m->wait_for_sps_pps && (job->vcodec & HB_VCODEC_H264_MASK)) { - #ifdef USE_QSV - if( job->vcodec == HB_VCODEC_QSV_H264 && !m->qsv_delay && job->qsv && job->qsv->enc_space && job->qsv->enc_space->is_init_done ){ - MP4AddH264SequenceParameterSet( m->file, mux_data->track, - job->config.h264.sps, job->config.h264.sps_length ); - - MP4AddH264PictureParameterSet( m->file, mux_data->track, - job->config.h264.pps, job->config.h264.pps_length ); - m->qsv_delay = 1; - } + if (job->vcodec != HB_VCODEC_QSV_H264 || (job->qsv != NULL && + job->qsv->enc_space != NULL && + job->qsv->enc_space->is_init_done)) #endif + { + // add the SPS/PPS now that we know it + MP4AddH264SequenceParameterSet(m->file, job->mux_data->track, + job->config.h264.sps, + job->config.h264.sps_length); + + MP4AddH264PictureParameterSet(m->file, job->mux_data->track, + job->config.h264.pps, + job->config.h264.pps_length); + m->wait_for_sps_pps = 0; + } + } + if( mux_data == job->mux_data ) + { /* Video */ - if( job->vcodec == HB_VCODEC_X264 || job->vcodec == HB_VCODEC_QSV_H264 || - ( job->vcodec & HB_VCODEC_FFMPEG_MASK ) ) + if ((job->vcodec & HB_VCODEC_H264_MASK) || + (job->vcodec & HB_VCODEC_FFMPEG_MASK)) { if ( buf && buf->s.start < buf->s.renderOffset ) { @@ -948,8 +965,8 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data, if ( !buf ) return 0; - if( job->vcodec == HB_VCODEC_X264 || job->vcodec == HB_VCODEC_QSV_H264 || - ( job->vcodec & HB_VCODEC_FFMPEG_MASK ) ) + if ((job->vcodec & HB_VCODEC_H264_MASK) || + (job->vcodec & HB_VCODEC_FFMPEG_MASK)) { // x264 supplies us with DTS, so offset is PTS - DTS offset = buf->s.start - buf->s.renderOffset; @@ -1064,9 +1081,8 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data, } /* Here's where the sample actually gets muxed. */ - if( ( job->vcodec == HB_VCODEC_X264 || job->vcodec == HB_VCODEC_QSV_H264 || - ( job->vcodec & HB_VCODEC_FFMPEG_MASK ) ) - && mux_data == job->mux_data ) + if (mux_data == job->mux_data && ((job->vcodec & HB_VCODEC_H264_MASK) || + (job->vcodec & HB_VCODEC_FFMPEG_MASK))) { /* Compute dependency flags. * @@ -1405,6 +1421,11 @@ static int MP4End( hb_mux_object_t * m ) MP4Close( m->file ); + if (m->wait_for_sps_pps && (job->vcodec & HB_VCODEC_H264_MASK)) + { + hb_log("MP4End: warning: we didn't get any SPS/PPS (H.264)"); + } + if ( job->mp4_optimize ) { hb_log( "muxmp4: optimizing file" ); @@ -1426,6 +1447,7 @@ hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job ) m->mux = MP4Mux; m->end = MP4End; m->job = job; + m->wait_for_sps_pps = 0; return m; } -- 2.40.0