[PR] avcodec/h264_slice: do not allocate motion_val/ref_index tables under a hwaccel (PR #24322)
Bradyok via ffmpeg-devel <[email protected]>
| Newsgroups | gmane.comp.video.ffmpeg.devel |
|---|---|
| Message-ID | <[email protected]> |
PR #24322 opened by Bradyok URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24322 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24322.patch alloc_picture() takes two motion_val and two ref_index entries from the refstruct pools for every H264Picture regardless of the decode path. Pool entries are zeroed on allocation, so each picture carries about 1 MiB of resident memory at 1080p (2 x 523344 B motion_val + 2 x 32912 B ref_index) that nothing reads when a hwaccel does the decoding: - h264_mvpred.h, h264_direct.c and fill_filter_caches() are reached only from decode_slice() via ff_h264_execute_decode_slices(), which returns before doing any work when avctx->hwaccel is set; - error resilience runs from ff_er_frame_end(), whose er_supported() returns 0 under a hwaccel, and ff_er_frame_end() allocates its own tables when cur_pic.motion_val[0] is NULL anyway; - ff_print_debug_info2() is guarded on motion_val[0] being non-NULL; - ff_h264_set_erpic() and the H264Picture copy/unref helpers only copy the pointers, through av_refstruct_replace()/av_refstruct_unref(), which accept NULL. avctx->hwaccel is settled by get_pixel_format() in h264_init_ps() before h264_frame_start() allocates the picture, and a pixel format change reinitializes through ff_h264_flush_change() -> ff_h264_remove_all_refs(), so a hwaccel picture without tables can never become a reference for a software-decoded one. The condition is the same avctx->hwaccel check that alloc_picture() and h264_frame_start() already use. Decoding 32 concurrent 1080p H.264 streams through VideoToolbox, this saves about 160 MB of RSS. Signed-off-by: Braden O'Keefe <[email protected]> Tested: `make fate-h264` passes (222 tests) on macOS arm64 with the patch applied to a clean n9.0 tree; the change is also in daily use on a VideoToolbox-based multi-stream player (that is where the 160 MB figure was measured). The patch applies unchanged to master. >From 2322a65960a54f1b03f4941b5b5e45f76011ef4d Mon Sep 17 00:00:00 2001 From: Braden O'Keefe <[email protected]> Date: Sat, 29 Aug 2026 18:42:50 -0400 Subject: [PATCH] avcodec/h264_slice: do not allocate motion_val/ref_index tables under a hwaccel alloc_picture() takes two motion_val and two ref_index entries from the refstruct pools for every H264Picture regardless of the decode path. Pool entries are zeroed on allocation, so each picture carries about 1 MiB of resident memory at 1080p (2 x 523344 B motion_val + 2 x 32912 B ref_index) that nothing reads when a hwaccel does the decoding: - h264_mvpred.h, h264_direct.c and fill_filter_caches() are reached only from decode_slice() via ff_h264_execute_decode_slices(), which returns before doing any work when avctx->hwaccel is set; - error resilience runs from ff_er_frame_end(), whose er_supported() returns 0 under a hwaccel, and ff_er_frame_end() allocates its own tables when cur_pic.motion_val[0] is NULL anyway; - ff_print_debug_info2() is guarded on motion_val[0] being non-NULL; - ff_h264_set_erpic() and the H264Picture copy/unref helpers only copy the pointers, through av_refstruct_replace()/av_refstruct_unref(), which accept NULL. avctx->hwaccel is settled by get_pixel_format() in h264_init_ps() before h264_frame_start() allocates the picture, and a pixel format change reinitializes through ff_h264_flush_change() -> ff_h264_remove_all_refs(), so a hwaccel picture without tables can never become a reference for a software-decoded one. The condition is the same avctx->hwaccel check that alloc_picture() and h264_frame_start() already use. Decoding 32 concurrent 1080p H.264 streams through VideoToolbox, this saves about 160 MB of RSS. Signed-off-by: Braden O'Keefe <[email protected]> --- libavcodec/h264_slice.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c index a13047ffe8..5713d76948 100644 --- a/libavcodec/h264_slice.c +++ b/libavcodec/h264_slice.c @@ -250,13 +250,25 @@ static int alloc_picture(H264Context *h, H264Picture *pic) pic->mb_type = pic->mb_type_base + 2 * h->mb_stride + 1; pic->qscale_table = pic->qscale_table_base + 2 * h->mb_stride + 1; - for (i = 0; i < 2; i++) { - pic->motion_val_base[i] = av_refstruct_pool_get(h->motion_val_pool); - pic->ref_index[i] = av_refstruct_pool_get(h->ref_index_pool); - if (!pic->motion_val_base[i] || !pic->ref_index[i]) - goto fail; + /* The motion-vector and reference-index tables are read only by the + * software macroblock path (h264_mvpred.h, h264_direct.c, + * fill_filter_caches()), which is never entered under a hwaccel + * (ff_h264_execute_decode_slices() returns before doing anything), and + * by error resilience, which er_supported() disables under a hwaccel and + * which allocates its own tables when these are NULL. Every other user + * (ff_print_debug_info2(), ff_h264_set_erpic(), the picture copies) is + * NULL-safe. The pool entries are zeroed on allocation, so skipping them + * for hwaccel pictures saves about 1 MiB of resident memory per 1080p + * picture. */ + if (!h->avctx->hwaccel) { + for (i = 0; i < 2; i++) { + pic->motion_val_base[i] = av_refstruct_pool_get(h->motion_val_pool); + pic->ref_index[i] = av_refstruct_pool_get(h->ref_index_pool); + if (!pic->motion_val_base[i] || !pic->ref_index[i]) + goto fail; - pic->motion_val[i] = pic->motion_val_base[i] + 4; + pic->motion_val[i] = pic->motion_val_base[i] + 4; + } } pic->pps = av_refstruct_ref_c(h->ps.pps); -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]