Skip to content

Commit e25a6ca

Browse files
committed
Add AV1 video codec support
This commit adds support for AV1 video decoding in lilliput, following the same pattern as HEVC support with build flag gating. Changes: - Add AV1 decoder enablement behind build flag (av1Enabled) - Update FFmpeg build scripts to include --enable-decoder=av1 and --enable-libaom - Add codec gating logic to reject AV1 when disabled (default) - Add basic tests for AV1 support and decoder creation - Include test AV1 video file for validation Usage: Enable AV1 support: go build -ldflags="-X=github.com/discord/lilliput.av1Enabled=true" Features: - AV1 video detection in containers (MP4, WebM, etc.) - First-frame extraction from AV1 videos - Same security model as HEVC (disabled by default)
1 parent 93305eb commit e25a6ca

File tree

160 files changed

+57409
-534
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

160 files changed

+57409
-534
lines changed

avcodec.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ bool avcodec_decoder_is_streamable(const opencv_mat mat)
154154
return false;
155155
}
156156

157-
avcodec_decoder avcodec_decoder_create(const opencv_mat buf, const bool hevc_enabled)
157+
avcodec_decoder avcodec_decoder_create(const opencv_mat buf, const bool hevc_enabled, const bool av1_enabled)
158158
{
159159
avcodec_decoder d = new struct avcodec_decoder_struct();
160160
memset(d, 0, sizeof(struct avcodec_decoder_struct));
@@ -238,6 +238,11 @@ avcodec_decoder avcodec_decoder_create(const opencv_mat buf, const bool hevc_ena
238238
return NULL;
239239
}
240240

241+
if (codec->id == AV_CODEC_ID_AV1 && !av1_enabled) {
242+
avcodec_decoder_release(d);
243+
return NULL;
244+
}
245+
241246
d->codec = avcodec_alloc_context3(codec);
242247

243248
res = avcodec_parameters_to_context(d->codec, codec_params);
@@ -411,7 +416,14 @@ bool avcodec_decoder_has_subtitles(const avcodec_decoder d)
411416

412417
static int avcodec_decoder_copy_frame(const avcodec_decoder d, opencv_mat mat, AVFrame* frame)
413418
{
419+
if (!d || !d->codec || !d->codec->codec || !mat || !frame) {
420+
return -1;
421+
}
422+
414423
auto cvMat = static_cast<cv::Mat*>(mat);
424+
if (!cvMat) {
425+
return -1;
426+
}
415427

416428
int res = avcodec_receive_frame(d->codec, frame);
417429
if (res >= 0) {
@@ -505,13 +517,7 @@ static int avcodec_decoder_decode_packet(const avcodec_decoder d, opencv_mat mat
505517

506518
bool avcodec_decoder_decode(const avcodec_decoder d, opencv_mat mat)
507519
{
508-
if (!d) {
509-
return false;
510-
}
511-
if (!d->container) {
512-
return false;
513-
}
514-
if (!d->codec) {
520+
if (!d || !d->container || !d->codec || !mat) {
515521
return false;
516522
}
517523
AVPacket packet;

avcodec.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ const atomHeaderSize = 8
1616
// Enable by building/running with "-ldflags=-X=github.com/discord/lilliput.hevcEnabled=true"
1717
var hevcEnabled string
1818

19+
// Set AV1 decoder enablement behind a build flag, defaults to off
20+
// Enable by building/running with "-ldflags=-X=github.com/discord/lilliput.av1Enabled=true"
21+
var av1Enabled string
22+
1923
// avCodecDecoder handles decoding of various video/image formats using FFmpeg's avcodec.
2024
type avCodecDecoder struct {
2125
decoder C.avcodec_decoder
@@ -35,7 +39,7 @@ func newAVCodecDecoder(buf []byte) (*avCodecDecoder, error) {
3539
return nil, ErrBufTooSmall
3640
}
3741

38-
decoder := C.avcodec_decoder_create(mat, hevcEnabled == "true")
42+
decoder := C.avcodec_decoder_create(mat, hevcEnabled == "true", av1Enabled == "true")
3943
if decoder == nil {
4044
C.opencv_mat_release(mat)
4145
return nil, ErrInvalidImage

avcodec.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ typedef struct avcodec_decoder_struct* avcodec_decoder;
1111

1212
void avcodec_init();
1313

14-
avcodec_decoder avcodec_decoder_create(const opencv_mat buf, const bool hevc_enabled);
14+
avcodec_decoder avcodec_decoder_create(const opencv_mat buf, const bool hevc_enabled, const bool av1_enabled);
1515
void avcodec_decoder_release(avcodec_decoder d);
1616
int avcodec_decoder_get_width(const avcodec_decoder d);
1717
int avcodec_decoder_get_height(const avcodec_decoder d);

avcodec_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,65 @@ func TestICCProfile(t *testing.T) {
5959
}
6060
}
6161

62+
func TestAV1Support(t *testing.T) {
63+
// Test that AV1 variables are properly defined
64+
if av1Enabled != "true" && av1Enabled != "" {
65+
// This should be empty by default, confirming our variable is defined
66+
t.Logf("AV1 support is disabled (expected): %q", av1Enabled)
67+
}
68+
}
69+
70+
func TestAV1VideoDecoding(t *testing.T) {
71+
// Test AV1 video file
72+
av1Mp4, err := os.ReadFile("testdata/av1-mp4.mp4")
73+
if err != nil {
74+
t.Fatalf("failed to open AV1 test file: %v", err)
75+
}
76+
77+
t.Run("AV1_Disabled_By_Default", func(t *testing.T) {
78+
// Should fail when AV1 is disabled (default)
79+
_, err := newAVCodecDecoder(av1Mp4)
80+
if err == nil {
81+
t.Logf("AV1 decoder created successfully with av1Enabled=%q", av1Enabled)
82+
} else {
83+
t.Logf("AV1 decoder failed as expected when disabled: %v", err)
84+
}
85+
})
86+
87+
// Note: Testing with AV1 enabled requires building with the flag:
88+
// go test -ldflags="-X=github.com/discord/lilliput.av1Enabled=true"
89+
if av1Enabled == "true" {
90+
t.Run("AV1_Enabled_Decoding", func(t *testing.T) {
91+
decoder, err := newAVCodecDecoder(av1Mp4)
92+
if err != nil {
93+
t.Fatalf("failed to create AV1 decoder: %v", err)
94+
}
95+
defer decoder.Close()
96+
97+
// Test basic metadata
98+
header, err := decoder.Header()
99+
if err != nil {
100+
t.Fatalf("failed to get AV1 header: %v", err)
101+
}
102+
103+
t.Logf("AV1 video dimensions: %dx%d", header.Width(), header.Height())
104+
t.Logf("AV1 video duration: %v", decoder.Duration())
105+
t.Logf("AV1 video description: %s", decoder.Description())
106+
107+
// Test first frame extraction
108+
framebuffer := NewFramebuffer(header.Width(), header.Height())
109+
defer framebuffer.Close()
110+
111+
err = decoder.DecodeTo(framebuffer)
112+
if err != nil {
113+
t.Fatalf("failed to decode AV1 first frame: %v", err)
114+
}
115+
116+
t.Log("Successfully extracted first frame from AV1 video")
117+
})
118+
}
119+
}
120+
62121
func BenchmarkIsStreamableWebMp4(b *testing.B) {
63122
// Read the web-optimized streamable MP4 file
64123
webMp4, err := os.ReadFile("testdata/big_buck_bunny_480p_10s_web.mp4")

cgo.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ package lilliput
88
#cgo darwin CXXFLAGS: -I${SRCDIR}/deps/osx/include -I${SRCDIR}/deps/osx/include/opencv4
99
#cgo linux,amd64 CXXFLAGS: -I${SRCDIR}/deps/linux/amd64/include -I${SRCDIR}/deps/linux/amd64/include/opencv4
1010
#cgo linux,arm64 CXXFLAGS: -I${SRCDIR}/deps/linux/aarch64/include -I${SRCDIR}/deps/linux/aarch64/include/opencv4
11-
#cgo darwin LDFLAGS: -L${SRCDIR}/deps/osx/lib -L${SRCDIR}/deps/osx/lib/opencv4/3rdparty -lavif -lyuv -laom -lavformat -lavcodec -lavutil -lopencv_photo -lopencv_imgcodecs -lopencv_imgproc -lopencv_core -lbz2 -lgif -ljpeg -lpng -lswscale -lwebp -lwebpmux -lwebpdemux -lsharpyuv -lz -llibopenjp2 -littnotify -framework Accelerate -framework CoreFoundation -framework CoreMedia -framework CoreVideo -framework VideoToolbox -llcms2
12-
#cgo linux,amd64 LDFLAGS: -L${SRCDIR}/deps/linux/amd64/lib -L${SRCDIR}/deps/linux/amd64/lib/opencv4/3rdparty -lavif -lyuv -laom -lavformat -lavcodec -lavutil -lopencv_photo -lopencv_imgcodecs -lopencv_imgproc -lopencv_core -lbz2 -lgif -ljpeg -lpng16 -lswscale -lwebp -lwebpmux -lwebpdemux -lsharpyuv -lz -llibopenjp2 -littnotify -lippiw -lippicv -llcms2
13-
#cgo linux,arm64 LDFLAGS: -L${SRCDIR}/deps/linux/aarch64/lib -L${SRCDIR}/deps/linux/aarch64/lib/opencv4/3rdparty -lavif -lyuv -laom -lavformat -lavcodec -lavutil -lopencv_photo -lopencv_imgcodecs -lopencv_imgproc -lopencv_core -lbz2 -lgif -ljpeg -lpng16 -lswscale -lwebp -lwebpmux -lwebpdemux -lsharpyuv -lz -llibopenjp2 -littnotify -llcms2
11+
#cgo darwin LDFLAGS: -L${SRCDIR}/deps/osx/lib -L${SRCDIR}/deps/osx/lib/opencv4/3rdparty -lavif -lyuv -laom -ldav1d -lavformat -lavcodec -lavutil -lopencv_photo -lopencv_imgcodecs -lopencv_imgproc -lopencv_core -lbz2 -lgif -ljpeg -lpng -lswscale -lwebp -lwebpmux -lwebpdemux -lsharpyuv -lz -llibopenjp2 -littnotify -framework Accelerate -framework CoreFoundation -framework CoreMedia -framework CoreVideo -framework VideoToolbox -llcms2
12+
#cgo linux,amd64 LDFLAGS: -L${SRCDIR}/deps/linux/amd64/lib -L${SRCDIR}/deps/linux/amd64/lib/opencv4/3rdparty -lavif -lyuv -laom -ldav1d -lavformat -lavcodec -lavutil -lopencv_photo -lopencv_imgcodecs -lopencv_imgproc -lopencv_core -lbz2 -lgif -ljpeg -lpng16 -lswscale -lwebp -lwebpmux -lwebpdemux -lsharpyuv -lz -llibopenjp2 -littnotify -lippiw -lippicv -llcms2
13+
#cgo linux,arm64 LDFLAGS: -L${SRCDIR}/deps/linux/aarch64/lib -L${SRCDIR}/deps/linux/aarch64/lib/opencv4/3rdparty -lavif -lyuv -laom -ldav1d -lavformat -lavcodec -lavutil -lopencv_photo -lopencv_imgcodecs -lopencv_imgproc -lopencv_core -lbz2 -lgif -ljpeg -lpng16 -lswscale -lwebp -lwebpmux -lwebpdemux -lsharpyuv -lz -llibopenjp2 -littnotify -llcms2
1414
void dummy() {}
1515
*/
1616
import "C"

deps/build-deps-linux.sh

Lines changed: 74 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,15 @@ verify_arch() {
116116
esac
117117
}
118118

119+
echo '\n--------------------'
120+
echo 'Installing build tools'
121+
echo '--------------------\n'
122+
123+
if ! command -v meson >/dev/null 2>&1; then
124+
echo "Installing meson and ninja..."
125+
sudo apt-get update && sudo apt-get install -y meson ninja-build python3-setuptools
126+
fi
127+
119128
BASEDIR=$(cd $(dirname "$0") && pwd)
120129
PREFIX="$BASEDIR/linux/$ARCH"
121130
BUILDDIR="$BASEDIR/build"
@@ -138,10 +147,11 @@ rm -rf lcms
138147
rm -rf ffmpeg
139148
rm -rf libyuv
140149
rm -rf aom
150+
rm -rf dav1d
141151
rm -rf libavif
142152

143153
if [ ! -d "$SRCDIR" ]; then
144-
git clone --depth 1 --branch 1.4.1 https://github.com/discord/lilliput-dep-source "$SRCDIR"
154+
git clone --depth 1 --branch skidder/add-libdav1d https://github.com/discord/lilliput-dep-source "$SRCDIR"
145155
fi
146156

147157
echo '\n--------------------'
@@ -319,13 +329,68 @@ make
319329
make install
320330
verify_arch "$PREFIX/lib/liblcms2.a"
321331

332+
echo '\n--------------------'
333+
echo 'Building libdav1d'
334+
echo '--------------------\n'
335+
mkdir -p $BASEDIR/dav1d
336+
tar -xzf $SRCDIR/dav1d-1.5.1.tar.gz -C $BASEDIR/dav1d --strip-components 1
337+
mkdir -p $BUILDDIR/dav1d
338+
cd $BUILDDIR/dav1d
339+
meson setup $BASEDIR/dav1d \
340+
--prefix=$PREFIX \
341+
--default-library=static \
342+
--buildtype=release \
343+
-Denable_tools=false \
344+
-Denable_tests=false \
345+
-Db_lto=true \
346+
--cross-file=$BASEDIR/meson-cross-$ARCH.txt 2>/dev/null || \
347+
meson setup $BASEDIR/dav1d \
348+
--prefix=$PREFIX \
349+
--default-library=static \
350+
--buildtype=release \
351+
-Denable_tools=false \
352+
-Denable_tests=false \
353+
-Db_lto=true
354+
ninja
355+
ninja install
356+
# Move libdav1d.a and dav1d.pc from architecture-specific subdirectory to main directories (AMD64 only)
357+
mkdir -p "$PREFIX/lib/pkgconfig"
358+
if [ -f "$PREFIX/lib/x86_64-linux-gnu/libdav1d.a" ]; then
359+
mv "$PREFIX/lib/x86_64-linux-gnu/libdav1d.a" "$PREFIX/lib/libdav1d.a"
360+
fi
361+
if [ -f "$PREFIX/lib/x86_64-linux-gnu/pkgconfig/dav1d.pc" ]; then
362+
mv "$PREFIX/lib/x86_64-linux-gnu/pkgconfig/dav1d.pc" "$PREFIX/lib/pkgconfig/dav1d.pc"
363+
fi
364+
verify_arch "$PREFIX/lib/libdav1d.a"
365+
366+
echo '\n--------------------'
367+
echo 'Building libaom'
368+
echo '--------------------\n'
369+
mkdir -p $BASEDIR/aom
370+
tar -xzf $SRCDIR/libaom-3.11.0.tar.gz -C $BASEDIR/aom
371+
mkdir -p $BUILDDIR/aom
372+
cd $BUILDDIR/aom
373+
cmake $BASEDIR/aom $AOM_CMAKE_FLAGS \
374+
-DCMAKE_BUILD_TYPE=Release \
375+
-DBUILD_SHARED_LIBS=OFF \
376+
-DENABLE_TESTS=0 \
377+
-DENABLE_TOOLS=0 \
378+
-DENABLE_DOCS=0 \
379+
-DCMAKE_INSTALL_PREFIX=$PREFIX \
380+
-DCMAKE_C_FLAGS="$ARCH_CFLAGS" \
381+
-DCMAKE_CXX_FLAGS="$ARCH_CXXFLAGS"
382+
make
383+
make install
384+
verify_arch "$PREFIX/lib/libaom.a"
385+
322386
echo '\n--------------------'
323387
echo 'Building ffmpeg'
324388
echo '--------------------\n'
325389
mkdir -p $BASEDIR/ffmpeg
326390
tar -xJf $SRCDIR/ffmpeg-7.0.2.orig.tar.xz -C $BASEDIR/ffmpeg --strip-components 1
327391
mkdir -p $BUILDDIR/ffmpeg
328392
cd $BUILDDIR/ffmpeg
393+
export PKG_CONFIG_PATH="$PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH"
329394
$BASEDIR/ffmpeg/configure $FFMPEG_CROSS_COMPILE_FLAGS \
330395
--prefix=$PREFIX \
331396
--disable-doc \
@@ -343,10 +408,15 @@ $BASEDIR/ffmpeg/configure $FFMPEG_CROSS_COMPILE_FLAGS \
343408
--enable-decoder=hevc \
344409
--enable-decoder=vp9 \
345410
--enable-decoder=vp8 \
411+
--enable-decoder=av1 \
346412
--enable-decoder=flac \
347413
--enable-decoder=mp3 \
348414
--enable-decoder=aac \
349415
--enable-decoder=vorbis \
416+
--enable-decoder=libaom \
417+
--enable-decoder=libdav1d \
418+
--enable-libaom \
419+
--enable-libdav1d \
350420
--disable-iconv \
351421
--disable-cuda \
352422
--disable-cuvid \
@@ -376,26 +446,6 @@ cp libyuv.a "$PREFIX/lib"
376446
cp -r include/* "$PREFIX/include/"
377447
verify_arch "$PREFIX/lib/libyuv.a"
378448

379-
echo '\n--------------------'
380-
echo 'Building libaom'
381-
echo '--------------------\n'
382-
mkdir -p $BASEDIR/aom
383-
tar -xzf $SRCDIR/libaom-3.11.0.tar.gz -C $BASEDIR/aom
384-
mkdir -p $BUILDDIR/aom
385-
cd $BUILDDIR/aom
386-
cmake $BASEDIR/aom $AOM_CMAKE_FLAGS \
387-
-DCMAKE_BUILD_TYPE=Release \
388-
-DBUILD_SHARED_LIBS=OFF \
389-
-DENABLE_TESTS=0 \
390-
-DENABLE_TOOLS=0 \
391-
-DENABLE_DOCS=0 \
392-
-DCMAKE_INSTALL_PREFIX=$PREFIX \
393-
-DCMAKE_C_FLAGS="$ARCH_CFLAGS" \
394-
-DCMAKE_CXX_FLAGS="$ARCH_CXXFLAGS"
395-
make
396-
make install
397-
verify_arch "$PREFIX/lib/libaom.a"
398-
399449
echo '\n--------------------'
400450
echo 'Building libavif'
401451
echo '--------------------\n'
@@ -406,11 +456,14 @@ cd $BUILDDIR/libavif
406456
cmake $BASEDIR/libavif $CMAKE_CROSS_COMPILE_FLAGS \
407457
-DCMAKE_BUILD_TYPE=Release \
408458
-DAVIF_CODEC_AOM=SYSTEM \
459+
-DAVIF_CODEC_DAV1D=SYSTEM \
409460
-DAVIF_BUILD_APPS=OFF \
410461
-DLIBYUV_LIBRARY=$PREFIX/lib/libyuv.a \
411462
-DLIBYUV_INCLUDE_DIR=$PREFIX/include \
412463
-DAOM_LIBRARY=$PREFIX/lib/libaom.a \
413464
-DAOM_INCLUDE_DIR=$PREFIX/include \
465+
-DDAV1D_LIBRARY=$PREFIX/lib/libdav1d.a \
466+
-DDAV1D_INCLUDE_DIR=$PREFIX/include \
414467
-DCMAKE_PREFIX_PATH=$PREFIX \
415468
-DBUILD_SHARED_LIBS=OFF \
416469
-DCMAKE_INSTALL_PREFIX=$PREFIX \

0 commit comments

Comments
 (0)