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.
|
# 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)
|
option(DAEDALUS_BUILD_TOOLS "Build daedalus-decoder CLI tools (requires libavcodec)" OFF)
|
||||||
if(DAEDALUS_BUILD_TOOLS)
|
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)
|
add_executable(daedalus_decode_h264 tools/daedalus_decode_h264.c)
|
||||||
target_link_libraries(daedalus_decode_h264
|
target_link_libraries(daedalus_decode_h264
|
||||||
PRIVATE daedalus_decoder ${FFMPEG_LIBRARIES})
|
PRIVATE daedalus_decoder ${FFMPEG_LIBRARIES})
|
||||||
|
|||||||
@@ -50,6 +50,22 @@
|
|||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
#include <libavutil/imgutils.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 <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -59,6 +75,42 @@
|
|||||||
static const char *substrate_str = "auto";
|
static const char *substrate_str = "auto";
|
||||||
static int max_frames = -1;
|
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) {
|
||||||
|
if (st->out_of_bounds < 4)
|
||||||
|
fprintf(stderr, " OOB cb: mb=(%d,%d) grid=%dx%d\n",
|
||||||
|
mb_x, mb_y, st->mb_w, 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
|
/* Extract one MB's predicted-samples block from a YUV420P AVFrame
|
||||||
* (stock libavcodec) and pack it into the 384-byte mb_input.predicted
|
* (stock libavcodec) and pack it into the 384-byte mb_input.predicted
|
||||||
* layout: 16x16 luma raster, then 8x8 Cb raster, then 8x8 Cr raster.
|
* layout: 16x16 luma raster, then 8x8 Cb raster, then 8x8 Cr raster.
|
||||||
@@ -206,6 +258,15 @@ int main(int argc, char **argv)
|
|||||||
AVPacket *pkt = av_packet_alloc();
|
AVPacket *pkt = av_packet_alloc();
|
||||||
AVFrame *fr = av_frame_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_duplicate = 0;
|
||||||
|
int inspect_total_oob = 0;
|
||||||
|
int inspect_total_missing = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ---- Create daedalus_decoder. Coded width/height come from
|
/* ---- Create daedalus_decoder. Coded width/height come from
|
||||||
* the bitstream's SPS via libavcodec (after the first packet
|
* the bitstream's SPS via libavcodec (after the first packet
|
||||||
* is decoded — defer creation until then). ---- */
|
* is decoded — defer creation until then). ---- */
|
||||||
@@ -269,6 +330,15 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
printf("daedalus_decode_h264: %dx%d, substrate=%s\n",
|
printf("daedalus_decode_h264: %dx%d, substrate=%s\n",
|
||||||
W, H, substrate_str);
|
W, H, substrate_str);
|
||||||
|
#ifdef DAEDALUS_HAVE_H264_MB_INSPECT_CB
|
||||||
|
inspect_st.mb_w = W / 16;
|
||||||
|
inspect_st.mb_h = H / 16;
|
||||||
|
inspect_st.seen = calloc(1, (size_t) inspect_st.mb_w * inspect_st.mb_h);
|
||||||
|
if (!inspect_st.seen) { rc = 1; goto cleanup; }
|
||||||
|
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.
|
/* Pack each MB's predicted samples from the AVFrame.
|
||||||
@@ -320,6 +390,33 @@ int main(int argc, char **argv)
|
|||||||
if (out_uv_dadec[i] != out_uv_ref[i]) uv_diffs++;
|
if (out_uv_dadec[i] != out_uv_ref[i]) uv_diffs++;
|
||||||
total_y_diffs += y_diffs;
|
total_y_diffs += y_diffs;
|
||||||
total_uv_diffs += uv_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",
|
printf(" frame %d: Y diff %zu/%zu UV diff %zu/%zu%s\n",
|
||||||
n_frames, y_diffs, y_size, uv_diffs, uv_size,
|
n_frames, y_diffs, y_size, uv_diffs, uv_size,
|
||||||
(y_diffs || uv_diffs) ? " ***" : "");
|
(y_diffs || uv_diffs) ? " ***" : "");
|
||||||
@@ -348,11 +445,21 @@ int main(int argc, char **argv)
|
|||||||
drained:
|
drained:
|
||||||
printf("\n%d frames decoded; total Y diff %zu, UV diff %zu\n",
|
printf("\n%d frames decoded; total Y diff %zu, UV diff %zu\n",
|
||||||
n_frames, total_y_diffs, total_uv_diffs);
|
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");
|
printf("FAIL: daedalus-decoder output does NOT match libavcodec reference byte-for-byte\n");
|
||||||
rc = 4;
|
rc = 4;
|
||||||
} else {
|
} else if (rc == 0) {
|
||||||
printf("PASS: byte-exact identity-passthrough across %d frames\n", n_frames);
|
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:
|
cleanup:
|
||||||
@@ -360,6 +467,9 @@ cleanup:
|
|||||||
if (out_ref_f) fclose(out_ref_f);
|
if (out_ref_f) fclose(out_ref_f);
|
||||||
free(out_uv_ref); free(out_y_ref);
|
free(out_uv_ref); free(out_y_ref);
|
||||||
free(out_uv_dadec);free(out_y_dadec);
|
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);
|
if (dec) daedalus_decoder_destroy(dec);
|
||||||
av_frame_free(&fr);
|
av_frame_free(&fr);
|
||||||
av_packet_free(&pkt);
|
av_packet_free(&pkt);
|
||||||
|
|||||||
Reference in New Issue
Block a user