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".
next prev parent 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