summaryrefslogtreecommitdiff
path: root/libavcodec
diff options
context:
space:
mode:
authorIvan Uskov <ivan.uskov@nablet.com>2015-08-04 06:40:06 -0400
committerMichael Niedermayer <michael@niedermayer.cc>2015-09-07 22:04:02 +0200
commitcc167f7e55dafdeeca7ac9622331db8d8f6cb463 (patch)
tree415efdf769f298e11791555e67f382205d0b17c2 /libavcodec
parentef359e724d990abb3cb6a5199e738232260eee1b (diff)
libavcodec/qsvdec.c: correct handling of dynamic frame size changing has been implemented
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
Diffstat (limited to 'libavcodec')
-rw-r--r--libavcodec/qsvdec.c145
-rw-r--r--libavcodec/qsvdec.h11
2 files changed, 134 insertions, 22 deletions
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;