Commit c90dbc67 authored by Ivan Uskov's avatar Ivan Uskov Committed by Michael Niedermayer

libavcodec/qsvdec.c: The ff_qsv_decode() now guarantees the consumption of whole packet.

Signed-off-by: 's avatarMichael Niedermayer <michael@niedermayer.cc>
parent 5da90d7e
......@@ -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);
if (MFX_WRN_VIDEO_PARAM_CHANGED==ret) {
/* TODO: handle here sequence header changing */
}
} while (ret == MFX_WRN_DEVICE_BUSY || ret == MFX_ERR_MORE_SURFACE);
if (sync) {
QSVFrame *out_frame = find_frame(q, outsurf);
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 (!out_frame) {
av_log(avctx, AV_LOG_ERROR,
"The returned surface does not correspond to any frame\n");
return AVERROR_BUG;
}
if (sync) {
QSVFrame *out_frame = find_frame(q, outsurf);
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 (!out_frame) {
av_log(avctx, AV_LOG_ERROR,
"The returned surface does not correspond to any frame\n");
return AVERROR_BUG;
continue;
}
if (MFX_ERR_MORE_SURFACE != ret && ret < 0)
break;
}
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 (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 (!av_fifo_space(q->async_fifo) ||
(!avpkt->size && av_fifo_size(q->async_fifo))) {
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;
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment