Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/build-ffmpeg.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,15 @@ jobs:
install: >-
base-devel
openssl-devel
vim
${{ matrix.msys_prefix }}-pkgconf
${{ matrix.msys_system == 'CLANGARM64' && format('{0}-clang', matrix.msys_prefix) || format('{0}-gcc {0}-nasm', matrix.msys_prefix) }}
msystem: ${{ matrix.msys_system }}
path-type: inherit
- name: Build FFmpeg
env:
CIBW_ARCHS: ${{ matrix.msys_system == 'CLANGARM64' && 'ARM64' || (matrix.msys_prefix && 'AMD64' || matrix.arch) }}
CIBW_BEFORE_ALL_LINUX: python scripts/install-vmaf-build-tools.py
CIBW_BEFORE_BUILD: python scripts/build-ffmpeg.py /tmp/vendor
CIBW_BEFORE_BUILD_WINDOWS: python scripts\build-ffmpeg.py C:\cibw\vendor
CIBW_BUILD: cp311-*
Expand All @@ -125,6 +127,7 @@ jobs:
with:
name: output-${{ matrix.os }}
path: output/
if-no-files-found: error

cross-build:
needs: fan
Expand All @@ -151,7 +154,7 @@ jobs:
- name: Build FFmpeg
env:
CIBW_ARCHS: ${{ matrix.arch }}
CIBW_BEFORE_ALL_LINUX: ./scripts/install-static-clang.sh
CIBW_BEFORE_ALL_LINUX: python scripts/install-vmaf-build-tools.py && ./scripts/install-static-clang.sh
CIBW_BEFORE_BUILD_LINUX: python scripts/build-ffmpeg.py /tmp/vendor
CIBW_BUILD: cp311-${{ matrix.build }}${{ matrix.arch }}
CIBW_ENVIRONMENT_LINUX: >
Expand All @@ -171,6 +174,7 @@ jobs:
with:
name: output-${{ matrix.build }}${{ matrix.arch }}
path: output/
if-no-files-found: error

upload-release:
if: github.event_name == 'release'
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Currently FFmpeg 8.1.2 is built with the following packages enabled for all plat
- png 1.6.58
- webp 1.6.0
- opencore-amr 0.1.6
- vmaf 3.2.0
- x264 32c3b801191522961102d4bea292cdb61068d0dd (except armv7l)
- x265 4.2 (except armv7l)

Expand Down
39 changes: 39 additions & 0 deletions scripts/build-ffmpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,41 @@ def make_tarball_name() -> str:
else:
return "ffmpeg-unknown"


def validate_output_tarball(path: str) -> None:
runtime_member = None
with tarfile.open(path, "r:gz") as tar:
for member in tar.getmembers():
if not member.isfile() or member.size <= 0:
continue

name = member.name.lower().replace("\\", "/")
basename = os.path.basename(name)
if plat == "Windows":
matches = name == "bin/libvmaf.dll"
elif plat == "Darwin":
matches = (
name.startswith("lib/")
and basename.startswith("libvmaf.")
and basename.endswith(".dylib")
)
else:
matches = (
name.startswith("lib/")
and basename.startswith("libvmaf.so.")
)

if matches:
runtime_member = (member.name, member.size)

if runtime_member is None:
raise RuntimeError(f"VMAF runtime library missing from {path}")

print(
f"{os.path.basename(path)}: {os.path.getsize(path)} bytes "
f"({runtime_member[0]}: {runtime_member[1]} bytes)"
)

def main():
parser = argparse.ArgumentParser("build-ffmpeg")
parser.add_argument("destination")
Expand Down Expand Up @@ -112,6 +147,7 @@ def main():

output_tarball = os.path.join(output_dir, make_tarball_name() + ".tar.gz")
if os.path.exists(output_tarball):
validate_output_tarball(output_tarball)
return

builder = Builder(dest_dir=dest_dir)
Expand Down Expand Up @@ -159,6 +195,7 @@ def main():
"--enable-libopencore-amrwb",
"--enable-libopus",
"--enable-libsvtav1",
"--enable-libvmaf",
"--enable-libvpx",
"--enable-libwebp",
"--enable-libxcb" if plat == "Linux" else "--disable-libxcb",
Expand Down Expand Up @@ -342,6 +379,8 @@ def main():
with open(filepath, "rb") as f:
tar.addfile(info, f)

validate_output_tarball(output_tarball)


if __name__ == "__main__":
main()
55 changes: 55 additions & 0 deletions scripts/install-vmaf-build-tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import os
import shutil
import subprocess
import tempfile


def xxd_supports_model_embedding() -> bool:
xxd = shutil.which("xxd")
if xxd is None:
return False

with tempfile.TemporaryDirectory() as temp_dir:
input_path = os.path.join(temp_dir, "input")
output_path = os.path.join(temp_dir, "output.c")
with open(input_path, "wb") as input_file:
input_file.write(b"vmaf")
result = subprocess.run(
[xxd, "-i", input_path, output_path],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=False,
)
return (
result.returncode == 0
and os.path.exists(output_path)
and os.path.getsize(output_path) > 0
)


def main() -> None:
if xxd_supports_model_embedding():
return

if shutil.which("apt-get"):
subprocess.run(["apt-get", "update"], check=True)
subprocess.run(["apt-get", "install", "-y", "xxd"], check=True)
else:
package_commands = (
("apk", ["apk", "add", "--no-cache", "xxd"]),
("dnf", ["dnf", "install", "-y", "vim-common"]),
("yum", ["yum", "install", "-y", "vim-common"]),
)
for package_manager, command in package_commands:
if shutil.which(package_manager):
subprocess.run(command, check=True)
break
else:
raise RuntimeError("Unable to install xxd for VMAF model embedding")

if not xxd_supports_model_embedding():
raise RuntimeError("xxd does not support VMAF model embedding")


if __name__ == "__main__":
main()
17 changes: 16 additions & 1 deletion scripts/pkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,21 @@ def __lt__(self, other):
sha256="483eb4061088e2b34b358e47540b5d495a96cd468e361050fae615b1809dc4a1",
build_arguments=["--disable-dependency-tracking"],
),
Package(
name="vmaf",
source_url="https://github.com/Netflix/vmaf/archive/refs/tags/v3.2.0.tar.gz",
source_filename="vmaf-3.2.0.tar.gz",
sha256="a28f93f3b4fa65601be324587072e32a6a704a304ba7b1aec9b70b3f709bc1dc",
build_system="meson",
source_dir="libvmaf",
build_arguments=[
"--buildtype=release",
"-Ddefault_library=shared",
"-Denable_tests=false",
"-Denable_docs=false",
"-Denable_tools=false",
],
),
Package(
name="x264",
source_url="https://code.videolan.org/videolan/x264/-/archive/b35605ace3ddf7c1a5d67a2eb553f034aef41d55/x264-b35605ace3ddf7c1a5d67a2eb553f034aef41d55.tar.bz2",
Expand Down Expand Up @@ -199,7 +214,7 @@ def __lt__(self, other):

all_packages: list[Package] = [ffmpeg_package]
all_packages.extend(codec_group)
all_packages.extend(gnutls_group)
all_packages.extend(gnutls_group)
all_packages.extend(
[nasm_package, alsa_package, nvheaders_package, amfheaders_package, libvpl_package]
)
38 changes: 37 additions & 1 deletion src/dummy/binding.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,53 @@
#include <Python.h>
#include "libavcodec/avcodec.h"
#include "libavdevice/avdevice.h"
#include "libavfilter/avfilter.h"
#include "libavformat/avformat.h"

#define MODULE_NAME "dummy.binding"

static int
verify_libvmaf_filter(void)
{
const AVFilter *libvmaf;
AVFilterContext *libvmaf_context;
AVFilterGraph *graph;

libvmaf = avfilter_get_by_name("libvmaf");
if (libvmaf == NULL) {
PyErr_SetString(PyExc_RuntimeError, "FFmpeg libvmaf filter is unavailable");
return -1;
}

graph = avfilter_graph_alloc();
if (graph == NULL) {
PyErr_NoMemory();
return -1;
}
if (avfilter_graph_create_filter(
&libvmaf_context, libvmaf, "libvmaf", NULL, NULL, graph) < 0) {
avfilter_graph_free(&graph);
PyErr_SetString(
PyExc_RuntimeError,
"FFmpeg libvmaf filter initialization failed"
);
return -1;
}
avfilter_graph_free(&graph);

return 0;
}

static PyObject*
test(PyObject *args)
test(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored))
{
// initialise libraries
avformat_network_init();
avdevice_register_all();

if (verify_libvmaf_filter() < 0)
return NULL;

fprintf(stderr, "FFmpeg is OK\n");

Py_RETURN_NONE;
Expand Down