[FFmpeg-devel] [PATCH] avcodec/v4l2_m2m: fix draining process (dequeue without input)

Jorge Ramirez-Ortiz jorge.ramirez-ortiz at linaro.org
Wed Sep 27 02:22:23 EEST 2017


Stopping the codec when no more input is available causes captured
buffers that might be ready to be dequeued to be invalidated.

This commit follows the V4L2 API more closely:
1. on the last valid input buffer, it sets the V4L2_BUF_FLAG_LAST.
2. ffmpeg then will continue dequeuing captured buffers until EPIPE is
raised by the driver (EOF condition)

This also has the advantage of making the timeout on dequeuing capture
buffers while draining unnecessary.
---
 libavcodec/v4l2_context.c | 162 ++++++++++++++++++----------------------------
 1 file changed, 64 insertions(+), 98 deletions(-)

diff --git a/libavcodec/v4l2_context.c b/libavcodec/v4l2_context.c
index d675c55..f06a7ff 100644
--- a/libavcodec/v4l2_context.c
+++ b/libavcodec/v4l2_context.c
@@ -189,40 +189,6 @@ reinit_run:
     return 1;
 }
 
-static int v4l2_stop_decode(V4L2Context *ctx)
-{
-    struct v4l2_decoder_cmd cmd = {
-        .cmd = V4L2_DEC_CMD_STOP,
-    };
-    int ret;
-
-    ret = ioctl(ctx_to_m2mctx(ctx)->fd, VIDIOC_DECODER_CMD, &cmd);
-    if (ret) {
-        /* DECODER_CMD is optional */
-        if (errno == ENOTTY)
-            return ff_v4l2_context_set_status(ctx, VIDIOC_STREAMOFF);
-    }
-
-    return 0;
-}
-
-static int v4l2_stop_encode(V4L2Context *ctx)
-{
-    struct v4l2_encoder_cmd cmd = {
-        .cmd = V4L2_ENC_CMD_STOP,
-    };
-    int ret;
-
-    ret = ioctl(ctx_to_m2mctx(ctx)->fd, VIDIOC_ENCODER_CMD, &cmd);
-    if (ret) {
-        /* ENCODER_CMD is optional */
-        if (errno == ENOTTY)
-            return ff_v4l2_context_set_status(ctx, VIDIOC_STREAMOFF);
-    }
-
-    return 0;
-}
-
 static V4L2Buffer* v4l2_dequeue_v4l2buf(V4L2Context *ctx, int timeout)
 {
     struct v4l2_plane planes[VIDEO_MAX_PLANES];
@@ -236,6 +202,13 @@ static V4L2Buffer* v4l2_dequeue_v4l2buf(V4L2Context *ctx, int timeout)
 
     if (V4L2_TYPE_IS_OUTPUT(ctx->type))
         pfd.events =  POLLOUT | POLLWRNORM;
+    else {
+        /* on the capture queue */
+        if (ctx_to_m2mctx(ctx)->draining) {
+            /* ignore driver requests for more input and just wait for a valid frame */
+            pfd.events = POLLIN | POLLRDNORM | POLLPRI;
+        }
+    }
 
     for (;;) {
         ret = poll(&pfd, 1, timeout);
@@ -243,50 +216,45 @@ static V4L2Buffer* v4l2_dequeue_v4l2buf(V4L2Context *ctx, int timeout)
             break;
         if (errno == EINTR)
             continue;
-
-        /* timeout is being used to indicate last valid bufer when draining */
-        if (ctx_to_m2mctx(ctx)->draining)
-            ctx->done = 1;
-
         return NULL;
     }
 
-    /* 0. handle errors */
+    /* handle errors */
     if (pfd.revents & POLLERR) {
         av_log(logger(ctx), AV_LOG_WARNING, "%s POLLERR\n", ctx->name);
         return NULL;
     }
 
-    /* 1. handle resolution changes */
+    /* handle resolution changes */
     if (pfd.revents & POLLPRI) {
         ret = v4l2_handle_event(ctx);
         if (ret < 0) {
             /* if re-init failed, abort */
-            ctx->done = EINVAL;
+            ctx->done = 1;
             return NULL;
         }
         if (ret) {
             /* if re-init was successfull drop the buffer (if there was one)
-             * since we had to reconfigure capture (unmap all buffers)
-             */
+             * since we had to reconfigure capture (unmap all buffers) */
             return NULL;
         }
     }
 
-    /* 2. dequeue the buffer */
+    /* dequeue the buffer or provide more input */
     if (pfd.revents & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM)) {
 
-        if (!V4L2_TYPE_IS_OUTPUT(ctx->type)) {
-            /* there is a capture buffer ready */
-            if (pfd.revents & (POLLIN | POLLRDNORM))
-                goto dequeue;
+        if (V4L2_TYPE_IS_OUTPUT(ctx->type))
+            goto dequeue;
 
-            /* the driver is ready to accept more input; instead of waiting for the capture
-             * buffer to complete we return NULL so input can proceed (we are single threaded)
-             */
-            if (pfd.revents & (POLLOUT | POLLWRNORM))
-                return NULL;
-        }
+        /* there is a capture buffer ready */
+        if (pfd.revents & (POLLIN | POLLRDNORM))
+            goto dequeue;
+
+        /* the driver is ready to accept more input: instead of waiting for
+         * the capture buffer to complete, return NULL so input can proceed
+         * (we are single threaded after all) */
+        if (pfd.revents & (POLLOUT | POLLWRNORM))
+            return NULL;
 
 dequeue:
         memset(&buf, 0, sizeof(buf));
@@ -301,23 +269,25 @@ dequeue:
         ret = ioctl(ctx_to_m2mctx(ctx)->fd, VIDIOC_DQBUF, &buf);
         if (ret) {
             if (errno != EAGAIN) {
-                ctx->done = errno;
+                ctx->done = 1;
                 if (errno != EPIPE)
-                    av_log(logger(ctx), AV_LOG_DEBUG, "%s VIDIOC_DQBUF, errno (%s)\n",
+                    av_log(logger(ctx), AV_LOG_ERROR, "%s VIDIOC_DQBUF, errno (%s)\n",
                         ctx->name, av_err2str(AVERROR(errno)));
             }
-        } else {
-            avbuf = &ctx->buffers[buf.index];
-            avbuf->status = V4L2BUF_AVAILABLE;
-            avbuf->buf = buf;
-            if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) {
-                memcpy(avbuf->planes, planes, sizeof(planes));
-                avbuf->buf.m.planes = avbuf->planes;
-            }
+            return NULL;
         }
+
+        avbuf = &ctx->buffers[buf.index];
+        avbuf->status = V4L2BUF_AVAILABLE;
+        avbuf->buf = buf;
+        if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) {
+            memcpy(avbuf->planes, planes, sizeof(planes));
+            avbuf->buf.m.planes = avbuf->planes;
+        }
+        return avbuf;
     }
 
-    return avbuf;
+    return NULL;
 }
 
 static V4L2Buffer* v4l2_getfree_v4l2buf(V4L2Context *ctx)
@@ -476,50 +446,50 @@ int ff_v4l2_context_set_status(V4L2Context* ctx, int cmd)
 
 int ff_v4l2_context_enqueue_frame(V4L2Context* ctx, const AVFrame* frame)
 {
-    V4L2m2mContext *s = ctx_to_m2mctx(ctx);
     V4L2Buffer* avbuf;
     int ret;
 
-    if (!frame) {
-        ret = v4l2_stop_encode(ctx);
-        if (ret)
-            av_log(logger(ctx), AV_LOG_ERROR, "%s stop_encode\n", ctx->name);
-        s->draining= 1;
-        return 0;
-    }
-
     avbuf = v4l2_getfree_v4l2buf(ctx);
     if (!avbuf)
         return AVERROR(ENOMEM);
 
-    ret = ff_v4l2_buffer_avframe_to_buf(frame, avbuf);
-    if (ret)
-        return ret;
+    if (!frame) {
+        /* flag that we are draining */
+        ctx_to_m2mctx(ctx)->draining = 1;
+
+        /* send EOS */
+        avbuf->buf.m.planes[0].bytesused = 1;
+        avbuf->flags |= V4L2_BUF_FLAG_LAST;
+    } else {
+        ret = ff_v4l2_buffer_avframe_to_buf(frame, avbuf);
+        if (ret)
+            return ret;
+    }
 
     return ff_v4l2_buffer_enqueue(avbuf);
 }
 
 int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt)
 {
-    V4L2m2mContext *s = ctx_to_m2mctx(ctx);
     V4L2Buffer* avbuf;
     int ret;
 
-    if (!pkt->size) {
-        ret = v4l2_stop_decode(ctx);
-        if (ret)
-            av_log(logger(ctx), AV_LOG_ERROR, "%s stop_decode\n", ctx->name);
-        s->draining = 1;
-        return 0;
-    }
-
     avbuf = v4l2_getfree_v4l2buf(ctx);
     if (!avbuf)
         return AVERROR(ENOMEM);
 
-    ret = ff_v4l2_buffer_avpkt_to_buf(pkt, avbuf);
-    if (ret)
-        return ret;
+    if (!pkt->size) {
+        /* flag that we are draining */
+        ctx_to_m2mctx(ctx)->draining = 1;
+
+        /* send EOS */
+        avbuf->buf.m.planes[0].bytesused = 1;
+        avbuf->flags |= V4L2_BUF_FLAG_LAST;
+    } else {
+        ret = ff_v4l2_buffer_avpkt_to_buf(pkt, avbuf);
+        if (ret)
+            return ret;
+    }
 
     return ff_v4l2_buffer_enqueue(avbuf);
 }
@@ -528,14 +498,12 @@ int ff_v4l2_context_dequeue_frame(V4L2Context* ctx, AVFrame* frame)
 {
     V4L2Buffer* avbuf = NULL;
 
-    /* if we are draining, we are no longer inputing data, therefore enable a
-     * timeout so we can dequeue and flag the last valid buffer.
-     *
+    /*
      * blocks until:
      *  1. decoded frame available
      *  2. an input buffer is ready to be dequeued
      */
-    avbuf = v4l2_dequeue_v4l2buf(ctx, ctx_to_m2mctx(ctx)->draining ? 200 : -1);
+    avbuf = v4l2_dequeue_v4l2buf(ctx, -1);
     if (!avbuf) {
         if (ctx->done)
             return AVERROR_EOF;
@@ -550,14 +518,12 @@ int ff_v4l2_context_dequeue_packet(V4L2Context* ctx, AVPacket* pkt)
 {
     V4L2Buffer* avbuf = NULL;
 
-    /* if we are draining, we are no longer inputing data, therefore enable a
-     * timeout so we can dequeue and flag the last valid buffer.
-     *
+    /*
      * blocks until:
      *  1. encoded packet available
      *  2. an input buffer ready to be dequeued
      */
-    avbuf = v4l2_dequeue_v4l2buf(ctx, ctx_to_m2mctx(ctx)->draining ? 200 : -1);
+    avbuf = v4l2_dequeue_v4l2buf(ctx, -1);
     if (!avbuf) {
         if (ctx->done)
             return AVERROR_EOF;
-- 
2.7.4



More information about the ffmpeg-devel mailing list