qsvdec: properly handle asynchronous decoding
authorAnton Khirnov <anton@khirnov.net>
Tue, 14 Jul 2015 16:16:26 +0000 (18:16 +0200)
committerAnton Khirnov <anton@khirnov.net>
Sun, 19 Jul 2015 07:47:45 +0000 (09:47 +0200)
Wait for async_depth frames before syncing.

libavcodec/qsv_internal.h
libavcodec/qsvdec.c
libavcodec/qsvdec.h

index a0f4c7c..949f6eb 100644 (file)
@@ -40,6 +40,8 @@ typedef struct QSVFrame {
 
     mfxFrameSurface1 surface_internal;
 
+    int queued;
+
     struct QSVFrame *next;
 } QSVFrame;
 
index 6fd9442..da9b082 100644 (file)
@@ -73,6 +73,11 @@ int ff_qsv_decode_init(AVCodecContext *avctx, QSVContext *q, mfxSession session)
     mfxVideoParam param = { { 0 } };
     int ret;
 
+    q->async_fifo = av_fifo_alloc((1 + q->async_depth) *
+                                  (sizeof(mfxSyncPoint) + sizeof(QSVFrame*)));
+    if (!q->async_fifo)
+        return AVERROR(ENOMEM);
+
     ret = qsv_init_session(avctx, q, session);
     if (ret < 0) {
         av_log(avctx, AV_LOG_ERROR, "Error initializing an MFX session\n");
@@ -142,7 +147,7 @@ static void qsv_clear_unused_frames(QSVContext *q)
 {
     QSVFrame *cur = q->work_frames;
     while (cur) {
-        if (cur->surface && !cur->surface->Data.Locked) {
+        if (cur->surface && !cur->surface->Data.Locked && !cur->queued) {
             cur->surface = NULL;
             av_frame_unref(cur->frame);
         }
@@ -191,12 +196,12 @@ static int get_surface(AVCodecContext *avctx, QSVContext *q, mfxFrameSurface1 **
     return 0;
 }
 
-static AVFrame *find_frame(QSVContext *q, mfxFrameSurface1 *surf)
+static QSVFrame *find_frame(QSVContext *q, mfxFrameSurface1 *surf)
 {
     QSVFrame *cur = q->work_frames;
     while (cur) {
         if (surf == cur->surface)
-            return cur->frame;
+            return cur;
         cur = cur->next;
     }
     return NULL;
@@ -206,6 +211,7 @@ int ff_qsv_decode(AVCodecContext *avctx, QSVContext *q,
                   AVFrame *frame, int *got_frame,
                   AVPacket *avpkt)
 {
+    QSVFrame *out_frame;
     mfxFrameSurface1 *insurf;
     mfxFrameSurface1 *outsurf;
     mfxSyncPoint sync;
@@ -240,21 +246,37 @@ int ff_qsv_decode(AVCodecContext *avctx, QSVContext *q,
     }
 
     if (sync) {
-        AVFrame *src_frame;
-
-        MFXVideoCORE_SyncOperation(q->session, sync, 60000);
+        QSVFrame *out_frame = find_frame(q, outsurf);
 
-        src_frame = find_frame(q, outsurf);
-        if (!src_frame) {
+        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);
+    }
+
+    if (!av_fifo_space(q->async_fifo) ||
+        (!avpkt->size && av_fifo_size(q->async_fifo))) {
+        AVFrame *src_frame;
+
+        av_fifo_generic_read(q->async_fifo, &out_frame, sizeof(out_frame), NULL);
+        av_fifo_generic_read(q->async_fifo, &sync,      sizeof(sync),      NULL);
+        out_frame->queued = 0;
+
+        MFXVideoCORE_SyncOperation(q->session, sync, 60000);
+
+        src_frame = out_frame->frame;
+
         ret = av_frame_ref(frame, src_frame);
         if (ret < 0)
             return ret;
 
+        outsurf = out_frame->surface;
+
         frame->pkt_pts = frame->pts = outsurf->Data.TimeStamp;
 
         frame->repeat_pict =
@@ -283,6 +305,9 @@ int ff_qsv_decode_close(QSVContext *q)
         cur = q->work_frames;
     }
 
+    av_fifo_free(q->async_fifo);
+    q->async_fifo = NULL;
+
     if (q->internal_session)
         MFXClose(q->internal_session);
 
index 486a7e3..01f7690 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <mfx/mfxvideo.h>
 
+#include "libavutil/fifo.h"
 #include "libavutil/frame.h"
 #include "libavutil/pixfmt.h"
 
@@ -47,6 +48,8 @@ typedef struct QSVContext {
      */
     QSVFrame *work_frames;
 
+    AVFifoBuffer *async_fifo;
+
     // options set by the caller
     int async_depth;
     int iopattern;