From cc167f7e55dafdeeca7ac9622331db8d8f6cb463 Mon Sep 17 00:00:00 2001 From: Ivan Uskov Date: Tue, 4 Aug 2015 06:40:06 -0400 Subject: libavcodec/qsvdec.c: correct handling of dynamic frame size changing has been implemented Signed-off-by: Michael Niedermayer --- libavcodec/qsvdec.c | 145 ++++++++++++++++++++++++++++++++++++++++++++-------- libavcodec/qsvdec.h | 11 ++++ 2 files changed, 134 insertions(+), 22 deletions(-) (limited to 'libavcodec') diff --git a/libavcodec/qsvdec.c b/libavcodec/qsvdec.c index 1062ef096a..51ad2f7090 100644 --- a/libavcodec/qsvdec.c +++ b/libavcodec/qsvdec.c @@ -146,10 +146,17 @@ int ff_qsv_decode_init(AVCodecContext *avctx, QSVContext *q, AVPacket *avpkt) return AVERROR(ENOMEM); } - q->input_fifo = av_fifo_alloc(1024*16); - if (!q->input_fifo) - return AVERROR(ENOMEM); + if (!q->input_fifo) { + q->input_fifo = av_fifo_alloc(1024*16); + if (!q->input_fifo) + return AVERROR(ENOMEM); + } + if (!q->pkt_fifo) { + q->pkt_fifo = av_fifo_alloc( sizeof(AVPacket) * (1 + 16) ); + if (!q->pkt_fifo) + return AVERROR(ENOMEM); + } q->engine_ready = 1; return 0; @@ -281,7 +288,26 @@ static void qsv_fifo_relocate(AVFifoBuffer *f, int bytes_to_free) f->rndx = 0; } -int ff_qsv_decode(AVCodecContext *avctx, QSVContext *q, + +static void close_decoder(QSVContext *q) +{ + QSVFrame *cur; + + MFXVideoDECODE_Close(q->session); + + cur = q->work_frames; + while (cur) { + q->work_frames = cur->next; + av_frame_free(&cur->frame); + av_freep(&cur); + cur = q->work_frames; + } + + q->engine_ready = 0; + q->reinit_pending = 0; +} + +static int do_qsv_decode(AVCodecContext *avctx, QSVContext *q, AVFrame *frame, int *got_frame, AVPacket *avpkt) { @@ -293,6 +319,7 @@ int ff_qsv_decode(AVCodecContext *avctx, QSVContext *q, int ret; int n_out_frames; int buffered = 0; + int flush = !avpkt->size || q->reinit_pending; if (!q->engine_ready) { ret = ff_qsv_decode_init(avctx, q, avpkt); @@ -300,7 +327,7 @@ int ff_qsv_decode(AVCodecContext *avctx, QSVContext *q, return ret; } - if (avpkt->size ) { + if (!flush) { if (av_fifo_size(q->input_fifo)) { /* we have got rest of previous packet into buffer */ if (av_fifo_space(q->input_fifo) < avpkt->size) { @@ -325,7 +352,7 @@ int ff_qsv_decode(AVCodecContext *avctx, QSVContext *q, if (ret < 0) return ret; do { - ret = MFXVideoDECODE_DecodeFrameAsync(q->session, avpkt->size ? &bs : NULL, + ret = MFXVideoDECODE_DecodeFrameAsync(q->session, flush ? NULL : &bs, insurf, &outsurf, &sync); if (ret != MFX_WRN_DEVICE_BUSY) break; @@ -333,7 +360,11 @@ int ff_qsv_decode(AVCodecContext *avctx, QSVContext *q, } while (1); if (MFX_WRN_VIDEO_PARAM_CHANGED==ret) { - /* TODO: handle here sequence header changing */ + /* TODO: handle here minor sequence header changing */ + } else if (MFX_ERR_INCOMPATIBLE_VIDEO_PARAM==ret) { + av_fifo_reset(q->input_fifo); + flush = q->reinit_pending = 1; + continue; } if (sync) { @@ -357,7 +388,7 @@ int ff_qsv_decode(AVCodecContext *avctx, QSVContext *q, /* make sure we do not enter an infinite loop if the SDK * did not consume any data and did not return anything */ - if (!sync && !bs.DataOffset) { + if (!sync && !bs.DataOffset && !flush) { av_log(avctx, AV_LOG_WARNING, "A decode call did not consume any data\n"); bs.DataOffset = avpkt->size; } @@ -376,7 +407,7 @@ int ff_qsv_decode(AVCodecContext *avctx, QSVContext *q, } n_out_frames = av_fifo_size(q->async_fifo) / (sizeof(out_frame)+sizeof(sync)); - if (n_out_frames > q->async_depth || (!avpkt->size && n_out_frames) ) { + if (n_out_frames > q->async_depth || (flush && n_out_frames) ) { AVFrame *src_frame; av_fifo_generic_read(q->async_fifo, &out_frame, sizeof(out_frame), NULL); @@ -409,30 +440,100 @@ int ff_qsv_decode(AVCodecContext *avctx, QSVContext *q, return avpkt->size; } +/* + This function inserts a packet at fifo front. +*/ +static void qsv_packet_push_front(QSVContext *q, AVPacket *avpkt) +{ + int fifo_size = av_fifo_size(q->pkt_fifo); + if (!fifo_size) { + /* easy case fifo is empty */ + av_fifo_generic_write(q->pkt_fifo, avpkt, sizeof(*avpkt), NULL); + } else { + /* realloc necessary */ + AVPacket pkt; + AVFifoBuffer *fifo = av_fifo_alloc(fifo_size+av_fifo_space(q->pkt_fifo)); -int ff_qsv_decode_close(QSVContext *q) + av_fifo_generic_write(fifo, avpkt, sizeof(*avpkt), NULL); + + while (av_fifo_size(q->pkt_fifo)) { + av_fifo_generic_read(q->pkt_fifo, &pkt, sizeof(pkt), NULL); + av_fifo_generic_write(fifo, &pkt, sizeof(pkt), NULL); + } + av_fifo_free(q->pkt_fifo); + q->pkt_fifo = fifo; + } +} +int ff_qsv_decode(AVCodecContext *avctx, QSVContext *q, + AVFrame *frame, int *got_frame, + AVPacket *avpkt) { - QSVFrame *cur = q->work_frames; + AVPacket pkt_ref = { 0 }; + int ret = 0; - while (cur) { - q->work_frames = cur->next; - av_frame_free(&cur->frame); - av_freep(&cur); - cur = q->work_frames; + if (q->pkt_fifo && av_fifo_size(q->pkt_fifo) >= sizeof(AVPacket)) { + /* we already have got some buffered packets. so add new to tail */ + ret = av_packet_ref(&pkt_ref, avpkt); + if (ret < 0) + return ret; + av_fifo_generic_write(q->pkt_fifo, &pkt_ref, sizeof(pkt_ref), NULL); } + if (q->reinit_pending) { + ret = do_qsv_decode(avctx, q, frame, got_frame, avpkt); - av_fifo_free(q->async_fifo); - q->async_fifo = NULL; + if (!*got_frame) { + /* Flushing complete, no more frames */ + close_decoder(q); + //return ff_qsv_decode(avctx, q, frame, got_frame, avpkt); + } + } + if (!q->reinit_pending) { + if (q->pkt_fifo && av_fifo_size(q->pkt_fifo) >= sizeof(AVPacket)) { + /* process buffered packets */ + while (!*got_frame && av_fifo_size(q->pkt_fifo) >= sizeof(AVPacket)) { + av_fifo_generic_read(q->pkt_fifo, &pkt_ref, sizeof(pkt_ref), NULL); + ret = do_qsv_decode(avctx, q, frame, got_frame, &pkt_ref); + if (q->reinit_pending) { + /* + A rare case: new reinit pending when buffering existing. + We should to return the pkt_ref back to same place of fifo + */ + qsv_packet_push_front(q, &pkt_ref); + } else { + av_packet_unref(&pkt_ref); + } + } + } else { + /* general decoding */ + ret = do_qsv_decode(avctx, q, frame, got_frame, avpkt); + if (q->reinit_pending) { + ret = av_packet_ref(&pkt_ref, avpkt); + if (ret < 0) + return ret; + av_fifo_generic_write(q->pkt_fifo, &pkt_ref, sizeof(pkt_ref), NULL); + } + } + } - av_fifo_free(q->input_fifo); - q->input_fifo = NULL; + return ret; +} + +int ff_qsv_decode_close(QSVContext *q) +{ + close_decoder(q); - MFXVideoDECODE_Close(q->session); q->session = NULL; ff_qsv_close_internal_session(&q->internal_qs); - q->engine_ready = 0; + av_fifo_free(q->async_fifo); + q->async_fifo = NULL; + + av_fifo_free(q->input_fifo); + q->input_fifo = NULL; + + av_fifo_free(q->pkt_fifo); + q->pkt_fifo = NULL; return 0; } diff --git a/libavcodec/qsvdec.h b/libavcodec/qsvdec.h index c30627a4bd..5211fb2ee8 100644 --- a/libavcodec/qsvdec.h +++ b/libavcodec/qsvdec.h @@ -51,10 +51,21 @@ typedef struct QSVContext { AVFifoBuffer *async_fifo; AVFifoBuffer *input_fifo; + // we should to buffer input packets at some cases + // else it is not possible to handle dynamic stream changes correctly + // this fifo uses for input packets buffering + AVFifoBuffer *pkt_fifo; + // this flag indicates that header parsed, // decoder instance created and ready to general decoding int engine_ready; + // we can not just re-init decoder if different sequence header arrived + // we should to deliver all buffered frames but we can not decode new packets + // this time. So when reinit_pending is non-zero we flushing decoder and + // accumulate new arrived packets into pkt_fifo + int reinit_pending; + // options set by the caller int async_depth; int iopattern; -- cgit v1.2.3