Skip to content

Commit 4998db0

Browse files
authored
Audit manylinux FFmpeg feature selection (#2086)
1 parent b633435 commit 4998db0

4 files changed

Lines changed: 170 additions & 29 deletions

File tree

.github/actions/build-manylinux-variant/action.yml

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ runs:
5757
- name: Install system tools (${{ inputs.variant_label }})
5858
shell: bash
5959
run: |
60-
pkgs="git zip unzip nasm yasm pkg-config ninja-build kernel-headers perl-IPC-Cmd perl-Time-Piece"
60+
pkgs="git zip unzip pkg-config ninja-build kernel-headers perl-IPC-Cmd perl-Time-Piece"
6161
if [ "${{ inputs.install_gtk3_devel }}" = "true" ]; then
6262
pkgs="$pkgs gtk3-devel"
6363
fi
@@ -105,6 +105,17 @@ runs:
105105
shell: bash
106106
run: bash packaging/docker/manylinux/build_static_deps.sh
107107

108+
- name: Verify FFmpeg feature surface (${{ inputs.variant_label }})
109+
shell: bash
110+
run: bash packaging/docker/manylinux/verify_ffmpeg_features.sh
111+
112+
- name: Upload FFmpeg feature inventory
113+
if: inputs.save_shared_caches == 'true'
114+
uses: actions/upload-artifact@v7
115+
with:
116+
name: ffmpeg-feature-inventory
117+
path: /opt/ffmpeg/share/opencvsharp/ffmpeg-feature-inventory.txt
118+
108119
- name: Save FFmpeg cache (${{ inputs.variant_label }})
109120
if: steps.ffmpeg-cache.outputs.cache-hit != 'true' && inputs.save_shared_caches == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/main'
110121
uses: actions/cache/save@v6
@@ -223,8 +234,7 @@ runs:
223234
cmake --build ${{ inputs.extern_build_dir }} -j
224235
cp ${{ inputs.extern_build_dir }}/OpenCvSharpExtern/libOpenCvSharpExtern.so ${GITHUB_WORKSPACE}/${{ inputs.output_so_name }}
225236
226-
- name: Verify no GTK3/X11/DRM dependency (${{ inputs.variant_label }})
227-
if: inputs.verify_no_gui_deps == 'true'
237+
- name: Verify ELF dependencies (${{ inputs.variant_label }})
228238
shell: bash
229239
run: |
230240
so_path=${GITHUB_WORKSPACE}/${{ inputs.output_so_name }}
@@ -240,7 +250,7 @@ runs:
240250
echo "ERROR: readelf found no NEEDED entries at all in ${{ inputs.variant_label }} .so - the check itself is broken, treat as inconclusive"
241251
exit 1
242252
fi
243-
if echo "$needed" | grep -qi 'libgtk\|libx11\|libdrm'; then
253+
if [ "${{ inputs.verify_no_gui_deps }}" = "true" ] && echo "$needed" | grep -qi 'libgtk\|libx11\|libdrm'; then
244254
echo "ERROR: ${{ inputs.variant_label }} .so unexpectedly depends on GTK3/X11/DRM:"
245255
echo "$needed"
246256
exit 1
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# manylinux FFmpeg feature policy
2+
3+
The full and headless `linux-x64` packages statically link the same FFmpeg build. The slim package does not include FFmpeg or OpenCV video I/O.
4+
5+
The FFmpeg build provides OpenCV's required libraries (`avcodec`, `avformat`, `avutil`, and `swscale`), its built-in decoders, encoders, demuxers, and muxers, and non-secure local and network protocols. RTSP, TCP, UDP, HTTP, local files, and pipes are intended to work. CI exercises H.264-over-RTSP through OpenCV and checks a representative baseline of file formats and codecs in the generated feature inventory.
6+
7+
External-library autodetection is disabled. FFmpeg must not silently acquire a dependency merely because the manylinux container adds a development package. The glibc-provided iconv implementation is explicitly enabled and introduces no additional ELF dependency. Hardware acceleration, `libdrm`, `avdevice`, `avfilter`, post-processing, and `swresample` are intentionally excluded because the packaged OpenCV backend does not require them. Assembly remains disabled pending a successful position-independent static link with the current toolchain and a representative H.264 file/RTSP decode benchmark.
8+
9+
TLS is not currently included, so HTTPS and RTSPS are outside the supported feature surface. Adding a TLS backend requires an explicit decision covering license compatibility, CA certificate discovery, binary size, transitive dependencies, manylinux portability, and regression tests.
10+
11+
`build_static_deps.sh` writes the exact configure flags and all enabled protocols, demuxers, muxers, decoders, and encoders to `/opt/ffmpeg/share/opencvsharp/ffmpeg-feature-inventory.txt`. CI uploads that file as the `ffmpeg-feature-inventory` artifact. `verify_ffmpeg_features.sh` reports section counts and fails if the expected baseline disappears, host autodetection is restored, or a TLS backend appears unexpectedly. The final full and headless shared libraries have their ELF `NEEDED` entries printed and validated with `readelf`.

packaging/docker/manylinux/build_static_deps.sh

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,12 @@ if [[ -f "${INSTALL_PREFIX}/lib/pkgconfig/libavcodec.pc" ]]; then
2727
exit 0
2828
fi
2929

30-
# ---------------------------------------------------------------------------
31-
# System build tools (nasm/yasm required by FFmpeg's assembly optimisations)
32-
# ---------------------------------------------------------------------------
33-
dnf install -y nasm yasm
34-
3530
# ---------------------------------------------------------------------------
3631
# FFmpeg (LGPL v2.1+ — statically linked, no patented external codecs)
3732
# Internal decoders cover H.264, H.265, VP8, VP9, MPEG-4, MPEG-2, and many others.
3833
# Networking remains enabled so videoio can open RTSP and other network streams.
34+
# External-library autodetection is disabled so the feature set and final ELF
35+
# dependencies do not change when the manylinux image adds a development package.
3936
# Hwaccel autodetection (vaapi/vdpau/v4l2-m2m) is disabled explicitly: this build
4037
# never uses hardware acceleration, but if libva happens to be present in the
4138
# build container, FFmpeg's configure would auto-enable vaapi and silently add
@@ -51,28 +48,67 @@ curl -fL --retry 5 --retry-delay 2 \
5148
-o ffmpeg.tar.xz
5249
tar xf ffmpeg.tar.xz
5350
cd "ffmpeg-${FFMPEG_VERSION}"
54-
./configure \
55-
--prefix="${INSTALL_PREFIX}" \
56-
--enable-static \
57-
--disable-shared \
58-
--enable-pic \
59-
--disable-asm \
60-
--disable-doc \
61-
--disable-programs \
62-
--disable-debug \
63-
--enable-network \
64-
--disable-avdevice \
65-
--disable-postproc \
66-
--enable-avcodec \
67-
--enable-avformat \
68-
--enable-avutil \
69-
--enable-swscale \
70-
--enable-swresample \
71-
--disable-vaapi \
72-
--disable-vdpau \
73-
--disable-v4l2-m2m \
51+
52+
CONFIGURE_FLAGS=(
53+
--prefix="${INSTALL_PREFIX}"
54+
--enable-static
55+
--disable-shared
56+
--enable-pic
57+
--disable-asm
58+
--disable-autodetect
59+
--disable-doc
60+
--disable-programs
61+
--disable-debug
62+
--enable-network
63+
--enable-iconv
64+
--disable-avdevice
65+
--disable-avfilter
66+
--disable-postproc
67+
--disable-swresample
68+
--enable-avcodec
69+
--enable-avformat
70+
--enable-avutil
71+
--enable-swscale
72+
--disable-vaapi
73+
--disable-vdpau
74+
--disable-v4l2-m2m
7475
--disable-libdrm
76+
)
77+
78+
./configure "${CONFIGURE_FLAGS[@]}"
7579
make -j"${NPROC}"
7680
make install
7781

82+
INVENTORY_DIR="${INSTALL_PREFIX}/share/opencvsharp"
83+
INVENTORY_PATH="${INVENTORY_DIR}/ffmpeg-feature-inventory.txt"
84+
mkdir -p "${INVENTORY_DIR}"
85+
86+
{
87+
echo "FFmpeg ${FFMPEG_VERSION}"
88+
echo
89+
echo "[configure-flags]"
90+
printf '%s\n' "${CONFIGURE_FLAGS[@]}"
91+
92+
for component in protocol demuxer muxer decoder encoder; do
93+
echo
94+
echo "[${component}s]"
95+
awk -v suffix="_${component^^}" '
96+
$1 == "#define" && $3 == "1" && index($2, "CONFIG_") == 1 &&
97+
substr($2, length($2) - length(suffix) + 1) == suffix {
98+
name = substr($2, length("CONFIG_") + 1)
99+
name = substr(name, 1, length(name) - length(suffix))
100+
print tolower(name)
101+
}
102+
' config_components.h | sort
103+
done
104+
105+
echo
106+
echo "[tls-backends]"
107+
for backend in gnutls libtls mbedtls openssl schannel securetransport; do
108+
value=$(awk -v macro="CONFIG_${backend^^}" '$1 == "#define" && $2 == macro { print $3 }' config.h)
109+
echo "${backend}=${value:-0}"
110+
done
111+
} > "${INVENTORY_PATH}"
112+
113+
echo "FFmpeg feature inventory written to ${INVENTORY_PATH}"
78114
echo "FFmpeg installed to ${INSTALL_PREFIX}"
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
INVENTORY_PATH=${1:-/opt/ffmpeg/share/opencvsharp/ffmpeg-feature-inventory.txt}
6+
7+
if [[ ! -f "${INVENTORY_PATH}" ]]; then
8+
echo "ERROR: FFmpeg feature inventory not found: ${INVENTORY_PATH}"
9+
exit 1
10+
fi
11+
12+
feature_enabled()
13+
{
14+
local section=$1
15+
local feature=$2
16+
awk -v section="[${section}]" -v feature="${feature}" '
17+
$0 == section { in_section = 1; next }
18+
/^\[/ { in_section = 0 }
19+
in_section && $0 == feature { found = 1 }
20+
END { exit found ? 0 : 1 }
21+
' "${INVENTORY_PATH}"
22+
}
23+
24+
require_feature()
25+
{
26+
local section=$1
27+
local feature=$2
28+
if feature_enabled "${section}" "${feature}"; then
29+
echo "OK: ${section}/${feature}"
30+
else
31+
echo "ERROR: required FFmpeg feature is disabled: ${section}/${feature}"
32+
return 1
33+
fi
34+
}
35+
36+
for protocol in file pipe tcp udp; do
37+
require_feature protocols "${protocol}"
38+
done
39+
40+
for demuxer in matroska mov rtsp; do
41+
require_feature demuxers "${demuxer}"
42+
done
43+
44+
for muxer in avi matroska mov; do
45+
require_feature muxers "${muxer}"
46+
done
47+
48+
for decoder in h264 hevc mjpeg mpeg4 vp8 vp9; do
49+
require_feature decoders "${decoder}"
50+
done
51+
52+
for encoder in ffv1 mjpeg mpeg4 rawvideo; do
53+
require_feature encoders "${encoder}"
54+
done
55+
56+
for protocol in https tls; do
57+
if feature_enabled protocols "${protocol}"; then
58+
echo "ERROR: secure protocol is enabled without an approved TLS dependency policy: ${protocol}"
59+
exit 1
60+
fi
61+
done
62+
63+
if ! grep -qx -- '--disable-autodetect' "${INVENTORY_PATH}"; then
64+
echo "ERROR: FFmpeg was built without --disable-autodetect"
65+
exit 1
66+
fi
67+
68+
if grep -Eq '^(gnutls|libtls|mbedtls|openssl|schannel|securetransport)=1$' "${INVENTORY_PATH}"; then
69+
echo "ERROR: an unapproved TLS backend was enabled"
70+
grep -E '^(gnutls|libtls|mbedtls|openssl|schannel|securetransport)=' "${INVENTORY_PATH}"
71+
exit 1
72+
fi
73+
74+
echo
75+
for section in protocols demuxers muxers decoders encoders; do
76+
count=$(awk -v section="[${section}]" '
77+
$0 == section { in_section = 1; next }
78+
/^\[/ { in_section = 0 }
79+
in_section && NF { count++ }
80+
END { print count + 0 }
81+
' "${INVENTORY_PATH}")
82+
echo "${section}: ${count} enabled"
83+
done
84+
echo "Full inventory: ${INVENTORY_PATH}"

0 commit comments

Comments
 (0)