From c4c0dce09886259df6903b12b2f9ab10ad9cbba7 Mon Sep 17 00:00:00 2001 From: claude-noether Date: Tue, 26 May 2026 07:02:29 +0200 Subject: [PATCH] wip: callback wiring --- CMakeLists.txt | 26 ++++++++++- tools/daedalus_decode_h264.c | 91 +++++++++++++++++++++++++++++++++++- 2 files changed, 114 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 41e57f3..387772e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -175,7 +175,31 @@ target_compile_options(bench_flush_frame PRIVATE -O2) # so the standard ctest build doesn't pull in FFmpeg as a hard dep. option(DAEDALUS_BUILD_TOOLS "Build daedalus-decoder CLI tools (requires libavcodec)" OFF) if(DAEDALUS_BUILD_TOOLS) - pkg_check_modules(FFMPEG REQUIRED libavcodec libavformat libavutil) + # Optional path to a private FFmpeg install carrying the per-MB + # inspection callback (marfrit-packages patch 0016). When set, + # the CLI links against it instead of the system FFmpeg and the + # inspection-callback code path is compiled in. + set(DAEDALUS_FFMPEG_PREFIX "" CACHE PATH + "Path to a patched FFmpeg install (with 0016 mb-inspect-callback) for daedalus_decode_h264. Empty = use system pkg-config FFmpeg.") + + if(DAEDALUS_FFMPEG_PREFIX) + message(STATUS "daedalus_decode_h264: patched FFmpeg at ${DAEDALUS_FFMPEG_PREFIX}") + set(FFMPEG_INCLUDE_DIRS ${DAEDALUS_FFMPEG_PREFIX}/include) + set(FFMPEG_LIBRARY_DIRS ${DAEDALUS_FFMPEG_PREFIX}/lib) + # Patched libavcodec is built static (no shared libs in the private prefix). + # System pull-ins are still needed for libav* dependencies. + set(FFMPEG_LIBRARIES + ${DAEDALUS_FFMPEG_PREFIX}/lib/libavformat.a + ${DAEDALUS_FFMPEG_PREFIX}/lib/libavcodec.a + ${DAEDALUS_FFMPEG_PREFIX}/lib/libavutil.a + ${DAEDALUS_FFMPEG_PREFIX}/lib/libswresample.a + m z pthread) + set(FFMPEG_CFLAGS_OTHER "-DDAEDALUS_HAVE_H264_MB_INSPECT_CB=1") + else() + pkg_check_modules(FFMPEG REQUIRED libavcodec libavformat libavutil) + message(STATUS "daedalus_decode_h264: system FFmpeg (no inspection callback)") + endif() + add_executable(daedalus_decode_h264 tools/daedalus_decode_h264.c) target_link_libraries(daedalus_decode_h264 PRIVATE daedalus_decoder ${FFMPEG_LIBRARIES}) diff --git a/tools/daedalus_decode_h264.c b/tools/daedalus_decode_h264.c index cfccca9..6f53a68 100644 --- a/tools/daedalus_decode_h264.c +++ b/tools/daedalus_decode_h264.c @@ -50,6 +50,22 @@ #include #include +/* Per-MB inspection callback API — provided by the patched FFmpeg + * fork via marfrit-packages 0016. The H264Context struct itself + * remains internal (declared in libavcodec/h264dec.h which isn't + * installed), so we only forward-declare it here and use it + * opaquely through the callback signature. Real per-MB state + * extraction (sl->mb coefficients, mb_type, etc.) will land in + * PR-A3 alongside an internal-header include path. */ +#ifdef DAEDALUS_HAVE_H264_MB_INSPECT_CB +struct H264Context; +typedef void (*ff_h264_mb_inspect_cb)(void *opaque, + const struct H264Context *h, + int mb_x, int mb_y); +void ff_h264_set_mb_inspect_cb(AVCodecContext *avctx, + ff_h264_mb_inspect_cb cb, void *opaque); +#endif + #include #include #include @@ -59,6 +75,37 @@ static const char *substrate_str = "auto"; static int max_frames = -1; +/* Inspection-callback state: per-frame counter + raster-order check. + * Reset at end of each frame by the main loop. */ +#ifdef DAEDALUS_HAVE_H264_MB_INSPECT_CB +struct inspect_state { + int n_cbs_this_frame; + int expected_mb_w; + int raster_violations; + int last_mb_x, last_mb_y; /* for raster-order detection */ +}; + +static void inspect_cb(void *opaque, + const struct H264Context *h, + int mb_x, int mb_y) +{ + (void) h; + struct inspect_state *st = opaque; + + /* Raster order check: each subsequent (mb_x, mb_y) should be the + * raster successor of the previous one within the frame. */ + if (st->n_cbs_this_frame > 0) { + int expected_x = (st->last_mb_x + 1) % st->expected_mb_w; + int expected_y = st->last_mb_y + (st->last_mb_x + 1 == st->expected_mb_w ? 1 : 0); + if (mb_x != expected_x || mb_y != expected_y) + st->raster_violations++; + } + st->last_mb_x = mb_x; + st->last_mb_y = mb_y; + st->n_cbs_this_frame++; +} +#endif + /* Extract one MB's predicted-samples block from a YUV420P AVFrame * (stock libavcodec) and pack it into the 384-byte mb_input.predicted * layout: 16x16 luma raster, then 8x8 Cb raster, then 8x8 Cr raster. @@ -206,6 +253,13 @@ int main(int argc, char **argv) AVPacket *pkt = av_packet_alloc(); AVFrame *fr = av_frame_alloc(); +#ifdef DAEDALUS_HAVE_H264_MB_INSPECT_CB + struct inspect_state inspect_st = {0}; + ff_h264_set_mb_inspect_cb(avctx, inspect_cb, &inspect_st); + int inspect_total_cbs = 0; + int inspect_total_violations = 0; +#endif + /* ---- Create daedalus_decoder. Coded width/height come from * the bitstream's SPS via libavcodec (after the first packet * is decoded — defer creation until then). ---- */ @@ -269,6 +323,12 @@ int main(int argc, char **argv) } printf("daedalus_decode_h264: %dx%d, substrate=%s\n", W, H, substrate_str); +#ifdef DAEDALUS_HAVE_H264_MB_INSPECT_CB + inspect_st.expected_mb_w = W / 16; + printf(" inspection callback: ACTIVE (patched libavcodec)\n"); +#else + printf(" inspection callback: not built in (stock libavcodec)\n"); +#endif } /* Pack each MB's predicted samples from the AVFrame. @@ -320,6 +380,24 @@ int main(int argc, char **argv) if (out_uv_dadec[i] != out_uv_ref[i]) uv_diffs++; total_y_diffs += y_diffs; total_uv_diffs += uv_diffs; +#ifdef DAEDALUS_HAVE_H264_MB_INSPECT_CB + { + const int expected = mb_w * mb_h; + if (inspect_st.n_cbs_this_frame != expected) { + fprintf(stderr, + " frame %d: callback fired %d times, expected %d (mb_w*mb_h)\n", + n_frames, inspect_st.n_cbs_this_frame, expected); + rc = 4; /* treat as bit-exact failure */ + } + inspect_total_cbs += inspect_st.n_cbs_this_frame; + inspect_total_violations += inspect_st.raster_violations; + /* Reset for next frame. */ + inspect_st.n_cbs_this_frame = 0; + inspect_st.raster_violations = 0; + inspect_st.last_mb_x = 0; + inspect_st.last_mb_y = 0; + } +#endif printf(" frame %d: Y diff %zu/%zu UV diff %zu/%zu%s\n", n_frames, y_diffs, y_size, uv_diffs, uv_size, (y_diffs || uv_diffs) ? " ***" : ""); @@ -348,11 +426,20 @@ int main(int argc, char **argv) drained: printf("\n%d frames decoded; total Y diff %zu, UV diff %zu\n", n_frames, total_y_diffs, total_uv_diffs); - if (total_y_diffs || total_uv_diffs) { +#ifdef DAEDALUS_HAVE_H264_MB_INSPECT_CB + printf("inspection callback: %d total invocations, %d raster-order violations\n", + inspect_total_cbs, inspect_total_violations); + if (inspect_total_violations) rc = 4; +#endif + if (rc == 0 && (total_y_diffs || total_uv_diffs)) { printf("FAIL: daedalus-decoder output does NOT match libavcodec reference byte-for-byte\n"); rc = 4; - } else { + } else if (rc == 0) { printf("PASS: byte-exact identity-passthrough across %d frames\n", n_frames); + } else { + printf("FAIL: %s\n", + (total_y_diffs || total_uv_diffs) ? "byte-exact comparison failed" + : "inspection callback invariants violated"); } cleanup: