[FFmpeg-devel] [PATCHv2] avcodec/v4l2_m2m: fix draining process (dequeue without input)
Jorge Ramirez-Ortiz
jorge.ramirez-ortiz at linaro.org
Wed Sep 27 20:27:44 EEST 2017
Stopping the codec when no more input is available causes captured
buffers that might be ready to be invalidated.
This commit follows the V4L2 API more closely:
1. when ffmpeg indicates EOS (NULL frame or packet size), the
v4l2_m2m codec will send a dummy buffer to the driver with the
V4L2_BUF_FLAG_LAST flag.
2. the v4l2_m2m codec will then continue dequeuing captured buffers
until EPIPE is raised by the driver (v4l2 EOF condition)
This makes the current timeout on dequeuing capture buffers while
draining unnecessary.
---
libavcodec/v4l2_buffers.c | 18 +++++++
libavcodec/v4l2_context.c | 132 ++++++++++++++--------------------------------
2 files changed, 58 insertions(+), 92 deletions(-)
diff --git a/libavcodec/v4l2_buffers.c b/libavcodec/v4l2_buffers.c
index ef7d040..26e7b12 100644
--- a/libavcodec/v4l2_buffers.c
+++ b/libavcodec/v4l2_buffers.c
@@ -255,6 +255,18 @@ static int v4l2_bufref_to_buf(V4L2Buffer *out, int plane, const uint8_t* data, i
return 0;
}
+static int v4l2_buffer_set_eos(V4L2Buffer* avbuf)
+{
+ /* flag that we are draining to the v4l2_m2m context */
+ buf_to_m2mctx(avbuf)->draining = 1;
+
+ /* flag to the v4l2 driver - either of these two conditions (use both to be safe on old drivers) */
+ avbuf->buf.m.planes[0].bytesused = 0;
+ avbuf->flags |= V4L2_BUF_FLAG_LAST;
+
+ return 0;
+}
+
/******************************************************************************
*
* V4L2uffer interface
@@ -265,6 +277,9 @@ int ff_v4l2_buffer_avframe_to_buf(const AVFrame *frame, V4L2Buffer* out)
{
int i, ret;
+ if (!frame)
+ return v4l2_buffer_set_eos(out);
+
for(i = 0; i < out->num_planes; i++) {
ret = v4l2_bufref_to_buf(out, i, frame->buf[i]->data, frame->buf[i]->size, frame->buf[i]);
if (ret)
@@ -356,6 +371,9 @@ int ff_v4l2_buffer_avpkt_to_buf(const AVPacket *pkt, V4L2Buffer *out)
{
int ret;
+ if (!pkt->size)
+ return v4l2_buffer_set_eos(out);
+
ret = v4l2_bufref_to_buf(out, 0, pkt->data, pkt->size, pkt->buf);
if (ret)
return ret;
diff --git a/libavcodec/v4l2_context.c b/libavcodec/v4l2_context.c
index d675c55..ef3d263 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,18 +446,9 @@ 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);
@@ -501,18 +462,9 @@ int ff_v4l2_context_enqueue_frame(V4L2Context* ctx, const AVFrame* frame)
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);
@@ -528,14 +480,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 +500,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