wip: callback wiring
This commit is contained in:
+25
-1
@@ -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})
|
||||
|
||||
@@ -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,39 @@
|
||||
static const char *substrate_str = "auto";
|
||||
static int max_frames = -1;
|
||||
|
||||
/* Inspection-callback state: per-frame counter + "each MB seen exactly
|
||||
* once" check. We use a bitmap rather than a raster-order assertion
|
||||
* because libavcodec's MB-level threading + multi-slice frames mean
|
||||
* MBs reach the callback in non-strictly-raster order; the contract
|
||||
* is "every MB fires the callback exactly once per frame", not "in
|
||||
* raster order". Reset at end of each frame. */
|
||||
#ifdef DAEDALUS_HAVE_H264_MB_INSPECT_CB
|
||||
struct inspect_state {
|
||||
int n_cbs_this_frame;
|
||||
int mb_w, mb_h;
|
||||
uint8_t *seen; /* mb_w * mb_h bitmap */
|
||||
int duplicate_mbs; /* same (mb_x, mb_y) seen twice this frame */
|
||||
int out_of_bounds; /* (mb_x, mb_y) outside the coded grid */
|
||||
};
|
||||
|
||||
static void inspect_cb(void *opaque,
|
||||
const struct H264Context *h,
|
||||
int mb_x, int mb_y)
|
||||
{
|
||||
(void) h;
|
||||
struct inspect_state *st = opaque;
|
||||
|
||||
if (mb_x < 0 || mb_x >= st->mb_w || mb_y < 0 || mb_y >= st->mb_h) {
|
||||
st->out_of_bounds++;
|
||||
} else {
|
||||
const size_t idx = (size_t) mb_y * st->mb_w + (size_t) mb_x;
|
||||
if (st->seen[idx]) st->duplicate_mbs++;
|
||||
st->seen[idx] = 1;
|
||||
}
|
||||
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 +255,29 @@ int main(int argc, char **argv)
|
||||
AVPacket *pkt = av_packet_alloc();
|
||||
AVFrame *fr = av_frame_alloc();
|
||||
|
||||
#ifdef DAEDALUS_HAVE_H264_MB_INSPECT_CB
|
||||
/* Init inspect state BEFORE the first avcodec_send_packet — the
|
||||
* callback fires from inside send_packet (i.e. before the first
|
||||
* receive_frame ever returns), so lazy-init after-the-fact
|
||||
* would miss the entire first frame. Use codecpar dims; round
|
||||
* up to MB granularity (H.264 codes 1080 height as 1088). */
|
||||
struct inspect_state inspect_st = {0};
|
||||
{
|
||||
const AVCodecParameters *cp = fmt->streams[vstream]->codecpar;
|
||||
const int W_round = (cp->width + 15) & ~15;
|
||||
const int H_round = (cp->height + 15) & ~15;
|
||||
inspect_st.mb_w = W_round / 16;
|
||||
inspect_st.mb_h = H_round / 16;
|
||||
inspect_st.seen = calloc(1, (size_t) inspect_st.mb_w * inspect_st.mb_h);
|
||||
if (!inspect_st.seen) { rc = 1; goto cleanup; }
|
||||
}
|
||||
ff_h264_set_mb_inspect_cb(avctx, inspect_cb, &inspect_st);
|
||||
int inspect_total_cbs = 0;
|
||||
int inspect_total_duplicate = 0;
|
||||
int inspect_total_oob = 0;
|
||||
int inspect_total_missing = 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 +341,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
|
||||
printf(" inspection callback: ACTIVE (patched libavcodec); "
|
||||
"mb-grid %dx%d\n", inspect_st.mb_w, inspect_st.mb_h);
|
||||
#else
|
||||
printf(" inspection callback: not built in (stock libavcodec)\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Pack each MB's predicted samples from the AVFrame.
|
||||
@@ -320,6 +398,33 @@ 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;
|
||||
/* Count MBs that fired the callback. */
|
||||
int seen_count = 0;
|
||||
for (int i = 0; i < expected; i++)
|
||||
if (inspect_st.seen[i]) seen_count++;
|
||||
int missing = expected - seen_count;
|
||||
if (missing || inspect_st.duplicate_mbs || inspect_st.out_of_bounds) {
|
||||
fprintf(stderr,
|
||||
" frame %d: callback invariants: fired=%d expected=%d "
|
||||
"missing=%d duplicates=%d oob=%d\n",
|
||||
n_frames, inspect_st.n_cbs_this_frame, expected,
|
||||
missing, inspect_st.duplicate_mbs, inspect_st.out_of_bounds);
|
||||
rc = 4;
|
||||
}
|
||||
inspect_total_cbs += inspect_st.n_cbs_this_frame;
|
||||
inspect_total_duplicate += inspect_st.duplicate_mbs;
|
||||
inspect_total_oob += inspect_st.out_of_bounds;
|
||||
inspect_total_missing += missing;
|
||||
/* Reset for next frame. */
|
||||
inspect_st.n_cbs_this_frame = 0;
|
||||
inspect_st.duplicate_mbs = 0;
|
||||
inspect_st.out_of_bounds = 0;
|
||||
memset(inspect_st.seen, 0, (size_t) expected);
|
||||
}
|
||||
#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 +453,21 @@ 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 missing, %d duplicates, %d oob\n",
|
||||
inspect_total_cbs, inspect_total_missing, inspect_total_duplicate, inspect_total_oob);
|
||||
if (inspect_total_missing || inspect_total_duplicate || inspect_total_oob)
|
||||
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:
|
||||
@@ -360,6 +475,9 @@ cleanup:
|
||||
if (out_ref_f) fclose(out_ref_f);
|
||||
free(out_uv_ref); free(out_y_ref);
|
||||
free(out_uv_dadec);free(out_y_dadec);
|
||||
#ifdef DAEDALUS_HAVE_H264_MB_INSPECT_CB
|
||||
free(inspect_st.seen);
|
||||
#endif
|
||||
if (dec) daedalus_decoder_destroy(dec);
|
||||
av_frame_free(&fr);
|
||||
av_packet_free(&pkt);
|
||||
|
||||
Reference in New Issue
Block a user