diff mbox series

[FFmpeg-devel,v3,3/3] avcodec/v4l2_m2m_dec: start capture after source change event is received

Message ID 20210729060027.9513-3-ming.qian@nxp.com
State New
Headers show
Series [FFmpeg-devel,v3,1/3] avcodec/v4l2_context: don't reinit output queue when dynamic resolution change | expand

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

Ming Qian July 29, 2021, 6 a.m. UTC
if client start the capture queue without waiting the source change
event,
there may be some timing issues.
For example, in client, the sequence is:
capture streamon -> source change -> capture streamoff -> capture
streamon.
but in driver side, the sequence may be:
source change -> capture streamon -> capture streamoff -> capture
streamon.
Then it may led to some unforeseen and serious problems.
So to avoid such timing issues, the client should setup the
capture queue after the first source change event is received.

Signed-off-by: Ming Qian <ming.qian@nxp.com>
---
 libavcodec/v4l2_context.c |  6 +++++-
 libavcodec/v4l2_m2m.c     |  1 +
 libavcodec/v4l2_m2m.h     |  1 +
 libavcodec/v4l2_m2m_dec.c | 34 +++++++++++++++-------------------
 4 files changed, 22 insertions(+), 20 deletions(-)

Comments

Andriy Gelman Aug. 16, 2021, 5:41 p.m. UTC | #1
On Thu, 29. Jul 14:00, Ming Qian wrote:
> if client start the capture queue without waiting the source change
> event,
> there may be some timing issues.
> For example, in client, the sequence is:
> capture streamon -> source change -> capture streamoff -> capture
> streamon.
> but in driver side, the sequence may be:
> source change -> capture streamon -> capture streamoff -> capture
> streamon.
> Then it may led to some unforeseen and serious problems.
> So to avoid such timing issues, the client should setup the
> capture queue after the first source change event is received.
> 
> Signed-off-by: Ming Qian <ming.qian@nxp.com>
> ---
>  libavcodec/v4l2_context.c |  6 +++++-
>  libavcodec/v4l2_m2m.c     |  1 +
>  libavcodec/v4l2_m2m.h     |  1 +
>  libavcodec/v4l2_m2m_dec.c | 34 +++++++++++++++-------------------
>  4 files changed, 22 insertions(+), 20 deletions(-)
> 
> diff --git a/libavcodec/v4l2_context.c b/libavcodec/v4l2_context.c
> index df41e982fc56..543fc9523cf1 100644
> --- a/libavcodec/v4l2_context.c
> +++ b/libavcodec/v4l2_context.c
> @@ -179,6 +179,7 @@ static int v4l2_handle_event(V4L2Context *ctx)
>      if (evt.type != V4L2_EVENT_SOURCE_CHANGE)
>          return 0;
>  
> +    s->source_change_cnt++;
>      ret = ioctl(s->fd, VIDIOC_G_FMT, &cap_fmt);
>      if (ret) {
>          av_log(logger(ctx), AV_LOG_ERROR, "%s VIDIOC_G_FMT\n", s->capture.name);
> @@ -272,7 +273,7 @@ static V4L2Buffer* v4l2_dequeue_v4l2buf(V4L2Context *ctx, int timeout)
>      }
>  
>      /* if we are draining and there are no more capture buffers queued in the driver we are done */
> -    if (!V4L2_TYPE_IS_OUTPUT(ctx->type) && ctx_to_m2mctx(ctx)->draining) {
> +    if (!V4L2_TYPE_IS_OUTPUT(ctx->type) && ctx_to_m2mctx(ctx)->draining && ctx->streamon) {
>          for (i = 0; i < ctx->num_buffers; i++) {
>              /* capture buffer initialization happens during decode hence
>               * detection happens at runtime
> @@ -542,6 +543,9 @@ int ff_v4l2_context_set_status(V4L2Context* ctx, uint32_t cmd)
>      int type = ctx->type;
>      int ret;
>  
> +    if (ctx->streamon == (cmd == VIDIOC_STREAMON))
> +        return 0;
> +
>      ret = ioctl(ctx_to_m2mctx(ctx)->fd, cmd, &type);
>      if (ret < 0)
>          return AVERROR(errno);
> diff --git a/libavcodec/v4l2_m2m.c b/libavcodec/v4l2_m2m.c
> index cdfd579810f2..e2d5d8c968a9 100644
> --- a/libavcodec/v4l2_m2m.c
> +++ b/libavcodec/v4l2_m2m.c
> @@ -422,6 +422,7 @@ int ff_v4l2_m2m_create_context(V4L2m2mPriv *priv, V4L2m2mContext **s)
>      priv->context->output.num_buffers  = priv->num_output_buffers;
>      priv->context->self_ref = priv->context_ref;
>      priv->context->fd = -1;
> +    priv->context->source_change_cnt = 0;
>  
>      priv->context->frame = av_frame_alloc();
>      if (!priv->context->frame) {
> diff --git a/libavcodec/v4l2_m2m.h b/libavcodec/v4l2_m2m.h
> index b67b21633109..72f1a579f1ca 100644
> --- a/libavcodec/v4l2_m2m.h
> +++ b/libavcodec/v4l2_m2m.h
> @@ -53,6 +53,7 @@ typedef struct V4L2m2mContext {
>      sem_t refsync;
>      atomic_uint refcount;
>      int reinit;
> +    int source_change_cnt;
>  
>      /* null frame/packet received */
>      int draining;
> diff --git a/libavcodec/v4l2_m2m_dec.c b/libavcodec/v4l2_m2m_dec.c
> index 224eb3d5e7be..60eb39deef88 100644
> --- a/libavcodec/v4l2_m2m_dec.c
> +++ b/libavcodec/v4l2_m2m_dec.c
> @@ -51,7 +51,7 @@ static int v4l2_try_start(AVCodecContext *avctx)
>          }
>      }
>  
> -    if (capture->streamon)
> +    if (capture->streamon || !s->source_change_cnt)
>          return 0;
>  
>      /* 2. get the capture format */
> @@ -146,28 +146,24 @@ static int v4l2_receive_frame(AVCodecContext *avctx, AVFrame *frame)
>              return ret;
>      }
>  
> -    if (s->draining)
> -        goto dequeue;
> -
> -    ret = ff_v4l2_context_enqueue_packet(output, &s->buf_pkt);
> -    if (ret < 0 && ret != AVERROR(EAGAIN))
> -        goto fail;
> -
> -    /* if EAGAIN don't unref packet and try to enqueue in the next iteration */
> -    if (ret != AVERROR(EAGAIN))
> -        av_packet_unref(&s->buf_pkt);
> -
>      if (!s->draining) {
> -        ret = v4l2_try_start(avctx);
> -        if (ret) {
> -            /* cant recover */
> -            if (ret != AVERROR(ENOMEM))
> -                ret = 0;
> +        ret = ff_v4l2_context_enqueue_packet(output, &s->buf_pkt);
> +        if (ret < 0 && ret != AVERROR(EAGAIN))
>              goto fail;
> -        }
> +
> +        /* if EAGAIN don't unref packet and try to enqueue in the next iteration */
> +        if (ret != AVERROR(EAGAIN))
> +            av_packet_unref(&s->buf_pkt);
> +    }
> +
> +    ret = v4l2_try_start(avctx);
> +    if (ret) {
> +        /* can't recover */
> +        if (ret != AVERROR(ENOMEM))
> +            ret = 0;
> +        goto fail;
>      }
>  
> -dequeue:
>      return ff_v4l2_context_dequeue_frame(capture, frame, -1);
>  fail:
>      av_packet_unref(&s->buf_pkt);
> -- 
> 2.32.0
> 

I tested with a sample (RPi4) where the resolution change event is not
triggered at the start, so I think the capture queue is not initialized at all following this
change. Will share with you the sample.
diff mbox series

Patch

diff --git a/libavcodec/v4l2_context.c b/libavcodec/v4l2_context.c
index df41e982fc56..543fc9523cf1 100644
--- a/libavcodec/v4l2_context.c
+++ b/libavcodec/v4l2_context.c
@@ -179,6 +179,7 @@  static int v4l2_handle_event(V4L2Context *ctx)
     if (evt.type != V4L2_EVENT_SOURCE_CHANGE)
         return 0;
 
+    s->source_change_cnt++;
     ret = ioctl(s->fd, VIDIOC_G_FMT, &cap_fmt);
     if (ret) {
         av_log(logger(ctx), AV_LOG_ERROR, "%s VIDIOC_G_FMT\n", s->capture.name);
@@ -272,7 +273,7 @@  static V4L2Buffer* v4l2_dequeue_v4l2buf(V4L2Context *ctx, int timeout)
     }
 
     /* if we are draining and there are no more capture buffers queued in the driver we are done */
-    if (!V4L2_TYPE_IS_OUTPUT(ctx->type) && ctx_to_m2mctx(ctx)->draining) {
+    if (!V4L2_TYPE_IS_OUTPUT(ctx->type) && ctx_to_m2mctx(ctx)->draining && ctx->streamon) {
         for (i = 0; i < ctx->num_buffers; i++) {
             /* capture buffer initialization happens during decode hence
              * detection happens at runtime
@@ -542,6 +543,9 @@  int ff_v4l2_context_set_status(V4L2Context* ctx, uint32_t cmd)
     int type = ctx->type;
     int ret;
 
+    if (ctx->streamon == (cmd == VIDIOC_STREAMON))
+        return 0;
+
     ret = ioctl(ctx_to_m2mctx(ctx)->fd, cmd, &type);
     if (ret < 0)
         return AVERROR(errno);
diff --git a/libavcodec/v4l2_m2m.c b/libavcodec/v4l2_m2m.c
index cdfd579810f2..e2d5d8c968a9 100644
--- a/libavcodec/v4l2_m2m.c
+++ b/libavcodec/v4l2_m2m.c
@@ -422,6 +422,7 @@  int ff_v4l2_m2m_create_context(V4L2m2mPriv *priv, V4L2m2mContext **s)
     priv->context->output.num_buffers  = priv->num_output_buffers;
     priv->context->self_ref = priv->context_ref;
     priv->context->fd = -1;
+    priv->context->source_change_cnt = 0;
 
     priv->context->frame = av_frame_alloc();
     if (!priv->context->frame) {
diff --git a/libavcodec/v4l2_m2m.h b/libavcodec/v4l2_m2m.h
index b67b21633109..72f1a579f1ca 100644
--- a/libavcodec/v4l2_m2m.h
+++ b/libavcodec/v4l2_m2m.h
@@ -53,6 +53,7 @@  typedef struct V4L2m2mContext {
     sem_t refsync;
     atomic_uint refcount;
     int reinit;
+    int source_change_cnt;
 
     /* null frame/packet received */
     int draining;
diff --git a/libavcodec/v4l2_m2m_dec.c b/libavcodec/v4l2_m2m_dec.c
index 224eb3d5e7be..60eb39deef88 100644
--- a/libavcodec/v4l2_m2m_dec.c
+++ b/libavcodec/v4l2_m2m_dec.c
@@ -51,7 +51,7 @@  static int v4l2_try_start(AVCodecContext *avctx)
         }
     }
 
-    if (capture->streamon)
+    if (capture->streamon || !s->source_change_cnt)
         return 0;
 
     /* 2. get the capture format */
@@ -146,28 +146,24 @@  static int v4l2_receive_frame(AVCodecContext *avctx, AVFrame *frame)
             return ret;
     }
 
-    if (s->draining)
-        goto dequeue;
-
-    ret = ff_v4l2_context_enqueue_packet(output, &s->buf_pkt);
-    if (ret < 0 && ret != AVERROR(EAGAIN))
-        goto fail;
-
-    /* if EAGAIN don't unref packet and try to enqueue in the next iteration */
-    if (ret != AVERROR(EAGAIN))
-        av_packet_unref(&s->buf_pkt);
-
     if (!s->draining) {
-        ret = v4l2_try_start(avctx);
-        if (ret) {
-            /* cant recover */
-            if (ret != AVERROR(ENOMEM))
-                ret = 0;
+        ret = ff_v4l2_context_enqueue_packet(output, &s->buf_pkt);
+        if (ret < 0 && ret != AVERROR(EAGAIN))
             goto fail;
-        }
+
+        /* if EAGAIN don't unref packet and try to enqueue in the next iteration */
+        if (ret != AVERROR(EAGAIN))
+            av_packet_unref(&s->buf_pkt);
+    }
+
+    ret = v4l2_try_start(avctx);
+    if (ret) {
+        /* can't recover */
+        if (ret != AVERROR(ENOMEM))
+            ret = 0;
+        goto fail;
     }
 
-dequeue:
     return ff_v4l2_context_dequeue_frame(capture, frame, -1);
 fail:
     av_packet_unref(&s->buf_pkt);