514da29a73
Switch the daemon's runtime dlopen targets from Debian-stock soname 61/61/59 (FFmpeg 7.1.3) to the Kwiboo fourier fork's soname 62/62/60 (FFmpeg 8.1) installed at the /opt/fourier prefix. Why --- The substitution arc tracked at daedalus-v4l2#11 needs daedalus- fourier kernel calls woven into libavcodec's H264DSPContext NEON init (replacing ff_h264_idct_add_neon etc. with thunks calling daedalus_recipe_dispatch_h264_*). We do that via patches in the ffmpeg-v4l2-request-fourier package source — which we own, in marfrit-packages, alongside the existing libudev-bypass and nv15-to-p010 patches. But that package builds the Kwiboo fork at soname 62 / /opt/fourier. The daemon currently dlopens soname 61 (Debian-stock + a separately-built +fourier2 patch that isn't in marfrit-packages' source tree), so substitution patches there wouldn't reach the daemon. Switching to soname 62 routes the daemon through the package we control — first step toward landing daedalus-fourier kernel substitution into the production decode path. Compat ------ - /opt/fourier libs are already on every host running the daemon (hard build-dep of ffmpeg-v4l2-request-fourier). Firefox-fourier and mpv-fourier already dlopen them via the same path. - /etc/ld.so.conf.d/fourier.conf entry resolves the new sonames from /opt/fourier/lib via the ld cache; dlopen-by-soname works without LD_LIBRARY_PATH wrappers. - Build-side: daemon's pkg_check_modules picks up libav*.pc from /opt/fourier/lib/pkgconfig when PKG_CONFIG_PATH includes that directory (build-deb.sh follow-up will set it). - API surface unchanged: avcodec_send_packet / receive_frame / AVCodecContext flags / AVFrame fields are all stable between FFmpeg 7.1 and 8.1. Verified clean cross-compile on hertz. Wire protocol unchanged. No kmod bump. Next step (follow-up PRs) ------------------------- 1. ffmpeg-v4l2-request-fourier patch: add 0003-daedalus-fourier- substitute-h264-idct4.patch that replaces ff_h264_idct_add_neon in libavcodec/aarch64/h264dsp_init_aarch64.c with a thunk calling daedalus_recipe_dispatch_h264_idct4. 2. Repeat for IDCT 8×8, deblock luma-v, qpel mc20 (one kernel per PR for reviewability; bench delta + decode_us delta documented per substitution). 3. marfrit-packages bump to pick up the new daemon + the substituted fourier package.
134 lines
4.8 KiB
C
134 lines
4.8 KiB
C
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
/*
|
|
* ffmpeg_loader.c — runtime FFmpeg loader.
|
|
*/
|
|
#include "ffmpeg_loader.h"
|
|
#include "log.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <dlfcn.h>
|
|
|
|
/*
|
|
* SONAME versions match the Kwiboo ffmpeg-v4l2-request-fourier
|
|
* fork (FFmpeg 8.1) installed at the /opt/fourier prefix. The
|
|
* fourier campaign's ld.so.conf.d/fourier.conf entry resolves
|
|
* these sonames from /opt/fourier/lib via the ld cache, so
|
|
* dlopen-by-soname works without LD_LIBRARY_PATH wrappers.
|
|
*
|
|
* Switched from Debian-stock soname 61/61/59 (FFmpeg 7.1.3) at
|
|
* 2026-05-21 to land daedalus-fourier kernel substitution into
|
|
* the production decode path via patches in the Kwiboo fork
|
|
* (see daedalus-v4l2#11 substitution arc): we own the fork
|
|
* source in marfrit-packages, so we can layer NEON-DSP
|
|
* substitution patches there for libavcodec/aarch64/h264dsp_init
|
|
* → daedalus_recipe_dispatch_* thunks. The Debian-stock 7.1.3
|
|
* is built outside the marfrit-packages source tree, which
|
|
* would have made layering substitution patches awkward.
|
|
*
|
|
* Note: libavutil bumps soname 59 → 60 between FFmpeg 7.1 and
|
|
* 8.1; libavformat + libavcodec each bump 61 → 62. The public
|
|
* API surface the daemon uses (avcodec_send_packet /
|
|
* receive_frame / AVCodecContext flags / AVFrame fields) is
|
|
* stable across the bump.
|
|
*/
|
|
#define LIBAVFORMAT_SONAME "libavformat.so.62"
|
|
#define LIBAVCODEC_SONAME "libavcodec.so.62"
|
|
#define LIBAVUTIL_SONAME "libavutil.so.60"
|
|
|
|
/*
|
|
* Resolve a symbol from a dlopen'd handle. Logs the failure
|
|
* and goto's `out_fail` if missing. Use only inside
|
|
* ffmpeg_loader_init.
|
|
*
|
|
* The `*(void **) &loader->sym = dlsym(...)` form is the
|
|
* POSIX-recommended workaround for the ISO C "function pointer
|
|
* is not a void pointer" rule (POSIX.1-2017 dlsym(3p), RATIONALE).
|
|
* POSIX requires the conversion to work; ISO C does not.
|
|
*/
|
|
#define RESOLVE(handle_field, soname, sym) \
|
|
do { \
|
|
*(void **) &loader->sym = \
|
|
dlsym(loader->handle_field, #sym); \
|
|
if (!loader->sym) { \
|
|
log_err("dlsym(%s, %s): %s", \
|
|
soname, #sym, dlerror()); \
|
|
goto out_fail; \
|
|
} \
|
|
} while (0)
|
|
|
|
int ffmpeg_loader_init(struct ffmpeg_loader *loader)
|
|
{
|
|
memset(loader, 0, sizeof(*loader));
|
|
|
|
loader->libavformat = dlopen(LIBAVFORMAT_SONAME, RTLD_LAZY);
|
|
if (!loader->libavformat) {
|
|
log_err("dlopen(%s): %s", LIBAVFORMAT_SONAME, dlerror());
|
|
goto out_fail;
|
|
}
|
|
loader->libavcodec = dlopen(LIBAVCODEC_SONAME, RTLD_LAZY);
|
|
if (!loader->libavcodec) {
|
|
log_err("dlopen(%s): %s", LIBAVCODEC_SONAME, dlerror());
|
|
goto out_fail;
|
|
}
|
|
loader->libavutil = dlopen(LIBAVUTIL_SONAME, RTLD_LAZY);
|
|
if (!loader->libavutil) {
|
|
log_err("dlopen(%s): %s", LIBAVUTIL_SONAME, dlerror());
|
|
goto out_fail;
|
|
}
|
|
|
|
/* libavformat */
|
|
RESOLVE(libavformat, LIBAVFORMAT_SONAME, avformat_alloc_context);
|
|
RESOLVE(libavformat, LIBAVFORMAT_SONAME, avformat_free_context);
|
|
RESOLVE(libavformat, LIBAVFORMAT_SONAME, avformat_open_input);
|
|
RESOLVE(libavformat, LIBAVFORMAT_SONAME, avformat_close_input);
|
|
RESOLVE(libavformat, LIBAVFORMAT_SONAME, avformat_find_stream_info);
|
|
RESOLVE(libavformat, LIBAVFORMAT_SONAME, av_find_best_stream);
|
|
RESOLVE(libavformat, LIBAVFORMAT_SONAME, av_read_frame);
|
|
RESOLVE(libavformat, LIBAVFORMAT_SONAME, avformat_version);
|
|
|
|
/* libavcodec */
|
|
RESOLVE(libavcodec, LIBAVCODEC_SONAME, av_packet_alloc);
|
|
RESOLVE(libavcodec, LIBAVCODEC_SONAME, av_packet_free);
|
|
RESOLVE(libavcodec, LIBAVCODEC_SONAME, av_packet_unref);
|
|
RESOLVE(libavcodec, LIBAVCODEC_SONAME, av_frame_alloc);
|
|
RESOLVE(libavcodec, LIBAVCODEC_SONAME, av_frame_free);
|
|
RESOLVE(libavcodec, LIBAVCODEC_SONAME, av_frame_unref);
|
|
RESOLVE(libavcodec, LIBAVCODEC_SONAME, avcodec_find_decoder);
|
|
RESOLVE(libavcodec, LIBAVCODEC_SONAME, avcodec_alloc_context3);
|
|
RESOLVE(libavcodec, LIBAVCODEC_SONAME, avcodec_free_context);
|
|
RESOLVE(libavcodec, LIBAVCODEC_SONAME, avcodec_parameters_to_context);
|
|
RESOLVE(libavcodec, LIBAVCODEC_SONAME, avcodec_open2);
|
|
RESOLVE(libavcodec, LIBAVCODEC_SONAME, avcodec_send_packet);
|
|
RESOLVE(libavcodec, LIBAVCODEC_SONAME, avcodec_receive_frame);
|
|
|
|
/* libavutil */
|
|
RESOLVE(libavutil, LIBAVUTIL_SONAME, av_log_set_level);
|
|
RESOLVE(libavutil, LIBAVUTIL_SONAME, av_get_media_type_string);
|
|
RESOLVE(libavutil, LIBAVUTIL_SONAME, av_version_info);
|
|
RESOLVE(libavutil, LIBAVUTIL_SONAME, av_pix_fmt_desc_get);
|
|
|
|
{
|
|
unsigned int v = loader->avformat_version();
|
|
log_info("FFmpeg loaded: %s (libavformat %u.%u.%u)",
|
|
loader->av_version_info(),
|
|
(v >> 16) & 0xff, (v >> 8) & 0xff, v & 0xff);
|
|
}
|
|
return 0;
|
|
|
|
out_fail:
|
|
ffmpeg_loader_cleanup(loader);
|
|
return -1;
|
|
}
|
|
|
|
void ffmpeg_loader_cleanup(struct ffmpeg_loader *loader)
|
|
{
|
|
if (!loader)
|
|
return;
|
|
if (loader->libavformat) dlclose(loader->libavformat);
|
|
if (loader->libavcodec) dlclose(loader->libavcodec);
|
|
if (loader->libavutil) dlclose(loader->libavutil);
|
|
memset(loader, 0, sizeof(*loader));
|
|
}
|