[FFmpeg-devel] [PATCH v3 3/3] avcodec/v4l2_m2m_dec: start capture after source change event is received
Andriy Gelman
andriy.gelman at gmail.com
Mon Aug 16 20:41:54 EEST 2021
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 at 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.
--
Andriy
More information about the ffmpeg-devel
mailing list