From 5f4dbd6f17348ffeef6dd0de282f9f5ee68237bf Mon Sep 17 00:00:00 2001 From: John Stebbins Date: Thu, 11 Oct 2018 11:24:06 -0700 Subject: [PATCH] decavcodec: packetize data before searching for extradata The necessary data for setting up extradata may be scattered across multiple input buffers. The buffers must be combined before extradata can be extracted. --- libhb/decavcodec.c | 355 ++++++++++++++++++++------------------------- 1 file changed, 158 insertions(+), 197 deletions(-) diff --git a/libhb/decavcodec.c b/libhb/decavcodec.c index 16c4e31b8..499de9731 100644 --- a/libhb/decavcodec.c +++ b/libhb/decavcodec.c @@ -112,6 +112,7 @@ struct hb_work_private_s { hb_job_t * job; hb_title_t * title; + AVCodec * codec; AVCodecContext * context; AVCodecParserContext * parser; AVFrame * frame; @@ -1349,9 +1350,8 @@ static void filter_video(hb_work_private_t *pv) * The output of this function is stored in 'pv->list', which contains a list * of zero or more decoded packets. */ -static int decodeFrame( hb_work_object_t *w, packet_info_t * packet_info ) +static int decodeFrame( hb_work_private_t * pv, packet_info_t * packet_info ) { - hb_work_private_t *pv = w->private_data; int got_picture = 0, oldlevel = 0, ret; AVPacket avp; reordered_data_t * reordered; @@ -1449,128 +1449,6 @@ static int decodeFrame( hb_work_object_t *w, packet_info_t * packet_info ) return got_picture; } -static void decodeVideo( hb_work_object_t *w, hb_buffer_t * in) -{ - hb_work_private_t *pv = w->private_data; - - /* - * The following loop is a do..while because we need to handle both - * data & the flush at the end (signaled by size=0). At the end there's - * generally a frame in the parser & one or more frames in the decoder - * (depending on the bframes setting). - */ - int pos, len; - int64_t pts = in->s.start; - int64_t dts = in->s.renderOffset; - - if (in->s.new_chap > 0) - { - pv->new_chap = in->s.new_chap; - pv->chap_scr = in->s.scr_sequence; - if (in->s.start != AV_NOPTS_VALUE) - { - pv->chap_time = in->s.start; - } - else - { - pv->chap_time = pv->last_pts + 1; - } - } - if (in->s.start != AV_NOPTS_VALUE) - { - pv->last_pts = in->s.start; - } - - // There are a 3 scenarios that can happen here. - // 1. The buffer contains exactly one frame of data - // 2. The buffer contains multiple frames of data - // 3. The buffer contains a partial frame of data - // - // In scenario 2, we want to be sure that the timestamps are only - // applied to the first frame in the buffer. Additional frames - // in the buffer will have their timestamps computed in sync. - // - // In scenario 3, we need to save the ancillary buffer info of an - // unfinished frame so it can be applied when we receive the last - // buffer of that frame. - if (!pv->unfinished) - { - // New packet, and no previous data pending - pv->packet_info.scr_sequence = in->s.scr_sequence; - pv->packet_info.new_chap = in->s.new_chap; - pv->packet_info.frametype = in->s.frametype; - } - for (pos = 0; pos < in->size; pos += len) - { - uint8_t * pout; - int pout_len; - int64_t parser_pts, parser_dts; - - if (pv->parser) - { - len = av_parser_parse2(pv->parser, pv->context, &pout, &pout_len, - in->data + pos, in->size - pos, - pts, dts, 0 ); - parser_pts = pv->parser->pts; - parser_dts = pv->parser->dts; - pts = AV_NOPTS_VALUE; - dts = AV_NOPTS_VALUE; - } - else - { - pout = in->data; - len = pout_len = in->size; - parser_pts = pts; - parser_dts = dts; - } - - if (pout != NULL && pout_len > 0) - { - pv->packet_info.data = pout; - pv->packet_info.size = pout_len; - pv->packet_info.pts = parser_pts; - pv->packet_info.dts = parser_dts; - - decodeFrame(w, &pv->packet_info); - w->frame_count++; - - // There could have been an unfinished packet when we entered - // decodeVideo that is now finished. The next packet is associated - // with the input buffer, so set it's chapter and scr info. - pv->packet_info.scr_sequence = in->s.scr_sequence; - pv->packet_info.new_chap = in->s.new_chap; - pv->packet_info.frametype = in->s.frametype; - pv->unfinished = 0; - } - if (len > 0 && pout_len <= 0) - { - pv->unfinished = 1; - } - } - - /* the stuff above flushed the parser, now flush the decoder */ - if (in->s.flags & HB_BUF_FLAG_EOF) - { - while (decodeFrame(w, NULL)) - { - continue; - } -#ifdef USE_QSV - if (pv->qsv.decode && - pv->qsv.config.io_pattern == MFX_IOPATTERN_OUT_OPAQUE_MEMORY) - { - // flush a second time - while (decodeFrame(w, NULL)) - { - continue; - } - } -#endif - if (pv->list_subtitle != NULL) - cc_send_to_decoder(pv, hb_buffer_eof_init()); - } -} - static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job ) { @@ -1618,34 +1496,33 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job ) pv->threads = HB_FFMPEG_THREADS_AUTO; } - AVCodec *codec = NULL; - #ifdef USE_QSV if (pv->qsv.decode) { - codec = avcodec_find_decoder_by_name(pv->qsv.codec_name); + pv->codec = avcodec_find_decoder_by_name(pv->qsv.codec_name); } else #endif { - codec = avcodec_find_decoder(w->codec_param); + pv->codec = avcodec_find_decoder(w->codec_param); } - if ( codec == NULL ) + if ( pv->codec == NULL ) { hb_log( "decavcodecvInit: failed to find codec for id (%d)", w->codec_param ); return 1; } + pv->context = avcodec_alloc_context3( pv->codec ); + pv->context->workaround_bugs = FF_BUG_AUTODETECT; + pv->context->err_recognition = AV_EF_CRCCHECK; + pv->context->error_concealment = FF_EC_GUESS_MVS|FF_EC_DEBLOCK; + if ( pv->title->opaque_priv ) { AVFormatContext *ic = (AVFormatContext*)pv->title->opaque_priv; - pv->context = avcodec_alloc_context3(codec); avcodec_parameters_to_context(pv->context, ic->streams[pv->title->video_id]->codecpar); - pv->context->workaround_bugs = FF_BUG_AUTODETECT; - pv->context->err_recognition = AV_EF_CRCCHECK; - pv->context->error_concealment = FF_EC_GUESS_MVS|FF_EC_DEBLOCK; #ifdef USE_QSV if (pv->qsv.decode && @@ -1671,7 +1548,7 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job ) } #endif - if ( hb_avcodec_open( pv->context, codec, &av_opts, pv->threads ) ) + if ( hb_avcodec_open( pv->context, pv->codec, &av_opts, pv->threads ) ) { av_dict_free( &av_opts ); hb_log( "decavcodecvInit: avcodec_open failed" ); @@ -1719,10 +1596,8 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job ) return 0; } -static int setup_extradata( hb_work_object_t *w, hb_buffer_t *in ) +static int setup_extradata( hb_work_private_t * pv ) { - hb_work_private_t *pv = w->private_data; - // we can't call the avstream funcs but the read_header func in the // AVInputFormat may set up some state in the AVContext. In particular // vc1t_read_header allocates 'extradata' to deal with header issues @@ -1738,7 +1613,8 @@ static int setup_extradata( hb_work_object_t *w, hb_buffer_t *in ) else { int size; - size = pv->parser->parser->split(pv->context, in->data, in->size); + size = pv->parser->parser->split(pv->context, pv->packet_info.data, + pv->packet_info.size); if (size > 0) { pv->context->extradata_size = size; @@ -1746,7 +1622,7 @@ static int setup_extradata( hb_work_object_t *w, hb_buffer_t *in ) av_malloc(size + AV_INPUT_BUFFER_PADDING_SIZE); if (pv->context->extradata == NULL) return 1; - memcpy(pv->context->extradata, in->data, size); + memcpy(pv->context->extradata, pv->packet_info.data, size); return 0; } } @@ -1756,73 +1632,23 @@ static int setup_extradata( hb_work_object_t *w, hb_buffer_t *in ) return 0; } -static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in, - hb_buffer_t ** buf_out ) +static int decodePacket( hb_work_object_t * w ) { - hb_work_private_t *pv = w->private_data; - hb_buffer_t *in = *buf_in; - - *buf_in = NULL; - *buf_out = NULL; - - // libavcodec/mpeg12dec.c requires buffers to be zero padded. - // If not zero padded, it can get stuck in an infinite loop. - // It's likely there are other decoders that expect the same. - if (in->data != NULL) - { - memset(in->data + in->size, 0, in->alloc - in->size); - } - - /* if we got an empty buffer signaling end-of-stream send it downstream */ - if (in->s.flags & HB_BUF_FLAG_EOF) - { - if (pv->context != NULL && pv->context->codec != NULL) - { - decodeVideo(w, in); - } - hb_buffer_list_append(&pv->list, in); - *buf_out = hb_buffer_list_clear(&pv->list); - return HB_WORK_DONE; - } + hb_work_private_t * pv = w->private_data; // if this is the first frame open the codec (we have to wait for the // first frame because of M$ VC1 braindamage). if ( !pv->video_codec_opened ) { - - AVCodec *codec = NULL; -#ifdef USE_QSV - if (pv->qsv.decode) - { - codec = avcodec_find_decoder_by_name(pv->qsv.codec_name); - } - else -#endif - { - codec = avcodec_find_decoder(w->codec_param); - } - - if ( codec == NULL ) - { - hb_log( "decavcodecvWork: failed to find codec for id (%d)", w->codec_param ); - *buf_out = hb_buffer_eof_init(); - return HB_WORK_DONE; - } - - if (pv->context == NULL) - { - pv->context = avcodec_alloc_context3( codec ); - } pv->context->workaround_bugs = FF_BUG_AUTODETECT; pv->context->err_recognition = AV_EF_CRCCHECK; pv->context->error_concealment = FF_EC_GUESS_MVS|FF_EC_DEBLOCK; - if ( setup_extradata( w, in ) ) + if (setup_extradata(pv)) { // we didn't find the headers needed to set up extradata. // the codec will abort if we open it so just free the buf // and hope we eventually get the info we need. - hb_buffer_close( &in ); return HB_WORK_OK; } @@ -1843,7 +1669,7 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in, } // disable threaded decoding for scan, can cause crashes - if ( hb_avcodec_open( pv->context, codec, &av_opts, pv->threads ) ) + if ( hb_avcodec_open( pv->context, pv->codec, &av_opts, pv->threads ) ) { av_dict_free( &av_opts ); hb_log( "decavcodecvWork: avcodec_open failed" ); @@ -1851,7 +1677,6 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in, // so try again when this fails av_freep( &pv->context->extradata ); pv->context->extradata_size = 0; - hb_buffer_close( &in ); return HB_WORK_OK; } pv->context->pkt_timebase.num = pv->title->video_timebase.num; @@ -1860,17 +1685,152 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in, pv->video_codec_opened = 1; } + decodeFrame(pv, &pv->packet_info); + + return HB_WORK_OK; +} + +static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_work_private_t * pv = w->private_data; + hb_buffer_t * in = *buf_in; + int result = HB_WORK_OK; + + *buf_out = NULL; + + // libavcodec/mpeg12dec.c requires buffers to be zero padded. + // If not zero padded, it can get stuck in an infinite loop. + // It's likely there are other decoders that expect the same. + if (in->data != NULL) + { + memset(in->data + in->size, 0, in->alloc - in->size); + } if (in->palette != NULL) { pv->palette = in->palette; in->palette = NULL; } - decodeVideo(w, in); - hb_buffer_close( &in ); + + /* if we got an empty buffer signaling end-of-stream send it downstream */ + if (in->s.flags & HB_BUF_FLAG_EOF) + { + if (pv->context != NULL && pv->context->codec != NULL) + { + while (decodeFrame(pv, NULL)) + { + continue; + } + } + hb_buffer_list_append(&pv->list, in); + *buf_out = hb_buffer_list_clear(&pv->list); + return HB_WORK_DONE; + } + + /* + * The following loop is a do..while because we need to handle both + * data & the flush at the end (signaled by size=0). At the end there's + * generally a frame in the parser & one or more frames in the decoder + * (depending on the bframes setting). + */ + int pos, len; + int64_t pts = in->s.start; + int64_t dts = in->s.renderOffset; + + if (in->s.new_chap > 0) + { + pv->new_chap = in->s.new_chap; + pv->chap_scr = in->s.scr_sequence; + if (in->s.start != AV_NOPTS_VALUE) + { + pv->chap_time = in->s.start; + } + else + { + pv->chap_time = pv->last_pts + 1; + } + } + if (in->s.start != AV_NOPTS_VALUE) + { + pv->last_pts = in->s.start; + } + + // There are a 3 scenarios that can happen here. + // 1. The buffer contains exactly one frame of data + // 2. The buffer contains multiple frames of data + // 3. The buffer contains a partial frame of data + // + // In scenario 2, we want to be sure that the timestamps are only + // applied to the first frame in the buffer. Additional frames + // in the buffer will have their timestamps computed in sync. + // + // In scenario 3, we need to save the ancillary buffer info of an + // unfinished frame so it can be applied when we receive the last + // buffer of that frame. + if (!pv->unfinished) + { + // New packet, and no previous data pending + pv->packet_info.scr_sequence = in->s.scr_sequence; + pv->packet_info.new_chap = in->s.new_chap; + pv->packet_info.frametype = in->s.frametype; + } + for (pos = 0; pos < in->size; pos += len) + { + uint8_t * pout; + int pout_len; + int64_t parser_pts, parser_dts; + + if (pv->parser) + { + len = av_parser_parse2(pv->parser, pv->context, &pout, &pout_len, + in->data + pos, in->size - pos, + pts, dts, 0 ); + parser_pts = pv->parser->pts; + parser_dts = pv->parser->dts; + pts = AV_NOPTS_VALUE; + dts = AV_NOPTS_VALUE; + } + else + { + pout = in->data; + len = pout_len = in->size; + parser_pts = pts; + parser_dts = dts; + } + + if (pout != NULL && pout_len > 0) + { + pv->packet_info.data = pout; + pv->packet_info.size = pout_len; + pv->packet_info.pts = parser_pts; + pv->packet_info.dts = parser_dts; + + result = decodePacket(w); + if (result != HB_WORK_OK) + { + break; + } + w->frame_count++; + + // There could have been an unfinished packet that is now finished. + // The next packet is associated with the input buffer, so set + // it's chapter and scr info. + pv->packet_info.scr_sequence = in->s.scr_sequence; + pv->packet_info.new_chap = in->s.new_chap; + pv->packet_info.frametype = in->s.frametype; + pv->unfinished = 0; + } + if (len > 0 && pout_len <= 0) + { + pv->unfinished = 1; + } + } *buf_out = hb_buffer_list_clear(&pv->list); - return HB_WORK_OK; + + return result; } + static void compute_frame_duration( hb_work_private_t *pv ) { double duration = 0.; @@ -2138,6 +2098,7 @@ static void decavcodecvFlush( hb_work_object_t *w ) av_parser_close(pv->parser); } pv->parser = av_parser_init( w->codec_param ); + pv->context = avcodec_alloc_context3( pv->codec ); } else { -- 2.40.0