From: Anton Khirnov <anton@khirnov.net> To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 27/39] lavc/ffv1: change FFV1SliceContext.plane into a RefStruct object Date: Tue, 16 Jul 2024 19:11:42 +0200 Message-ID: <20240716171155.31838-27-anton@khirnov.net> (raw) In-Reply-To: <20240716171155.31838-1-anton@khirnov.net> Frame threading in the FFV1 decoder works in a very unusual way - the state that needs to be propagated from the previous frame is not decoded pixels(¹), but each slice's entropy coder state after decoding the slice. For that purpose, the decoder's update_thread_context() callback stores a pointer to the previous frame thread's private data. Then, when decoding each slice, the frame thread uses the standard progress mechanism to wait for the corresponding slice in the previous frame to be completed, then copies the entropy coder state from the previously-stored pointer. This approach is highly dubious, as update_thread_context() should be the only point where frame-thread contexts come into direct contact. There are no guarantees that the stored pointer will be valid at all, or will contain any particular data after update_thread_context() finishes. More specifically, this code can break due to the fact that keyframes reset entropy coder state and thus do not need to wait for the previous frame. As an example, consider a decoder process with 2 frame threads - thread 0 with its context 0, and thread 1 with context 1 - decoding a previous frame P, current frame F, followed by a keyframe K. Then consider concurrent execution consistent with the following sequence of events: * thread 0 starts decoding P * thread 0 reads P's slice header, then calls ff_thread_finish_setup() allowing next frame thread to start * main thread calls update_thread_context() to transfer state from context 0 to context 1; context 1 stores a pointer to context 0's private data * thread 1 starts decoding F * thread 1 reads F's slice header, then calls ff_thread_finish_setup() allowing the next frame thread to start decoding * thread 0 finishes decoding P * thread 0 starts decoding K; since K is a keyframe, it does not wait for F and reallocates the arrays holding entropy coder state * thread 0 finishes decoding K * thread 1 reads entropy coder state from its stored pointer to context 0, however it finds state from K rather than from P This execution is currently prevented by special-casing FFV1 in the generic frame threading code, however that is supremely ugly. It also involves unnecessary copies of the state arrays, when in fact they can only be used by one thread at a time. This commit addresses these deficiencies by changing the array of PlaneContext (each of which contains the allocated state arrays) embedded in FFV1SliceContext into a RefStruct object. This object can then be propagated across frame threads in standard manner. Since the code structure guarantees only one thread accesses it at a time, no copies are necessary. It is also re-created for keyframes, solving the above issue cleanly. Special-casing of FFV1 in the generic frame threading code will be removed in a later commit. (¹) except in the case of a damaged slice, when previous frame's pixels are used directly --- libavcodec/ffv1.c | 30 ++++++++++++++++++++++++------ libavcodec/ffv1.h | 4 +++- libavcodec/ffv1dec.c | 33 +++++++++------------------------ 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/libavcodec/ffv1.c b/libavcodec/ffv1.c index 07cf5564cc..9c219b5ddb 100644 --- a/libavcodec/ffv1.c +++ b/libavcodec/ffv1.c @@ -31,6 +31,7 @@ #include "avcodec.h" #include "ffv1.h" +#include "refstruct.h" av_cold int ff_ffv1_common_init(AVCodecContext *avctx) { @@ -52,6 +53,24 @@ av_cold int ff_ffv1_common_init(AVCodecContext *avctx) return 0; } +static void planes_free(FFRefStructOpaque opaque, void *obj) +{ + PlaneContext *planes = obj; + + for (int i = 0; i < MAX_PLANES; i++) { + PlaneContext *p = &planes[i]; + + av_freep(&p->state); + av_freep(&p->vlc_state); + } +} + +PlaneContext* ff_ffv1_planes_alloc(void) +{ + return ff_refstruct_alloc_ext(sizeof(PlaneContext) * MAX_PLANES, + 0, NULL, planes_free); +} + av_cold int ff_ffv1_init_slice_state(const FFV1Context *f, FFV1SliceContext *sc) { @@ -132,6 +151,10 @@ av_cold int ff_ffv1_init_slice_contexts(FFV1Context *f) sizeof(*sc->sample_buffer32)); if (!sc->sample_buffer || !sc->sample_buffer32) return AVERROR(ENOMEM); + + sc->plane = ff_ffv1_planes_alloc(); + if (!sc->plane) + return AVERROR(ENOMEM); } return 0; @@ -188,12 +211,7 @@ av_cold int ff_ffv1_close(AVCodecContext *avctx) av_freep(&sc->sample_buffer); av_freep(&sc->sample_buffer32); - for (i = 0; i < s->plane_count; i++) { - PlaneContext *p = &sc->plane[i]; - - av_freep(&p->state); - av_freep(&p->vlc_state); - } + ff_refstruct_unref(&sc->plane); } av_freep(&avctx->stats_out); diff --git a/libavcodec/ffv1.h b/libavcodec/ffv1.h index 9d79219921..edc3f6aef0 100644 --- a/libavcodec/ffv1.h +++ b/libavcodec/ffv1.h @@ -81,7 +81,8 @@ typedef struct FFV1SliceContext { int slice_rct_by_coef; int slice_rct_ry_coef; - PlaneContext plane[MAX_PLANES]; + // RefStruct reference, array of MAX_PLANES elements + PlaneContext *plane; PutBitContext pb; RangeCoder c; @@ -153,6 +154,7 @@ int ff_ffv1_common_init(AVCodecContext *avctx); int ff_ffv1_init_slice_state(const FFV1Context *f, FFV1SliceContext *sc); int ff_ffv1_init_slices_state(FFV1Context *f); int ff_ffv1_init_slice_contexts(FFV1Context *f); +PlaneContext *ff_ffv1_planes_alloc(void); int ff_ffv1_allocate_initial_states(FFV1Context *f); void ff_ffv1_clear_slice_state(const FFV1Context *f, FFV1SliceContext *sc); int ff_ffv1_close(AVCodecContext *avctx); diff --git a/libavcodec/ffv1dec.c b/libavcodec/ffv1dec.c index be4a1a2bf3..7dc4a537a9 100644 --- a/libavcodec/ffv1dec.c +++ b/libavcodec/ffv1dec.c @@ -38,6 +38,7 @@ #include "mathops.h" #include "ffv1.h" #include "progressframe.h" +#include "refstruct.h" #include "thread.h" static inline av_flatten int get_symbol_inline(RangeCoder *c, uint8_t *state, @@ -265,30 +266,11 @@ static int decode_slice(AVCodecContext *c, void *arg) if (f->fsrc && !(p->flags & AV_FRAME_FLAG_KEY) && f->last_picture.f) ff_progress_frame_await(&f->last_picture, si); - if(f->fsrc && !(p->flags & AV_FRAME_FLAG_KEY)) { + if (f->fsrc) { const FFV1SliceContext *scsrc = &f->fsrc->slices[si]; if (!(p->flags & AV_FRAME_FLAG_KEY)) sc->slice_damaged |= scsrc->slice_damaged; - - for (int i = 0; i < f->plane_count; i++) { - const PlaneContext *psrc = &scsrc->plane[i]; - PlaneContext *pdst = &sc->plane[i]; - - av_free(pdst->state); - av_free(pdst->vlc_state); - memcpy(pdst, psrc, sizeof(*pdst)); - pdst->state = NULL; - pdst->vlc_state = NULL; - - if (f->ac) { - pdst->state = av_malloc_array(CONTEXT_SIZE, psrc->context_count); - memcpy(pdst->state, psrc->state, CONTEXT_SIZE * psrc->context_count); - } else { - pdst->vlc_state = av_malloc_array(sizeof(*pdst->vlc_state), psrc->context_count); - memcpy(pdst->vlc_state, psrc->vlc_state, sizeof(*pdst->vlc_state) * psrc->context_count); - } - } } sc->slice_rct_by_coef = 1; @@ -812,6 +794,11 @@ static int read_header(FFV1Context *f) && (unsigned)sc->slice_y + (uint64_t)sc->slice_height <= f->height); } + ff_refstruct_unref(&sc->plane); + sc->plane = ff_ffv1_planes_alloc(); + if (!sc->plane) + return AVERROR(ENOMEM); + for (int i = 0; i < f->plane_count; i++) { PlaneContext *const p = &sc->plane[i]; @@ -828,10 +815,6 @@ static int read_header(FFV1Context *f) if (f->version <= 2) { av_assert0(context_count >= 0); - if (p->context_count < context_count) { - av_freep(&p->state); - av_freep(&p->vlc_state); - } p->context_count = context_count; } } @@ -1060,6 +1043,8 @@ static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src) sc->slice_damaged = sc0->slice_damaged; + ff_refstruct_replace(&sc->plane, sc0->plane); + if (fsrc->version < 3) { sc->slice_x = sc0->slice_x; sc->slice_y = sc0->slice_y; -- 2.43.0 _______________________________________________ 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:[~2024-07-16 18:15 UTC|newest] Thread overview: 110+ messages / expand[flat|nested] mbox.gz Atom feed top 2024-07-16 17:11 [FFmpeg-devel] [PATCH 01/39] tests/fate/vcodec: add vsynth tests for FFV1 version 2 Anton Khirnov 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 02/39] lavc/ffv1dec: declare loop variables in the loop where possible Anton Khirnov 2024-07-24 18:22 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 03/39] lavc/ffv1dec: simplify slice index calculation Anton Khirnov 2024-07-24 18:24 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 04/39] lavc/ffv1dec: drop FFV1Context.cur Anton Khirnov 2024-07-24 18:27 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 05/39] lavc/ffv1dec: drop a pointless variable in decode_slice() Anton Khirnov 2024-07-24 18:58 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 06/39] lavc/ffv1dec: move copy_fields() under HAVE_THREADS Anton Khirnov 2024-07-24 18:58 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 07/39] lavc/ffv1: add a per-slice context Anton Khirnov 2024-07-24 19:01 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 08/39] lavc/ffv1: move sample_buffer to the " Anton Khirnov 2024-07-24 19:04 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 09/39] lavc/ffv1: move run_index " Anton Khirnov 2024-07-17 22:49 ` Michael Niedermayer 2024-07-18 15:36 ` Anton Khirnov 2024-07-18 17:41 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 10/39] lavc/ffv1dec: move the bitreader to stack Anton Khirnov 2024-07-17 22:42 ` Michael Niedermayer 2024-07-18 9:08 ` Anton Khirnov 2024-07-18 14:48 ` Michael Niedermayer 2024-07-18 15:31 ` Anton Khirnov 2024-07-18 15:35 ` Paul B Mahol 2024-07-18 18:18 ` Michael Niedermayer 2024-07-20 12:15 ` Anton Khirnov 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 11/39] lavc/ffv1enc: move bit writer to per-slice context Anton Khirnov 2024-07-24 19:07 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 12/39] lavc/ffv1: drop redundant FFV1Context.quant_table Anton Khirnov 2024-07-17 22:37 ` Michael Niedermayer 2024-07-17 23:24 ` James Almer 2024-07-18 8:22 ` Anton Khirnov 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 13/39] lavc/ffv1: drop redundant PlaneContext.quant_table Anton Khirnov 2024-07-17 22:32 ` Michael Niedermayer 2024-07-18 8:20 ` Anton Khirnov 2024-07-18 14:31 ` Michael Niedermayer 2024-07-18 15:14 ` Anton Khirnov 2024-07-18 17:03 ` Michael Niedermayer 2024-07-18 15:31 ` Paul B Mahol 2024-07-18 15:43 ` Anton Khirnov 2024-07-18 15:47 ` Paul B Mahol 2024-07-18 17:40 ` Michael Niedermayer 2024-07-20 9:22 ` Anton Khirnov 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 14/39] lavc/ffv1: drop write-only PlaneContext.interlace_bit_state Anton Khirnov 2024-07-24 19:12 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 15/39] lavc/ffv1: always use the main context values of plane_count/transparency Anton Khirnov 2024-07-24 19:15 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 16/39] lavc/ffv1: move FFV1Context.slice_{coding_mode, rct_.y_coef} to per-slice context Anton Khirnov 2024-07-24 19:16 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 17/39] lavc/ffv1: always use the main context values of ac Anton Khirnov 2024-07-24 19:23 ` Michael Niedermayer 2024-07-31 8:33 ` [FFmpeg-devel] [PATCH v2 " Anton Khirnov 2024-07-31 12:20 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 18/39] lavc/ffv1: move FFV1Context.plane to per-slice context Anton Khirnov 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 19/39] lavc/ffv1: move RangeCoder " Anton Khirnov 2024-07-24 19:28 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 20/39] lavc/ffv1enc: store per-slice rc_stat(2?) in FFV1SliceContext Anton Khirnov 2024-07-24 19:30 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 21/39] lavc/ffv1: move ac_byte_count to per-slice context Anton Khirnov 2024-07-24 19:31 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 22/39] lavc/ffv1enc: stop using per-slice FFV1Context Anton Khirnov 2024-07-24 19:42 ` Michael Niedermayer 2024-07-31 8:50 ` [FFmpeg-devel] [PATCH v2 " Anton Khirnov 2024-07-31 12:32 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 23/39] lavc/ffv1dec: move slice_reset_contexts to per-slice context Anton Khirnov 2024-07-24 19:44 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 24/39] lavc/ffv1dec: move slice_damaged " Anton Khirnov 2024-07-24 19:45 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 25/39] lavc/ffv1dec: stop using per-slice FFV1Context Anton Khirnov 2024-07-24 19:48 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 26/39] lavc/ffv1dec: inline copy_fields() into update_thread_context() Anton Khirnov 2024-07-24 19:48 ` Michael Niedermayer 2024-07-16 17:11 ` Anton Khirnov [this message] 2024-07-24 19:53 ` [FFmpeg-devel] [PATCH 27/39] lavc/ffv1: change FFV1SliceContext.plane into a RefStruct object Michael Niedermayer 2024-08-01 8:17 ` Anton Khirnov 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 28/39] lavc/ffv1dec: fix races in accessing FFV1SliceContext.slice_damaged Anton Khirnov 2024-07-17 20:51 ` Michael Niedermayer 2024-07-22 9:43 ` [FFmpeg-devel] [PATCH 1/3] lavc/ffv1dec: drop code handling AV_PIX_FMT_FLAG_PAL Anton Khirnov 2024-07-22 9:43 ` [FFmpeg-devel] [PATCH 2/3] lavc/ffv1: move damage handling code to decode_slice() Anton Khirnov 2024-07-22 21:14 ` Michael Niedermayer 2024-07-23 6:52 ` Anton Khirnov 2024-07-23 20:14 ` Michael Niedermayer 2024-07-23 21:02 ` Anton Khirnov 2024-07-22 9:43 ` [FFmpeg-devel] [PATCH 3/3] lavc/ffv1dec: fix races in accessing FFV1SliceContext.slice_damaged Anton Khirnov 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 29/39] lavc/thread: move generic-layer API to avcodec_internal.h Anton Khirnov 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 30/39] lavc/internal: document the precise meaning of AVCodecInternal.draining Anton Khirnov 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 31/39] lavc/decode: wrap AV_FRAME_FLAG_DISCARD handling in a loop Anton Khirnov 2024-07-17 21:20 ` Michael Niedermayer 2024-07-18 8:14 ` Anton Khirnov 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 32/39] lavc/decode: reindent Anton Khirnov 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 33/39] lavc: convert frame threading to the receive_frame() pattern Anton Khirnov 2024-07-24 18:44 ` Michael Niedermayer 2024-07-31 11:26 ` Anton Khirnov 2024-07-31 12:59 ` Michael Niedermayer 2024-08-01 14:33 ` [FFmpeg-devel] [PATCH] lavc/ffv1dec: fix races in accessing FFV1SliceContext.slice_damaged Anton Khirnov 2024-08-06 4:39 ` Anton Khirnov 2024-08-09 21:26 ` Michael Niedermayer 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 34/39] lavc/decode: reindent after previous commit Anton Khirnov 2024-08-12 12:49 ` Anton Khirnov 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 35/39] lavc/hevcdec: switch to receive_frame() Anton Khirnov 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 36/39] lavc: add private container FIFO API Anton Khirnov 2024-08-10 0:09 ` Andreas Rheinhardt 2024-08-12 12:14 ` Anton Khirnov 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 37/39] lavc/hevcdec: use a ContainerFifo to hold frames scheduled for output Anton Khirnov 2024-08-09 23:52 ` Andreas Rheinhardt 2024-08-12 12:28 ` Anton Khirnov 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 38/39] lavc/hevcdec: simplify output logic Anton Khirnov 2024-07-16 17:11 ` [FFmpeg-devel] [PATCH 39/39] lavc/hevcdec: call ff_thread_finish_setup() even if hwaccel is in use Anton Khirnov 2024-07-24 18:20 ` [FFmpeg-devel] [PATCH 01/39] tests/fate/vcodec: add vsynth tests for FFV1 version 2 Michael Niedermayer
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=20240716171155.31838-27-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