wip: callback wiring

This commit is contained in:
2026-05-26 07:02:29 +02:00
parent 972a79dde2
commit c4c0dce098
2 changed files with 114 additions and 3 deletions
+24
View File
@@ -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)
# 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})
+89 -2
View File
@@ -50,6 +50,22 @@
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
/* 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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: