refactor: FFmpeg 7.1 support (#101)

* feat: FFmpeg 7.1

* test: reset golden

* fix encoder

* test: update golden files
This commit is contained in:
Adrian Shum 2025-07-21 20:43:26 +08:00 committed by GitHub
parent 1b26cf82f8
commit d01256b982
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 73 additions and 65 deletions

View file

@ -13,17 +13,17 @@ jobs:
env: env:
CGO_CFLAGS_ALLOW: -Xpreprocessor CGO_CFLAGS_ALLOW: -Xpreprocessor
VIPS_VERSION: 8.17.1 VIPS_VERSION: 8.17.1
FFMPEG_VERSION: 5.1.2 FFMPEG_VERSION: 7.1.1
V: 4 V: 5
steps: steps:
- name: Set up Go 1.x - name: Set up Go 1.x
uses: actions/setup-go@v2 uses: actions/setup-go@v4
with: with:
go-version: ^1.24 go-version: ^1.24
- name: Check out code - name: Check out code
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Install linux dependencies - name: Install linux dependencies
run: | run: |
@ -38,7 +38,7 @@ jobs:
libopenslide-dev libcfitsio-dev liborc-0.4-dev libpango1.0-dev \ libopenslide-dev libcfitsio-dev liborc-0.4-dev libpango1.0-dev \
libtiff5-dev libgsf-1-dev giflib-tools libwebp-dev \ libtiff5-dev libgsf-1-dev giflib-tools libwebp-dev \
yasm libx264-dev libx265-dev libnuma-dev libvpx-dev libtheora-dev \ yasm libx264-dev libx265-dev libnuma-dev libvpx-dev libtheora-dev \
librtmp-dev libvorbis-dev \ librtmp-dev libvorbis-dev libdav1d-dev libaom-dev \
libopenjp2-7-dev libcgif-dev libopenjp2-7-dev libcgif-dev
# Install newer libheif from backports # Install newer libheif from backports
sudo apt-get install -y -t jammy-backports libheif-dev || sudo apt-get install -y libheif-dev sudo apt-get install -y -t jammy-backports libheif-dev || sudo apt-get install -y libheif-dev
@ -78,13 +78,12 @@ jobs:
restore-keys: | restore-keys: |
${{ runner.os }}-ffmpeg-${{ env.V }}- ${{ runner.os }}-ffmpeg-${{ env.V }}-
- name: Build ffmpeg from source - name: Build ffmpeg from source
run: | run: |
if [ ! -d "ffmpeg-${{ env.FFMPEG_VERSION }}" ] if [ ! -d "ffmpeg-${{ env.FFMPEG_VERSION }}" ]
then then
wget https://ffmpeg.org/releases/ffmpeg-${{ env.FFMPEG_VERSION }}.tar.bz2 wget https://ffmpeg.org/releases/ffmpeg-${{ env.FFMPEG_VERSION }}.tar.xz
tar jvxf ffmpeg-${{ env.FFMPEG_VERSION }}.tar.bz2 tar xf ffmpeg-${{ env.FFMPEG_VERSION }}.tar.xz
fi fi
cd ffmpeg-${{ env.FFMPEG_VERSION }} cd ffmpeg-${{ env.FFMPEG_VERSION }}
./configure --prefix=/usr/local \ ./configure --prefix=/usr/local \
@ -101,7 +100,9 @@ jobs:
--enable-libwebp \ --enable-libwebp \
--enable-libvpx \ --enable-libvpx \
--enable-libx265 \ --enable-libx265 \
--enable-libx264 --enable-libx264 \
--enable-libdav1d \
--enable-libaom
make && sudo make install make && sudo make install
sudo ldconfig sudo ldconfig

View file

@ -1,14 +1,13 @@
ARG GOLANG_VERSION=1.24.5 ARG GOLANG_VERSION=1.24.5
FROM golang:${GOLANG_VERSION}-bookworm as builder FROM golang:${GOLANG_VERSION}-bookworm as builder
ARG FFMPEG_VERSION=5.1.2 ARG FFMPEG_VERSION=7.1.1
ARG VIPS_VERSION=8.17.1 ARG VIPS_VERSION=8.17.1
ARG TARGETARCH ARG TARGETARCH
ENV PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ENV PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
ENV MAKEFLAGS="-j8" ENV MAKEFLAGS="-j8"
# Installs libvips + required libraries # Installs libvips + required libraries
RUN DEBIAN_FRONTEND=noninteractive \ RUN DEBIAN_FRONTEND=noninteractive \
apt-get update && \ apt-get update && \
@ -21,7 +20,7 @@ RUN DEBIAN_FRONTEND=noninteractive \
swig libpango1.0-dev libmatio-dev libopenslide-dev libcfitsio-dev libopenjp2-7-dev \ swig libpango1.0-dev libmatio-dev libopenslide-dev libcfitsio-dev libopenjp2-7-dev \
libgsf-1-dev libfftw3-dev liborc-0.4-dev librsvg2-dev libimagequant-dev libaom-dev libheif-dev \ libgsf-1-dev libfftw3-dev liborc-0.4-dev librsvg2-dev libimagequant-dev libaom-dev libheif-dev \
yasm libx264-dev libx265-dev libnuma-dev libvpx-dev libtheora-dev \ yasm libx264-dev libx265-dev libnuma-dev libvpx-dev libtheora-dev \
libspng-dev libcgif-dev librtmp-dev libvorbis-dev && \ libspng-dev libcgif-dev librtmp-dev libvorbis-dev libdav1d-dev && \
cd /tmp && \ cd /tmp && \
curl -fsSLO https://github.com/libvips/libvips/releases/download/v${VIPS_VERSION}/vips-${VIPS_VERSION}.tar.xz && \ curl -fsSLO https://github.com/libvips/libvips/releases/download/v${VIPS_VERSION}/vips-${VIPS_VERSION}.tar.xz && \
tar xf vips-${VIPS_VERSION}.tar.xz && \ tar xf vips-${VIPS_VERSION}.tar.xz && \
@ -36,8 +35,8 @@ RUN DEBIAN_FRONTEND=noninteractive \
ninja -C _build && \ ninja -C _build && \
ninja -C _build install && \ ninja -C _build install && \
cd /tmp && \ cd /tmp && \
curl -fsSLO https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.bz2 && \ curl -fsSLO https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.xz && \
tar jvxf ffmpeg-${FFMPEG_VERSION}.tar.bz2 && \ tar xf ffmpeg-${FFMPEG_VERSION}.tar.xz && \
cd /tmp/ffmpeg-${FFMPEG_VERSION} && \ cd /tmp/ffmpeg-${FFMPEG_VERSION} && \
./configure --prefix=/usr/local \ ./configure --prefix=/usr/local \
--disable-debug \ --disable-debug \
@ -53,7 +52,9 @@ RUN DEBIAN_FRONTEND=noninteractive \
--enable-libwebp \ --enable-libwebp \
--enable-libvpx \ --enable-libvpx \
--enable-libx265 \ --enable-libx265 \
--enable-libx264 && \ --enable-libx264 \
--enable-libdav1d \
--enable-libaom && \
make && make install && \ make && make install && \
ldconfig && \ ldconfig && \
rm -rf /usr/local/lib/python* && \ rm -rf /usr/local/lib/python* && \
@ -89,7 +90,7 @@ RUN echo "deb http://deb.debian.org/debian bookworm-backports main" > /etc/apt/s
libpango1.0-0 libmatio11 libopenslide0 libopenjp2-7 libjemalloc2 \ libpango1.0-0 libmatio11 libopenslide0 libopenjp2-7 libjemalloc2 \
libgsf-1-114 libfftw3-bin liborc-0.4-0 librsvg2-2 libcfitsio10 libimagequant0 libaom3 \ libgsf-1-114 libfftw3-bin liborc-0.4-0 librsvg2-2 libcfitsio10 libimagequant0 libaom3 \
libx264-dev libx265-dev libnuma-dev libvpx7 libtheora0 libvorbis-dev \ libx264-dev libx265-dev libnuma-dev libvpx7 libtheora0 libvorbis-dev \
libspng0 libcgif0 && \ libspng0 libcgif0 libdav1d6 && \
apt-get install --no-install-recommends -y -t bookworm-backports libheif1 && \ apt-get install --no-install-recommends -y -t bookworm-backports libheif1 && \
ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so && \ ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so && \
apt-get autoremove -y && \ apt-get autoremove -y && \

View file

@ -210,23 +210,28 @@ AVFrame *convert_frame_to_rgb(AVFrame *frame, int alpha) {
return output_frame; return output_frame;
} }
AVPacket create_packet() { AVPacket *create_packet() {
AVPacket *pkt = av_packet_alloc(); AVPacket *pkt = av_packet_alloc();
if (!pkt) {
return NULL;
}
pkt->data = NULL; pkt->data = NULL;
pkt->size = 0; pkt->size = 0;
return *pkt; return pkt;
} }
int int obtain_next_frame(AVFormatContext *fmt_ctx, AVCodecContext *dec_ctx, int stream_index, AVPacket *pkt, AVFrame **frame) {
obtain_next_frame(AVFormatContext *fmt_ctx, AVCodecContext *dec_ctx, int stream_index, AVPacket *pkt, AVFrame **frame) {
int err = 0, retry = 0; int err = 0, retry = 0;
if (!(*frame) && !(*frame = av_frame_alloc())) { if (!(*frame) && !(*frame = av_frame_alloc())) {
err = AVERROR(ENOMEM); err = AVERROR(ENOMEM);
return err; return err;
} }
// Try to receive a frame from the decoder first
if ((err = avcodec_receive_frame(dec_ctx, *frame)) != AVERROR(EAGAIN)) { if ((err = avcodec_receive_frame(dec_ctx, *frame)) != AVERROR(EAGAIN)) {
return err; return err;
} }
while (1) { while (1) {
if ((err = av_read_frame(fmt_ctx, pkt)) < 0) { if ((err = av_read_frame(fmt_ctx, pkt)) < 0) {
break; break;
@ -239,6 +244,7 @@ obtain_next_frame(AVFormatContext *fmt_ctx, AVCodecContext *dec_ctx, int stream_
if (retry++ >= 10) { if (retry++ >= 10) {
break; break;
} }
av_packet_unref(pkt);
continue; continue;
} }
if (!(*frame) && !(*frame = av_frame_alloc())) { if (!(*frame) && !(*frame = av_frame_alloc())) {
@ -251,7 +257,7 @@ obtain_next_frame(AVFormatContext *fmt_ctx, AVCodecContext *dec_ctx, int stream_
} }
av_packet_unref(pkt); av_packet_unref(pkt);
} }
if (pkt->buf) { if (pkt->data) {
av_packet_unref(pkt); av_packet_unref(pkt);
} }
return err; return err;

View file

@ -301,8 +301,13 @@ func populateFrames(av *AVContext, frames <-chan *C.AVFrame) <-chan struct{} {
func createThumbContext(av *AVContext, maxFrames C.int) error { func createThumbContext(av *AVContext, maxFrames C.int) error {
pkt := C.create_packet() pkt := C.create_packet()
if pkt == nil {
return avError(C.int(ErrNoMem))
}
defer C.av_packet_free(&pkt)
var frame *C.AVFrame var frame *C.AVFrame
err := C.obtain_next_frame(av.formatContext, av.codecContext, av.stream.index, &pkt, &frame) err := C.obtain_next_frame(av.formatContext, av.codecContext, av.stream.index, pkt, &frame)
if err >= 0 { if err >= 0 {
incrementDuration(av, frame, 0) incrementDuration(av, frame, 0)
av.thumbContext = C.create_thumb_context(av.stream, frame) av.thumbContext = C.create_thumb_context(av.stream, frame)
@ -311,9 +316,6 @@ func createThumbContext(av *AVContext, maxFrames C.int) error {
} }
} }
if err < 0 { if err < 0 {
if pkt.buf != nil {
C.av_packet_unref(&pkt)
}
if frame != nil { if frame != nil {
C.av_frame_free(&frame) C.av_frame_free(&frame)
} }
@ -332,18 +334,20 @@ func createThumbContext(av *AVContext, maxFrames C.int) error {
frames := make(chan *C.AVFrame, n) frames := make(chan *C.AVFrame, n)
done := populateFrames(av, frames) done := populateFrames(av, frames)
frames <- frame frames <- frame
if pkt.buf != nil {
C.av_packet_unref(&pkt)
}
return populateThumbContext(av, frames, n, done) return populateThumbContext(av, frames, n, done)
} }
func populateThumbContext(av *AVContext, frames chan *C.AVFrame, n C.int, done <-chan struct{}) error { func populateThumbContext(av *AVContext, frames chan *C.AVFrame, n C.int, done <-chan struct{}) error {
pkt := C.create_packet() pkt := C.create_packet()
if pkt == nil {
return avError(C.int(ErrNoMem))
}
defer C.av_packet_free(&pkt)
var frame *C.AVFrame var frame *C.AVFrame
var err C.int var err C.int
for i := C.int(1); i < n; i++ { for i := C.int(1); i < n; i++ {
err = C.obtain_next_frame(av.formatContext, av.codecContext, av.stream.index, &pkt, &frame) err = C.obtain_next_frame(av.formatContext, av.codecContext, av.stream.index, pkt, &frame)
if err < 0 { if err < 0 {
break break
} }
@ -362,9 +366,6 @@ func populateThumbContext(av *AVContext, frames chan *C.AVFrame, n C.int, done <
av.selectedIndex = av.availableIndex av.selectedIndex = av.availableIndex
} }
close(frames) close(frames)
if pkt.buf != nil {
C.av_packet_unref(&pkt)
}
if frame != nil { if frame != nil {
C.av_frame_free(&frame) C.av_frame_free(&frame)
} }

View file

@ -47,10 +47,9 @@ int create_codec_context(AVStream *video_stream, AVCodecContext **dec_ctx);
AVFrame *convert_frame_to_rgb(AVFrame *frame, int alpha); AVFrame *convert_frame_to_rgb(AVFrame *frame, int alpha);
AVPacket create_packet(); AVPacket *create_packet();
int int obtain_next_frame(AVFormatContext *fmt_ctx, AVCodecContext *dec_ctx, int stream_index, AVPacket *pkt, AVFrame **frame);
obtain_next_frame(AVFormatContext *fmt_ctx, AVCodecContext *dec_ctx, int stream_index, AVPacket *pkt, AVFrame **frame);
ThumbContext *create_thumb_context(AVStream *stream, AVFrame *frame); ThumbContext *create_thumb_context(AVStream *stream, AVFrame *frame);

View file

@ -1 +1 @@
{"orientation":1,"duration":3925,"width":492,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":1,"duration":3924,"width":492,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":1,"duration":3925,"width":492,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":1,"duration":3924,"width":492,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":1,"duration":3925,"width":492,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":1,"duration":3924,"width":492,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":1,"duration":3925,"width":492,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":1,"duration":3924,"width":492,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":1,"duration":3925,"width":492,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":1,"duration":3924,"width":492,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":1,"duration":3925,"width":492,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":1,"duration":3924,"width":492,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":1,"duration":2544,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":1,"duration":2535,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":1,"duration":2544,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":1,"duration":2535,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":1,"duration":2544,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":1,"duration":2535,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":1,"duration":2544,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":1,"duration":2535,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":1,"duration":2544,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":1,"duration":2535,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":1,"duration":2544,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":1,"duration":2535,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":3,"duration":2544,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":3,"duration":2535,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":3,"duration":2544,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":3,"duration":2535,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":3,"duration":2544,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":3,"duration":2535,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":3,"duration":2544,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":3,"duration":2535,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":3,"duration":2544,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":3,"duration":2535,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":3,"duration":2544,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":3,"duration":2535,"width":480,"height":360,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":6,"duration":2544,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":6,"duration":2535,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":6,"duration":2544,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":6,"duration":2535,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":6,"duration":2544,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":6,"duration":2535,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":6,"duration":2544,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":6,"duration":2535,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":6,"duration":2544,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":6,"duration":2535,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":6,"duration":2544,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":6,"duration":2535,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":8,"duration":2544,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":8,"duration":2535,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":8,"duration":2544,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":8,"duration":2535,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":8,"duration":2544,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":8,"duration":2535,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":8,"duration":2544,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":8,"duration":2535,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":8,"duration":2544,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":8,"duration":2535,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true}

View file

@ -1 +1 @@
{"orientation":8,"duration":2544,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true} {"orientation":8,"duration":2535,"width":360,"height":480,"fps":29.97002997002997,"has_video":true,"has_audio":true}