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

Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
This commit is contained in:
Ivan Uskov 2015-07-24 06:26:14 -04:00 committed by Michael Niedermayer
parent 5da90d7ec3
commit c90dbc67ed
2 changed files with 108 additions and 32 deletions

View File

@ -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;

View File

@ -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