Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
From: Anton Khirnov <anton@khirnov.net>
To: ffmpeg-devel@ffmpeg.org
Subject: [FFmpeg-devel] [PATCH 2/2] lavc/vp9: fix the race in exporting video enc params
Date: Tue, 15 Mar 2022 11:49:32 +0100
Message-ID: <20220315104932.25496-2-anton@khirnov.net> (raw)
In-Reply-To: <20220315104932.25496-1-anton@khirnov.net>

The parameters are currently attached directly to the decoded frame
after it is decoded. This is racy, since the frame is shared with other
threads, as reported e.g. by the fate-vp9-encparams test in TSAN build.

Instead, write the params to a refcounted AVBuffer. Each output frame
then gets its own reference to this buffer, which should be thread-safe.
---
 libavcodec/vp9.c | 93 ++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 83 insertions(+), 10 deletions(-)

diff --git a/libavcodec/vp9.c b/libavcodec/vp9.c
index 7ef10f7a80..f9df898733 100644
--- a/libavcodec/vp9.c
+++ b/libavcodec/vp9.c
@@ -41,6 +41,10 @@
 
 #define VP9_SYNCCODE 0x498342
 
+typedef struct VP9TFPriv {
+    AVBufferRef *enc_par;
+} VP9TFPriv;
+
 #if HAVE_THREADS
 DEFINE_OFFSET_ARRAY(VP9Context, vp9_context, pthread_init_cnt,
                     (offsetof(VP9Context, progress_mutex)),
@@ -100,6 +104,13 @@ static void vp9_frame_unref(AVCodecContext *avctx, VP9Frame *f)
     f->hwaccel_picture_private = NULL;
 }
 
+static void vp9_tf_priv_free(void *opaque, uint8_t *data)
+{
+    VP9TFPriv *p = (VP9TFPriv*)data;
+    av_buffer_unref(&p->enc_par);
+    av_freep(&data);
+}
+
 static int vp9_frame_alloc(AVCodecContext *avctx, VP9Frame *f)
 {
     VP9Context *s = avctx->priv_data;
@@ -139,6 +150,19 @@ static int vp9_frame_alloc(AVCodecContext *avctx, VP9Frame *f)
         }
     }
 
+    if (avctx->export_side_data & AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS) {
+        VP9TFPriv *p = av_mallocz(sizeof(*p));
+        if (!p)
+            goto fail;
+
+        f->tf.priv_buf = av_buffer_create((uint8_t*)p, sizeof(*p), vp9_tf_priv_free,
+                                          NULL, AV_BUFFER_FLAG_READONLY);
+        if (!f->tf.priv_buf) {
+            av_freep(&p);
+            goto fail;
+        }
+    }
+
     return 0;
 
 fail:
@@ -1497,7 +1521,9 @@ int loopfilter_proc(AVCodecContext *avctx)
 
 static int vp9_export_enc_params(VP9Context *s, VP9Frame *frame)
 {
+    VP9TFPriv *p = (VP9TFPriv*)frame->tf.priv_buf->data;
     AVVideoEncParams *par;
+    size_t par_size;
     unsigned int tile, nb_blocks = 0;
 
     if (s->s.h.segmentation.enabled) {
@@ -1505,11 +1531,16 @@ static int vp9_export_enc_params(VP9Context *s, VP9Frame *frame)
             nb_blocks += s->td[tile].nb_block_structure;
     }
 
-    par = av_video_enc_params_create_side_data(frame->tf.f,
-        AV_VIDEO_ENC_PARAMS_VP9, nb_blocks);
+    par = av_video_enc_params_alloc(AV_VIDEO_ENC_PARAMS_VP9, nb_blocks, &par_size);
     if (!par)
         return AVERROR(ENOMEM);
 
+    p->enc_par = av_buffer_create((uint8_t *)par, par_size, NULL, NULL, 0);
+    if (!p->enc_par) {
+        av_freep(&par);
+        return AVERROR(ENOMEM);
+    }
+
     par->qp             = s->s.h.yac_qi;
     par->delta_qp[0][0] = s->s.h.ydc_qdelta;
     par->delta_qp[1][0] = s->s.h.uvdc_qdelta;
@@ -1547,6 +1578,38 @@ static int vp9_export_enc_params(VP9Context *s, VP9Frame *frame)
     return 0;
 }
 
+static int vp9_output_frame(AVFrame *dst, const ThreadFrame *src)
+{
+    int ret = 0;
+
+    ret = av_frame_ref(dst, src->f);
+    if (ret < 0)
+        return ret;
+
+    if (src->priv_buf) {
+        VP9TFPriv *p = (VP9TFPriv*)src->priv_buf->data;
+
+        if (p->enc_par) {
+            AVBufferRef *buf = av_buffer_ref(p->enc_par);
+            if (!buf) {
+                ret = AVERROR(ENOMEM);
+                goto finish;
+            }
+
+            if (!av_frame_new_side_data_from_buf(dst, AV_FRAME_DATA_VIDEO_ENC_PARAMS, buf)) {
+                av_buffer_unref(&buf);
+                ret = AVERROR(ENOMEM);
+                goto finish;
+            }
+        }
+    }
+
+finish:
+    if (ret < 0)
+        av_frame_unref(dst);
+    return ret;
+}
+
 static int vp9_decode_frame(AVCodecContext *avctx, void *frame,
                             int *got_frame, AVPacket *pkt)
 {
@@ -1561,11 +1624,14 @@ static int vp9_decode_frame(AVCodecContext *avctx, void *frame,
     if ((ret = decode_frame_header(avctx, data, size, &ref)) < 0) {
         return ret;
     } else if (ret == 0) {
-        if (!s->s.refs[ref].f->buf[0]) {
+        ThreadFrame *tf = &s->s.refs[ref];
+        if (!tf->f->buf[0]) {
             av_log(avctx, AV_LOG_ERROR, "Requested reference %d not available\n", ref);
             return AVERROR_INVALIDDATA;
         }
-        if ((ret = av_frame_ref(frame, s->s.refs[ref].f)) < 0)
+        ff_thread_await_progress(tf, INT_MAX, 0);
+        ret = vp9_output_frame(frame, tf);
+        if (ret < 0)
             return ret;
         ((AVFrame *)frame)->pts = pkt->pts;
         ((AVFrame *)frame)->pkt_dts = pkt->dts;
@@ -1743,6 +1809,17 @@ static int vp9_decode_frame(AVCodecContext *avctx, void *frame,
             ff_thread_finish_setup(avctx);
         }
     } while (s->pass++ == 1);
+
+    /* this must be done before report_progress(INT_MAX), since the exported
+     * data may be read in other threads */
+    if (avctx->export_side_data & AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS) {
+        ret = vp9_export_enc_params(s, &s->s.frames[CUR_FRAME]);
+        if (ret < 0) {
+            ff_thread_report_progress(&s->s.frames[CUR_FRAME].tf, INT_MAX, 0);
+            return ret;
+        }
+    }
+
     ff_thread_report_progress(&s->s.frames[CUR_FRAME].tf, INT_MAX, 0);
 
     if (s->td->error_info < 0) {
@@ -1750,11 +1827,6 @@ static int vp9_decode_frame(AVCodecContext *avctx, void *frame,
         s->td->error_info = 0;
         return AVERROR_INVALIDDATA;
     }
-    if (avctx->export_side_data & AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS) {
-        ret = vp9_export_enc_params(s, &s->s.frames[CUR_FRAME]);
-        if (ret < 0)
-            return ret;
-    }
 
 finish:
     // ref frame setup
@@ -1767,7 +1839,8 @@ finish:
     }
 
     if (!s->s.h.invisible) {
-        if ((ret = av_frame_ref(frame, s->s.frames[CUR_FRAME].tf.f)) < 0)
+        ret = vp9_output_frame(frame, &s->s.frames[CUR_FRAME].tf);
+        if (ret < 0)
             return ret;
         *got_frame = 1;
     }
-- 
2.34.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

  reply	other threads:[~2022-03-15 10:49 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-15 10:49 [FFmpeg-devel] [PATCH 1/2] lavc/threadframe: allow decoders to attach buffers to ThreadFrame Anton Khirnov
2022-03-15 10:49 ` Anton Khirnov [this message]
2022-03-15 13:59 ` Andreas Rheinhardt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220315104932.25496-2-anton@khirnov.net \
    --to=anton@khirnov.net \
    --cc=ffmpeg-devel@ffmpeg.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git