From c90dbc67ed6b849cc3e7f24d83cef3d015f2c3d0 Mon Sep 17 00:00:00 2001 From: Ivan Uskov Date: Fri, 24 Jul 2015 06:26:14 -0400 Subject: [PATCH] libavcodec/qsvdec.c: The ff_qsv_decode() now guarantees the consumption of whole packet. Signed-off-by: Michael Niedermayer --- libavcodec/qsvdec.c | 139 ++++++++++++++++++++++++++++++++++---------- libavcodec/qsvdec.h | 1 + 2 files changed, 108 insertions(+), 32 deletions(-) diff --git a/libavcodec/qsvdec.c b/libavcodec/qsvdec.c index 4e7a0ac271..9d69c1afe9 100644 --- a/libavcodec/qsvdec.c +++ b/libavcodec/qsvdec.c @@ -121,11 +121,19 @@ int ff_qsv_decode_init(AVCodecContext *avctx, QSVContext *q, AVPacket *avpkt) avctx->width = param.mfx.FrameInfo.CropW - param.mfx.FrameInfo.CropX; avctx->height = param.mfx.FrameInfo.CropH - param.mfx.FrameInfo.CropY; - q->async_fifo = av_fifo_alloc((1 + q->async_depth) * + /* maximum decoder latency should be not exceed max DPB size for h.264 and + HEVC which is 16 for both cases. + So weare pre-allocating fifo big enough for 17 elements: + */ + q->async_fifo = av_fifo_alloc((1 + 16) * (sizeof(mfxSyncPoint) + sizeof(QSVFrame*))); if (!q->async_fifo) return AVERROR(ENOMEM); + q->input_fifo = av_fifo_alloc(1024*16); + if (!q->input_fifo) + return AVERROR(ENOMEM); + q->engine_ready = 1; return 0; @@ -223,6 +231,40 @@ static QSVFrame *find_frame(QSVContext *q, mfxFrameSurface1 *surf) return NULL; } +/* This function uses for 'smart' releasing of consumed data + from the input bitstream fifo. + Since the input fifo mapped to mfxBitstream which does not understand + a wrapping of data over fifo end, we should also to relocate a possible + data rest to fifo begin. If rest of data is absent then we just reset fifo's + pointers to initial positions. + NOTE the case when fifo does contain unconsumed data is rare and typical + amount of such data is 1..4 bytes. +*/ +static void qsv_fifo_relocate(AVFifoBuffer *f, int bytes_to_free) +{ + int data_size; + int data_rest = 0; + + av_fifo_drain(f, bytes_to_free); + + data_size = av_fifo_size(f); + if (data_size > 0) { + if (f->buffer!=f->rptr) { + if ( (f->end - f->rptr) < data_size) { + data_rest = data_size - (f->end - f->rptr); + data_size-=data_rest; + memmove(f->buffer+data_size, f->buffer, data_rest); + } + memmove(f->buffer, f->rptr, data_size); + data_size+= data_rest; + } + } + f->rptr = f->buffer; + f->wptr = f->buffer + data_size; + f->wndx = data_size; + f->rndx = 0; +} + int ff_qsv_decode(AVCodecContext *avctx, QSVContext *q, AVFrame *frame, int *got_frame, AVPacket *avpkt) @@ -233,55 +275,85 @@ int ff_qsv_decode(AVCodecContext *avctx, QSVContext *q, mfxSyncPoint sync; mfxBitstream bs = { { { 0 } } }; int ret; + int n_out_frames; + int buffered = 0; if (!q->engine_ready) { ret = ff_qsv_decode_init(avctx, q, avpkt); if (ret) return ret; } - if (avpkt->size) { - bs.Data = avpkt->data; - bs.DataLength = avpkt->size; + + if (avpkt->size ) { + 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) { + ret = av_fifo_grow(q->input_fifo, avpkt->size); + if (ret < 0) + return ret; + } + av_fifo_generic_write(q->input_fifo, avpkt->data, avpkt->size, NULL); + bs.Data = q->input_fifo->rptr; + bs.DataLength = av_fifo_size(q->input_fifo); + buffered = 1; + } else { + bs.Data = avpkt->data; + bs.DataLength = avpkt->size; + } bs.MaxLength = bs.DataLength; bs.TimeStamp = avpkt->pts; } - do { + while (1) { ret = get_surface(avctx, q, &insurf); if (ret < 0) return ret; - - ret = MFXVideoDECODE_DecodeFrameAsync(q->session, avpkt->size ? &bs : NULL, - insurf, &outsurf, &sync); - if (ret == MFX_WRN_DEVICE_BUSY) + do { + ret = MFXVideoDECODE_DecodeFrameAsync(q->session, avpkt->size ? &bs : NULL, + insurf, &outsurf, &sync); + if (ret != MFX_WRN_DEVICE_BUSY) + break; av_usleep(1); + } while (1); - } while (ret == MFX_WRN_DEVICE_BUSY || ret == MFX_ERR_MORE_SURFACE); - - if (ret != MFX_ERR_NONE && - ret != MFX_ERR_MORE_DATA && - ret != MFX_WRN_VIDEO_PARAM_CHANGED && - ret != MFX_ERR_MORE_SURFACE) { - av_log(avctx, AV_LOG_ERROR, "Error during QSV decoding.\n"); - return ff_qsv_error(ret); - } - - if (sync) { - QSVFrame *out_frame = find_frame(q, outsurf); - - if (!out_frame) { - av_log(avctx, AV_LOG_ERROR, - "The returned surface does not correspond to any frame\n"); - return AVERROR_BUG; + if (MFX_WRN_VIDEO_PARAM_CHANGED==ret) { + /* TODO: handle here sequence header changing */ } - out_frame->queued = 1; - av_fifo_generic_write(q->async_fifo, &out_frame, sizeof(out_frame), NULL); - av_fifo_generic_write(q->async_fifo, &sync, sizeof(sync), NULL); + if (sync) { + QSVFrame *out_frame = find_frame(q, outsurf); + + if (!out_frame) { + av_log(avctx, AV_LOG_ERROR, + "The returned surface does not correspond to any frame\n"); + return AVERROR_BUG; + } + + out_frame->queued = 1; + av_fifo_generic_write(q->async_fifo, &out_frame, sizeof(out_frame), NULL); + av_fifo_generic_write(q->async_fifo, &sync, sizeof(sync), NULL); + + continue; + } + if (MFX_ERR_MORE_SURFACE != ret && ret < 0) + break; } - if (!av_fifo_space(q->async_fifo) || - (!avpkt->size && av_fifo_size(q->async_fifo))) { + if (buffered) { + qsv_fifo_relocate(q->input_fifo, bs.DataOffset); + } else if (bs.DataOffset!=avpkt->size) { + /* some data of packet was not consumed. store it to local buffer */ + av_fifo_generic_write(q->input_fifo, avpkt->data+bs.DataOffset, + avpkt->size - bs.DataOffset, NULL); + } + + if (MFX_ERR_MORE_DATA!=ret && ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error %d during QSV decoding.\n", ret); + return ff_qsv_error(ret); + } + 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) ) { AVFrame *src_frame; av_fifo_generic_read(q->async_fifo, &out_frame, sizeof(out_frame), NULL); @@ -312,7 +384,7 @@ int ff_qsv_decode(AVCodecContext *avctx, QSVContext *q, *got_frame = 1; } - return bs.DataOffset; + return avpkt->size; } int ff_qsv_decode_close(QSVContext *q) @@ -329,6 +401,9 @@ int ff_qsv_decode_close(QSVContext *q) av_fifo_free(q->async_fifo); q->async_fifo = NULL; + av_fifo_free(q->input_fifo); + q->input_fifo = NULL; + MFXVideoDECODE_Close(q->session); q->session = NULL; diff --git a/libavcodec/qsvdec.h b/libavcodec/qsvdec.h index a4971ad9d0..2fadacbff8 100644 --- a/libavcodec/qsvdec.h +++ b/libavcodec/qsvdec.h @@ -49,6 +49,7 @@ typedef struct QSVContext { QSVFrame *work_frames; AVFifoBuffer *async_fifo; + AVFifoBuffer *input_fifo; // this flag indicates that header parsed, // decoder instance created and ready to general decoding