From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Tue, 26 May 2026 06:00:00 +0200 Subject: [PATCH] avcodec/h264: per-MB inspection callback (daedalus-decoder hook) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an opt-in callback fired in ff_h264_hl_decode_mb after the existing pixel work, used by tools that need per-MB visibility into the H.264 decode. Initially driven by daedalus-decoder's CLI test harness (tools/daedalus_decode_h264) which shadows libavcodec's decode with a frame-major daedalus-decoder run for byte-exact diff on real H.264 streams; later target is a daedalus-v4l2 daemon refactor that drives daedalus_decoder_append_mb directly from the callback instead of letting libavcodec do per-MB pixel work. Shape: ONE inspection point per MB. Distinct from the per-kernel function-pointer-hijack pattern that used to live in 0003-0014 patches (now reverted via 0015 for ctx, and architecturally retired per daedalus-fourier PR #37's measurement-correction). Per-block synchronous Vulkan dispatch from libavcodec was structurally non- competitive; per-MB CPU-side observation feeding a per-frame batch submit is the right shape. Two new fields in H264Context (appended at end of struct; no ABI surface visible to non-libavcodec callers since H264Context is internal — declared in h264dec.h, not h264.h). One new exported function ff_h264_set_mb_inspect_cb to set them. Zero behaviour change when cb == NULL (the default): one load + one branch per MB in the decoder hot path, both branch-predicted to fall through. Used by: - daedalus-decoder/tools/daedalus_decode_h264 (PR-A1b) - daedalus-v4l2 daemon shadow-mode path (PR-Q3a.1+) The CLI static-links libavcodec.a so symbol visibility doesn't matter there. The daemon dlopens libavcodec.so.62 and resolves the callback via dlsym, so the symbol MUST be exported — added to libavcodec.v explicitly (FFmpeg's default version script hides every `ff_*` symbol as LOCAL behind a glob). Refs reauktion/daedalus-decoder!12 (Stage 2 PR-b complete). --- libavcodec/h264_mb.c | 20 ++++++++++++++++++++ libavcodec/h264dec.h | 26 ++++++++++++++++++++++++++ libavcodec/libavcodec.v | 1 + 3 files changed, 47 insertions(+) --- a/libavcodec/h264dec.h +++ b/libavcodec/h264dec.h @@ -334,6 +334,16 @@ int pic_order_cnt_bit_size; } H264SliceContext; +/* Per-MB inspection callback type — see ff_h264_set_mb_inspect_cb() + * below. Fired by ff_h264_hl_decode_mb after the existing pixel work + * for every macroblock in coded order. Receives a const H264Context* + * so the callback can inspect any slice/picture state (h->slice_ctx + * for current slice, h->cur_pic.f->data[plane] for reconstructed + * samples, etc.). */ +typedef void (*ff_h264_mb_inspect_cb)(void *opaque, + const struct H264Context *h, + int mb_x, int mb_y); + /** * H264Context */ @@ -579,6 +589,10 @@ int non_gray; ///< Did we encounter a intra frame after a gray gap frame int noref_gray; int skip_gray; + + /* Per-MB inspection hook — set via ff_h264_set_mb_inspect_cb. */ + ff_h264_mb_inspect_cb mb_inspect_cb; + void *mb_inspect_opaque; } H264Context; extern const uint16_t ff_h264_mb_sizes[4]; @@ -607,6 +621,16 @@ const H2645NAL *nal, void *logctx); void ff_h264_hl_decode_mb(const H264Context *h, H264SliceContext *sl); + +/** + * Install an opt-in per-MB inspection callback that fires from + * ff_h264_hl_decode_mb after each macroblock's pixel work. Default + * is NULL (no callback installed); the check is a single branch on + * the decoder hot path. See ff_h264_mb_inspect_cb for signature. + */ +void ff_h264_set_mb_inspect_cb(AVCodecContext *avctx, + ff_h264_mb_inspect_cb cb, void *opaque); + void ff_h264_decode_init_vlc(void); /** --- a/libavcodec/h264_mb.c +++ b/libavcodec/h264_mb.c @@ -815,4 +815,20 @@ hl_decode_mb_simple_16(h, sl); } else hl_decode_mb_simple_8(h, sl); + + /* Per-MB inspection callback (opt-in via ff_h264_set_mb_inspect_cb). + * Fired AFTER pixel work — reconstructed samples are in + * h->cur_pic.f->data[plane] at the MB's raster position by the + * time this runs. Callback may inspect slice context via + * h->slice_ctx + sl->mb_xy, coeffs via sl->mb, etc. */ + if (h->mb_inspect_cb) + h->mb_inspect_cb(h->mb_inspect_opaque, h, sl->mb_x, sl->mb_y); +} + +void ff_h264_set_mb_inspect_cb(AVCodecContext *avctx, + ff_h264_mb_inspect_cb cb, void *opaque) +{ + H264Context *h = avctx->priv_data; + h->mb_inspect_cb = cb; + h->mb_inspect_opaque = opaque; } --- a/libavcodec/libavcodec.v +++ b/libavcodec/libavcodec.v @@ -3,6 +3,7 @@ av_*; avcodec_*; avpriv_*; + ff_h264_set_mb_inspect_cb; avsubtitle_free; local: *;