From 8f371caf76c9dd0cbe9d7ec533e234cbed117363 Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Fri, 30 Oct 2026 01:15:35 +0900 Subject: [PATCH] libvpx-m: libvpx 1.17.0 (VP9) and libwebm 1.0.0.32 (WebM) as C++ modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mcpplibs.mwrap.libvpx over compat.libvpx and compat.libwebm, generated from the pinned tags' headers (tools/gen_exports.py over tools/inventory.py): - mcpplibs.mwrap.libvpx: libvpx's 133 types, 192 enumerators, the 35 functions upstream exports and the two VP9 interfaces (VP8 is not built, so its interfaces are not exported); the headers' 86 typed controls (static functions made by VPX_CTRL_USE_TYPE) adapted under the same names in mcpplibs::mwrap::libvpx, each calling vpx_codec_control_ as upstream's body does; - mcpplibs.mwrap.libvpx.webm: libwebm's mkvmuxer, mkvparser and libwebm namespaces (classes, types, enumerators, free functions); the namespace-scope constants, which have internal linkage, under the same names in mcpplibs::mwrap::libvpx::mkvmuxer / ::mkvparser; - mcpplibs.mwrap.libvpx.macros: 74 macros as constants (of their expression's type), functions (vpx_codec_enc_init, vpx_codec_dec_init …) and constexpr functions (VPX_VERSION_MAJOR …); what stays a macro is in . Tests: VP9 at good quality, real time, four threads and lossless, muxed into WebM with mkvmuxer, parsed back with mkvparser and decoded, each file's bytes pinned; every macro mapping against the raw headers; every exported function and control linked. The export audit (tools/check_exports.py), and the conformance run (tools/run_conformance.py): examples/vpxwebm, written against the module, writes the same IVF bytes as upstream's vpxenc for six settings, and vpxdec reads its WebM files to the same frames as the module does vpxenc's. The default template (webm), the bilingual README, the usage documentation, CI (Linux x86_64 GCC/LLVM, Linux arm64 GCC, macOS, Windows, the Web), release and template workflows. Until mcpp-index publishes the two compat packages (mcpplibs/mcpp-index#624), CI resolves them from that PR's branch (tools/candidate_index.py). --- .gitattributes | 2 + .github/workflows/ci.yml | 150 ++++++++++ .github/workflows/release.yml | 49 ++++ .github/workflows/templates.yml | 33 +++ .gitignore | 7 + .xlings.json | 5 + NOTICE | 20 ++ README.md | 116 +++++++- README.zh.md | 101 +++++++ docs/usage.md | 84 ++++++ examples/vpxwebm/mcpp.toml | 15 + examples/vpxwebm/src/main.cpp | 340 ++++++++++++++++++++++ include/mwrap/libvpx/macros.hpp | 26 ++ mcpp.toml | 67 +++++ src/control.cpp | 100 +++++++ src/libvpx.cppm | 485 ++++++++++++++++++++++++++++++++ src/macros.cppm | 119 ++++++++ src/macros_capture.hpp | 162 +++++++++++ src/webm.cppm | 309 ++++++++++++++++++++ templates/webm/mcpp.toml.in | 10 + templates/webm/src/main.cpp.in | 150 ++++++++++ templates/webm/template.toml | 4 + tests/api_test.cpp | 262 +++++++++++++++++ tests/link_test.cpp | 141 ++++++++++ tests/macros_mix_test.cpp | 379 +++++++++++++++++++++++++ tools/candidate_index.py | 73 +++++ tools/check_exports.py | 92 ++++++ tools/check_mwrap_release.py | 50 ++++ tools/gen_exports.py | 386 +++++++++++++++++++++++++ tools/inventory.py | 280 ++++++++++++++++++ tools/run_conformance.py | 152 ++++++++++ tools/test_template.py | 83 ++++++ upstream/documents.json | 55 ++++ upstream/libvpx/AUTHORS | 263 +++++++++++++++++ upstream/libvpx/LICENSE | 31 ++ upstream/libvpx/PATENTS | 23 ++ upstream/libvpx/README | 241 ++++++++++++++++ upstream/libwebm/AUTHORS.TXT | 5 + upstream/libwebm/LICENSE.TXT | 30 ++ upstream/libwebm/PATENTS.TXT | 23 ++ upstream/libwebm/README.libwebm | 148 ++++++++++ 41 files changed, 5069 insertions(+), 2 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/templates.yml create mode 100644 .gitignore create mode 100644 .xlings.json create mode 100644 NOTICE create mode 100644 README.zh.md create mode 100644 docs/usage.md create mode 100644 examples/vpxwebm/mcpp.toml create mode 100644 examples/vpxwebm/src/main.cpp create mode 100644 include/mwrap/libvpx/macros.hpp create mode 100644 mcpp.toml create mode 100644 src/control.cpp create mode 100644 src/libvpx.cppm create mode 100644 src/macros.cppm create mode 100644 src/macros_capture.hpp create mode 100644 src/webm.cppm create mode 100644 templates/webm/mcpp.toml.in create mode 100644 templates/webm/src/main.cpp.in create mode 100644 templates/webm/template.toml create mode 100644 tests/api_test.cpp create mode 100644 tests/link_test.cpp create mode 100644 tests/macros_mix_test.cpp create mode 100644 tools/candidate_index.py create mode 100644 tools/check_exports.py create mode 100644 tools/check_mwrap_release.py create mode 100644 tools/gen_exports.py create mode 100644 tools/inventory.py create mode 100644 tools/run_conformance.py create mode 100644 tools/test_template.py create mode 100644 upstream/documents.json create mode 100644 upstream/libvpx/AUTHORS create mode 100644 upstream/libvpx/LICENSE create mode 100644 upstream/libvpx/PATENTS create mode 100644 upstream/libvpx/README create mode 100644 upstream/libwebm/AUTHORS.TXT create mode 100644 upstream/libwebm/LICENSE.TXT create mode 100644 upstream/libwebm/PATENTS.TXT create mode 100644 upstream/libwebm/README.libwebm diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8a6c241 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Preserve upstream document bytes and original whitespace on every platform. +upstream/** -text -whitespace diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2f8d238 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,150 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + mcpp: + name: mcpp (${{ matrix.os }}, ${{ matrix.toolchain }}) + strategy: + fail-fast: false + matrix: + include: + - { os: ubuntu-24.04, toolchain: 'gcc@16.1.0' } + - { os: ubuntu-24.04, toolchain: 'llvm@23.1.3' } + - { os: ubuntu-24.04-arm, toolchain: 'gcc@16.1.0' } + - { os: macos-15, toolchain: 'llvm@23.1.3' } + - { os: windows-2025, toolchain: 'llvm@23.1.3' } + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + defaults: + run: + shell: bash + env: + XLINGS_NON_INTERACTIVE: 1 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Mwrap identity and bilingual README contract + run: python3 tools/check_mwrap_release.py + + - name: Install Xlings (Linux/macOS) + if: runner.os != 'Windows' + run: | + curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/refs/heads/main/tools/other/quick_install.sh | bash + echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" + + - name: Install Xlings (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + irm https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.ps1 | iex + "$env:USERPROFILE\.xlings\subos\current\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + + - name: Install project tools (.xlings.json) + run: | + for attempt in 1 2 3; do + xlings update > /dev/null 2>&1 || true + if xlings install; then break; fi + [ "$attempt" = 3 ] && exit 1 + sleep 30 + done + mcpp --version + + - name: Select the toolchain + run: | + name="${{ matrix.toolchain }}" + mcpp toolchain install "${name%@*}" "${name#*@}" + mcpp toolchain default "$name" + + # Until mcpp-index publishes compat.libvpx and compat.libwebm + # (mcpplibs/mcpp-index#624), the compat namespace of this checkout's + # manifests resolves from that PR's branch, as an unchanged namespace + # view of its descriptors. Once the published index has the descriptors, + # this step does nothing. + - name: Candidate compat index (only while compat.libvpx is unpublished) + run: python3 tools/candidate_index.py --ci + + - name: Build the modules + run: mcpp build + + - name: Tests (VP9 in WebM through the modules, every macro mapping against the headers, every function linked) + run: mcpp test + + - name: Tests, release profile + run: mcpp test --profile release + + - name: Export audit (the generated sources are fresh; every declaration and macro of the public headers) + run: python3 tools/check_exports.py + + # Upstream's vpxenc and vpxdec from the same tag, built by its own + # configure and makefiles with the system compilers: needs make (not on + # Windows). + - name: Conformance (examples/vpxwebm byte-identical to upstream vpxenc; WebM read by vpxdec and by mkvparser alike) + if: runner.os != 'Windows' + run: python3 tools/run_conformance.py + + - name: The example builds (Windows) + if: runner.os == 'Windows' + working-directory: examples/vpxwebm + run: mcpp build + + # The Web: wasm32-emscripten (emsdk from xlings, through mcpp), the tests + # under node. + web: + name: mcpp (web, wasm32-emscripten) + runs-on: ubuntu-24.04 + timeout-minutes: 60 + defaults: + run: + shell: bash + env: + XLINGS_NON_INTERACTIVE: 1 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install Xlings + run: | + curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/refs/heads/main/tools/other/quick_install.sh | bash + echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" + + - name: Install project tools (.xlings.json) + run: | + for attempt in 1 2 3; do + xlings update > /dev/null 2>&1 || true + if xlings install; then break; fi + [ "$attempt" = 3 ] && exit 1 + sleep 30 + done + mcpp --version + + - name: Install emsdk + run: mcpp toolchain install emsdk 6.0.9 + + - name: Candidate compat index (only while compat.libvpx is unpublished) + run: python3 tools/candidate_index.py --ci + + - name: Tests under node + run: mcpp test --toolchain emsdk@6.0.9 --target wasm32-emscripten + + - name: Tests under node, release profile + run: mcpp test --toolchain emsdk@6.0.9 --target wasm32-emscripten --profile release diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..f0c6447 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,49 @@ +name: Release +on: + push: + tags: ["mwrap-*"] +permissions: + contents: read +jobs: + publish: + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: write + actions: read + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Check tag, upstream version and bilingual entry pages + run: python3 tools/check_mwrap_release.py --tag "$GITHUB_REF_NAME" + - name: Require successful source CI at this exact commit + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + result=$(gh run list --repo "$GITHUB_REPOSITORY" --workflow ci.yml --commit "$GITHUB_SHA" --limit 1 --json status,conclusion --jq '.[0] | .status == "completed" and .conclusion == "success"') + test "$result" = true || { echo 'No successful source CI at the tag commit'; exit 1; } + - name: Build immutable source asset and publish + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + version="${GITHUB_REF_NAME#mwrap-}" + repo="${GITHUB_REPOSITORY#*/}" + asset="${repo}-${version}.tar.gz" + git archive --format=tar.gz --prefix="${repo}-${version}/" "$GITHUB_SHA" > "$RUNNER_TEMP/$asset" + (cd "$RUNNER_TEMP" && sha256sum "$asset" > "$asset.sha256") + if gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then + echo 'Release already exists; refusing to change immutable assets'; exit 1 + fi + extra=() + if [[ "$version" == *-rc* ]]; then extra+=(--prerelease); fi + gh release create "$GITHUB_REF_NAME" "$RUNNER_TEMP/$asset" "$RUNNER_TEMP/$asset.sha256" \ + --repo "$GITHUB_REPOSITORY" --verify-tag --title "$repo $version" \ + --notes "Module wrapper for upstream $version. New mwrap package identities become installable after mcpp-index publication; see the bilingual README for the tested support scope." "${extra[@]}" diff --git a/.github/workflows/templates.yml b/.github/workflows/templates.yml new file mode 100644 index 0000000..ea63f41 --- /dev/null +++ b/.github/workflows/templates.yml @@ -0,0 +1,33 @@ +name: Actual project templates +on: + push: + branches: [main] + pull_request: + workflow_dispatch: +permissions: + contents: read +concurrency: + group: templates-${{ github.ref }} + cancel-in-progress: true +env: + MCPP_VERSION: "2026.10.27.1" +jobs: + templates: + runs-on: ubuntu-24.04 + timeout-minutes: 60 + steps: + - uses: actions/checkout@v4 + - name: Install the pinned mcpp + shell: bash + run: | + archive="mcpp-${MCPP_VERSION}-linux-x86_64.tar.gz" + curl -L -fsS -o "$archive" "https://github.com/mcpp-community/mcpp/releases/download/v${MCPP_VERSION}/$archive" + tar -xzf "$archive" + echo "$PWD/mcpp-${MCPP_VERSION}-linux-x86_64/bin" >> "$GITHUB_PATH" + # The candidate index (tools/candidate_index.py) only while mcpp-index + # does not publish compat.libvpx and compat.libwebm. + - name: Actual scaffold, strict build and run of the default template (webm) + shell: bash + run: | + index=$(python3 tools/candidate_index.py --checkout-only) + python3 tools/test_template.py --toolchain gcc@16.1.0 ${index:+--index "$index"} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6dfbae --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +target +compile_commands.json +mcpp.lock +.mcpp/ +.xlings/ +doctor.log +__pycache__/ diff --git a/.xlings.json b/.xlings.json new file mode 100644 index 0000000..9a0e146 --- /dev/null +++ b/.xlings.json @@ -0,0 +1,5 @@ +{ + "workspace": { + "xim:mcpp": "2026.10.27.1" + } +} diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..53488e3 --- /dev/null +++ b/NOTICE @@ -0,0 +1,20 @@ +libvpx-m: C++ module wrapper +Copyright (c) 2026 mcpp-community contributors + +The wrapper contributions are licensed under the Apache License, Version 2.0. +Upstream code, copied interfaces and dependencies retain their original +licenses and copyright notices. + +libvpx is distributed under the BSD 3-Clause license, with the WebM +project's additional patent grant: + + Copyright (c) 2010, The WebM Project authors. All rights reserved. + +libwebm is distributed under the BSD 3-Clause license, with the same grant: + + Copyright (c) 2010, Google Inc. All rights reserved. + +Their original READMEs, licenses, patent grants and authors are retained in +upstream/libvpx/ and upstream/libwebm/. The libraries themselves are built +by compat.libvpx and compat.libwebm from the same tags. Provenance and +checksums are recorded in upstream/documents.json. diff --git a/README.md b/README.md index 0a50ca6..f11c075 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,115 @@ -# libvpx-m +# libvpx -libvpx (VP9) and libwebm (WebM) as C++23 modules for mcpp. Work in progress. +> libvpx, the VP9 video codec, and libwebm, the WebM container, through C++23 modules. + +**English** · [简体中文](README.zh.md) · [Upstream](https://chromium.googlesource.com/webm/libvpx) · [libwebm](https://github.com/webmproject/libwebm) · [Releases](https://github.com/mcpplibs/libvpx-m/releases) · [mcpp](https://github.com/mcpp-community/mcpp) · [Index](https://github.com/mcpplibs/mcpp-index) + +[![C++23](https://img.shields.io/badge/C%2B%2B-23-blue.svg)](https://en.cppreference.com/w/cpp/23) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE) +[![libvpx](https://img.shields.io/badge/libvpx-BSD--3--Clause-blue.svg)](upstream/libvpx/LICENSE) +[![libwebm](https://img.shields.io/badge/libwebm-BSD--3--Clause-blue.svg)](upstream/libwebm/LICENSE.TXT) +[![mcpp-community](https://img.shields.io/badge/mcpp--community-mwrap-blue)](https://github.com/mcpp-community) +[![CI](https://github.com/mcpplibs/libvpx-m/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/mcpplibs/libvpx-m/actions/workflows/ci.yml) + +`mcpplibs.mwrap.libvpx` 1.17.0 encodes and decodes VP9, the royalty-free +video of the Web, and writes and reads WebM, the container every browser +plays. The upstream APIs are unchanged: `vpx_codec_enc_config_default`, +`vpx_codec_encode`, `vpx_codec_get_cx_data`, `vpx_codec_decode`, +`vpx_codec_get_frame`, `vpx_img_alloc` … are libvpx's globals, and +`mkvmuxer::Segment`, `mkvparser::Segment`, `mkvmuxer::IMkvWriter`, +`mkvparser::IMkvReader` … are libwebm's classes in their own namespaces. The +libraries are `compat.libvpx` 1.17.0 and `compat.libwebm` 1.0.0.32. The +headers' typed controls (`vpx_codec_control_VP8E_SET_CPUUSED` …) are adapted +under the same names, and the macros are constants and functions. On every +CI run the files written are byte-identical to upstream's own `vpxenc`, and +`vpxdec` reads them to the same frames. + +## Modules + +- `import mcpplibs.mwrap.libvpx;`: the encoder and decoder (`vpx/vpx_encoder.h`, `vpx/vpx_decoder.h`, `vpx/vp8cx.h`, `vpx/vp8dx.h` …), re-exporting the two below +- `import mcpplibs.mwrap.libvpx.webm;`: libwebm's `mkvmuxer`, `mkvparser` and `libwebm` namespaces +- `import mcpplibs.mwrap.libvpx.macros;`: `VPX_DL_REALTIME`, `VPX_FRAME_IS_KEY`, `VPX_CODEC_CAP_ENCODER`, `vpx_codec_enc_init` … in `mcpplibs::mwrap::libvpx::macros` + +> More Modules API -> [Usage and API](docs/usage.md) + +## Getting started + +```sh +mcpp new clip --template mcpplibs.mwrap.libvpx@1.17.0 +cd clip +mcpp run -- clip.webm +``` + +The default template encodes two seconds of moving pictures, muxes them into +a WebM in memory, parses it back, decodes every frame and prints the file's +size and the worst PSNR; given a file name it also writes the WebM. + +```toml +[dependencies] +"mcpplibs.mwrap.libvpx" = "1.17.0" +``` + +```cpp +import std; +import mcpplibs.mwrap.libvpx; + +namespace m = mcpplibs::mwrap::libvpx::macros; + +int main() { + vpx_codec_enc_cfg_t cfg; + vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &cfg, 0); + cfg.g_w = 320; + cfg.g_h = 180; + cfg.g_timebase = {1, 30}; + vpx_codec_ctx_t encoder; + m::vpx_codec_enc_init(&encoder, vpx_codec_vp9_cx(), &cfg, 0); + mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_CPUUSED(&encoder, VP8E_SET_CPUUSED, 6); + vpx_image_t img; + vpx_img_alloc(&img, VPX_IMG_FMT_I420, 320, 180, 16); + // … fill img.planes, then vpx_codec_encode(&encoder, &img, pts, 1, 0, m::VPX_DL_GOOD_QUALITY) + vpx_img_free(&img); + vpx_codec_destroy(&encoder); + std::println("{}", vpx_codec_version_str()); +} +``` + +## The libraries + +`compat.libvpx` builds the 101 C files upstream's own `configure +--target=generic-gnu --disable-vp8 --enable-vp9` compiles: libvpx's portable +C, without SIMD, assembler or run-time CPU detection, so one configuration +builds on x86-64 and aarch64, Linux, macOS, Windows and wasm32-emscripten. One +thread (the default) gives the same bytes on every target; more threads tile +the work (on Emscripten without pthreads, in turn on one). VP8 is not built. +`compat.libwebm` builds upstream's `webm` library (mkvmuxer, mkvparser and +the common helpers). mkvmuxer gives a track a random UID unless one is set +(`Track::set_uid`): set it for reproducible files. Details and limits: +[docs/usage.md](docs/usage.md). + +## Examples + +- [examples/vpxwebm](examples/vpxwebm): encodes Y4M to VP9 in WebM or IVF, decodes WebM or IVF to raw I420 and + describes a WebM, written against the module; [tools/run_conformance.py](tools/run_conformance.py) compares it with + upstream's `vpxenc` and `vpxdec`. + +## Build and testing + +Linux (x86_64 GCC and LLVM, arm64 GCC), macOS, Windows and wasm32-emscripten +(under node) CI build the modules and run the tests in the dev and release +profiles: VP9 at good quality, real time, four threads and lossless, muxed +into WebM, parsed and decoded back, each file's bytes pinned; every macro +mapping against the raw headers; every exported function linked. The native +rows also run the export audit ([tools/check_exports.py](tools/check_exports.py): +the generated modules are fresh, every declaration exported, every macro +mapped or accounted for) and, but on Windows, the conformance run against +upstream's tools. A separate job scaffolds the template as an installed +package and runs it. + +## License + +[Apache-2.0](LICENSE) for the wrapper, by mcpp-community; libvpx and libwebm +remain BSD-3-Clause with the WebM project's patent grant +([upstream/libvpx/LICENSE](upstream/libvpx/LICENSE), +[upstream/libvpx/PATENTS](upstream/libvpx/PATENTS), +[upstream/libwebm/LICENSE.TXT](upstream/libwebm/LICENSE.TXT), +[upstream/libwebm/PATENTS.TXT](upstream/libwebm/PATENTS.TXT)). diff --git a/README.zh.md b/README.zh.md new file mode 100644 index 0000000..7b700fe --- /dev/null +++ b/README.zh.md @@ -0,0 +1,101 @@ +# libvpx + +> 通过 C++23 模块使用 VP9 视频编解码库 libvpx 与 WebM 容器库 libwebm。 + +[English](README.md) · **简体中文** · [Upstream](https://chromium.googlesource.com/webm/libvpx) · [libwebm](https://github.com/webmproject/libwebm) · [Releases](https://github.com/mcpplibs/libvpx-m/releases) · [mcpp](https://github.com/mcpp-community/mcpp) · [Index](https://github.com/mcpplibs/mcpp-index) + +[![C++23](https://img.shields.io/badge/C%2B%2B-23-blue.svg)](https://en.cppreference.com/w/cpp/23) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE) +[![libvpx](https://img.shields.io/badge/libvpx-BSD--3--Clause-blue.svg)](upstream/libvpx/LICENSE) +[![libwebm](https://img.shields.io/badge/libwebm-BSD--3--Clause-blue.svg)](upstream/libwebm/LICENSE.TXT) +[![mcpp-community](https://img.shields.io/badge/mcpp--community-mwrap-blue)](https://github.com/mcpp-community) +[![CI](https://github.com/mcpplibs/libvpx-m/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/mcpplibs/libvpx-m/actions/workflows/ci.yml) + +`mcpplibs.mwrap.libvpx` 1.17.0 编码和解码 VP9(Web 上免版税的视频格式),并写入和读取 +WebM(所有浏览器都能播放的容器)。上游 API 保持不变:`vpx_codec_enc_config_default`、 +`vpx_codec_encode`、`vpx_codec_get_cx_data`、`vpx_codec_decode`、`vpx_codec_get_frame`、 +`vpx_img_alloc` … 是 libvpx 的全局名;`mkvmuxer::Segment`、`mkvparser::Segment`、 +`mkvmuxer::IMkvWriter`、`mkvparser::IMkvReader` … 是 libwebm 在各自命名空间里的类。 +库由 `compat.libvpx` 1.17.0 与 `compat.libwebm` 1.0.0.32 提供。头文件中的类型化控制函数 +(`vpx_codec_control_VP8E_SET_CPUUSED` …)以同名适配,宏映射为常量和函数。每次 CI 都验证: +写出的文件与上游自己的 `vpxenc` 逐字节相同,`vpxdec` 读出的帧也相同。 + +## 模块 + +- `import mcpplibs.mwrap.libvpx;`:编码器与解码器(`vpx/vpx_encoder.h`、`vpx/vpx_decoder.h`、`vpx/vp8cx.h`、`vpx/vp8dx.h` …),并重新导出下面两个模块 +- `import mcpplibs.mwrap.libvpx.webm;`:libwebm 的 `mkvmuxer`、`mkvparser` 与 `libwebm` 命名空间 +- `import mcpplibs.mwrap.libvpx.macros;`:`VPX_DL_REALTIME`、`VPX_FRAME_IS_KEY`、`VPX_CODEC_CAP_ENCODER`、`vpx_codec_enc_init` …,位于 `mcpplibs::mwrap::libvpx::macros` + +> 更多模块 API -> [用法与 API](docs/usage.md) + +## 快速开始 + +```sh +mcpp new clip --template mcpplibs.mwrap.libvpx@1.17.0 +cd clip +mcpp run -- clip.webm +``` + +默认模板把两秒的运动画面编码,在内存中封装成 WebM,再解析回来、逐帧解码,打印文件大小和最差的 +PSNR;给出文件名时同时写出 WebM。 + +```toml +[dependencies] +"mcpplibs.mwrap.libvpx" = "1.17.0" +``` + +```cpp +import std; +import mcpplibs.mwrap.libvpx; + +namespace m = mcpplibs::mwrap::libvpx::macros; + +int main() { + vpx_codec_enc_cfg_t cfg; + vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &cfg, 0); + cfg.g_w = 320; + cfg.g_h = 180; + cfg.g_timebase = {1, 30}; + vpx_codec_ctx_t encoder; + m::vpx_codec_enc_init(&encoder, vpx_codec_vp9_cx(), &cfg, 0); + mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_CPUUSED(&encoder, VP8E_SET_CPUUSED, 6); + vpx_image_t img; + vpx_img_alloc(&img, VPX_IMG_FMT_I420, 320, 180, 16); + // … 填好 img.planes,再 vpx_codec_encode(&encoder, &img, pts, 1, 0, m::VPX_DL_GOOD_QUALITY) + vpx_img_free(&img); + vpx_codec_destroy(&encoder); + std::println("{}", vpx_codec_version_str()); +} +``` + +## 库 + +`compat.libvpx` 构建上游自己的 `configure --target=generic-gnu --disable-vp8 --enable-vp9` +所编译的 101 个 C 源文件:libvpx 的可移植 C 实现,无 SIMD、无汇编、无运行时 CPU 检测,同一配置在 +x86-64 与 aarch64、Linux、macOS、Windows 和 wasm32-emscripten 上构建。单线程(默认)在所有目标上 +得到相同的字节;多线程按块分担(不带 pthreads 的 Emscripten 上在一个线程里依次执行)。不构建 VP8。 +`compat.libwebm` 构建上游的 `webm` 库(mkvmuxer、mkvparser 与公共辅助代码)。mkvmuxer 不设置时会给 +轨道随机的 UID(`Track::set_uid`):需要可重现的文件时请设置它。细节与限制见 +[docs/usage.md](docs/usage.md)。 + +## 示例 + +- [examples/vpxwebm](examples/vpxwebm):把 Y4M 编码成 WebM 或 IVF 中的 VP9,把 WebM 或 IVF 解码成原始 I420, + 描述 WebM 文件,基于模块编写;[tools/run_conformance.py](tools/run_conformance.py) 将它与上游的 `vpxenc`、 + `vpxdec` 对照。 + +## 构建与测试 + +Linux(x86_64 GCC 与 LLVM、arm64 GCC)、macOS、Windows 与 wasm32-emscripten(node 下运行)的 CI 在 +dev 与 release 配置下构建模块并运行测试:好质量、实时、四线程与无损的 VP9 封装进 WebM、解析并解码 +回来,每个文件的字节固定;每个宏映射与原始头文件对照;每个导出函数都能链接。原生行还运行导出审计 +([tools/check_exports.py](tools/check_exports.py):生成的模块是最新的、每个声明都已导出、每个宏都有着落), +除 Windows 外还运行与上游工具的一致性对照。另有一个作业把模板当作已安装的包生成并运行。 + +## 许可 + +封装部分为 [Apache-2.0](LICENSE),作者 mcpp-community;libvpx 与 libwebm 仍为 BSD-3-Clause,并附 WebM +项目的专利授权([upstream/libvpx/LICENSE](upstream/libvpx/LICENSE)、 +[upstream/libvpx/PATENTS](upstream/libvpx/PATENTS)、 +[upstream/libwebm/LICENSE.TXT](upstream/libwebm/LICENSE.TXT)、 +[upstream/libwebm/PATENTS.TXT](upstream/libwebm/PATENTS.TXT))。 diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..4449545 --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,84 @@ +# Using mcpplibs.mwrap.libvpx + +`import mcpplibs.mwrap.libvpx;` declares libvpx 1.17.0's API as its headers do, and libwebm 1.0.0.32's classes in their namespaces. Types, enumerators and functions are the upstream entities themselves, so a translation unit may include a header too. The libraries are `compat.libvpx` 1.17.0 and `compat.libwebm` 1.0.0.32, built from the same tags. + +## Modules + +| Module | What it holds | +|---|---| +| `mcpplibs.mwrap.libvpx` | `vpx/vpx_codec.h`, `vpx_image.h`, `vpx_frame_buffer.h`, `vpx_encoder.h` (with `vpx_ext_ratectrl.h`, `vpx_tpl.h`), `vpx_decoder.h`, `vp8.h`, `vp8cx.h`, `vp8dx.h`: 133 types (`vpx_codec_enc_cfg_t`, `vpx_codec_ctx_t`, `vpx_image_t`, `vpx_codec_cx_pkt_t`, `vpx_rational_t`, `vpx_svc_layer_id_t` …), 192 enumerators (`VPX_CODEC_OK`, `VPX_IMG_FMT_I420`, `VPX_Q`, `VPX_CBR`, `VP8E_SET_CPUUSED`, `VP9E_SET_ROW_MT`, `VPX_CS_BT_709`, `VPX_CR_STUDIO_RANGE` …), 35 functions and the two VP9 interfaces (`vpx_codec_vp9_cx`, `vpx_codec_vp9_dx`); 86 typed controls adapted; re-exports the two below | +| `mcpplibs.mwrap.libvpx.webm` | libwebm's `mkvmuxer` (Segment, VideoTrack, AudioTrack, Frame, Colour, Cues, IMkvWriter, MkvWriter … and the element writers), `mkvparser` (Segment, Tracks, Cluster, Block, BlockEntry, Colour, IMkvReader, MkvReader …) and `libwebm` (the `MkvId` element ids, `CopyColour`, `ParseVpxCodecPrivate` …) | +| `mcpplibs.mwrap.libvpx.macros` | the macros, in `mcpplibs::mwrap::libvpx::macros` | + +VP8 is not built (compat.libvpx is configured `--disable-vp8`): `vpx_codec_vp8_cx`, `vpx_codec_vp8_dx` and their `_algo` objects, which `vp8cx.h` and `vp8dx.h` declare, are not exported. The VP8 control ids are, as they are shared with VP9. + +## The typed controls + +In C, `vpx_codec_control(ctx, VP8E_SET_CPUUSED, 4)` is a macro that becomes `vpx_codec_control_VP8E_SET_CPUUSED(ctx, VP8E_SET_CPUUSED, 4)`, a static function the header defines for each control (`VPX_CTRL_USE_TYPE`) so the argument's type is checked. Internal linkage cannot be exported, and token concatenation is not C++: each of the 86 is adapted with its name and signature in `mcpplibs::mwrap::libvpx`, calling the variadic `vpx_codec_control_` as upstream's body does (`[[deprecated]]` where upstream deprecates it). + +```cpp +namespace vpx = mcpplibs::mwrap::libvpx; +vpx::vpx_codec_control_VP8E_SET_CPUUSED(&ctx, VP8E_SET_CPUUSED, 6); +vpx::vpx_codec_control_VP9E_SET_ROW_MT(&ctx, VP9E_SET_ROW_MT, 1u); +vpx::vpx_codec_control_VP9E_SET_COLOR_SPACE(&ctx, VP9E_SET_COLOR_SPACE, VPX_CS_BT_709); +``` + +A translation unit that includes `` before the import has the macro too: `vpx_codec_control(&ctx, VP8E_SET_CPUUSED, 6)` calls the header's static function, the same control. + +## The macros + +`mcpplibs.mwrap.libvpx.macros` maps 74 macros in `mcpplibs::mwrap::libvpx::macros`: + +- constants of the type their expression has: the ABI versions, `VPX_CODEC_CAP_*`, `VPX_CODEC_USE_*`, `VPX_FRAME_IS_KEY` and the other packet flags, `VPX_EFLAG_FORCE_KF`, `VP8_EFLAG_*`, `VPX_DL_REALTIME` / `_GOOD_QUALITY` / `_BEST_QUALITY`, the layer limits (`VPX_TS_MAX_LAYERS` …), `VPX_IMG_FMT_PLANAR` and the image format bits, `VPX_PLANE_Y` / `_U` / `_V` / `_ALPHA` …; +- functions: `vpx_codec_enc_init`, `vpx_codec_enc_init_multi`, `vpx_codec_dec_init` (each passes the ABI version the headers were written for to its `_ver` function), `vpx_codec_version_major` / `_minor` / `_patch`; +- constexpr functions: `VPX_VERSION_MAJOR(v)`, `VPX_VERSION_MINOR(v)`, `VPX_VERSION_PATCH(v)`. + +What stays a macro, in ``: `vpx_codec_control` (token concatenation), `VPX_CTRL_USE_TYPE`, `VPX_CTRL_USE_TYPE_DEPRECATED`, `VPX_CTRL_VOID`, the 86 empty `VPX_CTRL_` feature tests (`#ifdef VPX_CTRL_VP9E_SET_ROW_MT`), the declaration annotations (`VPX_INLINE`, `VPX_FORCE_INLINE`, `VPX_DEPRECATED`, `VPX_DECLSPEC_DEPRECATED`, `VPX_UNUSED`), libwebm's `LIBWEBM_DISALLOW_COPY_AND_ASSIGN` and the include guards. While a macro is defined it expands even inside `macros::NAME`; a function-like one does not when its name is followed by `)`, so `(macros::vpx_codec_enc_init)(…)` is the mapped function beside the header's macro. + +## libwebm's constants + +libwebm's headers define a few constants at namespace scope (`mkvmuxer::kMaxTrackNumber`, `kEbmlUnknownValue`, `kMaxBlockTimecode`; `mkvparser::E_PARSE_FAILED`, `E_FILE_FORMAT_INVALID`, `E_BUFFER_NOT_FULL`). In C++ a namespace-scope `const` has internal linkage and cannot be exported; their values are under the same names in `mcpplibs::mwrap::libvpx::mkvmuxer` and `mcpplibs::mwrap::libvpx::mkvparser`. Class constants (`mkvmuxer::Tracks::kVp9CodecId`, `mkvmuxer::Colour::kBt709` …) come with their classes. + +## Encoding into WebM, in brief + +```cpp +import std; +import mcpplibs.mwrap.libvpx; +namespace m = mcpplibs::mwrap::libvpx::macros; +namespace vpx = mcpplibs::mwrap::libvpx; + +// A seekable writer in memory: mkvmuxer goes back for the sizes, cues and duration. +class Memory final : public mkvmuxer::IMkvWriter { /* Write, Position, Position(pos), Seekable, ElementStartNotify */ }; + +vpx_codec_enc_cfg_t cfg; +vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &cfg, 0); +cfg.g_w = 1280; cfg.g_h = 720; cfg.g_timebase = {1, 60}; +cfg.rc_end_usage = VPX_Q; // constant quality … +vpx_codec_ctx_t enc; +m::vpx_codec_enc_init(&enc, vpx_codec_vp9_cx(), &cfg, 0); +vpx::vpx_codec_control_VP8E_SET_CQ_LEVEL(&enc, VP8E_SET_CQ_LEVEL, 20u); // … at this level +vpx::vpx_codec_control_VP8E_SET_CPUUSED(&enc, VP8E_SET_CPUUSED, 5); + +Memory out; +mkvmuxer::Segment segment; +segment.Init(&out); +segment.set_mode(mkvmuxer::Segment::kFile); +auto track = segment.AddVideoTrack(1280, 720, 1); +auto* video = static_cast(segment.GetTrackByNumber(track)); +video->set_codec_id(mkvmuxer::Tracks::kVp9CodecId); +video->set_uid(1); // reproducible: mkvmuxer's own is random + +// for each frame i: vpx_codec_encode(&enc, &img, i, 1, 0, m::VPX_DL_GOOD_QUALITY), then for each packet: +// segment.AddFrame(buf, size, track, pts * 1'000'000'000 / 60, (flags & m::VPX_FRAME_IS_KEY) != 0); +// at the end: vpx_codec_encode(&enc, nullptr, -1, 1, 0, …) until no packet comes, then segment.Finalize(). +``` + +Reading back: `mkvparser::EBMLHeader::Parse`, `mkvparser::Segment::CreateInstance` over an `IMkvReader`, `Load`, then the clusters' blocks' frames (`GetFirst`, `GetNext`, `Block::GetFrame(k).Read`), each given to `vpx_codec_decode`; `vpx_codec_get_frame` gives the pictures. [examples/vpxwebm](../examples/vpxwebm/src/main.cpp) does both. + +## Determinism and speed + +The codec is libvpx's portable C (no SIMD): with one thread the encoder writes the same bytes on every target — the tests pin each file's FNV-1a on Linux, macOS, Windows and the Web — and those bytes are upstream `vpxenc`'s for the same settings (tools/run_conformance.py). VP9 decoding is exact by specification. Speed is the C reference's: real-time speeds (`VPX_DL_REALTIME`, `VP8E_SET_CPUUSED` 6–9) and `VP9E_SET_ROW_MT` with threads are the fast paths; SIMD (the assembly and intrinsics behind upstream's run-time dispatch) is a later revision of compat.libvpx. + +## Platforms + +Linux (x86_64, arm64), macOS (arm64), Windows (x86_64, clang) and wasm32-emscripten. On Emscripten without its pthreads, `CONFIG_MULTITHREAD` is 0: asking for threads is accepted and the work runs in turn on the calling thread. diff --git a/examples/vpxwebm/mcpp.toml b/examples/vpxwebm/mcpp.toml new file mode 100644 index 0000000..6aedc9a --- /dev/null +++ b/examples/vpxwebm/mcpp.toml @@ -0,0 +1,15 @@ +# vpxwebm: encode Y4M to VP9 in WebM or IVF, decode WebM or IVF to raw I420, +# describe a WebM file — written against the module. tools/run_conformance.py +# compares it with upstream's vpxenc and vpxdec. +[package] +name = "vpxwebm" +version = "0.1.0" +description = "Encode Y4M to VP9 (WebM or IVF), decode WebM or IVF to raw I420, describe a WebM: libvpx and libwebm through import mcpplibs.mwrap.libvpx" + +[targets.vpxwebm] +kind = "bin" +main = "src/main.cpp" + +[dependencies] +"mcpplibs.mwrap.libvpx" = { path = "../.." } + diff --git a/examples/vpxwebm/src/main.cpp b/examples/vpxwebm/src/main.cpp new file mode 100644 index 0000000..6fabc6d --- /dev/null +++ b/examples/vpxwebm/src/main.cpp @@ -0,0 +1,340 @@ +// vpxwebm — VP9 in WebM (or IVF) through import mcpplibs.mwrap.libvpx. +// +// vpxwebm encode [--good | --rt] [--cpu-used N] [--end-usage q|vbr|cbr] +// [--cq-level N] [--target-bitrate KBPS] [--lag-in-frames N] [--threads N] [--tile-columns N] [--lossless] +// vpxwebm decode every frame as raw I420 +// vpxwebm info the tracks, frames and duration +// +// Encoding takes the Y4M's size and frame rate (the time base is the frame +// rate's inverse, as vpxenc's) and sets only the controls given, so the IVF it +// writes for a set of vpxenc's options is the one vpxenc writes for them +// (tools/run_conformance.py). +import std; +import mcpplibs.mwrap.libvpx; + +namespace m = mcpplibs::mwrap::libvpx::macros; +namespace vpx = mcpplibs::mwrap::libvpx; + +namespace { + +[[noreturn]] void fail(std::string_view what) { + std::println(std::cerr, "vpxwebm: {}", what); + std::exit(1); +} + +std::vector read_file(const std::string& name) { + std::ifstream in(name, std::ios::binary); + if (!in) fail("cannot read " + name); + return {std::istreambuf_iterator(in), {}}; +} + +void write_file(const std::string& name, std::span bytes) { + std::ofstream out(name, std::ios::binary); + out.write(reinterpret_cast(bytes.data()), static_cast(bytes.size())); + if (!out) fail("cannot write " + name); +} + +// ── Y4M in ── + +struct Y4m { + int width = 0, height = 0, rate = 30, scale = 1; + std::vector> frames; // I420 +}; + +Y4m read_y4m(const std::string& name) { + const auto b = read_file(name); + const std::string_view text(reinterpret_cast(b.data()), b.size()); + const auto eol = text.find('\n'); + if (!text.starts_with("YUV4MPEG2 ") || eol == std::string_view::npos) fail(name + " is not a Y4M file"); + Y4m y; + for (auto part : std::views::split(text.substr(10, eol - 10), ' ')) { + const std::string tag(part.begin(), part.end()); + if (tag.empty()) continue; + if (tag[0] == 'W') y.width = std::stoi(tag.substr(1)); + if (tag[0] == 'H') y.height = std::stoi(tag.substr(1)); + if (tag[0] == 'F') y.rate = std::stoi(tag.substr(1)), y.scale = std::stoi(tag.substr(tag.find(':') + 1)); + if (tag[0] == 'C' && !tag.starts_with("C420")) fail("only 4:2:0 Y4M is read"); + } + const std::size_t size = static_cast(y.width) * y.height + 2 * static_cast((y.width + 1) / 2) * ((y.height + 1) / 2); + for (std::size_t at = eol + 1; at < text.size();) { + at = text.find('\n', at) + 1; + if (at == 0 || text.size() - at < size) fail(name + ": a frame cut short"); + y.frames.emplace_back(b.begin() + static_cast(at), b.begin() + static_cast(at + size)); + at += size; + } + return y; +} + +// ── encoding ── + +struct Options { + vpx_enc_deadline_t deadline = m::VPX_DL_GOOD_QUALITY; + std::optional cpu_used, cq_level, tile_columns; + std::optional lag, threads, bitrate; + std::optional end_usage; + bool lossless = false; +}; + +struct Packet { + std::vector bytes; + std::int64_t pts = 0; + bool key = false; +}; + +std::vector encode(const Y4m& y, const Options& o) { + vpx_codec_enc_cfg_t cfg; + if (vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &cfg, 0) != VPX_CODEC_OK) fail("no default configuration"); + cfg.g_w = static_cast(y.width); + cfg.g_h = static_cast(y.height); + cfg.g_timebase = {y.scale, y.rate}; + if (o.lag) cfg.g_lag_in_frames = *o.lag; + if (o.threads) cfg.g_threads = *o.threads; + if (o.end_usage) cfg.rc_end_usage = *o.end_usage; + if (o.bitrate) cfg.rc_target_bitrate = *o.bitrate; + vpx_codec_ctx_t ctx; + if (m::vpx_codec_enc_init(&ctx, vpx_codec_vp9_cx(), &cfg, 0) != VPX_CODEC_OK) fail(std::string("encoder: ") + vpx_codec_error(&ctx)); + if (o.cpu_used) vpx::vpx_codec_control_VP8E_SET_CPUUSED(&ctx, VP8E_SET_CPUUSED, *o.cpu_used); + if (o.cq_level) vpx::vpx_codec_control_VP8E_SET_CQ_LEVEL(&ctx, VP8E_SET_CQ_LEVEL, static_cast(*o.cq_level)); + if (o.tile_columns) vpx::vpx_codec_control_VP9E_SET_TILE_COLUMNS(&ctx, VP9E_SET_TILE_COLUMNS, *o.tile_columns); + if (o.lossless) vpx::vpx_codec_control_VP9E_SET_LOSSLESS(&ctx, VP9E_SET_LOSSLESS, 1u); + std::vector out; + auto drain = [&] { + vpx_codec_iter_t it = nullptr; + while (const vpx_codec_cx_pkt_t* p = vpx_codec_get_cx_data(&ctx, &it)) + if (p->kind == VPX_CODEC_CX_FRAME_PKT) { + const auto* b = static_cast(p->data.frame.buf); + out.push_back({{b, b + p->data.frame.sz}, p->data.frame.pts, (p->data.frame.flags & m::VPX_FRAME_IS_KEY) != 0}); + } + }; + vpx_image_t img; + for (std::size_t i = 0; i < y.frames.size(); ++i) { + vpx_img_wrap(&img, VPX_IMG_FMT_I420, static_cast(y.width), static_cast(y.height), 1, + const_cast(y.frames[i].data())); + if (vpx_codec_encode(&ctx, &img, static_cast(i), 1, 0, o.deadline) != VPX_CODEC_OK) + fail(std::string("encoding: ") + vpx_codec_error(&ctx)); + drain(); + } + for (bool more = true; more;) { + if (vpx_codec_encode(&ctx, nullptr, -1, 1, 0, o.deadline) != VPX_CODEC_OK) fail("flushing"); + const std::size_t before = out.size(); + drain(); + more = out.size() != before; + } + vpx_codec_destroy(&ctx); + return out; +} + +void put32(std::vector& b, std::uint32_t v) { + for (int i = 0; i < 4; ++i) b.push_back(static_cast(v >> (8 * i))); +} + +std::vector ivf(const Y4m& y, const std::vector& packets) { + std::vector b{'D', 'K', 'I', 'F', 0, 0, 32, 0, 'V', 'P', '9', '0'}; + b.push_back(static_cast(y.width)), b.push_back(static_cast(y.width >> 8)); + b.push_back(static_cast(y.height)), b.push_back(static_cast(y.height >> 8)); + put32(b, static_cast(y.rate)); + put32(b, static_cast(y.scale)); + put32(b, static_cast(packets.size())); + put32(b, 0); + for (const Packet& p : packets) { + put32(b, static_cast(p.bytes.size())); + put32(b, static_cast(p.pts)); + put32(b, static_cast(static_cast(p.pts) >> 32)); + b.insert(b.end(), p.bytes.begin(), p.bytes.end()); + } + return b; +} + +class FileWriter final : public mkvmuxer::IMkvWriter { +public: + std::vector bytes; + std::int64_t at = 0; + mkvmuxer::int32 Write(const void* buf, mkvmuxer::uint32 len) override { + if (bytes.size() < static_cast(at) + len) bytes.resize(static_cast(at) + len); + std::memcpy(bytes.data() + at, buf, len); + at += len; + return 0; + } + mkvmuxer::int64 Position() const override { return at; } + mkvmuxer::int32 Position(mkvmuxer::int64 position) override { + at = position; + return 0; + } + bool Seekable() const override { return true; } + void ElementStartNotify(mkvmuxer::uint64, mkvmuxer::int64) override {} +}; + +std::vector webm(const Y4m& y, const std::vector& packets) { + FileWriter w; + mkvmuxer::Segment segment; + if (!segment.Init(&w)) fail("muxer"); + segment.set_mode(mkvmuxer::Segment::kFile); + segment.OutputCues(true); + segment.GetSegmentInfo()->set_writing_app("vpxwebm"); + segment.GetSegmentInfo()->set_muxing_app("vpxwebm"); + const auto track = segment.AddVideoTrack(y.width, y.height, 1); + auto* v = static_cast(segment.GetTrackByNumber(track)); + v->set_codec_id(mkvmuxer::Tracks::kVp9CodecId); + v->set_uid(1); // reproducible: mkvmuxer's own is random + v->set_frame_rate(static_cast(y.rate) / y.scale); + segment.CuesTrack(track); + for (const Packet& p : packets) { + const auto ns = static_cast(p.pts) * 1'000'000'000ull * static_cast(y.scale) / static_cast(y.rate); + if (!segment.AddFrame(p.bytes.data(), p.bytes.size(), track, ns, p.key)) fail("adding a frame"); + } + if (!segment.Finalize()) fail("finishing the file"); + return w.bytes; +} + +// ── decoding ── + +class MemoryReader final : public mkvparser::IMkvReader { +public: + explicit MemoryReader(const std::vector& b) : bytes(b) {} + int Read(long long pos, long len, unsigned char* buf) override { + if (pos < 0 || len < 0 || static_cast(pos + len) > bytes.size()) return -1; + std::memcpy(buf, bytes.data() + pos, static_cast(len)); + return 0; + } + int Length(long long* total, long long* available) override { + if (total) *total = static_cast(bytes.size()); + if (available) *available = static_cast(bytes.size()); + return 0; + } + const std::vector& bytes; +}; + +struct Stream { + std::vector> frames; + long long width = 0, height = 0, duration_ns = 0; + std::string codec; +}; + +Stream read_webm(const std::vector& file) { + Stream s; + MemoryReader reader(file); + long long pos = 0; + mkvparser::EBMLHeader header; + if (header.Parse(&reader, pos) != 0) fail("not a WebM file"); + mkvparser::Segment* raw = nullptr; + if (mkvparser::Segment::CreateInstance(&reader, pos, raw) != 0 || !raw) fail("no segment"); + std::unique_ptr segment(raw); + if (segment->Load() < 0) fail("the segment does not load"); + const mkvparser::Tracks* tracks = segment->GetTracks(); + const mkvparser::Track* video = nullptr; + for (unsigned long i = 0; i < tracks->GetTracksCount(); ++i) + if (tracks->GetTrackByIndex(i)->GetType() == mkvparser::Track::kVideo) video = tracks->GetTrackByIndex(i); + if (!video) fail("no video track"); + s.codec = video->GetCodecId(); + s.width = static_cast(video)->GetWidth(); + s.height = static_cast(video)->GetHeight(); + s.duration_ns = segment->GetDuration(); + for (const mkvparser::Cluster* c = segment->GetFirst(); c && !c->EOS(); c = segment->GetNext(c)) { + const mkvparser::BlockEntry* e = nullptr; + if (c->GetFirst(e) != 0) break; + while (e && !e->EOS()) { + const mkvparser::Block* b = e->GetBlock(); + if (b->GetTrackNumber() == video->GetNumber()) + for (int k = 0; k < b->GetFrameCount(); ++k) { + const mkvparser::Block::Frame& f = b->GetFrame(k); + std::vector data(static_cast(f.len)); + if (f.Read(&reader, data.data()) != 0) fail("reading a frame"); + s.frames.push_back(std::move(data)); + } + if (c->GetNext(e, e) != 0) break; + } + } + return s; +} + +Stream read_ivf(const std::vector& file) { + Stream s; + auto u32 = [&](std::size_t at) { return std::uint32_t(file[at]) | std::uint32_t(file[at + 1]) << 8 | std::uint32_t(file[at + 2]) << 16 | std::uint32_t(file[at + 3]) << 24; }; + if (file.size() < 32 || std::memcmp(file.data(), "DKIF", 4) != 0) fail("not an IVF file"); + s.codec = "V_VP9"; + s.width = file[12] | file[13] << 8; + s.height = file[14] | file[15] << 8; + for (std::size_t at = 32; at + 12 <= file.size();) { + const std::size_t n = u32(at); + if (at + 12 + n > file.size()) fail("an IVF frame cut short"); + s.frames.emplace_back(file.begin() + static_cast(at + 12), file.begin() + static_cast(at + 12 + n)); + at += 12 + n; + } + return s; +} + +std::vector decode(const Stream& s) { + std::vector out; + vpx_codec_dec_cfg_t cfg{}; + vpx_codec_ctx_t ctx; + if (m::vpx_codec_dec_init(&ctx, vpx_codec_vp9_dx(), &cfg, 0) != VPX_CODEC_OK) fail("decoder"); + for (const auto& f : s.frames) { + if (vpx_codec_decode(&ctx, f.data(), static_cast(f.size()), nullptr, 0) != VPX_CODEC_OK) + fail(std::string("decoding: ") + vpx_codec_error(&ctx)); + vpx_codec_iter_t it = nullptr; + while (vpx_image_t* img = vpx_codec_get_frame(&ctx, &it)) + for (int plane = 0; plane < 3; ++plane) { + const unsigned w = plane ? (img->d_w + 1) / 2 : img->d_w, h = plane ? (img->d_h + 1) / 2 : img->d_h; + for (unsigned y = 0; y < h; ++y) { + const unsigned char* row = img->planes[plane] + static_cast(y) * img->stride[plane]; + out.insert(out.end(), row, row + w); + } + } + } + vpx_codec_destroy(&ctx); + return out; +} + +Stream read_stream(const std::string& name) { + const auto b = read_file(name); + return name.ends_with(".ivf") ? read_ivf(b) : read_webm(b); +} + +} // namespace + +int main(int argc, char** argv) { + const std::vector a(argv + 1, argv + argc); + if (a.size() >= 3 && a[0] == "encode") { + Options o; + for (std::size_t i = 3; i < a.size(); ++i) { + const std::string& s = a[i]; + auto next = [&] { + if (i + 1 >= a.size()) fail(s + " takes a value"); + return a[++i]; + }; + if (s == "--good") o.deadline = m::VPX_DL_GOOD_QUALITY; + else if (s == "--rt") o.deadline = m::VPX_DL_REALTIME; + else if (s == "--cpu-used") o.cpu_used = std::stoi(next()); + else if (s == "--cq-level") o.cq_level = std::stoi(next()); + else if (s == "--tile-columns") o.tile_columns = std::stoi(next()); + else if (s == "--lag-in-frames") o.lag = static_cast(std::stoul(next())); + else if (s == "--threads") o.threads = static_cast(std::stoul(next())); + else if (s == "--target-bitrate") o.bitrate = static_cast(std::stoul(next())); + else if (s == "--end-usage") { + const std::string u = next(); + o.end_usage = u == "q" ? VPX_Q : u == "cbr" ? VPX_CBR : u == "cq" ? VPX_CQ : VPX_VBR; + } else if (s == "--lossless") o.lossless = true; + else fail("unknown option " + s); + } + const Y4m y = read_y4m(a[1]); + const auto packets = encode(y, o); + const auto bytes = a[2].ends_with(".ivf") ? ivf(y, packets) : webm(y, packets); + write_file(a[2], bytes); + std::println("{} frames, {} packets, {} bytes", y.frames.size(), packets.size(), bytes.size()); + return 0; + } + if (a.size() == 3 && a[0] == "decode") { + const Stream s = read_stream(a[1]); + const auto yuv = decode(s); + write_file(a[2], yuv); + std::println("{} frames, {} bytes of I420", s.frames.size(), yuv.size()); + return 0; + } + if (a.size() == 2 && a[0] == "info") { + const Stream s = read_stream(a[1]); + std::println("{} {} x {}, {} frames, {:.3f} s", s.codec, s.width, s.height, s.frames.size(), static_cast(s.duration_ns) / 1e9); + return 0; + } + std::println(std::cerr, "vpxwebm encode [options] | decode | info "); + return 2; +} diff --git a/include/mwrap/libvpx/macros.hpp b/include/mwrap/libvpx/macros.hpp new file mode 100644 index 0000000..076e466 --- /dev/null +++ b/include/mwrap/libvpx/macros.hpp @@ -0,0 +1,26 @@ +// — libvpx's headers as the modules see them, for the +// macros a C++ entity cannot be: vpx_codec_control (token concatenation: +// `vpx_codec_control(ctx, VP8E_SET_CPUUSED, 4)` calls the header's typed +// vpx_codec_control_VP8E_SET_CPUUSED), VPX_CTRL_USE_TYPE, VPX_CTRL_VOID, the +// empty VPX_CTRL_ feature tests (`#ifdef VPX_CTRL_VP9E_SET_ROW_MT`), the +// declaration annotations (VPX_INLINE, VPX_DEPRECATED, VPX_UNUSED …). +// +// Include it before `import mcpplibs.mwrap.libvpx;`, rather than the headers +// themselves. Every other macro is defined too; while a macro is defined it +// expands even inside `mcpplibs::mwrap::libvpx::macros::NAME`, and a +// function-like one does not when its name is followed by `)`: +// `(macros::vpx_codec_enc_init)(…)` is the mapped function, +// `vpx_codec_enc_init(…)` the header's macro. libwebm (the webm module) has no +// macros but its include guards and LIBWEBM_DISALLOW_COPY_AND_ASSIGN. +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/mcpp.toml b/mcpp.toml new file mode 100644 index 0000000..4835309 --- /dev/null +++ b/mcpp.toml @@ -0,0 +1,67 @@ +# Module wrapper; upstream API preserved. Policy: mcpp-index/docs/mwrap.md. +[package] +namespace = "mcpplibs.mwrap" +name = "libvpx" +version = "1.17.0" +description = "libvpx C++ modules for mcpp (import mcpplibs.mwrap.libvpx;): the libvpx 1.17.0 VP9 codec (encoder and decoder) and the libwebm 1.0.0.32 WebM container (mkvmuxer and mkvparser), over compat.libvpx and compat.libwebm; the typed controls adapted, the macros as constants and functions" +license = "Apache-2.0" +authors = ["mcpp-community"] +repo = "https://github.com/mcpplibs/libvpx-m" +platforms = ["linux", "macos", "windows", "emscripten"] + +[targets.libvpx] +kind = "lib" + +[build] +sources = ["src/*.cppm", "src/*.cpp"] +# : the headers, in the order the modules include +# them, for the macros a C++ entity cannot be. +include_dirs = ["include"] + +[dependencies] +compat.libvpx = "1.17.0" +compat.libwebm = "1.0.0.32" + +[package.metadata.mwrap] +upstream-version = "1.17.0" +libwebm-version = "1.0.0.32" + +[[runtime.deploy]] +from = "LICENSE" +to = "licenses/libvpx" + +[[runtime.deploy]] +from = "NOTICE" +to = "licenses/libvpx" + +[[runtime.deploy]] +from = "upstream/libvpx/README" +to = "licenses/libvpx/upstream/libvpx" + +[[runtime.deploy]] +from = "upstream/libvpx/LICENSE" +to = "licenses/libvpx/upstream/libvpx" + +[[runtime.deploy]] +from = "upstream/libvpx/PATENTS" +to = "licenses/libvpx/upstream/libvpx" + +[[runtime.deploy]] +from = "upstream/libvpx/AUTHORS" +to = "licenses/libvpx/upstream/libvpx" + +[[runtime.deploy]] +from = "upstream/libwebm/README.libwebm" +to = "licenses/libvpx/upstream/libwebm" + +[[runtime.deploy]] +from = "upstream/libwebm/LICENSE.TXT" +to = "licenses/libvpx/upstream/libwebm" + +[[runtime.deploy]] +from = "upstream/libwebm/PATENTS.TXT" +to = "licenses/libvpx/upstream/libwebm" + +[[runtime.deploy]] +from = "upstream/libwebm/AUTHORS.TXT" +to = "licenses/libvpx/upstream/libwebm" diff --git a/src/control.cpp b/src/control.cpp new file mode 100644 index 0000000..d0ce6d7 --- /dev/null +++ b/src/control.cpp @@ -0,0 +1,100 @@ +// Generated by tools/gen_exports.py from libvpx 1.17.0's and libwebm 1.0.0.32's +// headers — do not edit by hand. +// +// The typed controls' bodies: upstream's own (VPX_CTRL_USE_TYPE's), a call of +// the variadic vpx_codec_control_ with the typed argument. +module; +#include + +module mcpplibs.mwrap.libvpx; + +namespace mcpplibs::mwrap::libvpx { + +vpx_codec_err_t vpx_codec_control_VP8_SET_REFERENCE(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_ref_frame_t * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8_COPY_REFERENCE(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_ref_frame_t * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8_SET_POSTPROC(vpx_codec_ctx_t* ctx, int ctrl_id, vp8_postproc_cfg_t * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9_GET_REFERENCE(vpx_codec_ctx_t* ctx, int ctrl_id, vp9_ref_frame_t * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_ROI_MAP(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_roi_map_t * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_ACTIVEMAP(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_active_map_t * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_SCALEMODE(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_scaling_mode_t * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_CPUUSED(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_ENABLEAUTOALTREF(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_NOISE_SENSITIVITY(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_SHARPNESS(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_STATIC_THRESHOLD(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_TOKEN_PARTITIONS(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_GET_LAST_QUANTIZER(vpx_codec_ctx_t* ctx, int ctrl_id, int * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_GET_LAST_QUANTIZER_64(vpx_codec_ctx_t* ctx, int ctrl_id, int * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_ARNR_MAXFRAMES(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_ARNR_STRENGTH(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_ARNR_TYPE(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_TUNING(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_CQ_LEVEL(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_MAX_INTRA_BITRATE_PCT(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_FRAME_FLAGS(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_MAX_INTER_BITRATE_PCT(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_GF_CBR_BOOST_PCT(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_TEMPORAL_LAYER_ID(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_SCREEN_CONTENT_MODE(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_LOSSLESS(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_TILE_COLUMNS(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_TILE_ROWS(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_FRAME_PARALLEL_DECODING(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_AQ_MODE(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_FRAME_PERIODIC_BOOST(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_NOISE_SENSITIVITY(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_SVC(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_ROI_MAP(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_roi_map_t * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_SVC_PARAMETERS(vpx_codec_ctx_t* ctx, int ctrl_id, void * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_SVC_LAYER_ID(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_svc_layer_id_t * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_TUNE_CONTENT(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_GET_SVC_LAYER_ID(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_svc_layer_id_t * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_REGISTER_CX_CALLBACK(vpx_codec_ctx_t* ctx, int ctrl_id, void * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_COLOR_SPACE(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_MIN_GF_INTERVAL(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_MAX_GF_INTERVAL(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_GET_ACTIVEMAP(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_active_map_t * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_COLOR_RANGE(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_SVC_REF_FRAME_CONFIG(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_svc_ref_frame_config_t * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_RENDER_SIZE(vpx_codec_ctx_t* ctx, int ctrl_id, int * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_TARGET_LEVEL(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_ROW_MT(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_GET_LEVEL(vpx_codec_ctx_t* ctx, int ctrl_id, int * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_ALT_REF_AQ(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_GF_CBR_BOOST_PCT(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_ENABLE_MOTION_VECTOR_UNIT_TEST(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_SVC_INTER_LAYER_PRED(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_SVC_FRAME_DROP_LAYER(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_svc_frame_drop_t * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_GET_SVC_REF_FRAME_CONFIG(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_svc_ref_frame_config_t * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_SVC_GF_TEMPORAL_REF(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_SVC_SPATIAL_LAYER_SYNC(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_svc_spatial_layer_sync_t * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_TPL(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_POSTENCODE_DROP(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_DELTA_Q_UV(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_DISABLE_OVERSHOOT_MAXQ_CBR(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_DISABLE_LOOPFILTER(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_EXTERNAL_RATE_CONTROL(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_rc_funcs_t * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_RTC_EXTERNAL_RATECTRL(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_GET_LOOPFILTER_LEVEL(vpx_codec_ctx_t* ctx, int ctrl_id, int * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_GET_LAST_QUANTIZER_SVC_LAYERS(vpx_codec_ctx_t* ctx, int ctrl_id, int * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8E_SET_RTC_EXTERNAL_RATECTRL(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_QUANTIZER_ONE_PASS(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_KEY_FRAME_FILTERING(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9E_SET_VALIDATE_HBD_INPUT(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8D_GET_LAST_REF_UPDATES(vpx_codec_ctx_t* ctx, int ctrl_id, int * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8D_GET_FRAME_CORRUPTED(vpx_codec_ctx_t* ctx, int ctrl_id, int * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8D_GET_LAST_REF_USED(vpx_codec_ctx_t* ctx, int ctrl_id, int * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VPXD_SET_DECRYPTOR(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_decrypt_init * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP8D_SET_DECRYPTOR(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_decrypt_init * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9D_GET_FRAME_SIZE(vpx_codec_ctx_t* ctx, int ctrl_id, int * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9D_GET_DISPLAY_SIZE(vpx_codec_ctx_t* ctx, int ctrl_id, int * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9D_GET_BIT_DEPTH(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9_SET_BYTE_ALIGNMENT(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9_INVERT_TILE_DECODE_ORDER(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9_SET_SKIP_LOOP_FILTER(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9_DECODE_SVC_SPATIAL_LAYER(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VPXD_GET_LAST_QUANTIZER(vpx_codec_ctx_t* ctx, int ctrl_id, int * data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9D_SET_ROW_MT(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } +vpx_codec_err_t vpx_codec_control_VP9D_SET_LOOP_FILTER_OPT(vpx_codec_ctx_t* ctx, int ctrl_id, int data) { return ::vpx_codec_control_(ctx, ctrl_id, data); } + +} // namespace mcpplibs::mwrap::libvpx diff --git a/src/libvpx.cppm b/src/libvpx.cppm new file mode 100644 index 0000000..991c236 --- /dev/null +++ b/src/libvpx.cppm @@ -0,0 +1,485 @@ +// Generated by tools/gen_exports.py from libvpx 1.17.0's and libwebm 1.0.0.32's +// headers — do not edit by hand. +// +// mcpplibs.mwrap.libvpx — libvpx 1.17.0, the VP9 video codec, as a C++ module: +// every type, enumerator and function of the headers upstream installs +// (vpx_codec.h, vpx_image.h, vpx_frame_buffer.h, vpx_encoder.h with +// vpx_ext_ratectrl.h and vpx_tpl.h, vpx_decoder.h, vp8.h, vp8cx.h, vp8dx.h), +// the upstream globals unchanged: the encoder and decoder API, images and +// frame buffers, the control ids, the VP9 codec interfaces. The headers' +// typed controls (static functions made by VPX_CTRL_USE_TYPE) are adapted +// under the same names in namespace mcpplibs::mwrap::libvpx. It re-exports +// the WebM container (mcpplibs.mwrap.libvpx.webm: libwebm's mkvmuxer and +// mkvparser) and the macro mappings (mcpplibs.mwrap.libvpx.macros). The +// libraries are compat.libvpx and compat.libwebm, built from the same tags. +module; +#include + +export module mcpplibs.mwrap.libvpx; + +export import mcpplibs.mwrap.libvpx.webm; +export import mcpplibs.mwrap.libvpx.macros; + +// types (133: typedef names and struct and enum tags) +export using ::SVC_LAYER_DROP_MODE; +export using ::VP9E_TEMPORAL_LAYERING_MODE; +export using ::VPX_SCALING_MODE; +export using ::VpxTplBlockStats; +export using ::VpxTplFrameStats; +export using ::VpxTplGopStats; +export using ::sb_parameters; +export using ::sb_params; +export using ::vp8_com_control_id; +export using ::vp8_dec_control_id; +export using ::vp8_postproc_cfg; +export using ::vp8_postproc_cfg_t; +export using ::vp8_postproc_level; +export using ::vp8e_enc_control_id; +export using ::vp8e_token_partitions; +export using ::vp8e_tuning; +export using ::vp9_ref_frame; +export using ::vp9_ref_frame_t; +export using ::vp9e_temporal_layering_mode; +export using ::vp9e_tune_content; +export using ::vpx_active_map; +export using ::vpx_active_map_t; +export using ::vpx_bit_depth; +export using ::vpx_bit_depth_t; +export using ::vpx_codec_caps_t; +export using ::vpx_codec_ctx; +export using ::vpx_codec_ctx_t; +export using ::vpx_codec_cx_pkt; +export using ::vpx_codec_cx_pkt_kind; +export using ::vpx_codec_cx_pkt_t; +export using ::vpx_codec_dec_cfg; +export using ::vpx_codec_dec_cfg_t; +export using ::vpx_codec_enc_cfg; +export using ::vpx_codec_enc_cfg_t; +export using ::vpx_codec_enc_output_cx_cb_pair; +export using ::vpx_codec_enc_output_cx_pkt_cb_fn_t; +export using ::vpx_codec_er_flags_t; +export using ::vpx_codec_err_t; +export using ::vpx_codec_flags_t; +export using ::vpx_codec_frame_buffer; +export using ::vpx_codec_frame_buffer_t; +export using ::vpx_codec_frame_flags_t; +export using ::vpx_codec_iface_t; +export using ::vpx_codec_iter_t; +export using ::vpx_codec_priv_output_cx_pkt_cb_pair_t; +export using ::vpx_codec_priv_t; +export using ::vpx_codec_pts_t; +export using ::vpx_codec_put_frame_cb_fn_t; +export using ::vpx_codec_put_slice_cb_fn_t; +export using ::vpx_codec_stream_info; +export using ::vpx_codec_stream_info_t; +export using ::vpx_color_range; +export using ::vpx_color_range_t; +export using ::vpx_color_space; +export using ::vpx_color_space_t; +export using ::vpx_decrypt_cb; +export using ::vpx_decrypt_init; +export using ::vpx_enc_deadline_t; +export using ::vpx_enc_frame_flags_t; +export using ::vpx_enc_pass; +export using ::vpx_ext_rc_mode; +export using ::vpx_ext_rc_mode_t; +export using ::vpx_fixed_buf; +export using ::vpx_fixed_buf_t; +export using ::vpx_get_frame_buffer_cb_fn_t; +export using ::vpx_image; +export using ::vpx_image_rect; +export using ::vpx_image_rect_t; +export using ::vpx_image_t; +export using ::vpx_img_fmt; +export using ::vpx_img_fmt_t; +export using ::vpx_kf_mode; +export using ::vpx_rational; +export using ::vpx_rational_t; +export using ::vpx_rc_config; +export using ::vpx_rc_config_t; +export using ::vpx_rc_create_model_cb_fn_t; +export using ::vpx_rc_delete_model_cb_fn_t; +export using ::vpx_rc_encodeframe_decision; +export using ::vpx_rc_encodeframe_decision_t; +export using ::vpx_rc_encodeframe_info; +export using ::vpx_rc_encodeframe_info_t; +export using ::vpx_rc_encodeframe_result; +export using ::vpx_rc_encodeframe_result_t; +export using ::vpx_rc_firstpass_stats; +export using ::vpx_rc_firstpass_stats_t; +export using ::vpx_rc_frame_stats; +export using ::vpx_rc_frame_stats_t; +export using ::vpx_rc_frame_update_type; +export using ::vpx_rc_frame_update_type_t; +export using ::vpx_rc_funcs; +export using ::vpx_rc_funcs_t; +export using ::vpx_rc_get_encodeframe_decision_cb_fn_t; +export using ::vpx_rc_get_frame_rdmult_cb_fn_t; +export using ::vpx_rc_get_gop_decision_cb_fn_t; +export using ::vpx_rc_get_key_frame_decision_cb_fn_t; +export using ::vpx_rc_gop_decision; +export using ::vpx_rc_gop_decision_t; +export using ::vpx_rc_key_frame_decision; +export using ::vpx_rc_key_frame_decision_t; +export using ::vpx_rc_mode; +export using ::vpx_rc_model_t; +export using ::vpx_rc_ref_frame; +export using ::vpx_rc_ref_frame_t; +export using ::vpx_rc_ref_name; +export using ::vpx_rc_ref_name_t; +export using ::vpx_rc_send_firstpass_stats_cb_fn_t; +export using ::vpx_rc_send_tpl_gop_stats_cb_fn_t; +export using ::vpx_rc_status; +export using ::vpx_rc_status_t; +export using ::vpx_rc_type; +export using ::vpx_rc_type_t; +export using ::vpx_rc_update_encodeframe_result_cb_fn_t; +export using ::vpx_ref_frame; +export using ::vpx_ref_frame_t; +export using ::vpx_ref_frame_type; +export using ::vpx_ref_frame_type_t; +export using ::vpx_release_frame_buffer_cb_fn_t; +export using ::vpx_roi_map; +export using ::vpx_roi_map_t; +export using ::vpx_scaling_mode; +export using ::vpx_scaling_mode_1d; +export using ::vpx_scaling_mode_t; +export using ::vpx_svc_extra_cfg_t; +export using ::vpx_svc_frame_drop; +export using ::vpx_svc_frame_drop_t; +export using ::vpx_svc_layer_id; +export using ::vpx_svc_layer_id_t; +export using ::vpx_svc_parameters; +export using ::vpx_svc_ref_frame_config; +export using ::vpx_svc_ref_frame_config_t; +export using ::vpx_svc_spatial_layer_sync; +export using ::vpx_svc_spatial_layer_sync_t; + +// enumerators (192) +export using ::CONSTRAINED_FROM_ABOVE_DROP; +export using ::CONSTRAINED_LAYER_DROP; +export using ::FULL_SUPERFRAME_DROP; +export using ::LAYER_DROP; +export using ::VP8D_GET_FRAME_CORRUPTED; +export using ::VP8D_GET_LAST_REF_UPDATES; +export using ::VP8D_GET_LAST_REF_USED; +export using ::VP8D_SET_DECRYPTOR; +export using ::VP8E_FOURFIVE; +export using ::VP8E_GET_LAST_QUANTIZER; +export using ::VP8E_GET_LAST_QUANTIZER_64; +export using ::VP8E_NORMAL; +export using ::VP8E_ONETWO; +export using ::VP8E_SET_ACTIVEMAP; +export using ::VP8E_SET_ARNR_MAXFRAMES; +export using ::VP8E_SET_ARNR_STRENGTH; +export using ::VP8E_SET_ARNR_TYPE; +export using ::VP8E_SET_CPUUSED; +export using ::VP8E_SET_CQ_LEVEL; +export using ::VP8E_SET_ENABLEAUTOALTREF; +export using ::VP8E_SET_FRAME_FLAGS; +export using ::VP8E_SET_GF_CBR_BOOST_PCT; +export using ::VP8E_SET_MAX_INTRA_BITRATE_PCT; +export using ::VP8E_SET_NOISE_SENSITIVITY; +export using ::VP8E_SET_ROI_MAP; +export using ::VP8E_SET_RTC_EXTERNAL_RATECTRL; +export using ::VP8E_SET_SCALEMODE; +export using ::VP8E_SET_SCREEN_CONTENT_MODE; +export using ::VP8E_SET_SHARPNESS; +export using ::VP8E_SET_STATIC_THRESHOLD; +export using ::VP8E_SET_TEMPORAL_LAYER_ID; +export using ::VP8E_SET_TOKEN_PARTITIONS; +export using ::VP8E_SET_TUNING; +export using ::VP8E_THREEFIVE; +export using ::VP8_ADDNOISE; +export using ::VP8_ALTR_FRAME; +export using ::VP8_COMMON_CTRL_ID_MAX; +export using ::VP8_COPY_REFERENCE; +export using ::VP8_DEBLOCK; +export using ::VP8_DECODER_CTRL_ID_MAX; +export using ::VP8_DECODER_CTRL_ID_START; +export using ::VP8_DEMACROBLOCK; +export using ::VP8_EIGHT_TOKENPARTITION; +export using ::VP8_FOUR_TOKENPARTITION; +export using ::VP8_GOLD_FRAME; +export using ::VP8_LAST_FRAME; +export using ::VP8_MFQE; +export using ::VP8_NOFILTERING; +export using ::VP8_ONE_TOKENPARTITION; +export using ::VP8_SET_POSTPROC; +export using ::VP8_SET_REFERENCE; +export using ::VP8_TUNE_PSNR; +export using ::VP8_TUNE_SSIM; +export using ::VP8_TWO_TOKENPARTITION; +export using ::VP9D_GET_BIT_DEPTH; +export using ::VP9D_GET_DISPLAY_SIZE; +export using ::VP9D_GET_FRAME_SIZE; +export using ::VP9D_SET_LOOP_FILTER_OPT; +export using ::VP9D_SET_ROW_MT; +export using ::VP9E_CONTENT_DEFAULT; +export using ::VP9E_CONTENT_FILM; +export using ::VP9E_CONTENT_INVALID; +export using ::VP9E_CONTENT_SCREEN; +export using ::VP9E_ENABLE_MOTION_VECTOR_UNIT_TEST; +export using ::VP9E_GET_ACTIVEMAP; +export using ::VP9E_GET_LAST_QUANTIZER_SVC_LAYERS; +export using ::VP9E_GET_LEVEL; +export using ::VP9E_GET_LOOPFILTER_LEVEL; +export using ::VP9E_GET_SVC_LAYER_ID; +export using ::VP9E_GET_SVC_REF_FRAME_CONFIG; +export using ::VP9E_REGISTER_CX_CALLBACK; +export using ::VP9E_SET_ALT_REF_AQ; +export using ::VP9E_SET_AQ_MODE; +export using ::VP9E_SET_COLOR_RANGE; +export using ::VP9E_SET_COLOR_SPACE; +export using ::VP9E_SET_DELTA_Q_UV; +export using ::VP9E_SET_DISABLE_LOOPFILTER; +export using ::VP9E_SET_DISABLE_OVERSHOOT_MAXQ_CBR; +export using ::VP9E_SET_EXTERNAL_RATE_CONTROL; +export using ::VP9E_SET_FRAME_PARALLEL_DECODING; +export using ::VP9E_SET_FRAME_PERIODIC_BOOST; +export using ::VP9E_SET_GF_CBR_BOOST_PCT; +export using ::VP9E_SET_KEY_FRAME_FILTERING; +export using ::VP9E_SET_LOSSLESS; +export using ::VP9E_SET_MAX_GF_INTERVAL; +export using ::VP9E_SET_MAX_INTER_BITRATE_PCT; +export using ::VP9E_SET_MIN_GF_INTERVAL; +export using ::VP9E_SET_NOISE_SENSITIVITY; +export using ::VP9E_SET_POSTENCODE_DROP; +export using ::VP9E_SET_QUANTIZER_ONE_PASS; +export using ::VP9E_SET_RENDER_SIZE; +export using ::VP9E_SET_ROI_MAP; +export using ::VP9E_SET_ROW_MT; +export using ::VP9E_SET_RTC_EXTERNAL_RATECTRL; +export using ::VP9E_SET_SVC; +export using ::VP9E_SET_SVC_FRAME_DROP_LAYER; +export using ::VP9E_SET_SVC_GF_TEMPORAL_REF; +export using ::VP9E_SET_SVC_INTER_LAYER_PRED; +export using ::VP9E_SET_SVC_LAYER_ID; +export using ::VP9E_SET_SVC_PARAMETERS; +export using ::VP9E_SET_SVC_REF_FRAME_CONFIG; +export using ::VP9E_SET_SVC_SPATIAL_LAYER_SYNC; +export using ::VP9E_SET_TARGET_LEVEL; +export using ::VP9E_SET_TILE_COLUMNS; +export using ::VP9E_SET_TILE_ROWS; +export using ::VP9E_SET_TPL; +export using ::VP9E_SET_TUNE_CONTENT; +export using ::VP9E_SET_VALIDATE_HBD_INPUT; +export using ::VP9E_TEMPORAL_LAYERING_MODE_0101; +export using ::VP9E_TEMPORAL_LAYERING_MODE_0212; +export using ::VP9E_TEMPORAL_LAYERING_MODE_BYPASS; +export using ::VP9E_TEMPORAL_LAYERING_MODE_NOLAYERING; +export using ::VP9_DECODE_SVC_SPATIAL_LAYER; +export using ::VP9_GET_REFERENCE; +export using ::VP9_INVERT_TILE_DECODE_ORDER; +export using ::VP9_SET_BYTE_ALIGNMENT; +export using ::VP9_SET_SKIP_LOOP_FILTER; +export using ::VPXD_GET_LAST_QUANTIZER; +export using ::VPXD_SET_DECRYPTOR; +export using ::VPX_BITS_10; +export using ::VPX_BITS_12; +export using ::VPX_BITS_8; +export using ::VPX_CBR; +export using ::VPX_CODEC_ABI_MISMATCH; +export using ::VPX_CODEC_CORRUPT_FRAME; +export using ::VPX_CODEC_CUSTOM_PKT; +export using ::VPX_CODEC_CX_FRAME_PKT; +export using ::VPX_CODEC_ERROR; +export using ::VPX_CODEC_FPMB_STATS_PKT; +export using ::VPX_CODEC_INCAPABLE; +export using ::VPX_CODEC_INVALID_PARAM; +export using ::VPX_CODEC_LIST_END; +export using ::VPX_CODEC_MEM_ERROR; +export using ::VPX_CODEC_OK; +export using ::VPX_CODEC_PSNR_PKT; +export using ::VPX_CODEC_STATS_PKT; +export using ::VPX_CODEC_UNSUP_BITSTREAM; +export using ::VPX_CODEC_UNSUP_FEATURE; +export using ::VPX_CQ; +export using ::VPX_CR_FULL_RANGE; +export using ::VPX_CR_STUDIO_RANGE; +export using ::VPX_CS_BT_2020; +export using ::VPX_CS_BT_601; +export using ::VPX_CS_BT_709; +export using ::VPX_CS_RESERVED; +export using ::VPX_CS_SMPTE_170; +export using ::VPX_CS_SMPTE_240; +export using ::VPX_CS_SRGB; +export using ::VPX_CS_UNKNOWN; +export using ::VPX_IMG_FMT_I420; +export using ::VPX_IMG_FMT_I42016; +export using ::VPX_IMG_FMT_I422; +export using ::VPX_IMG_FMT_I42216; +export using ::VPX_IMG_FMT_I440; +export using ::VPX_IMG_FMT_I44016; +export using ::VPX_IMG_FMT_I444; +export using ::VPX_IMG_FMT_I44416; +export using ::VPX_IMG_FMT_NONE; +export using ::VPX_IMG_FMT_NV12; +export using ::VPX_IMG_FMT_YV12; +export using ::VPX_KF_AUTO; +export using ::VPX_KF_DISABLED; +export using ::VPX_KF_FIXED; +export using ::VPX_Q; +export using ::VPX_RC_ALTREF_FRAME; +export using ::VPX_RC_ARF_UPDATE; +export using ::VPX_RC_CQ; +export using ::VPX_RC_ERROR; +export using ::VPX_RC_FIRST_PASS; +export using ::VPX_RC_GF_UPDATE; +export using ::VPX_RC_GOLDEN_FRAME; +export using ::VPX_RC_GOP; +export using ::VPX_RC_GOP_QP; +export using ::VPX_RC_GOP_QP_RDMULT; +export using ::VPX_RC_INTRA_FRAME; +export using ::VPX_RC_INVALID_REF_FRAME; +export using ::VPX_RC_INVALID_UPDATE_TYPE; +export using ::VPX_RC_KF_UPDATE; +export using ::VPX_RC_LAST_FRAME; +export using ::VPX_RC_LAST_PASS; +export using ::VPX_RC_LF_UPDATE; +export using ::VPX_RC_MID_OVERLAY_UPDATE; +export using ::VPX_RC_NONE; +export using ::VPX_RC_OK; +export using ::VPX_RC_ONE_PASS; +export using ::VPX_RC_OVERLAY_UPDATE; +export using ::VPX_RC_QMODE; +export using ::VPX_RC_QP; +export using ::VPX_RC_RDMULT; +export using ::VPX_RC_USE_BUF_FRAME; +export using ::VPX_RC_VBR; +export using ::VPX_VBR; + +// functions (35: what upstream exports; VP8's interfaces are not built) +export using ::vpx_codec_build_config; +export using ::vpx_codec_control_; +export using ::vpx_codec_dec_init_ver; +export using ::vpx_codec_decode; +export using ::vpx_codec_destroy; +export using ::vpx_codec_enc_config_default; +export using ::vpx_codec_enc_config_set; +export using ::vpx_codec_enc_init_multi_ver; +export using ::vpx_codec_enc_init_ver; +export using ::vpx_codec_encode; +export using ::vpx_codec_err_to_string; +export using ::vpx_codec_error; +export using ::vpx_codec_error_detail; +export using ::vpx_codec_get_caps; +export using ::vpx_codec_get_cx_data; +export using ::vpx_codec_get_frame; +export using ::vpx_codec_get_global_headers; +export using ::vpx_codec_get_preview_frame; +export using ::vpx_codec_get_stream_info; +export using ::vpx_codec_iface_name; +export using ::vpx_codec_peek_stream_info; +export using ::vpx_codec_register_put_frame_cb; +export using ::vpx_codec_register_put_slice_cb; +export using ::vpx_codec_set_cx_data_buf; +export using ::vpx_codec_set_frame_buffer_functions; +export using ::vpx_codec_version; +export using ::vpx_codec_version_extra_str; +export using ::vpx_codec_version_str; +export using ::vpx_codec_vp9_cx; +export using ::vpx_codec_vp9_dx; +export using ::vpx_img_alloc; +export using ::vpx_img_flip; +export using ::vpx_img_free; +export using ::vpx_img_set_rect; +export using ::vpx_img_wrap; + +// the codec interfaces (2) +export using ::vpx_codec_vp9_cx_algo; +export using ::vpx_codec_vp9_dx_algo; + +// The typed controls (86): `vpx_codec_control(ctx, ID, data)` in C +// is vpx_codec_control_ID(ctx, ID, data), a static function of the headers; +// here an exported one of the same name and signature. +export namespace mcpplibs::mwrap::libvpx { + +vpx_codec_err_t vpx_codec_control_VP8_SET_REFERENCE(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_ref_frame_t * data); +vpx_codec_err_t vpx_codec_control_VP8_COPY_REFERENCE(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_ref_frame_t * data); +vpx_codec_err_t vpx_codec_control_VP8_SET_POSTPROC(vpx_codec_ctx_t* ctx, int ctrl_id, vp8_postproc_cfg_t * data); +vpx_codec_err_t vpx_codec_control_VP9_GET_REFERENCE(vpx_codec_ctx_t* ctx, int ctrl_id, vp9_ref_frame_t * data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_ROI_MAP(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_roi_map_t * data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_ACTIVEMAP(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_active_map_t * data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_SCALEMODE(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_scaling_mode_t * data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_CPUUSED(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_ENABLEAUTOALTREF(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_NOISE_SENSITIVITY(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_SHARPNESS(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_STATIC_THRESHOLD(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_TOKEN_PARTITIONS(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP8E_GET_LAST_QUANTIZER(vpx_codec_ctx_t* ctx, int ctrl_id, int * data); +vpx_codec_err_t vpx_codec_control_VP8E_GET_LAST_QUANTIZER_64(vpx_codec_ctx_t* ctx, int ctrl_id, int * data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_ARNR_MAXFRAMES(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_ARNR_STRENGTH(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +[[deprecated]] vpx_codec_err_t vpx_codec_control_VP8E_SET_ARNR_TYPE(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_TUNING(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_CQ_LEVEL(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_MAX_INTRA_BITRATE_PCT(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_FRAME_FLAGS(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_MAX_INTER_BITRATE_PCT(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_GF_CBR_BOOST_PCT(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_TEMPORAL_LAYER_ID(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_SCREEN_CONTENT_MODE(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_LOSSLESS(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_TILE_COLUMNS(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_TILE_ROWS(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_FRAME_PARALLEL_DECODING(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_AQ_MODE(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_FRAME_PERIODIC_BOOST(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_NOISE_SENSITIVITY(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_SVC(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_ROI_MAP(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_roi_map_t * data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_SVC_PARAMETERS(vpx_codec_ctx_t* ctx, int ctrl_id, void * data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_SVC_LAYER_ID(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_svc_layer_id_t * data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_TUNE_CONTENT(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9E_GET_SVC_LAYER_ID(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_svc_layer_id_t * data); +vpx_codec_err_t vpx_codec_control_VP9E_REGISTER_CX_CALLBACK(vpx_codec_ctx_t* ctx, int ctrl_id, void * data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_COLOR_SPACE(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_MIN_GF_INTERVAL(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_MAX_GF_INTERVAL(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP9E_GET_ACTIVEMAP(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_active_map_t * data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_COLOR_RANGE(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_SVC_REF_FRAME_CONFIG(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_svc_ref_frame_config_t * data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_RENDER_SIZE(vpx_codec_ctx_t* ctx, int ctrl_id, int * data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_TARGET_LEVEL(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_ROW_MT(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP9E_GET_LEVEL(vpx_codec_ctx_t* ctx, int ctrl_id, int * data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_ALT_REF_AQ(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_GF_CBR_BOOST_PCT(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP9E_ENABLE_MOTION_VECTOR_UNIT_TEST(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_SVC_INTER_LAYER_PRED(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_SVC_FRAME_DROP_LAYER(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_svc_frame_drop_t * data); +vpx_codec_err_t vpx_codec_control_VP9E_GET_SVC_REF_FRAME_CONFIG(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_svc_ref_frame_config_t * data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_SVC_GF_TEMPORAL_REF(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_SVC_SPATIAL_LAYER_SYNC(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_svc_spatial_layer_sync_t * data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_TPL(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_POSTENCODE_DROP(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_DELTA_Q_UV(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_DISABLE_OVERSHOOT_MAXQ_CBR(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_DISABLE_LOOPFILTER(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_EXTERNAL_RATE_CONTROL(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_rc_funcs_t * data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_RTC_EXTERNAL_RATECTRL(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9E_GET_LOOPFILTER_LEVEL(vpx_codec_ctx_t* ctx, int ctrl_id, int * data); +vpx_codec_err_t vpx_codec_control_VP9E_GET_LAST_QUANTIZER_SVC_LAYERS(vpx_codec_ctx_t* ctx, int ctrl_id, int * data); +vpx_codec_err_t vpx_codec_control_VP8E_SET_RTC_EXTERNAL_RATECTRL(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_QUANTIZER_ONE_PASS(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_KEY_FRAME_FILTERING(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9E_SET_VALIDATE_HBD_INPUT(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP8D_GET_LAST_REF_UPDATES(vpx_codec_ctx_t* ctx, int ctrl_id, int * data); +vpx_codec_err_t vpx_codec_control_VP8D_GET_FRAME_CORRUPTED(vpx_codec_ctx_t* ctx, int ctrl_id, int * data); +vpx_codec_err_t vpx_codec_control_VP8D_GET_LAST_REF_USED(vpx_codec_ctx_t* ctx, int ctrl_id, int * data); +vpx_codec_err_t vpx_codec_control_VPXD_SET_DECRYPTOR(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_decrypt_init * data); +vpx_codec_err_t vpx_codec_control_VP8D_SET_DECRYPTOR(vpx_codec_ctx_t* ctx, int ctrl_id, vpx_decrypt_init * data); +vpx_codec_err_t vpx_codec_control_VP9D_GET_FRAME_SIZE(vpx_codec_ctx_t* ctx, int ctrl_id, int * data); +vpx_codec_err_t vpx_codec_control_VP9D_GET_DISPLAY_SIZE(vpx_codec_ctx_t* ctx, int ctrl_id, int * data); +vpx_codec_err_t vpx_codec_control_VP9D_GET_BIT_DEPTH(vpx_codec_ctx_t* ctx, int ctrl_id, unsigned int * data); +vpx_codec_err_t vpx_codec_control_VP9_SET_BYTE_ALIGNMENT(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9_INVERT_TILE_DECODE_ORDER(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9_SET_SKIP_LOOP_FILTER(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9_DECODE_SVC_SPATIAL_LAYER(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VPXD_GET_LAST_QUANTIZER(vpx_codec_ctx_t* ctx, int ctrl_id, int * data); +vpx_codec_err_t vpx_codec_control_VP9D_SET_ROW_MT(vpx_codec_ctx_t* ctx, int ctrl_id, int data); +vpx_codec_err_t vpx_codec_control_VP9D_SET_LOOP_FILTER_OPT(vpx_codec_ctx_t* ctx, int ctrl_id, int data); + +} // namespace mcpplibs::mwrap::libvpx diff --git a/src/macros.cppm b/src/macros.cppm new file mode 100644 index 0000000..55528ca --- /dev/null +++ b/src/macros.cppm @@ -0,0 +1,119 @@ +// Generated by tools/gen_exports.py from libvpx 1.17.0's and libwebm 1.0.0.32's +// headers — do not edit by hand. +// +// mcpplibs.mwrap.libvpx.macros — the macros of libvpx's public headers (a named +// module cannot export a macro), under their original names in +// mcpplibs::mwrap::libvpx::macros: +// +// - the 65 constants (the ABI versions, VPX_CODEC_CAP_*, VPX_CODEC_USE_*, +// VPX_FRAME_IS_*, VPX_EFLAG_*, VP8_EFLAG_*, VPX_DL_*, the layer limits, +// the image format and plane constants …) as `inline constexpr`, of the +// type the macro's expression has; +// - vpx_codec_enc_init, vpx_codec_enc_init_multi, vpx_codec_dec_init and +// vpx_codec_version_major/_minor/_patch as functions; +// - VPX_VERSION_MAJOR/_MINOR/_PATCH(v) as constexpr functions. +// +// What stays a macro (in ): vpx_codec_control (token +// concatenation; the typed controls are functions of the main module), +// VPX_CTRL_USE_TYPE*, VPX_CTRL_VOID, the empty VPX_CTRL_ feature tests +// for #ifdef, the declaration annotations and the include guards; +// tools/check_exports.py accounts for every macro. +module; +#include "macros_capture.hpp" + +export module mcpplibs.mwrap.libvpx.macros; + +export namespace mcpplibs::mwrap::libvpx::macros { + +inline constexpr auto VPX_CODEC_ABI_VERSION = mwrap_libvpx_capture::value_VPX_CODEC_ABI_VERSION; +inline constexpr auto VPX_CODEC_CAP_DECODER = mwrap_libvpx_capture::value_VPX_CODEC_CAP_DECODER; +inline constexpr auto VPX_CODEC_CAP_ENCODER = mwrap_libvpx_capture::value_VPX_CODEC_CAP_ENCODER; +inline constexpr auto VPX_CODEC_CAP_HIGHBITDEPTH = mwrap_libvpx_capture::value_VPX_CODEC_CAP_HIGHBITDEPTH; +inline constexpr auto VPX_IMAGE_ABI_VERSION = mwrap_libvpx_capture::value_VPX_IMAGE_ABI_VERSION; +inline constexpr auto VPX_IMG_FMT_PLANAR = mwrap_libvpx_capture::value_VPX_IMG_FMT_PLANAR; +inline constexpr auto VPX_IMG_FMT_UV_FLIP = mwrap_libvpx_capture::value_VPX_IMG_FMT_UV_FLIP; +inline constexpr auto VPX_IMG_FMT_HAS_ALPHA = mwrap_libvpx_capture::value_VPX_IMG_FMT_HAS_ALPHA; +inline constexpr auto VPX_IMG_FMT_HIGHBITDEPTH = mwrap_libvpx_capture::value_VPX_IMG_FMT_HIGHBITDEPTH; +inline constexpr auto VPX_PLANE_PACKED = mwrap_libvpx_capture::value_VPX_PLANE_PACKED; +inline constexpr auto VPX_PLANE_Y = mwrap_libvpx_capture::value_VPX_PLANE_Y; +inline constexpr auto VPX_PLANE_U = mwrap_libvpx_capture::value_VPX_PLANE_U; +inline constexpr auto VPX_PLANE_V = mwrap_libvpx_capture::value_VPX_PLANE_V; +inline constexpr auto VPX_PLANE_ALPHA = mwrap_libvpx_capture::value_VPX_PLANE_ALPHA; +inline constexpr auto VPX_MAXIMUM_WORK_BUFFERS = mwrap_libvpx_capture::value_VPX_MAXIMUM_WORK_BUFFERS; +inline constexpr auto VP9_MAXIMUM_REF_BUFFERS = mwrap_libvpx_capture::value_VP9_MAXIMUM_REF_BUFFERS; +inline constexpr auto VPX_EXT_RATECTRL_ABI_VERSION = mwrap_libvpx_capture::value_VPX_EXT_RATECTRL_ABI_VERSION; +inline constexpr auto VPX_RC_MAX_STATIC_GF_GROUP_LENGTH = mwrap_libvpx_capture::value_VPX_RC_MAX_STATIC_GF_GROUP_LENGTH; +inline constexpr auto VPX_RC_MAX_REF_FRAMES = mwrap_libvpx_capture::value_VPX_RC_MAX_REF_FRAMES; +inline constexpr auto VPX_DEFAULT_Q = mwrap_libvpx_capture::value_VPX_DEFAULT_Q; +inline constexpr auto VPX_DEFAULT_RDMULT = mwrap_libvpx_capture::value_VPX_DEFAULT_RDMULT; +inline constexpr auto VPX_TPL_ABI_VERSION = mwrap_libvpx_capture::value_VPX_TPL_ABI_VERSION; +inline constexpr auto VPX_TS_MAX_PERIODICITY = mwrap_libvpx_capture::value_VPX_TS_MAX_PERIODICITY; +inline constexpr auto VPX_TS_MAX_LAYERS = mwrap_libvpx_capture::value_VPX_TS_MAX_LAYERS; +inline constexpr auto VPX_MAX_LAYERS = mwrap_libvpx_capture::value_VPX_MAX_LAYERS; +inline constexpr auto VPX_SS_MAX_LAYERS = mwrap_libvpx_capture::value_VPX_SS_MAX_LAYERS; +inline constexpr auto VPX_SS_DEFAULT_LAYERS = mwrap_libvpx_capture::value_VPX_SS_DEFAULT_LAYERS; +inline constexpr auto VPX_ENCODER_ABI_VERSION = mwrap_libvpx_capture::value_VPX_ENCODER_ABI_VERSION; +inline constexpr auto VPX_CODEC_CAP_PSNR = mwrap_libvpx_capture::value_VPX_CODEC_CAP_PSNR; +inline constexpr auto VPX_CODEC_CAP_OUTPUT_PARTITION = mwrap_libvpx_capture::value_VPX_CODEC_CAP_OUTPUT_PARTITION; +inline constexpr auto VPX_CODEC_USE_PSNR = mwrap_libvpx_capture::value_VPX_CODEC_USE_PSNR; +inline constexpr auto VPX_CODEC_USE_OUTPUT_PARTITION = mwrap_libvpx_capture::value_VPX_CODEC_USE_OUTPUT_PARTITION; +inline constexpr auto VPX_CODEC_USE_HIGHBITDEPTH = mwrap_libvpx_capture::value_VPX_CODEC_USE_HIGHBITDEPTH; +inline constexpr auto VPX_FRAME_IS_KEY = mwrap_libvpx_capture::value_VPX_FRAME_IS_KEY; +inline constexpr auto VPX_FRAME_IS_DROPPABLE = mwrap_libvpx_capture::value_VPX_FRAME_IS_DROPPABLE; +inline constexpr auto VPX_FRAME_IS_INVISIBLE = mwrap_libvpx_capture::value_VPX_FRAME_IS_INVISIBLE; +inline constexpr auto VPX_FRAME_IS_FRAGMENT = mwrap_libvpx_capture::value_VPX_FRAME_IS_FRAGMENT; +inline constexpr auto VPX_ERROR_RESILIENT_DEFAULT = mwrap_libvpx_capture::value_VPX_ERROR_RESILIENT_DEFAULT; +inline constexpr auto VPX_ERROR_RESILIENT_PARTITIONS = mwrap_libvpx_capture::value_VPX_ERROR_RESILIENT_PARTITIONS; +inline constexpr auto VPX_EFLAG_FORCE_KF = mwrap_libvpx_capture::value_VPX_EFLAG_FORCE_KF; +inline constexpr auto VPX_EFLAG_CALCULATE_PSNR = mwrap_libvpx_capture::value_VPX_EFLAG_CALCULATE_PSNR; +inline constexpr auto VPX_DL_REALTIME = mwrap_libvpx_capture::value_VPX_DL_REALTIME; +inline constexpr auto VPX_DL_GOOD_QUALITY = mwrap_libvpx_capture::value_VPX_DL_GOOD_QUALITY; +inline constexpr auto VPX_DL_BEST_QUALITY = mwrap_libvpx_capture::value_VPX_DL_BEST_QUALITY; +inline constexpr auto VPX_DECODER_ABI_VERSION = mwrap_libvpx_capture::value_VPX_DECODER_ABI_VERSION; +inline constexpr auto VPX_CODEC_CAP_PUT_SLICE = mwrap_libvpx_capture::value_VPX_CODEC_CAP_PUT_SLICE; +inline constexpr auto VPX_CODEC_CAP_PUT_FRAME = mwrap_libvpx_capture::value_VPX_CODEC_CAP_PUT_FRAME; +inline constexpr auto VPX_CODEC_CAP_POSTPROC = mwrap_libvpx_capture::value_VPX_CODEC_CAP_POSTPROC; +inline constexpr auto VPX_CODEC_CAP_ERROR_CONCEALMENT = mwrap_libvpx_capture::value_VPX_CODEC_CAP_ERROR_CONCEALMENT; +inline constexpr auto VPX_CODEC_CAP_INPUT_FRAGMENTS = mwrap_libvpx_capture::value_VPX_CODEC_CAP_INPUT_FRAGMENTS; +inline constexpr auto VPX_CODEC_CAP_FRAME_THREADING = mwrap_libvpx_capture::value_VPX_CODEC_CAP_FRAME_THREADING; +inline constexpr auto VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER = mwrap_libvpx_capture::value_VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER; +inline constexpr auto VPX_CODEC_USE_POSTPROC = mwrap_libvpx_capture::value_VPX_CODEC_USE_POSTPROC; +inline constexpr auto VPX_CODEC_USE_ERROR_CONCEALMENT = mwrap_libvpx_capture::value_VPX_CODEC_USE_ERROR_CONCEALMENT; +inline constexpr auto VPX_CODEC_USE_INPUT_FRAGMENTS = mwrap_libvpx_capture::value_VPX_CODEC_USE_INPUT_FRAGMENTS; +inline constexpr auto VPX_CODEC_USE_FRAME_THREADING = mwrap_libvpx_capture::value_VPX_CODEC_USE_FRAME_THREADING; +inline constexpr auto VP8_EFLAG_NO_REF_LAST = mwrap_libvpx_capture::value_VP8_EFLAG_NO_REF_LAST; +inline constexpr auto VP8_EFLAG_NO_REF_GF = mwrap_libvpx_capture::value_VP8_EFLAG_NO_REF_GF; +inline constexpr auto VP8_EFLAG_NO_REF_ARF = mwrap_libvpx_capture::value_VP8_EFLAG_NO_REF_ARF; +inline constexpr auto VP8_EFLAG_NO_UPD_LAST = mwrap_libvpx_capture::value_VP8_EFLAG_NO_UPD_LAST; +inline constexpr auto VP8_EFLAG_NO_UPD_GF = mwrap_libvpx_capture::value_VP8_EFLAG_NO_UPD_GF; +inline constexpr auto VP8_EFLAG_NO_UPD_ARF = mwrap_libvpx_capture::value_VP8_EFLAG_NO_UPD_ARF; +inline constexpr auto VP8_EFLAG_FORCE_GF = mwrap_libvpx_capture::value_VP8_EFLAG_FORCE_GF; +inline constexpr auto VP8_EFLAG_FORCE_ARF = mwrap_libvpx_capture::value_VP8_EFLAG_FORCE_ARF; +inline constexpr auto VP8_EFLAG_NO_UPD_ENTROPY = mwrap_libvpx_capture::value_VP8_EFLAG_NO_UPD_ENTROPY; + +// An encoder for `iface` (vpx_codec_enc_init_ver with the ABI version the headers were written for). +vpx_codec_err_t vpx_codec_enc_init(vpx_codec_ctx_t* ctx, vpx_codec_iface_t* iface, const vpx_codec_enc_cfg_t* cfg, vpx_codec_flags_t flags); +// Several encoders at once (multi-resolution; VP8 only). +vpx_codec_err_t vpx_codec_enc_init_multi(vpx_codec_ctx_t* ctx, vpx_codec_iface_t* iface, vpx_codec_enc_cfg_t* cfg, int num_enc, vpx_codec_flags_t flags, vpx_rational_t* dsf); +// A decoder for `iface` (vpx_codec_dec_init_ver with the ABI version the headers were written for). +vpx_codec_err_t vpx_codec_dec_init(vpx_codec_ctx_t* ctx, vpx_codec_iface_t* iface, const vpx_codec_dec_cfg_t* cfg, vpx_codec_flags_t flags); +// The library's version, its parts (from vpx_codec_version()). +int vpx_codec_version_major(); +int vpx_codec_version_minor(); +int vpx_codec_version_patch(); + +// The parts of a packed version (vpx_codec_version()). +constexpr int VPX_VERSION_MAJOR(int v) { return mwrap_libvpx_function::VPX_VERSION_MAJOR(v); } +constexpr int VPX_VERSION_MINOR(int v) { return mwrap_libvpx_function::VPX_VERSION_MINOR(v); } +constexpr int VPX_VERSION_PATCH(int v) { return mwrap_libvpx_function::VPX_VERSION_PATCH(v); } + +} // namespace mcpplibs::mwrap::libvpx::macros + +namespace mcpplibs::mwrap::libvpx::macros { +vpx_codec_err_t vpx_codec_enc_init(vpx_codec_ctx_t* ctx, vpx_codec_iface_t* iface, const vpx_codec_enc_cfg_t* cfg, vpx_codec_flags_t flags) { return mwrap_libvpx_function::vpx_codec_enc_init(ctx, iface, cfg, flags); } +vpx_codec_err_t vpx_codec_enc_init_multi(vpx_codec_ctx_t* ctx, vpx_codec_iface_t* iface, vpx_codec_enc_cfg_t* cfg, int num_enc, vpx_codec_flags_t flags, vpx_rational_t* dsf) { return mwrap_libvpx_function::vpx_codec_enc_init_multi(ctx, iface, cfg, num_enc, flags, dsf); } +vpx_codec_err_t vpx_codec_dec_init(vpx_codec_ctx_t* ctx, vpx_codec_iface_t* iface, const vpx_codec_dec_cfg_t* cfg, vpx_codec_flags_t flags) { return mwrap_libvpx_function::vpx_codec_dec_init(ctx, iface, cfg, flags); } +int vpx_codec_version_major() { return mwrap_libvpx_function::vpx_codec_version_major(); } +int vpx_codec_version_minor() { return mwrap_libvpx_function::vpx_codec_version_minor(); } +int vpx_codec_version_patch() { return mwrap_libvpx_function::vpx_codec_version_patch(); } +} // namespace mcpplibs::mwrap::libvpx::macros diff --git a/src/macros_capture.hpp b/src/macros_capture.hpp new file mode 100644 index 0000000..9758f57 --- /dev/null +++ b/src/macros_capture.hpp @@ -0,0 +1,162 @@ +// Generated by tools/gen_exports.py from libvpx 1.17.0's and libwebm 1.0.0.32's +// headers — do not edit by hand. +// +// The values of libvpx's macros, captured before src/macros.cppm undefines +// them (the global module fragment holds only what the preprocessor includes). +#pragma once +#include + +namespace mwrap_libvpx_capture { +inline constexpr auto value_VPX_CODEC_ABI_VERSION = VPX_CODEC_ABI_VERSION; +inline constexpr auto value_VPX_CODEC_CAP_DECODER = VPX_CODEC_CAP_DECODER; +inline constexpr auto value_VPX_CODEC_CAP_ENCODER = VPX_CODEC_CAP_ENCODER; +inline constexpr auto value_VPX_CODEC_CAP_HIGHBITDEPTH = VPX_CODEC_CAP_HIGHBITDEPTH; +inline constexpr auto value_VPX_IMAGE_ABI_VERSION = VPX_IMAGE_ABI_VERSION; +inline constexpr auto value_VPX_IMG_FMT_PLANAR = VPX_IMG_FMT_PLANAR; +inline constexpr auto value_VPX_IMG_FMT_UV_FLIP = VPX_IMG_FMT_UV_FLIP; +inline constexpr auto value_VPX_IMG_FMT_HAS_ALPHA = VPX_IMG_FMT_HAS_ALPHA; +inline constexpr auto value_VPX_IMG_FMT_HIGHBITDEPTH = VPX_IMG_FMT_HIGHBITDEPTH; +inline constexpr auto value_VPX_PLANE_PACKED = VPX_PLANE_PACKED; +inline constexpr auto value_VPX_PLANE_Y = VPX_PLANE_Y; +inline constexpr auto value_VPX_PLANE_U = VPX_PLANE_U; +inline constexpr auto value_VPX_PLANE_V = VPX_PLANE_V; +inline constexpr auto value_VPX_PLANE_ALPHA = VPX_PLANE_ALPHA; +inline constexpr auto value_VPX_MAXIMUM_WORK_BUFFERS = VPX_MAXIMUM_WORK_BUFFERS; +inline constexpr auto value_VP9_MAXIMUM_REF_BUFFERS = VP9_MAXIMUM_REF_BUFFERS; +inline constexpr auto value_VPX_EXT_RATECTRL_ABI_VERSION = VPX_EXT_RATECTRL_ABI_VERSION; +inline constexpr auto value_VPX_RC_MAX_STATIC_GF_GROUP_LENGTH = VPX_RC_MAX_STATIC_GF_GROUP_LENGTH; +inline constexpr auto value_VPX_RC_MAX_REF_FRAMES = VPX_RC_MAX_REF_FRAMES; +inline constexpr auto value_VPX_DEFAULT_Q = VPX_DEFAULT_Q; +inline constexpr auto value_VPX_DEFAULT_RDMULT = VPX_DEFAULT_RDMULT; +inline constexpr auto value_VPX_TPL_ABI_VERSION = VPX_TPL_ABI_VERSION; +inline constexpr auto value_VPX_TS_MAX_PERIODICITY = VPX_TS_MAX_PERIODICITY; +inline constexpr auto value_VPX_TS_MAX_LAYERS = VPX_TS_MAX_LAYERS; +inline constexpr auto value_VPX_MAX_LAYERS = VPX_MAX_LAYERS; +inline constexpr auto value_VPX_SS_MAX_LAYERS = VPX_SS_MAX_LAYERS; +inline constexpr auto value_VPX_SS_DEFAULT_LAYERS = VPX_SS_DEFAULT_LAYERS; +inline constexpr auto value_VPX_ENCODER_ABI_VERSION = VPX_ENCODER_ABI_VERSION; +inline constexpr auto value_VPX_CODEC_CAP_PSNR = VPX_CODEC_CAP_PSNR; +inline constexpr auto value_VPX_CODEC_CAP_OUTPUT_PARTITION = VPX_CODEC_CAP_OUTPUT_PARTITION; +inline constexpr auto value_VPX_CODEC_USE_PSNR = VPX_CODEC_USE_PSNR; +inline constexpr auto value_VPX_CODEC_USE_OUTPUT_PARTITION = VPX_CODEC_USE_OUTPUT_PARTITION; +inline constexpr auto value_VPX_CODEC_USE_HIGHBITDEPTH = VPX_CODEC_USE_HIGHBITDEPTH; +inline constexpr auto value_VPX_FRAME_IS_KEY = VPX_FRAME_IS_KEY; +inline constexpr auto value_VPX_FRAME_IS_DROPPABLE = VPX_FRAME_IS_DROPPABLE; +inline constexpr auto value_VPX_FRAME_IS_INVISIBLE = VPX_FRAME_IS_INVISIBLE; +inline constexpr auto value_VPX_FRAME_IS_FRAGMENT = VPX_FRAME_IS_FRAGMENT; +inline constexpr auto value_VPX_ERROR_RESILIENT_DEFAULT = VPX_ERROR_RESILIENT_DEFAULT; +inline constexpr auto value_VPX_ERROR_RESILIENT_PARTITIONS = VPX_ERROR_RESILIENT_PARTITIONS; +inline constexpr auto value_VPX_EFLAG_FORCE_KF = VPX_EFLAG_FORCE_KF; +inline constexpr auto value_VPX_EFLAG_CALCULATE_PSNR = VPX_EFLAG_CALCULATE_PSNR; +inline constexpr auto value_VPX_DL_REALTIME = VPX_DL_REALTIME; +inline constexpr auto value_VPX_DL_GOOD_QUALITY = VPX_DL_GOOD_QUALITY; +inline constexpr auto value_VPX_DL_BEST_QUALITY = VPX_DL_BEST_QUALITY; +inline constexpr auto value_VPX_DECODER_ABI_VERSION = VPX_DECODER_ABI_VERSION; +inline constexpr auto value_VPX_CODEC_CAP_PUT_SLICE = VPX_CODEC_CAP_PUT_SLICE; +inline constexpr auto value_VPX_CODEC_CAP_PUT_FRAME = VPX_CODEC_CAP_PUT_FRAME; +inline constexpr auto value_VPX_CODEC_CAP_POSTPROC = VPX_CODEC_CAP_POSTPROC; +inline constexpr auto value_VPX_CODEC_CAP_ERROR_CONCEALMENT = VPX_CODEC_CAP_ERROR_CONCEALMENT; +inline constexpr auto value_VPX_CODEC_CAP_INPUT_FRAGMENTS = VPX_CODEC_CAP_INPUT_FRAGMENTS; +inline constexpr auto value_VPX_CODEC_CAP_FRAME_THREADING = VPX_CODEC_CAP_FRAME_THREADING; +inline constexpr auto value_VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER = VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER; +inline constexpr auto value_VPX_CODEC_USE_POSTPROC = VPX_CODEC_USE_POSTPROC; +inline constexpr auto value_VPX_CODEC_USE_ERROR_CONCEALMENT = VPX_CODEC_USE_ERROR_CONCEALMENT; +inline constexpr auto value_VPX_CODEC_USE_INPUT_FRAGMENTS = VPX_CODEC_USE_INPUT_FRAGMENTS; +inline constexpr auto value_VPX_CODEC_USE_FRAME_THREADING = VPX_CODEC_USE_FRAME_THREADING; +inline constexpr auto value_VP8_EFLAG_NO_REF_LAST = VP8_EFLAG_NO_REF_LAST; +inline constexpr auto value_VP8_EFLAG_NO_REF_GF = VP8_EFLAG_NO_REF_GF; +inline constexpr auto value_VP8_EFLAG_NO_REF_ARF = VP8_EFLAG_NO_REF_ARF; +inline constexpr auto value_VP8_EFLAG_NO_UPD_LAST = VP8_EFLAG_NO_UPD_LAST; +inline constexpr auto value_VP8_EFLAG_NO_UPD_GF = VP8_EFLAG_NO_UPD_GF; +inline constexpr auto value_VP8_EFLAG_NO_UPD_ARF = VP8_EFLAG_NO_UPD_ARF; +inline constexpr auto value_VP8_EFLAG_FORCE_GF = VP8_EFLAG_FORCE_GF; +inline constexpr auto value_VP8_EFLAG_FORCE_ARF = VP8_EFLAG_FORCE_ARF; +inline constexpr auto value_VP8_EFLAG_NO_UPD_ENTROPY = VP8_EFLAG_NO_UPD_ENTROPY; +} // namespace mwrap_libvpx_capture + +namespace mwrap_libvpx_function { +inline vpx_codec_err_t (vpx_codec_enc_init)(vpx_codec_ctx_t* ctx, vpx_codec_iface_t* iface, const vpx_codec_enc_cfg_t* cfg, vpx_codec_flags_t flags) { return vpx_codec_enc_init(ctx, iface, cfg, flags); } +inline vpx_codec_err_t (vpx_codec_enc_init_multi)(vpx_codec_ctx_t* ctx, vpx_codec_iface_t* iface, vpx_codec_enc_cfg_t* cfg, int num_enc, vpx_codec_flags_t flags, vpx_rational_t* dsf) { return vpx_codec_enc_init_multi(ctx, iface, cfg, num_enc, flags, dsf); } +inline vpx_codec_err_t (vpx_codec_dec_init)(vpx_codec_ctx_t* ctx, vpx_codec_iface_t* iface, const vpx_codec_dec_cfg_t* cfg, vpx_codec_flags_t flags) { return vpx_codec_dec_init(ctx, iface, cfg, flags); } +inline int (vpx_codec_version_major)() { return vpx_codec_version_major(); } +inline int (vpx_codec_version_minor)() { return vpx_codec_version_minor(); } +inline int (vpx_codec_version_patch)() { return vpx_codec_version_patch(); } +constexpr int (VPX_VERSION_MAJOR)(int v) { return VPX_VERSION_MAJOR(v); } +constexpr int (VPX_VERSION_MINOR)(int v) { return VPX_VERSION_MINOR(v); } +constexpr int (VPX_VERSION_PATCH)(int v) { return VPX_VERSION_PATCH(v); } +} // namespace mwrap_libvpx_function + +#undef VPX_CODEC_ABI_VERSION +#undef VPX_CODEC_CAP_DECODER +#undef VPX_CODEC_CAP_ENCODER +#undef VPX_CODEC_CAP_HIGHBITDEPTH +#undef VPX_IMAGE_ABI_VERSION +#undef VPX_IMG_FMT_PLANAR +#undef VPX_IMG_FMT_UV_FLIP +#undef VPX_IMG_FMT_HAS_ALPHA +#undef VPX_IMG_FMT_HIGHBITDEPTH +#undef VPX_PLANE_PACKED +#undef VPX_PLANE_Y +#undef VPX_PLANE_U +#undef VPX_PLANE_V +#undef VPX_PLANE_ALPHA +#undef VPX_MAXIMUM_WORK_BUFFERS +#undef VP9_MAXIMUM_REF_BUFFERS +#undef VPX_EXT_RATECTRL_ABI_VERSION +#undef VPX_RC_MAX_STATIC_GF_GROUP_LENGTH +#undef VPX_RC_MAX_REF_FRAMES +#undef VPX_DEFAULT_Q +#undef VPX_DEFAULT_RDMULT +#undef VPX_TPL_ABI_VERSION +#undef VPX_TS_MAX_PERIODICITY +#undef VPX_TS_MAX_LAYERS +#undef VPX_MAX_LAYERS +#undef VPX_SS_MAX_LAYERS +#undef VPX_SS_DEFAULT_LAYERS +#undef VPX_ENCODER_ABI_VERSION +#undef VPX_CODEC_CAP_PSNR +#undef VPX_CODEC_CAP_OUTPUT_PARTITION +#undef VPX_CODEC_USE_PSNR +#undef VPX_CODEC_USE_OUTPUT_PARTITION +#undef VPX_CODEC_USE_HIGHBITDEPTH +#undef VPX_FRAME_IS_KEY +#undef VPX_FRAME_IS_DROPPABLE +#undef VPX_FRAME_IS_INVISIBLE +#undef VPX_FRAME_IS_FRAGMENT +#undef VPX_ERROR_RESILIENT_DEFAULT +#undef VPX_ERROR_RESILIENT_PARTITIONS +#undef VPX_EFLAG_FORCE_KF +#undef VPX_EFLAG_CALCULATE_PSNR +#undef VPX_DL_REALTIME +#undef VPX_DL_GOOD_QUALITY +#undef VPX_DL_BEST_QUALITY +#undef VPX_DECODER_ABI_VERSION +#undef VPX_CODEC_CAP_PUT_SLICE +#undef VPX_CODEC_CAP_PUT_FRAME +#undef VPX_CODEC_CAP_POSTPROC +#undef VPX_CODEC_CAP_ERROR_CONCEALMENT +#undef VPX_CODEC_CAP_INPUT_FRAGMENTS +#undef VPX_CODEC_CAP_FRAME_THREADING +#undef VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER +#undef VPX_CODEC_USE_POSTPROC +#undef VPX_CODEC_USE_ERROR_CONCEALMENT +#undef VPX_CODEC_USE_INPUT_FRAGMENTS +#undef VPX_CODEC_USE_FRAME_THREADING +#undef VP8_EFLAG_NO_REF_LAST +#undef VP8_EFLAG_NO_REF_GF +#undef VP8_EFLAG_NO_REF_ARF +#undef VP8_EFLAG_NO_UPD_LAST +#undef VP8_EFLAG_NO_UPD_GF +#undef VP8_EFLAG_NO_UPD_ARF +#undef VP8_EFLAG_FORCE_GF +#undef VP8_EFLAG_FORCE_ARF +#undef VP8_EFLAG_NO_UPD_ENTROPY +#undef vpx_codec_enc_init +#undef vpx_codec_enc_init_multi +#undef vpx_codec_dec_init +#undef vpx_codec_version_major +#undef vpx_codec_version_minor +#undef vpx_codec_version_patch +#undef VPX_VERSION_MAJOR +#undef VPX_VERSION_MINOR +#undef VPX_VERSION_PATCH diff --git a/src/webm.cppm b/src/webm.cppm new file mode 100644 index 0000000..8e81283 --- /dev/null +++ b/src/webm.cppm @@ -0,0 +1,309 @@ +// Generated by tools/gen_exports.py from libvpx 1.17.0's and libwebm 1.0.0.32's +// headers — do not edit by hand. +// +// mcpplibs.mwrap.libvpx.webm — libwebm 1.0.0.32, the WebM (Matroska) container, +// as a C++ module: the classes, types, enumerators and functions of the +// headers of upstream's `webm` library in their own namespaces — mkvmuxer +// (Segment, the tracks, Frame, Colour, IMkvWriter and MkvWriter …), mkvparser +// (Segment, Tracks, Cluster, Block, IMkvReader and MkvReader …) and libwebm +// (the element ids, the colour copiers). The namespace-scope constants of the +// headers have internal linkage, so they are not exported by name: their +// values are under the same names in mcpplibs::mwrap::libvpx::mkvmuxer and +// mcpplibs::mwrap::libvpx::mkvparser. The library is compat.libwebm. +module; +#include +#include +#include +#include +#include +#include +#include +#include +#include + +export module mcpplibs.mwrap.libvpx.webm; + +export namespace mkvmuxer { +// types (29) +using ::mkvmuxer::AudioTrack; +using ::mkvmuxer::Chapter; +using ::mkvmuxer::Chapters; +using ::mkvmuxer::Cluster; +using ::mkvmuxer::Colour; +using ::mkvmuxer::ContentEncAESSettings; +using ::mkvmuxer::ContentEncoding; +using ::mkvmuxer::CuePoint; +using ::mkvmuxer::Cues; +using ::mkvmuxer::Frame; +using ::mkvmuxer::IMkvWriter; +using ::mkvmuxer::MasteringMetadata; +using ::mkvmuxer::MkvWriter; +using ::mkvmuxer::PrimaryChromaticity; +using ::mkvmuxer::Projection; +using ::mkvmuxer::SeekHead; +using ::mkvmuxer::Segment; +using ::mkvmuxer::SegmentInfo; +using ::mkvmuxer::Tag; +using ::mkvmuxer::Tags; +using ::mkvmuxer::Track; +using ::mkvmuxer::Tracks; +using ::mkvmuxer::VideoTrack; +using ::mkvmuxer::int16; +using ::mkvmuxer::int32; +using ::mkvmuxer::int64; +using ::mkvmuxer::uint32; +using ::mkvmuxer::uint64; +using ::mkvmuxer::uint8; +// functions (26) +using ::mkvmuxer::ChunkedCopy; +using ::mkvmuxer::EbmlDateElementSize; +using ::mkvmuxer::EbmlElementSize; +using ::mkvmuxer::EbmlMasterElementSize; +using ::mkvmuxer::GetCodedUIntSize; +using ::mkvmuxer::GetIntSize; +using ::mkvmuxer::GetUIntSize; +using ::mkvmuxer::GetVersion; +using ::mkvmuxer::IsChromaSitingHorzValueValid; +using ::mkvmuxer::IsChromaSitingVertValueValid; +using ::mkvmuxer::IsColourRangeValueValid; +using ::mkvmuxer::IsMatrixCoefficientsValueValid; +using ::mkvmuxer::IsPrimariesValueValid; +using ::mkvmuxer::IsTransferCharacteristicsValueValid; +using ::mkvmuxer::MakeUID; +using ::mkvmuxer::SerializeFloat; +using ::mkvmuxer::SerializeInt; +using ::mkvmuxer::WriteEbmlDateElement; +using ::mkvmuxer::WriteEbmlElement; +using ::mkvmuxer::WriteEbmlHeader; +using ::mkvmuxer::WriteEbmlMasterElement; +using ::mkvmuxer::WriteFrame; +using ::mkvmuxer::WriteID; +using ::mkvmuxer::WriteUInt; +using ::mkvmuxer::WriteUIntSize; +using ::mkvmuxer::WriteVoidElement; +} // namespace mkvmuxer + +export namespace mkvparser { +// types (24) +using ::mkvparser::AudioTrack; +using ::mkvparser::Block; +using ::mkvparser::BlockEntry; +using ::mkvparser::BlockGroup; +using ::mkvparser::Chapters; +using ::mkvparser::Cluster; +using ::mkvparser::Colour; +using ::mkvparser::ContentEncoding; +using ::mkvparser::CuePoint; +using ::mkvparser::Cues; +using ::mkvparser::EBMLHeader; +using ::mkvparser::IMkvReader; +using ::mkvparser::MasteringMetadata; +using ::mkvparser::MkvReader; +using ::mkvparser::PrimaryChromaticity; +using ::mkvparser::Projection; +using ::mkvparser::SeekHead; +using ::mkvparser::Segment; +using ::mkvparser::SegmentInfo; +using ::mkvparser::SimpleBlock; +using ::mkvparser::Tags; +using ::mkvparser::Track; +using ::mkvparser::Tracks; +using ::mkvparser::VideoTrack; +// functions (11) +using ::mkvparser::GetUIntLength; +using ::mkvparser::GetVersion; +using ::mkvparser::Match; +using ::mkvparser::ParseElementHeader; +using ::mkvparser::ReadID; +using ::mkvparser::ReadUInt; +using ::mkvparser::SafeArrayAlloc; +using ::mkvparser::UnserializeFloat; +using ::mkvparser::UnserializeInt; +using ::mkvparser::UnserializeString; +using ::mkvparser::UnserializeUInt; +} // namespace mkvparser + +export namespace libwebm { +// types (4) +using ::libwebm::MkvId; +using ::libwebm::PrimaryChromaticityPtr; +using ::libwebm::TempFileDeleter; +using ::libwebm::Vp9CodecFeatures; +// enumerators (154) +using ::libwebm::kMkvAESSettingsCipherInitData; +using ::libwebm::kMkvAESSettingsCipherMode; +using ::libwebm::kMkvAlphaMode; +using ::libwebm::kMkvAspectRatioType; +using ::libwebm::kMkvAudio; +using ::libwebm::kMkvBitDepth; +using ::libwebm::kMkvBitsPerChannel; +using ::libwebm::kMkvBlock; +using ::libwebm::kMkvBlockAddID; +using ::libwebm::kMkvBlockAdditional; +using ::libwebm::kMkvBlockAdditions; +using ::libwebm::kMkvBlockDuration; +using ::libwebm::kMkvBlockGroup; +using ::libwebm::kMkvBlockMore; +using ::libwebm::kMkvCbSubsamplingHorz; +using ::libwebm::kMkvCbSubsamplingVert; +using ::libwebm::kMkvChannels; +using ::libwebm::kMkvChapCountry; +using ::libwebm::kMkvChapLanguage; +using ::libwebm::kMkvChapString; +using ::libwebm::kMkvChapterAtom; +using ::libwebm::kMkvChapterDisplay; +using ::libwebm::kMkvChapterStringUID; +using ::libwebm::kMkvChapterTimeEnd; +using ::libwebm::kMkvChapterTimeStart; +using ::libwebm::kMkvChapterUID; +using ::libwebm::kMkvChapters; +using ::libwebm::kMkvChromaSitingHorz; +using ::libwebm::kMkvChromaSitingVert; +using ::libwebm::kMkvChromaSubsamplingHorz; +using ::libwebm::kMkvChromaSubsamplingVert; +using ::libwebm::kMkvCluster; +using ::libwebm::kMkvCodecDelay; +using ::libwebm::kMkvCodecID; +using ::libwebm::kMkvCodecName; +using ::libwebm::kMkvCodecPrivate; +using ::libwebm::kMkvColour; +using ::libwebm::kMkvColourSpace; +using ::libwebm::kMkvContentCompAlgo; +using ::libwebm::kMkvContentCompSettings; +using ::libwebm::kMkvContentCompression; +using ::libwebm::kMkvContentEncAESSettings; +using ::libwebm::kMkvContentEncAlgo; +using ::libwebm::kMkvContentEncKeyID; +using ::libwebm::kMkvContentEncoding; +using ::libwebm::kMkvContentEncodingOrder; +using ::libwebm::kMkvContentEncodingScope; +using ::libwebm::kMkvContentEncodingType; +using ::libwebm::kMkvContentEncodings; +using ::libwebm::kMkvContentEncryption; +using ::libwebm::kMkvContentSigAlgo; +using ::libwebm::kMkvContentSigHashAlgo; +using ::libwebm::kMkvContentSigKeyID; +using ::libwebm::kMkvContentSignature; +using ::libwebm::kMkvCueBlockNumber; +using ::libwebm::kMkvCueClusterPosition; +using ::libwebm::kMkvCuePoint; +using ::libwebm::kMkvCueTime; +using ::libwebm::kMkvCueTrack; +using ::libwebm::kMkvCueTrackPositions; +using ::libwebm::kMkvCues; +using ::libwebm::kMkvDateUTC; +using ::libwebm::kMkvDefaultDuration; +using ::libwebm::kMkvDiscardPadding; +using ::libwebm::kMkvDisplayHeight; +using ::libwebm::kMkvDisplayUnit; +using ::libwebm::kMkvDisplayWidth; +using ::libwebm::kMkvDocType; +using ::libwebm::kMkvDocTypeReadVersion; +using ::libwebm::kMkvDocTypeVersion; +using ::libwebm::kMkvDuration; +using ::libwebm::kMkvEBML; +using ::libwebm::kMkvEBMLMaxIDLength; +using ::libwebm::kMkvEBMLMaxSizeLength; +using ::libwebm::kMkvEBMLReadVersion; +using ::libwebm::kMkvEBMLVersion; +using ::libwebm::kMkvEditionEntry; +using ::libwebm::kMkvFlagDefault; +using ::libwebm::kMkvFlagEnabled; +using ::libwebm::kMkvFlagForced; +using ::libwebm::kMkvFlagInterlaced; +using ::libwebm::kMkvFlagLacing; +using ::libwebm::kMkvFrameRate; +using ::libwebm::kMkvInfo; +using ::libwebm::kMkvLaceNumber; +using ::libwebm::kMkvLanguage; +using ::libwebm::kMkvLuminanceMax; +using ::libwebm::kMkvLuminanceMin; +using ::libwebm::kMkvMasteringMetadata; +using ::libwebm::kMkvMatrixCoefficients; +using ::libwebm::kMkvMaxBlockAdditionID; +using ::libwebm::kMkvMaxCLL; +using ::libwebm::kMkvMaxFALL; +using ::libwebm::kMkvMuxingApp; +using ::libwebm::kMkvName; +using ::libwebm::kMkvOutputSamplingFrequency; +using ::libwebm::kMkvPixelCropBottom; +using ::libwebm::kMkvPixelCropLeft; +using ::libwebm::kMkvPixelCropRight; +using ::libwebm::kMkvPixelCropTop; +using ::libwebm::kMkvPixelHeight; +using ::libwebm::kMkvPixelWidth; +using ::libwebm::kMkvPrevSize; +using ::libwebm::kMkvPrimaries; +using ::libwebm::kMkvPrimaryBChromaticityX; +using ::libwebm::kMkvPrimaryBChromaticityY; +using ::libwebm::kMkvPrimaryGChromaticityX; +using ::libwebm::kMkvPrimaryGChromaticityY; +using ::libwebm::kMkvPrimaryRChromaticityX; +using ::libwebm::kMkvPrimaryRChromaticityY; +using ::libwebm::kMkvProjection; +using ::libwebm::kMkvProjectionPosePitch; +using ::libwebm::kMkvProjectionPoseRoll; +using ::libwebm::kMkvProjectionPoseYaw; +using ::libwebm::kMkvProjectionPrivate; +using ::libwebm::kMkvProjectionType; +using ::libwebm::kMkvRange; +using ::libwebm::kMkvReferenceBlock; +using ::libwebm::kMkvSamplingFrequency; +using ::libwebm::kMkvSeek; +using ::libwebm::kMkvSeekHead; +using ::libwebm::kMkvSeekID; +using ::libwebm::kMkvSeekPosition; +using ::libwebm::kMkvSeekPreRoll; +using ::libwebm::kMkvSegment; +using ::libwebm::kMkvSignature; +using ::libwebm::kMkvSignatureAlgo; +using ::libwebm::kMkvSignatureElementList; +using ::libwebm::kMkvSignatureElements; +using ::libwebm::kMkvSignatureHash; +using ::libwebm::kMkvSignaturePublicKey; +using ::libwebm::kMkvSignatureSlot; +using ::libwebm::kMkvSignedElement; +using ::libwebm::kMkvSimpleBlock; +using ::libwebm::kMkvSimpleTag; +using ::libwebm::kMkvStereoMode; +using ::libwebm::kMkvTag; +using ::libwebm::kMkvTagName; +using ::libwebm::kMkvTagString; +using ::libwebm::kMkvTags; +using ::libwebm::kMkvTimecode; +using ::libwebm::kMkvTimecodeScale; +using ::libwebm::kMkvTitle; +using ::libwebm::kMkvTrackEntry; +using ::libwebm::kMkvTrackNumber; +using ::libwebm::kMkvTrackType; +using ::libwebm::kMkvTrackUID; +using ::libwebm::kMkvTracks; +using ::libwebm::kMkvTransferCharacteristics; +using ::libwebm::kMkvVideo; +using ::libwebm::kMkvVoid; +using ::libwebm::kMkvWhitePointChromaticityX; +using ::libwebm::kMkvWhitePointChromaticityY; +using ::libwebm::kMkvWritingApp; +// functions (9) +using ::libwebm::ColourValuePresent; +using ::libwebm::CopyColour; +using ::libwebm::CopyMasteringMetadata; +using ::libwebm::CopyPrimaryChromaticity; +using ::libwebm::GetFileContents; +using ::libwebm::GetFileSize; +using ::libwebm::GetTempFileName; +using ::libwebm::MasteringMetadataValuePresent; +using ::libwebm::ParseVpxCodecPrivate; +} // namespace libwebm + +export namespace mcpplibs::mwrap::libvpx::mkvmuxer { +inline constexpr auto kMaxTrackNumber = ::mkvmuxer::kMaxTrackNumber; // uint64_t +inline constexpr auto kEbmlUnknownValue = ::mkvmuxer::kEbmlUnknownValue; // uint64 +inline constexpr auto kMaxBlockTimecode = ::mkvmuxer::kMaxBlockTimecode; // int64 +} // namespace mcpplibs::mwrap::libvpx::mkvmuxer + +export namespace mcpplibs::mwrap::libvpx::mkvparser { +inline constexpr auto E_PARSE_FAILED = ::mkvparser::E_PARSE_FAILED; // int +inline constexpr auto E_FILE_FORMAT_INVALID = ::mkvparser::E_FILE_FORMAT_INVALID; // int +inline constexpr auto E_BUFFER_NOT_FULL = ::mkvparser::E_BUFFER_NOT_FULL; // int +} // namespace mcpplibs::mwrap::libvpx::mkvparser diff --git a/templates/webm/mcpp.toml.in b/templates/webm/mcpp.toml.in new file mode 100644 index 0000000..a637560 --- /dev/null +++ b/templates/webm/mcpp.toml.in @@ -0,0 +1,10 @@ +[package] +name = "{{project.name}}" +version = "0.1.0" + +[targets.{{project.name}}] +kind = "bin" +main = "src/main.cpp" + +[dependencies] +"{{self.name}}" = "{{self.version}}" diff --git a/templates/webm/src/main.cpp.in b/templates/webm/src/main.cpp.in new file mode 100644 index 0000000..2d65c73 --- /dev/null +++ b/templates/webm/src/main.cpp.in @@ -0,0 +1,150 @@ +// {{project.name}} — VP9 in WebM through import mcpplibs.mwrap.libvpx: two +// seconds of moving pictures (a gradient and a bouncing square, 320 x 180 at +// 30 fps) encoded by libvpx at a constant quality, muxed into a WebM with +// mkvmuxer, parsed back with mkvparser, decoded, and compared with what went +// in. Give a file name to also write the WebM (any browser plays it). +import std; +import mcpplibs.mwrap.libvpx; + +namespace m = mcpplibs::mwrap::libvpx::macros; // the macros as constants and functions +namespace vpx = mcpplibs::mwrap::libvpx; // the typed controls (vpx_codec_control_) + +namespace { + +constexpr int W = 320, H = 180, FPS = 30, FRAMES = 60; + +void draw(vpx_image_t& img, int t) { + const int sx = 20 + (t * 7) % (W - 80), sy = 20 + (t * 3) % (H - 80); + for (int y = 0; y < H; ++y) + for (int x = 0; x < W; ++x) { + const bool square = x >= sx && x < sx + 60 && y >= sy && y < sy + 60; + img.planes[m::VPX_PLANE_Y][y * img.stride[m::VPX_PLANE_Y] + x] = static_cast(square ? 210 : 16 + (x + y + 4 * t) % 200); + } + for (int y = 0; y < H / 2; ++y) + for (int x = 0; x < W / 2; ++x) { + img.planes[m::VPX_PLANE_U][y * img.stride[m::VPX_PLANE_U] + x] = static_cast(96 + x * 64 / (W / 2)); + img.planes[m::VPX_PLANE_V][y * img.stride[m::VPX_PLANE_V] + x] = static_cast(160 - y * 64 / (H / 2)); + } +} + +// mkvmuxer writes through an IMkvWriter: here, memory (seekable, so the +// duration and the cues are written at the end). +class Memory final : public mkvmuxer::IMkvWriter { +public: + std::vector bytes; + std::int64_t at = 0; + mkvmuxer::int32 Write(const void* buf, mkvmuxer::uint32 len) override { + if (bytes.size() < static_cast(at) + len) bytes.resize(static_cast(at) + len); + std::memcpy(bytes.data() + at, buf, len); + at += len; + return 0; + } + mkvmuxer::int64 Position() const override { return at; } + mkvmuxer::int32 Position(mkvmuxer::int64 position) override { return at = position, 0; } + bool Seekable() const override { return true; } + void ElementStartNotify(mkvmuxer::uint64, mkvmuxer::int64) override {} +}; + +class Reader final : public mkvparser::IMkvReader { +public: + explicit Reader(const std::vector& b) : bytes(b) {} + int Read(long long pos, long len, unsigned char* buf) override { + if (pos < 0 || len < 0 || static_cast(pos + len) > bytes.size()) return -1; + std::memcpy(buf, bytes.data() + pos, static_cast(len)); + return 0; + } + int Length(long long* total, long long* available) override { + *total = *available = static_cast(bytes.size()); + return 0; + } + const std::vector& bytes; +}; + +} // namespace + +int main(int argc, char** argv) { + // The encoder: VP9, constant quality, one pass. + vpx_codec_enc_cfg_t cfg; + vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &cfg, 0); + cfg.g_w = W; + cfg.g_h = H; + cfg.g_timebase = {1, FPS}; + cfg.rc_end_usage = VPX_Q; + vpx_codec_ctx_t encoder; + if (m::vpx_codec_enc_init(&encoder, vpx_codec_vp9_cx(), &cfg, 0) != VPX_CODEC_OK) return 1; + vpx::vpx_codec_control_VP8E_SET_CPUUSED(&encoder, VP8E_SET_CPUUSED, 5); + vpx::vpx_codec_control_VP8E_SET_CQ_LEVEL(&encoder, VP8E_SET_CQ_LEVEL, 20u); + + // The container: one VP9 track. + Memory file; + mkvmuxer::Segment segment; + segment.Init(&file); + segment.set_mode(mkvmuxer::Segment::kFile); + const auto track = segment.AddVideoTrack(W, H, 1); + static_cast(segment.GetTrackByNumber(track))->set_codec_id(mkvmuxer::Tracks::kVp9CodecId); + + vpx_image_t img; + vpx_img_alloc(&img, VPX_IMG_FMT_I420, W, H, 16); + auto mux = [&] { + vpx_codec_iter_t it = nullptr; + while (const vpx_codec_cx_pkt_t* p = vpx_codec_get_cx_data(&encoder, &it)) + if (p->kind == VPX_CODEC_CX_FRAME_PKT) + segment.AddFrame(static_cast(p->data.frame.buf), p->data.frame.sz, track, + static_cast(p->data.frame.pts) * 1'000'000'000 / FPS, (p->data.frame.flags & m::VPX_FRAME_IS_KEY) != 0); + }; + for (int t = 0; t < FRAMES; ++t) { + draw(img, t); + vpx_codec_encode(&encoder, &img, t, 1, 0, m::VPX_DL_GOOD_QUALITY); + mux(); + } + for (int k = 0; k < 30; ++k) { // what the look-ahead holds + vpx_codec_encode(&encoder, nullptr, -1, 1, 0, m::VPX_DL_GOOD_QUALITY); + mux(); + } + vpx_codec_destroy(&encoder); + segment.Finalize(); + std::println("{} frames of {} x {} at {} fps: a WebM of {} bytes ({} kb/s)", FRAMES, W, H, FPS, file.bytes.size(), file.bytes.size() * 8 * FPS / FRAMES / 1000); + + // Back: mkvparser finds the frames, the VP9 decoder gives the pictures. + Reader reader(file.bytes); + long long pos = 0; + mkvparser::EBMLHeader header; + header.Parse(&reader, pos); + mkvparser::Segment* parsed = nullptr; + mkvparser::Segment::CreateInstance(&reader, pos, parsed); + std::unique_ptr owner(parsed); + parsed->Load(); + vpx_codec_dec_cfg_t dcfg{}; + vpx_codec_ctx_t decoder; + m::vpx_codec_dec_init(&decoder, vpx_codec_vp9_dx(), &dcfg, 0); + int frames = 0; + double worst = 99; + for (const mkvparser::Cluster* c = parsed->GetFirst(); c && !c->EOS(); c = parsed->GetNext(c)) { + const mkvparser::BlockEntry* e = nullptr; + for (c->GetFirst(e); e && !e->EOS(); c->GetNext(e, e)) { + const mkvparser::Block::Frame& f = e->GetBlock()->GetFrame(0); + std::vector data(static_cast(f.len)); + f.Read(&reader, data.data()); + vpx_codec_decode(&decoder, data.data(), static_cast(data.size()), nullptr, 0); + vpx_codec_iter_t it = nullptr; + while (vpx_image_t* out = vpx_codec_get_frame(&decoder, &it)) { + draw(img, frames++); + double se = 0; + for (int y = 0; y < H; ++y) + for (int x = 0; x < W; ++x) { + const int d = out->planes[0][y * out->stride[0] + x] - img.planes[0][y * img.stride[0] + x]; + se += d * d; + } + if (se > 0) worst = std::min(worst, 10 * std::log10(255.0 * 255.0 * W * H / se)); + } + } + } + vpx_codec_destroy(&decoder); + vpx_img_free(&img); + std::println("read back: {} frames, the worst at {:.1f} dB PSNR (luma)", frames, worst); + if (argc > 1) { + std::ofstream(argv[1], std::ios::binary).write(reinterpret_cast(file.bytes.data()), static_cast(file.bytes.size())); + std::println("wrote {}", argv[1]); + } + return frames == FRAMES && worst > 35 ? 0 : 1; +} diff --git a/templates/webm/template.toml b/templates/webm/template.toml new file mode 100644 index 0000000..9f0f526 --- /dev/null +++ b/templates/webm/template.toml @@ -0,0 +1,4 @@ +[template] +description = "VP9 in WebM with import mcpplibs.mwrap.libvpx: two seconds of moving pictures encoded by libvpx, muxed into a WebM with libwebm's mkvmuxer, parsed back with mkvparser and decoded — the frames' quality and the file's size — all in memory, no device or file needed" +default = true +post_message = "Run `mcpp run`, or `mcpp run -- clip.webm` to also write the WebM to a file (any browser plays it)." diff --git a/tests/api_test.cpp b/tests/api_test.cpp new file mode 100644 index 0000000..a7bce7d --- /dev/null +++ b/tests/api_test.cpp @@ -0,0 +1,262 @@ +// The API through the modules alone: VP9 frames encoded (good quality and real +// time, one thread and four, lossless), muxed into a WebM with mkvmuxer (a VP9 +// track with its colour, an Opus track's header, cues, the duration), parsed +// back with mkvparser, decoded and compared — every frame back, at a PSNR a +// correct codec reaches; lossless, every pixel. One thread, the same settings: +// the same bytes twice; each file's bytes pinned (the codec's portable C and +// the muxer with its track UID given write the same bytes on every target). +import std; +import mcpplibs.mwrap.libvpx; + +namespace m = mcpplibs::mwrap::libvpx::macros; +namespace vpx = mcpplibs::mwrap::libvpx; + +namespace { + +int failures = 0; + +void check(bool ok, std::string_view what) { + std::println("{:<72} {}", what, ok ? "ok" : "FAILED"); + if (!ok) ++failures; +} + +constexpr int W = 256, H = 144, FRAMES = 30; + +// A gradient that moves and a square that crosses it. +void draw(vpx_image_t& img, int t) { + for (int y = 0; y < H; ++y) + for (int x = 0; x < W; ++x) { + int v = (x * 2 + y + t * 5) & 255; + if (x >= 30 + t * 6 && x < 70 + t * 6 && y >= 50 && y < 90) v = 235; + img.planes[m::VPX_PLANE_Y][y * img.stride[m::VPX_PLANE_Y] + x] = static_cast(v); + } + for (int y = 0; y < H / 2; ++y) + for (int x = 0; x < W / 2; ++x) { + img.planes[m::VPX_PLANE_U][y * img.stride[m::VPX_PLANE_U] + x] = static_cast(128 + ((x + t) & 31) - 16); + img.planes[m::VPX_PLANE_V][y * img.stride[m::VPX_PLANE_V] + x] = static_cast(128 - ((y + t) & 31) + 16); + } +} + +struct Packet { + std::vector bytes; + std::int64_t pts = 0; + bool key = false; +}; + +std::vector encode(vpx_enc_deadline_t deadline, int speed, bool lossless, unsigned threads) { + std::vector out; + vpx_codec_enc_cfg_t cfg; + if (vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &cfg, 0) != VPX_CODEC_OK) return out; + cfg.g_w = W; + cfg.g_h = H; + cfg.g_timebase = {1, 30}; + cfg.g_threads = threads; + cfg.g_lag_in_frames = 0; + cfg.rc_end_usage = VPX_Q; + vpx_codec_ctx_t ctx; + if (m::vpx_codec_enc_init(&ctx, vpx_codec_vp9_cx(), &cfg, 0) != VPX_CODEC_OK) return out; + vpx::vpx_codec_control_VP8E_SET_CPUUSED(&ctx, VP8E_SET_CPUUSED, speed); + vpx::vpx_codec_control_VP8E_SET_CQ_LEVEL(&ctx, VP8E_SET_CQ_LEVEL, 18u); + vpx::vpx_codec_control_VP9E_SET_COLOR_SPACE(&ctx, VP9E_SET_COLOR_SPACE, VPX_CS_BT_709); + if (lossless) vpx::vpx_codec_control_VP9E_SET_LOSSLESS(&ctx, VP9E_SET_LOSSLESS, 1u); + if (threads > 1) vpx::vpx_codec_control_VP9E_SET_TILE_COLUMNS(&ctx, VP9E_SET_TILE_COLUMNS, 2); + vpx_image_t img; + vpx_img_alloc(&img, VPX_IMG_FMT_I420, W, H, 16); + auto drain = [&] { + vpx_codec_iter_t it = nullptr; + while (const vpx_codec_cx_pkt_t* p = vpx_codec_get_cx_data(&ctx, &it)) { + if (p->kind != VPX_CODEC_CX_FRAME_PKT) continue; + const auto* b = static_cast(p->data.frame.buf); + out.push_back({{b, b + p->data.frame.sz}, p->data.frame.pts, (p->data.frame.flags & m::VPX_FRAME_IS_KEY) != 0}); + } + }; + for (int t = 0; t < FRAMES; ++t) { + draw(img, t); + if (vpx_codec_encode(&ctx, &img, t, 1, 0, deadline) != VPX_CODEC_OK) return {}; + drain(); + } + vpx_codec_encode(&ctx, nullptr, FRAMES, 1, 0, deadline); + drain(); + vpx_img_free(&img); + vpx_codec_destroy(&ctx); + return out; +} + +class MemoryWriter final : public mkvmuxer::IMkvWriter { +public: + std::vector bytes; + std::int64_t at = 0; + mkvmuxer::int32 Write(const void* buf, mkvmuxer::uint32 len) override { + if (bytes.size() < static_cast(at) + len) bytes.resize(static_cast(at) + len); + std::memcpy(bytes.data() + at, buf, len); + at += len; + return 0; + } + mkvmuxer::int64 Position() const override { return at; } + mkvmuxer::int32 Position(mkvmuxer::int64 position) override { + at = position; + return 0; + } + bool Seekable() const override { return true; } + void ElementStartNotify(mkvmuxer::uint64, mkvmuxer::int64) override {} +}; + +class MemoryReader final : public mkvparser::IMkvReader { +public: + explicit MemoryReader(const std::vector& b) : bytes(b) {} + int Read(long long pos, long len, unsigned char* buf) override { + if (pos < 0 || len < 0 || static_cast(pos + len) > bytes.size()) return -1; + std::memcpy(buf, bytes.data() + pos, static_cast(len)); + return 0; + } + int Length(long long* total, long long* available) override { + if (total) *total = static_cast(bytes.size()); + if (available) *available = static_cast(bytes.size()); + return 0; + } + const std::vector& bytes; +}; + +// A WebM of the packets (30 fps: frame i at i/30 s), track UIDs given so the bytes are reproducible. +std::vector mux(const std::vector& packets) { + MemoryWriter w; + mkvmuxer::Segment segment; + if (!segment.Init(&w)) return {}; + segment.set_mode(mkvmuxer::Segment::kFile); + segment.OutputCues(true); + const auto video = segment.AddVideoTrack(W, H, 1); + auto* v = static_cast(segment.GetTrackByNumber(video)); + v->set_codec_id(mkvmuxer::Tracks::kVp9CodecId); + v->set_uid(1); + mkvmuxer::Colour colour; + colour.set_matrix_coefficients(mkvmuxer::Colour::kBt709); + colour.set_range(mkvmuxer::Colour::kBroadcastRange); + v->SetColour(colour); + segment.CuesTrack(video); + for (const Packet& p : packets) + if (!segment.AddFrame(p.bytes.data(), p.bytes.size(), video, static_cast(p.pts) * 1'000'000'000 / 30, p.key)) return {}; + segment.set_duration(1000.0 * FRAMES / 30); + if (!segment.Finalize()) return {}; + return w.bytes; +} + +// The frames of a WebM's VP9 track, in order. +std::vector> demux(const std::vector& file, int& track_width, bool& bt709) { + std::vector> frames; + MemoryReader reader(file); + long long pos = 0; + mkvparser::EBMLHeader header; + if (header.Parse(&reader, pos) != 0) return frames; + mkvparser::Segment* raw = nullptr; + if (mkvparser::Segment::CreateInstance(&reader, pos, raw) != 0 || !raw) return frames; + std::unique_ptr segment(raw); + if (segment->Load() < 0) return frames; + const auto* track = static_cast(segment->GetTracks()->GetTrackByNumber(1)); + if (!track || std::string_view(track->GetCodecId()) != "V_VP9") return frames; + track_width = static_cast(track->GetWidth()); + bt709 = track->GetColour() && track->GetColour()->matrix_coefficients == mkvmuxer::Colour::kBt709; + for (const mkvparser::Cluster* c = segment->GetFirst(); c && !c->EOS(); c = segment->GetNext(c)) { + const mkvparser::BlockEntry* e = nullptr; + if (c->GetFirst(e) != 0) break; + while (e && !e->EOS()) { + const mkvparser::Block::Frame& f = e->GetBlock()->GetFrame(0); + std::vector data(static_cast(f.len)); + if (f.Read(&reader, data.data()) != 0) return {}; + frames.push_back(std::move(data)); + if (c->GetNext(e, e) != 0) break; + } + } + return frames; +} + +struct Decoded { + int frames = 0; + double worst = 1e9; // luma PSNR, dB + bool exact = true; +}; + +Decoded decode(const std::vector>& frames, unsigned threads) { + Decoded d; + vpx_codec_dec_cfg_t cfg{}; + cfg.threads = threads; + vpx_codec_ctx_t ctx; + if (m::vpx_codec_dec_init(&ctx, vpx_codec_vp9_dx(), &cfg, 0) != VPX_CODEC_OK) return d; + vpx_image_t ref; + vpx_img_alloc(&ref, VPX_IMG_FMT_I420, W, H, 16); + for (const auto& f : frames) { + if (vpx_codec_decode(&ctx, f.data(), static_cast(f.size()), nullptr, 0) != VPX_CODEC_OK) break; + vpx_codec_iter_t it = nullptr; + while (vpx_image_t* img = vpx_codec_get_frame(&ctx, &it)) { + draw(ref, d.frames); + double se = 0; + for (int plane = 0; plane < 3; ++plane) + for (int y = 0; y < (plane ? H / 2 : H); ++y) + for (int x = 0; x < (plane ? W / 2 : W); ++x) { + const int a = img->planes[plane][y * img->stride[plane] + x], b = ref.planes[plane][y * ref.stride[plane] + x]; + d.exact = d.exact && a == b; + if (plane == 0) se += double(a - b) * (a - b); + } + const double mse = se / (W * H); + d.worst = std::min(d.worst, mse == 0 ? 99.0 : 10 * std::log10(255.0 * 255.0 / mse)); + ++d.frames; + } + } + vpx_img_free(&ref); + vpx_codec_destroy(&ctx); + return d; +} + +std::uint64_t fnv(const std::vector& b) { + std::uint64_t h = 0xcbf29ce484222325ull; + for (unsigned char c : b) h = (h ^ c) * 0x100000001b3ull; + return h; +} + +} // namespace + +int main() { + check(std::string_view(vpx_codec_version_str()) == "v1.17.0" && m::VPX_VERSION_MAJOR(vpx_codec_version()) == 1 && + m::VPX_VERSION_MINOR(vpx_codec_version()) == 17, + "libvpx v1.17.0"); + // libwebm 1.0.0.32's parser and muxer report their own versions. + int major = 0, minor = 0, build = 0, revision = 0; + mkvparser::GetVersion(major, minor, build, revision); + mkvmuxer::int32 mmajor = 0, mminor = 0, mbuild = 0, mrevision = 0; + mkvmuxer::GetVersion(&mmajor, &mminor, &mbuild, &mrevision); + check(major == 1 && minor == 1 && build == 4 && revision == 0 && mmajor == 0 && mminor == 3 && mbuild == 4 && mrevision == 0, + "libwebm 1.0.0.32: mkvparser 1.1.4.0, mkvmuxer 0.3.4.0"); + check((vpx_codec_get_caps(vpx_codec_vp9_cx()) & m::VPX_CODEC_CAP_ENCODER) != 0 && (vpx_codec_get_caps(vpx_codec_vp9_dx()) & m::VPX_CODEC_CAP_DECODER) != 0, + "the VP9 encoder and decoder interfaces"); + + struct Case { + std::string_view name; + vpx_enc_deadline_t deadline; + int speed; + bool lossless; + unsigned threads; + double floor; + std::uint64_t pinned; // the WebM's FNV-1a: the encoder's bytes, the same on every target + }; + for (const Case& k : {Case{"good quality", static_cast(m::VPX_DL_GOOD_QUALITY), 4, false, 1, 38, 0xc19238c601547696}, + Case{"real time, speed 8", static_cast(m::VPX_DL_REALTIME), 8, false, 1, 30, 0xe8396a4100a874f6}, + Case{"four threads, tile columns", static_cast(m::VPX_DL_GOOD_QUALITY), 5, false, 4, 36, 0x2c42d979708eaf23}, + Case{"lossless", static_cast(m::VPX_DL_GOOD_QUALITY), 4, true, 1, 99, 0xeed41b6461e2327a}}) { + const auto packets = encode(k.deadline, k.speed, k.lossless, k.threads); + check(packets.size() == FRAMES && packets.front().key, std::format("{}: a packet a frame, the first a key frame", k.name)); + const auto file = mux(packets); + int width = 0; + bool bt709 = false; + const auto frames = demux(file, width, bt709); + check(frames.size() == FRAMES && width == W && bt709, std::format("{}: muxed into WebM and parsed back ({} bytes)", k.name, file.size())); + bool same = frames.size() == packets.size(); + for (std::size_t i = 0; same && i < frames.size(); ++i) same = frames[i] == packets[i].bytes; + check(same, std::format("{}: every frame's bytes through the container", k.name)); + const Decoded d = decode(frames, k.threads > 1 ? 2 : 1); + std::println(" {}: worst PSNR {:.2f} dB, stream {:016x}", k.name, d.worst, fnv(file)); + check(d.frames == FRAMES && d.worst >= k.floor && (!k.lossless || d.exact), std::format("{}: decoded back, at least {} dB", k.name, k.floor)); + if (k.threads == 1 && !k.lossless) check(file == mux(encode(k.deadline, k.speed, k.lossless, k.threads)), std::format("{}: the same bytes twice", k.name)); + check(fnv(file) == k.pinned, std::format("{}: the bytes pinned ({:016x})", k.name, k.pinned)); + } + std::println("{}", failures ? "FAILED" : "all passed"); + return failures; +} diff --git a/tests/link_test.cpp b/tests/link_test.cpp new file mode 100644 index 0000000..cd8f48c --- /dev/null +++ b/tests/link_test.cpp @@ -0,0 +1,141 @@ +// Generated by tools/gen_exports.py from libvpx 1.17.0's and libwebm 1.0.0.32's +// headers — do not edit by hand. +// +// The address of every exported function and adapted control, and every codec +// interface, through the module only (no header): each is defined by +// compat.libvpx or by the module, so a program that calls any of them links. +import std; +import mcpplibs.mwrap.libvpx; + +using fn = void (*)(); + +int main() { + const fn functions[] = { + reinterpret_cast(&::vpx_codec_build_config), + reinterpret_cast(&::vpx_codec_control_), + reinterpret_cast(&::vpx_codec_dec_init_ver), + reinterpret_cast(&::vpx_codec_decode), + reinterpret_cast(&::vpx_codec_destroy), + reinterpret_cast(&::vpx_codec_enc_config_default), + reinterpret_cast(&::vpx_codec_enc_config_set), + reinterpret_cast(&::vpx_codec_enc_init_multi_ver), + reinterpret_cast(&::vpx_codec_enc_init_ver), + reinterpret_cast(&::vpx_codec_encode), + reinterpret_cast(&::vpx_codec_err_to_string), + reinterpret_cast(&::vpx_codec_error), + reinterpret_cast(&::vpx_codec_error_detail), + reinterpret_cast(&::vpx_codec_get_caps), + reinterpret_cast(&::vpx_codec_get_cx_data), + reinterpret_cast(&::vpx_codec_get_frame), + reinterpret_cast(&::vpx_codec_get_global_headers), + reinterpret_cast(&::vpx_codec_get_preview_frame), + reinterpret_cast(&::vpx_codec_get_stream_info), + reinterpret_cast(&::vpx_codec_iface_name), + reinterpret_cast(&::vpx_codec_peek_stream_info), + reinterpret_cast(&::vpx_codec_register_put_frame_cb), + reinterpret_cast(&::vpx_codec_register_put_slice_cb), + reinterpret_cast(&::vpx_codec_set_cx_data_buf), + reinterpret_cast(&::vpx_codec_set_frame_buffer_functions), + reinterpret_cast(&::vpx_codec_version), + reinterpret_cast(&::vpx_codec_version_extra_str), + reinterpret_cast(&::vpx_codec_version_str), + reinterpret_cast(&::vpx_codec_vp9_cx), + reinterpret_cast(&::vpx_codec_vp9_dx), + reinterpret_cast(&::vpx_img_alloc), + reinterpret_cast(&::vpx_img_flip), + reinterpret_cast(&::vpx_img_free), + reinterpret_cast(&::vpx_img_set_rect), + reinterpret_cast(&::vpx_img_wrap), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8_SET_REFERENCE), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8_COPY_REFERENCE), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8_SET_POSTPROC), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9_GET_REFERENCE), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_ROI_MAP), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_ACTIVEMAP), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_SCALEMODE), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_CPUUSED), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_ENABLEAUTOALTREF), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_NOISE_SENSITIVITY), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_SHARPNESS), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_STATIC_THRESHOLD), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_TOKEN_PARTITIONS), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_GET_LAST_QUANTIZER), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_GET_LAST_QUANTIZER_64), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_ARNR_MAXFRAMES), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_ARNR_STRENGTH), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_TUNING), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_CQ_LEVEL), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_MAX_INTRA_BITRATE_PCT), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_FRAME_FLAGS), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_MAX_INTER_BITRATE_PCT), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_GF_CBR_BOOST_PCT), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_TEMPORAL_LAYER_ID), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_SCREEN_CONTENT_MODE), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_LOSSLESS), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_TILE_COLUMNS), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_TILE_ROWS), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_FRAME_PARALLEL_DECODING), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_AQ_MODE), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_FRAME_PERIODIC_BOOST), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_NOISE_SENSITIVITY), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_SVC), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_ROI_MAP), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_SVC_PARAMETERS), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_SVC_LAYER_ID), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_TUNE_CONTENT), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_GET_SVC_LAYER_ID), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_REGISTER_CX_CALLBACK), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_COLOR_SPACE), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_MIN_GF_INTERVAL), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_MAX_GF_INTERVAL), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_GET_ACTIVEMAP), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_COLOR_RANGE), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_SVC_REF_FRAME_CONFIG), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_RENDER_SIZE), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_TARGET_LEVEL), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_ROW_MT), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_GET_LEVEL), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_ALT_REF_AQ), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_GF_CBR_BOOST_PCT), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_ENABLE_MOTION_VECTOR_UNIT_TEST), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_SVC_INTER_LAYER_PRED), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_SVC_FRAME_DROP_LAYER), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_GET_SVC_REF_FRAME_CONFIG), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_SVC_GF_TEMPORAL_REF), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_SVC_SPATIAL_LAYER_SYNC), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_TPL), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_POSTENCODE_DROP), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_DELTA_Q_UV), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_DISABLE_OVERSHOOT_MAXQ_CBR), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_DISABLE_LOOPFILTER), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_EXTERNAL_RATE_CONTROL), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_RTC_EXTERNAL_RATECTRL), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_GET_LOOPFILTER_LEVEL), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_GET_LAST_QUANTIZER_SVC_LAYERS), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_RTC_EXTERNAL_RATECTRL), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_QUANTIZER_ONE_PASS), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_KEY_FRAME_FILTERING), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9E_SET_VALIDATE_HBD_INPUT), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8D_GET_LAST_REF_UPDATES), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8D_GET_FRAME_CORRUPTED), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8D_GET_LAST_REF_USED), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VPXD_SET_DECRYPTOR), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP8D_SET_DECRYPTOR), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9D_GET_FRAME_SIZE), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9D_GET_DISPLAY_SIZE), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9D_GET_BIT_DEPTH), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9_SET_BYTE_ALIGNMENT), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9_INVERT_TILE_DECODE_ORDER), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9_SET_SKIP_LOOP_FILTER), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9_DECODE_SVC_SPATIAL_LAYER), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VPXD_GET_LAST_QUANTIZER), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9D_SET_ROW_MT), + reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_VP9D_SET_LOOP_FILTER_OPT), + }; + const void* interfaces[] = {&::vpx_codec_vp9_cx_algo, &::vpx_codec_vp9_dx_algo}; + int missing = 0; + for (fn f : functions) missing += f == nullptr; + for (const void* d : interfaces) missing += d == nullptr; + std::println("{} functions and controls, {} codec interfaces: {}", std::size(functions), std::size(interfaces), missing ? "MISSING" : "all linked"); + return missing; +} diff --git a/tests/macros_mix_test.cpp b/tests/macros_mix_test.cpp new file mode 100644 index 0000000..0808fe8 --- /dev/null +++ b/tests/macros_mix_test.cpp @@ -0,0 +1,379 @@ +// Generated by tools/gen_exports.py from libvpx 1.17.0's and libwebm 1.0.0.32's +// headers — do not edit by hand. +// +// Every mapping against the raw headers, in one translation unit that includes +// beside the import: each constant has its macro's +// type and value (the macro is set aside with push_macro around the +// comparison: while it is defined it expands even inside `m::NAME`); each +// function-like mapping answers what its macro does (a function-like macro is +// not expanded when its name is followed by `)`, so `(m::NAME)(args)` calls +// the function beside the macro `NAME(args)`); the header's +// vpx_codec_control macro and the adapted typed control set the same thing. +#include + +import std; +import mcpplibs.mwrap.libvpx; + +namespace m = mcpplibs::mwrap::libvpx::macros; + +constexpr auto raw_VPX_CODEC_ABI_VERSION = VPX_CODEC_ABI_VERSION; +#pragma push_macro("VPX_CODEC_ABI_VERSION") +#undef VPX_CODEC_ABI_VERSION +static_assert(std::is_same_v && raw_VPX_CODEC_ABI_VERSION == m::VPX_CODEC_ABI_VERSION); +#pragma pop_macro("VPX_CODEC_ABI_VERSION") +constexpr auto raw_VPX_CODEC_CAP_DECODER = VPX_CODEC_CAP_DECODER; +#pragma push_macro("VPX_CODEC_CAP_DECODER") +#undef VPX_CODEC_CAP_DECODER +static_assert(std::is_same_v && raw_VPX_CODEC_CAP_DECODER == m::VPX_CODEC_CAP_DECODER); +#pragma pop_macro("VPX_CODEC_CAP_DECODER") +constexpr auto raw_VPX_CODEC_CAP_ENCODER = VPX_CODEC_CAP_ENCODER; +#pragma push_macro("VPX_CODEC_CAP_ENCODER") +#undef VPX_CODEC_CAP_ENCODER +static_assert(std::is_same_v && raw_VPX_CODEC_CAP_ENCODER == m::VPX_CODEC_CAP_ENCODER); +#pragma pop_macro("VPX_CODEC_CAP_ENCODER") +constexpr auto raw_VPX_CODEC_CAP_HIGHBITDEPTH = VPX_CODEC_CAP_HIGHBITDEPTH; +#pragma push_macro("VPX_CODEC_CAP_HIGHBITDEPTH") +#undef VPX_CODEC_CAP_HIGHBITDEPTH +static_assert(std::is_same_v && raw_VPX_CODEC_CAP_HIGHBITDEPTH == m::VPX_CODEC_CAP_HIGHBITDEPTH); +#pragma pop_macro("VPX_CODEC_CAP_HIGHBITDEPTH") +constexpr auto raw_VPX_IMAGE_ABI_VERSION = VPX_IMAGE_ABI_VERSION; +#pragma push_macro("VPX_IMAGE_ABI_VERSION") +#undef VPX_IMAGE_ABI_VERSION +static_assert(std::is_same_v && raw_VPX_IMAGE_ABI_VERSION == m::VPX_IMAGE_ABI_VERSION); +#pragma pop_macro("VPX_IMAGE_ABI_VERSION") +constexpr auto raw_VPX_IMG_FMT_PLANAR = VPX_IMG_FMT_PLANAR; +#pragma push_macro("VPX_IMG_FMT_PLANAR") +#undef VPX_IMG_FMT_PLANAR +static_assert(std::is_same_v && raw_VPX_IMG_FMT_PLANAR == m::VPX_IMG_FMT_PLANAR); +#pragma pop_macro("VPX_IMG_FMT_PLANAR") +constexpr auto raw_VPX_IMG_FMT_UV_FLIP = VPX_IMG_FMT_UV_FLIP; +#pragma push_macro("VPX_IMG_FMT_UV_FLIP") +#undef VPX_IMG_FMT_UV_FLIP +static_assert(std::is_same_v && raw_VPX_IMG_FMT_UV_FLIP == m::VPX_IMG_FMT_UV_FLIP); +#pragma pop_macro("VPX_IMG_FMT_UV_FLIP") +constexpr auto raw_VPX_IMG_FMT_HAS_ALPHA = VPX_IMG_FMT_HAS_ALPHA; +#pragma push_macro("VPX_IMG_FMT_HAS_ALPHA") +#undef VPX_IMG_FMT_HAS_ALPHA +static_assert(std::is_same_v && raw_VPX_IMG_FMT_HAS_ALPHA == m::VPX_IMG_FMT_HAS_ALPHA); +#pragma pop_macro("VPX_IMG_FMT_HAS_ALPHA") +constexpr auto raw_VPX_IMG_FMT_HIGHBITDEPTH = VPX_IMG_FMT_HIGHBITDEPTH; +#pragma push_macro("VPX_IMG_FMT_HIGHBITDEPTH") +#undef VPX_IMG_FMT_HIGHBITDEPTH +static_assert(std::is_same_v && raw_VPX_IMG_FMT_HIGHBITDEPTH == m::VPX_IMG_FMT_HIGHBITDEPTH); +#pragma pop_macro("VPX_IMG_FMT_HIGHBITDEPTH") +constexpr auto raw_VPX_PLANE_PACKED = VPX_PLANE_PACKED; +#pragma push_macro("VPX_PLANE_PACKED") +#undef VPX_PLANE_PACKED +static_assert(std::is_same_v && raw_VPX_PLANE_PACKED == m::VPX_PLANE_PACKED); +#pragma pop_macro("VPX_PLANE_PACKED") +constexpr auto raw_VPX_PLANE_Y = VPX_PLANE_Y; +#pragma push_macro("VPX_PLANE_Y") +#undef VPX_PLANE_Y +static_assert(std::is_same_v && raw_VPX_PLANE_Y == m::VPX_PLANE_Y); +#pragma pop_macro("VPX_PLANE_Y") +constexpr auto raw_VPX_PLANE_U = VPX_PLANE_U; +#pragma push_macro("VPX_PLANE_U") +#undef VPX_PLANE_U +static_assert(std::is_same_v && raw_VPX_PLANE_U == m::VPX_PLANE_U); +#pragma pop_macro("VPX_PLANE_U") +constexpr auto raw_VPX_PLANE_V = VPX_PLANE_V; +#pragma push_macro("VPX_PLANE_V") +#undef VPX_PLANE_V +static_assert(std::is_same_v && raw_VPX_PLANE_V == m::VPX_PLANE_V); +#pragma pop_macro("VPX_PLANE_V") +constexpr auto raw_VPX_PLANE_ALPHA = VPX_PLANE_ALPHA; +#pragma push_macro("VPX_PLANE_ALPHA") +#undef VPX_PLANE_ALPHA +static_assert(std::is_same_v && raw_VPX_PLANE_ALPHA == m::VPX_PLANE_ALPHA); +#pragma pop_macro("VPX_PLANE_ALPHA") +constexpr auto raw_VPX_MAXIMUM_WORK_BUFFERS = VPX_MAXIMUM_WORK_BUFFERS; +#pragma push_macro("VPX_MAXIMUM_WORK_BUFFERS") +#undef VPX_MAXIMUM_WORK_BUFFERS +static_assert(std::is_same_v && raw_VPX_MAXIMUM_WORK_BUFFERS == m::VPX_MAXIMUM_WORK_BUFFERS); +#pragma pop_macro("VPX_MAXIMUM_WORK_BUFFERS") +constexpr auto raw_VP9_MAXIMUM_REF_BUFFERS = VP9_MAXIMUM_REF_BUFFERS; +#pragma push_macro("VP9_MAXIMUM_REF_BUFFERS") +#undef VP9_MAXIMUM_REF_BUFFERS +static_assert(std::is_same_v && raw_VP9_MAXIMUM_REF_BUFFERS == m::VP9_MAXIMUM_REF_BUFFERS); +#pragma pop_macro("VP9_MAXIMUM_REF_BUFFERS") +constexpr auto raw_VPX_EXT_RATECTRL_ABI_VERSION = VPX_EXT_RATECTRL_ABI_VERSION; +#pragma push_macro("VPX_EXT_RATECTRL_ABI_VERSION") +#undef VPX_EXT_RATECTRL_ABI_VERSION +static_assert(std::is_same_v && raw_VPX_EXT_RATECTRL_ABI_VERSION == m::VPX_EXT_RATECTRL_ABI_VERSION); +#pragma pop_macro("VPX_EXT_RATECTRL_ABI_VERSION") +constexpr auto raw_VPX_RC_MAX_STATIC_GF_GROUP_LENGTH = VPX_RC_MAX_STATIC_GF_GROUP_LENGTH; +#pragma push_macro("VPX_RC_MAX_STATIC_GF_GROUP_LENGTH") +#undef VPX_RC_MAX_STATIC_GF_GROUP_LENGTH +static_assert(std::is_same_v && raw_VPX_RC_MAX_STATIC_GF_GROUP_LENGTH == m::VPX_RC_MAX_STATIC_GF_GROUP_LENGTH); +#pragma pop_macro("VPX_RC_MAX_STATIC_GF_GROUP_LENGTH") +constexpr auto raw_VPX_RC_MAX_REF_FRAMES = VPX_RC_MAX_REF_FRAMES; +#pragma push_macro("VPX_RC_MAX_REF_FRAMES") +#undef VPX_RC_MAX_REF_FRAMES +static_assert(std::is_same_v && raw_VPX_RC_MAX_REF_FRAMES == m::VPX_RC_MAX_REF_FRAMES); +#pragma pop_macro("VPX_RC_MAX_REF_FRAMES") +constexpr auto raw_VPX_DEFAULT_Q = VPX_DEFAULT_Q; +#pragma push_macro("VPX_DEFAULT_Q") +#undef VPX_DEFAULT_Q +static_assert(std::is_same_v && raw_VPX_DEFAULT_Q == m::VPX_DEFAULT_Q); +#pragma pop_macro("VPX_DEFAULT_Q") +constexpr auto raw_VPX_DEFAULT_RDMULT = VPX_DEFAULT_RDMULT; +#pragma push_macro("VPX_DEFAULT_RDMULT") +#undef VPX_DEFAULT_RDMULT +static_assert(std::is_same_v && raw_VPX_DEFAULT_RDMULT == m::VPX_DEFAULT_RDMULT); +#pragma pop_macro("VPX_DEFAULT_RDMULT") +constexpr auto raw_VPX_TPL_ABI_VERSION = VPX_TPL_ABI_VERSION; +#pragma push_macro("VPX_TPL_ABI_VERSION") +#undef VPX_TPL_ABI_VERSION +static_assert(std::is_same_v && raw_VPX_TPL_ABI_VERSION == m::VPX_TPL_ABI_VERSION); +#pragma pop_macro("VPX_TPL_ABI_VERSION") +constexpr auto raw_VPX_TS_MAX_PERIODICITY = VPX_TS_MAX_PERIODICITY; +#pragma push_macro("VPX_TS_MAX_PERIODICITY") +#undef VPX_TS_MAX_PERIODICITY +static_assert(std::is_same_v && raw_VPX_TS_MAX_PERIODICITY == m::VPX_TS_MAX_PERIODICITY); +#pragma pop_macro("VPX_TS_MAX_PERIODICITY") +constexpr auto raw_VPX_TS_MAX_LAYERS = VPX_TS_MAX_LAYERS; +#pragma push_macro("VPX_TS_MAX_LAYERS") +#undef VPX_TS_MAX_LAYERS +static_assert(std::is_same_v && raw_VPX_TS_MAX_LAYERS == m::VPX_TS_MAX_LAYERS); +#pragma pop_macro("VPX_TS_MAX_LAYERS") +constexpr auto raw_VPX_MAX_LAYERS = VPX_MAX_LAYERS; +#pragma push_macro("VPX_MAX_LAYERS") +#undef VPX_MAX_LAYERS +static_assert(std::is_same_v && raw_VPX_MAX_LAYERS == m::VPX_MAX_LAYERS); +#pragma pop_macro("VPX_MAX_LAYERS") +constexpr auto raw_VPX_SS_MAX_LAYERS = VPX_SS_MAX_LAYERS; +#pragma push_macro("VPX_SS_MAX_LAYERS") +#undef VPX_SS_MAX_LAYERS +static_assert(std::is_same_v && raw_VPX_SS_MAX_LAYERS == m::VPX_SS_MAX_LAYERS); +#pragma pop_macro("VPX_SS_MAX_LAYERS") +constexpr auto raw_VPX_SS_DEFAULT_LAYERS = VPX_SS_DEFAULT_LAYERS; +#pragma push_macro("VPX_SS_DEFAULT_LAYERS") +#undef VPX_SS_DEFAULT_LAYERS +static_assert(std::is_same_v && raw_VPX_SS_DEFAULT_LAYERS == m::VPX_SS_DEFAULT_LAYERS); +#pragma pop_macro("VPX_SS_DEFAULT_LAYERS") +constexpr auto raw_VPX_ENCODER_ABI_VERSION = VPX_ENCODER_ABI_VERSION; +#pragma push_macro("VPX_ENCODER_ABI_VERSION") +#undef VPX_ENCODER_ABI_VERSION +static_assert(std::is_same_v && raw_VPX_ENCODER_ABI_VERSION == m::VPX_ENCODER_ABI_VERSION); +#pragma pop_macro("VPX_ENCODER_ABI_VERSION") +constexpr auto raw_VPX_CODEC_CAP_PSNR = VPX_CODEC_CAP_PSNR; +#pragma push_macro("VPX_CODEC_CAP_PSNR") +#undef VPX_CODEC_CAP_PSNR +static_assert(std::is_same_v && raw_VPX_CODEC_CAP_PSNR == m::VPX_CODEC_CAP_PSNR); +#pragma pop_macro("VPX_CODEC_CAP_PSNR") +constexpr auto raw_VPX_CODEC_CAP_OUTPUT_PARTITION = VPX_CODEC_CAP_OUTPUT_PARTITION; +#pragma push_macro("VPX_CODEC_CAP_OUTPUT_PARTITION") +#undef VPX_CODEC_CAP_OUTPUT_PARTITION +static_assert(std::is_same_v && raw_VPX_CODEC_CAP_OUTPUT_PARTITION == m::VPX_CODEC_CAP_OUTPUT_PARTITION); +#pragma pop_macro("VPX_CODEC_CAP_OUTPUT_PARTITION") +constexpr auto raw_VPX_CODEC_USE_PSNR = VPX_CODEC_USE_PSNR; +#pragma push_macro("VPX_CODEC_USE_PSNR") +#undef VPX_CODEC_USE_PSNR +static_assert(std::is_same_v && raw_VPX_CODEC_USE_PSNR == m::VPX_CODEC_USE_PSNR); +#pragma pop_macro("VPX_CODEC_USE_PSNR") +constexpr auto raw_VPX_CODEC_USE_OUTPUT_PARTITION = VPX_CODEC_USE_OUTPUT_PARTITION; +#pragma push_macro("VPX_CODEC_USE_OUTPUT_PARTITION") +#undef VPX_CODEC_USE_OUTPUT_PARTITION +static_assert(std::is_same_v && raw_VPX_CODEC_USE_OUTPUT_PARTITION == m::VPX_CODEC_USE_OUTPUT_PARTITION); +#pragma pop_macro("VPX_CODEC_USE_OUTPUT_PARTITION") +constexpr auto raw_VPX_CODEC_USE_HIGHBITDEPTH = VPX_CODEC_USE_HIGHBITDEPTH; +#pragma push_macro("VPX_CODEC_USE_HIGHBITDEPTH") +#undef VPX_CODEC_USE_HIGHBITDEPTH +static_assert(std::is_same_v && raw_VPX_CODEC_USE_HIGHBITDEPTH == m::VPX_CODEC_USE_HIGHBITDEPTH); +#pragma pop_macro("VPX_CODEC_USE_HIGHBITDEPTH") +constexpr auto raw_VPX_FRAME_IS_KEY = VPX_FRAME_IS_KEY; +#pragma push_macro("VPX_FRAME_IS_KEY") +#undef VPX_FRAME_IS_KEY +static_assert(std::is_same_v && raw_VPX_FRAME_IS_KEY == m::VPX_FRAME_IS_KEY); +#pragma pop_macro("VPX_FRAME_IS_KEY") +constexpr auto raw_VPX_FRAME_IS_DROPPABLE = VPX_FRAME_IS_DROPPABLE; +#pragma push_macro("VPX_FRAME_IS_DROPPABLE") +#undef VPX_FRAME_IS_DROPPABLE +static_assert(std::is_same_v && raw_VPX_FRAME_IS_DROPPABLE == m::VPX_FRAME_IS_DROPPABLE); +#pragma pop_macro("VPX_FRAME_IS_DROPPABLE") +constexpr auto raw_VPX_FRAME_IS_INVISIBLE = VPX_FRAME_IS_INVISIBLE; +#pragma push_macro("VPX_FRAME_IS_INVISIBLE") +#undef VPX_FRAME_IS_INVISIBLE +static_assert(std::is_same_v && raw_VPX_FRAME_IS_INVISIBLE == m::VPX_FRAME_IS_INVISIBLE); +#pragma pop_macro("VPX_FRAME_IS_INVISIBLE") +constexpr auto raw_VPX_FRAME_IS_FRAGMENT = VPX_FRAME_IS_FRAGMENT; +#pragma push_macro("VPX_FRAME_IS_FRAGMENT") +#undef VPX_FRAME_IS_FRAGMENT +static_assert(std::is_same_v && raw_VPX_FRAME_IS_FRAGMENT == m::VPX_FRAME_IS_FRAGMENT); +#pragma pop_macro("VPX_FRAME_IS_FRAGMENT") +constexpr auto raw_VPX_ERROR_RESILIENT_DEFAULT = VPX_ERROR_RESILIENT_DEFAULT; +#pragma push_macro("VPX_ERROR_RESILIENT_DEFAULT") +#undef VPX_ERROR_RESILIENT_DEFAULT +static_assert(std::is_same_v && raw_VPX_ERROR_RESILIENT_DEFAULT == m::VPX_ERROR_RESILIENT_DEFAULT); +#pragma pop_macro("VPX_ERROR_RESILIENT_DEFAULT") +constexpr auto raw_VPX_ERROR_RESILIENT_PARTITIONS = VPX_ERROR_RESILIENT_PARTITIONS; +#pragma push_macro("VPX_ERROR_RESILIENT_PARTITIONS") +#undef VPX_ERROR_RESILIENT_PARTITIONS +static_assert(std::is_same_v && raw_VPX_ERROR_RESILIENT_PARTITIONS == m::VPX_ERROR_RESILIENT_PARTITIONS); +#pragma pop_macro("VPX_ERROR_RESILIENT_PARTITIONS") +constexpr auto raw_VPX_EFLAG_FORCE_KF = VPX_EFLAG_FORCE_KF; +#pragma push_macro("VPX_EFLAG_FORCE_KF") +#undef VPX_EFLAG_FORCE_KF +static_assert(std::is_same_v && raw_VPX_EFLAG_FORCE_KF == m::VPX_EFLAG_FORCE_KF); +#pragma pop_macro("VPX_EFLAG_FORCE_KF") +constexpr auto raw_VPX_EFLAG_CALCULATE_PSNR = VPX_EFLAG_CALCULATE_PSNR; +#pragma push_macro("VPX_EFLAG_CALCULATE_PSNR") +#undef VPX_EFLAG_CALCULATE_PSNR +static_assert(std::is_same_v && raw_VPX_EFLAG_CALCULATE_PSNR == m::VPX_EFLAG_CALCULATE_PSNR); +#pragma pop_macro("VPX_EFLAG_CALCULATE_PSNR") +constexpr auto raw_VPX_DL_REALTIME = VPX_DL_REALTIME; +#pragma push_macro("VPX_DL_REALTIME") +#undef VPX_DL_REALTIME +static_assert(std::is_same_v && raw_VPX_DL_REALTIME == m::VPX_DL_REALTIME); +#pragma pop_macro("VPX_DL_REALTIME") +constexpr auto raw_VPX_DL_GOOD_QUALITY = VPX_DL_GOOD_QUALITY; +#pragma push_macro("VPX_DL_GOOD_QUALITY") +#undef VPX_DL_GOOD_QUALITY +static_assert(std::is_same_v && raw_VPX_DL_GOOD_QUALITY == m::VPX_DL_GOOD_QUALITY); +#pragma pop_macro("VPX_DL_GOOD_QUALITY") +constexpr auto raw_VPX_DL_BEST_QUALITY = VPX_DL_BEST_QUALITY; +#pragma push_macro("VPX_DL_BEST_QUALITY") +#undef VPX_DL_BEST_QUALITY +static_assert(std::is_same_v && raw_VPX_DL_BEST_QUALITY == m::VPX_DL_BEST_QUALITY); +#pragma pop_macro("VPX_DL_BEST_QUALITY") +constexpr auto raw_VPX_DECODER_ABI_VERSION = VPX_DECODER_ABI_VERSION; +#pragma push_macro("VPX_DECODER_ABI_VERSION") +#undef VPX_DECODER_ABI_VERSION +static_assert(std::is_same_v && raw_VPX_DECODER_ABI_VERSION == m::VPX_DECODER_ABI_VERSION); +#pragma pop_macro("VPX_DECODER_ABI_VERSION") +constexpr auto raw_VPX_CODEC_CAP_PUT_SLICE = VPX_CODEC_CAP_PUT_SLICE; +#pragma push_macro("VPX_CODEC_CAP_PUT_SLICE") +#undef VPX_CODEC_CAP_PUT_SLICE +static_assert(std::is_same_v && raw_VPX_CODEC_CAP_PUT_SLICE == m::VPX_CODEC_CAP_PUT_SLICE); +#pragma pop_macro("VPX_CODEC_CAP_PUT_SLICE") +constexpr auto raw_VPX_CODEC_CAP_PUT_FRAME = VPX_CODEC_CAP_PUT_FRAME; +#pragma push_macro("VPX_CODEC_CAP_PUT_FRAME") +#undef VPX_CODEC_CAP_PUT_FRAME +static_assert(std::is_same_v && raw_VPX_CODEC_CAP_PUT_FRAME == m::VPX_CODEC_CAP_PUT_FRAME); +#pragma pop_macro("VPX_CODEC_CAP_PUT_FRAME") +constexpr auto raw_VPX_CODEC_CAP_POSTPROC = VPX_CODEC_CAP_POSTPROC; +#pragma push_macro("VPX_CODEC_CAP_POSTPROC") +#undef VPX_CODEC_CAP_POSTPROC +static_assert(std::is_same_v && raw_VPX_CODEC_CAP_POSTPROC == m::VPX_CODEC_CAP_POSTPROC); +#pragma pop_macro("VPX_CODEC_CAP_POSTPROC") +constexpr auto raw_VPX_CODEC_CAP_ERROR_CONCEALMENT = VPX_CODEC_CAP_ERROR_CONCEALMENT; +#pragma push_macro("VPX_CODEC_CAP_ERROR_CONCEALMENT") +#undef VPX_CODEC_CAP_ERROR_CONCEALMENT +static_assert(std::is_same_v && raw_VPX_CODEC_CAP_ERROR_CONCEALMENT == m::VPX_CODEC_CAP_ERROR_CONCEALMENT); +#pragma pop_macro("VPX_CODEC_CAP_ERROR_CONCEALMENT") +constexpr auto raw_VPX_CODEC_CAP_INPUT_FRAGMENTS = VPX_CODEC_CAP_INPUT_FRAGMENTS; +#pragma push_macro("VPX_CODEC_CAP_INPUT_FRAGMENTS") +#undef VPX_CODEC_CAP_INPUT_FRAGMENTS +static_assert(std::is_same_v && raw_VPX_CODEC_CAP_INPUT_FRAGMENTS == m::VPX_CODEC_CAP_INPUT_FRAGMENTS); +#pragma pop_macro("VPX_CODEC_CAP_INPUT_FRAGMENTS") +constexpr auto raw_VPX_CODEC_CAP_FRAME_THREADING = VPX_CODEC_CAP_FRAME_THREADING; +#pragma push_macro("VPX_CODEC_CAP_FRAME_THREADING") +#undef VPX_CODEC_CAP_FRAME_THREADING +static_assert(std::is_same_v && raw_VPX_CODEC_CAP_FRAME_THREADING == m::VPX_CODEC_CAP_FRAME_THREADING); +#pragma pop_macro("VPX_CODEC_CAP_FRAME_THREADING") +constexpr auto raw_VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER = VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER; +#pragma push_macro("VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER") +#undef VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER +static_assert(std::is_same_v && raw_VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER == m::VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER); +#pragma pop_macro("VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER") +constexpr auto raw_VPX_CODEC_USE_POSTPROC = VPX_CODEC_USE_POSTPROC; +#pragma push_macro("VPX_CODEC_USE_POSTPROC") +#undef VPX_CODEC_USE_POSTPROC +static_assert(std::is_same_v && raw_VPX_CODEC_USE_POSTPROC == m::VPX_CODEC_USE_POSTPROC); +#pragma pop_macro("VPX_CODEC_USE_POSTPROC") +constexpr auto raw_VPX_CODEC_USE_ERROR_CONCEALMENT = VPX_CODEC_USE_ERROR_CONCEALMENT; +#pragma push_macro("VPX_CODEC_USE_ERROR_CONCEALMENT") +#undef VPX_CODEC_USE_ERROR_CONCEALMENT +static_assert(std::is_same_v && raw_VPX_CODEC_USE_ERROR_CONCEALMENT == m::VPX_CODEC_USE_ERROR_CONCEALMENT); +#pragma pop_macro("VPX_CODEC_USE_ERROR_CONCEALMENT") +constexpr auto raw_VPX_CODEC_USE_INPUT_FRAGMENTS = VPX_CODEC_USE_INPUT_FRAGMENTS; +#pragma push_macro("VPX_CODEC_USE_INPUT_FRAGMENTS") +#undef VPX_CODEC_USE_INPUT_FRAGMENTS +static_assert(std::is_same_v && raw_VPX_CODEC_USE_INPUT_FRAGMENTS == m::VPX_CODEC_USE_INPUT_FRAGMENTS); +#pragma pop_macro("VPX_CODEC_USE_INPUT_FRAGMENTS") +constexpr auto raw_VPX_CODEC_USE_FRAME_THREADING = VPX_CODEC_USE_FRAME_THREADING; +#pragma push_macro("VPX_CODEC_USE_FRAME_THREADING") +#undef VPX_CODEC_USE_FRAME_THREADING +static_assert(std::is_same_v && raw_VPX_CODEC_USE_FRAME_THREADING == m::VPX_CODEC_USE_FRAME_THREADING); +#pragma pop_macro("VPX_CODEC_USE_FRAME_THREADING") +constexpr auto raw_VP8_EFLAG_NO_REF_LAST = VP8_EFLAG_NO_REF_LAST; +#pragma push_macro("VP8_EFLAG_NO_REF_LAST") +#undef VP8_EFLAG_NO_REF_LAST +static_assert(std::is_same_v && raw_VP8_EFLAG_NO_REF_LAST == m::VP8_EFLAG_NO_REF_LAST); +#pragma pop_macro("VP8_EFLAG_NO_REF_LAST") +constexpr auto raw_VP8_EFLAG_NO_REF_GF = VP8_EFLAG_NO_REF_GF; +#pragma push_macro("VP8_EFLAG_NO_REF_GF") +#undef VP8_EFLAG_NO_REF_GF +static_assert(std::is_same_v && raw_VP8_EFLAG_NO_REF_GF == m::VP8_EFLAG_NO_REF_GF); +#pragma pop_macro("VP8_EFLAG_NO_REF_GF") +constexpr auto raw_VP8_EFLAG_NO_REF_ARF = VP8_EFLAG_NO_REF_ARF; +#pragma push_macro("VP8_EFLAG_NO_REF_ARF") +#undef VP8_EFLAG_NO_REF_ARF +static_assert(std::is_same_v && raw_VP8_EFLAG_NO_REF_ARF == m::VP8_EFLAG_NO_REF_ARF); +#pragma pop_macro("VP8_EFLAG_NO_REF_ARF") +constexpr auto raw_VP8_EFLAG_NO_UPD_LAST = VP8_EFLAG_NO_UPD_LAST; +#pragma push_macro("VP8_EFLAG_NO_UPD_LAST") +#undef VP8_EFLAG_NO_UPD_LAST +static_assert(std::is_same_v && raw_VP8_EFLAG_NO_UPD_LAST == m::VP8_EFLAG_NO_UPD_LAST); +#pragma pop_macro("VP8_EFLAG_NO_UPD_LAST") +constexpr auto raw_VP8_EFLAG_NO_UPD_GF = VP8_EFLAG_NO_UPD_GF; +#pragma push_macro("VP8_EFLAG_NO_UPD_GF") +#undef VP8_EFLAG_NO_UPD_GF +static_assert(std::is_same_v && raw_VP8_EFLAG_NO_UPD_GF == m::VP8_EFLAG_NO_UPD_GF); +#pragma pop_macro("VP8_EFLAG_NO_UPD_GF") +constexpr auto raw_VP8_EFLAG_NO_UPD_ARF = VP8_EFLAG_NO_UPD_ARF; +#pragma push_macro("VP8_EFLAG_NO_UPD_ARF") +#undef VP8_EFLAG_NO_UPD_ARF +static_assert(std::is_same_v && raw_VP8_EFLAG_NO_UPD_ARF == m::VP8_EFLAG_NO_UPD_ARF); +#pragma pop_macro("VP8_EFLAG_NO_UPD_ARF") +constexpr auto raw_VP8_EFLAG_FORCE_GF = VP8_EFLAG_FORCE_GF; +#pragma push_macro("VP8_EFLAG_FORCE_GF") +#undef VP8_EFLAG_FORCE_GF +static_assert(std::is_same_v && raw_VP8_EFLAG_FORCE_GF == m::VP8_EFLAG_FORCE_GF); +#pragma pop_macro("VP8_EFLAG_FORCE_GF") +constexpr auto raw_VP8_EFLAG_FORCE_ARF = VP8_EFLAG_FORCE_ARF; +#pragma push_macro("VP8_EFLAG_FORCE_ARF") +#undef VP8_EFLAG_FORCE_ARF +static_assert(std::is_same_v && raw_VP8_EFLAG_FORCE_ARF == m::VP8_EFLAG_FORCE_ARF); +#pragma pop_macro("VP8_EFLAG_FORCE_ARF") +constexpr auto raw_VP8_EFLAG_NO_UPD_ENTROPY = VP8_EFLAG_NO_UPD_ENTROPY; +#pragma push_macro("VP8_EFLAG_NO_UPD_ENTROPY") +#undef VP8_EFLAG_NO_UPD_ENTROPY +static_assert(std::is_same_v && raw_VP8_EFLAG_NO_UPD_ENTROPY == m::VP8_EFLAG_NO_UPD_ENTROPY); +#pragma pop_macro("VP8_EFLAG_NO_UPD_ENTROPY") + +static_assert((m::VPX_VERSION_MAJOR)(0x011100) == VPX_VERSION_MAJOR(0x011100) && (m::VPX_VERSION_MINOR)(0x011100) == VPX_VERSION_MINOR(0x011100) && + (m::VPX_VERSION_PATCH)(0x011102) == VPX_VERSION_PATCH(0x011102)); + +int main() { + int failures = 0; + auto check = [&](bool ok, const char* what) { + std::println("{:<64} {}", what, ok ? "ok" : "FAILED"); + failures += !ok; + }; + check((m::vpx_codec_version_major)() == vpx_codec_version_major() && (m::vpx_codec_version_minor)() == vpx_codec_version_minor() && + (m::vpx_codec_version_patch)() == vpx_codec_version_patch(), + "vpx_codec_version_major/_minor/_patch"); + vpx_codec_enc_cfg_t cfg; + check(vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &cfg, 0) == VPX_CODEC_OK, "a default configuration"); + cfg.g_w = 64; + cfg.g_h = 64; + vpx_codec_ctx_t a, b; + check((m::vpx_codec_enc_init)(&a, vpx_codec_vp9_cx(), &cfg, 0) == VPX_CODEC_OK && vpx_codec_enc_init(&b, vpx_codec_vp9_cx(), &cfg, 0) == VPX_CODEC_OK, + "the mapped vpx_codec_enc_init beside the macro"); + // The header's typed control through its macro, and the module's adapted one. + check(vpx_codec_control(&a, VP8E_SET_CPUUSED, 7) == VPX_CODEC_OK && + mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_CPUUSED(&b, VP8E_SET_CPUUSED, 7) == VPX_CODEC_OK, + "vpx_codec_control (the macro) and the adapted control"); +#ifndef VPX_CTRL_VP9E_SET_ROW_MT + check(false, "the VPX_CTRL_ feature tests are defined"); +#endif + vpx_codec_destroy(&a); + vpx_codec_destroy(&b); + vpx_codec_dec_cfg_t dcfg{}; + vpx_codec_ctx_t d; + check((m::vpx_codec_dec_init)(&d, vpx_codec_vp9_dx(), &dcfg, 0) == VPX_CODEC_OK, "the mapped vpx_codec_dec_init"); + vpx_codec_destroy(&d); + std::println("{}", failures ? "FAILED" : "all passed"); + return failures; +} diff --git a/tools/candidate_index.py b/tools/candidate_index.py new file mode 100644 index 0000000..4fb1f5b --- /dev/null +++ b/tools/candidate_index.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +"""The compat namespace from mcpp-index's candidate branch, while the published +index does not yet carry compat.libvpx and compat.libwebm. + +The two are added to mcpp-index by mcpplibs/mcpp-index#624. Until those +descriptors are on the index's main branch, a build of this repository cannot +resolve its dependencies from the published index. This tool then clones +the PR's branch, prepares the unchanged namespace views of its descriptors +(the index's own tests/prepare_mwrap_indices.py) and prints the checkout. + + --checkout-only print the candidate checkout (nothing once published), + for tools/test_template.py --index + --ci also append `[indices] compat = { path = }` to this + checkout's manifests (the root and examples/vpxwebm), in + CI's working copy only + +Once the published index has them, it does nothing. +""" +import argparse +from pathlib import Path +import subprocess +import sys +import urllib.error +import urllib.request + +INDEX = 'https://github.com/mcpplibs/mcpp-index' +BRANCH = 'compat-libvpx' +PUBLISHED = 'https://raw.githubusercontent.com/mcpplibs/mcpp-index/main/pkgs/c/compat.libwebm.lua' +MANIFESTS = ['mcpp.toml', 'examples/vpxwebm/mcpp.toml'] +PACKAGES = ['compat.libvpx', 'compat.libwebm'] + + +def published() -> bool: + try: + with urllib.request.urlopen(PUBLISHED, timeout=60) as response: + return response.status == 200 + except urllib.error.HTTPError as error: + if error.code == 404: + return False + raise + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + mode = parser.add_mutually_exclusive_group(required=True) + mode.add_argument('--checkout-only', action='store_true') + mode.add_argument('--ci', action='store_true') + args = parser.parse_args() + root = Path(__file__).resolve().parents[1] + if published(): + print('compat.libvpx and compat.libwebm are published: the published index is used', file=sys.stderr) + return + checkout = root / 'target/candidate-index' + if not (checkout / 'index.toml').exists(): + subprocess.run(['git', 'clone', '--quiet', '--depth', '1', '--branch', BRANCH, INDEX + '.git', str(checkout)], + check=True, stdout=sys.stderr) + subprocess.run([sys.executable, str(checkout / 'tests/prepare_mwrap_indices.py')], cwd=checkout, check=True, + stdout=sys.stderr) + view = checkout / 'tests/mwrap/.indices/compat' + for package in PACKAGES: + if not (view / f'pkgs/view/{package}.lua').exists(): + sys.exit(f'{INDEX} {BRANCH} has no {package}') + if args.ci: + for manifest in MANIFESTS: + with (root / manifest).open('a', encoding='utf-8', newline='\n') as out: + out.write('\n# CI only (tools/candidate_index.py): compat.libvpx and compat.libwebm from ' + BRANCH + '\n') + out.write('[indices]\ncompat = { path = "' + view.as_posix() + '" }\n') + print('candidate compat index for', manifest, file=sys.stderr) + print(checkout.as_posix()) + + +if __name__ == '__main__': + main() diff --git a/tools/check_exports.py b/tools/check_exports.py new file mode 100644 index 0000000..d7f1075 --- /dev/null +++ b/tools/check_exports.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +"""Audit the modules against libvpx's and libwebm's public headers (the pinned tags'). + +- The modules, the controls' unit and the generated tests are what + tools/gen_exports.py writes. +- Every type, enumerator, exported function and codec interface of libvpx's + headers is exported by mcpplibs.mwrap.libvpx (VP8's interfaces, declared but + not built, are listed with their reason), and every typed control adapted. +- Every class, type, enumerator and free function of libwebm's headers is + exported in its namespace by mcpplibs.mwrap.libvpx.webm, and every + namespace-scope constant given under mcpplibs::mwrap::libvpx::. +- Every macro is mapped in src/macros.cppm, stays in + with its reason (tools/gen_exports.py's STAY), is a VPX_CTRL_ feature + test, or an include guard. +""" +from pathlib import Path +import re +import subprocess +import sys + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from gen_exports import CONSTEXPR, FUNCTIONS, NOT_BUILT, STAY, is_feature_test, is_guard # noqa: E402 +from inventory import c_surface, cpp_surface, exported_symbols, fetch # noqa: E402 + + +def main(): + root = Path(__file__).resolve().parents[1] + problems = [] + generated = subprocess.run([sys.executable, str(root / 'tools/gen_exports.py'), '--check'], capture_output=True, text=True) + if generated.returncode != 0: + problems.append(generated.stderr.strip() or generated.stdout.strip()) + tops = fetch(root) + c = c_surface(tops['libvpx']) + functions, data = exported_symbols(tops['libvpx']) + + text = (root / 'src/libvpx.cppm').read_text(encoding='utf-8') + exported = set(re.findall(r'export using ::(\w+);', text)) + declared = set(c.types) | set(c.enumerators) | set(c.functions) | set(c.data) + missing = declared - exported - NOT_BUILT.keys() + if missing: + problems.append('src/libvpx.cppm lacks: ' + ', '.join(sorted(missing))) + built_but_listed = NOT_BUILT.keys() & (set(functions) | set(data)) + if built_but_listed: + problems.append('listed as not built, yet upstream exports: ' + ', '.join(sorted(built_but_listed))) + adapted = set(re.findall(r'vpx_codec_control_(\w+)\(', text[text.index('export namespace mcpplibs::mwrap::libvpx {'):])) + lacking = {k.id for k in c.controls} - adapted + if lacking: + problems.append('typed controls not adapted: ' + ', '.join(sorted(lacking))) + + w = cpp_surface(tops['libwebm']) + webm = (root / 'src/webm.cppm').read_text(encoding='utf-8') + counts = [] + for name, ns in w.namespaces.items(): + block = re.search(r'export namespace ' + name + r' \{(.*?)\} // namespace ' + name + r'\n', webm, flags=re.S) + have = set(re.findall(r'using ::' + name + r'::(\w+);', block.group(1))) if block else set() + missing = (set(ns.classes) | set(ns.enumerators) | set(ns.functions)) - have + if missing: + problems.append(f'src/webm.cppm lacks {name}::' + f', {name}::'.join(sorted(missing))) + for k in ns.constants: + if not re.search(rf'export namespace mcpplibs::mwrap::libvpx::{name} \{{[^}}]*inline constexpr auto {k} = ::{name}::{k};', webm): + problems.append(f'src/webm.cppm does not give the constant {name}::{k}') + counts.append(f'{name} {len(ns.classes)}/{len(ns.enumerators)}/{len(ns.functions)}/{len(ns.constants)}') + + macros_text = (root / 'src/macros.cppm').read_text(encoding='utf-8') + body = macros_text[macros_text.index('export namespace mcpplibs::mwrap::libvpx::macros'):] + mapped = set(re.findall(r'inline constexpr auto (\w+) =', body)) | set(re.findall(r'^(?:constexpr )?[\w:]+ (\w+)\(', body, flags=re.M)) + macros = set(c.macros) | set(w.macros) + guards = {m for m in macros if is_guard(m)} + feature_tests = {m for m in macros if is_feature_test(m) and m in c.macros} + unaccounted = macros - mapped - STAY.keys() - guards - feature_tests + if unaccounted: + problems.append('macros neither mapped nor staying: ' + ', '.join(sorted(unaccounted))) + for table, what in ((STAY.keys(), 'staying'), (FUNCTIONS.keys() | CONSTEXPR.keys(), 'mapped as functions')): + stale = set(table) - macros + if stale: + problems.append(f'{what} but not in the headers: ' + ', '.join(sorted(stale))) + both = STAY.keys() & mapped + if both: + problems.append('listed as staying yet mapped: ' + ', '.join(sorted(both))) + extra = mapped - macros + if extra: + problems.append('mapped but not a macro of the headers: ' + ', '.join(sorted(extra))) + if problems: + sys.exit('\n'.join(problems)) + print(f'libvpx: {len(c.types)} types, {len(c.enumerators)} enumerators, {len(functions)} functions, {len(data)} codec interfaces ' + f'exported, {len(NOT_BUILT)} of VP8 not built, {len(c.controls)} typed controls adapted; libwebm (types/enumerators/' + f'functions/constants): ' + ', '.join(counts) + f'; {len(macros)} macros: {len(macros & mapped)} mapped, ' + f'{len(STAY)} staying, {len(feature_tests)} VPX_CTRL_ feature tests, {len(guards)} include guards') + + +if __name__ == '__main__': + main() diff --git a/tools/check_mwrap_release.py b/tools/check_mwrap_release.py new file mode 100644 index 0000000..9bedddb --- /dev/null +++ b/tools/check_mwrap_release.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +"""Check the immutable mwrap release coordinate and bilingual entry pages.""" +import argparse +import hashlib +import json +from pathlib import Path +import re +import tomllib + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--tag', help='release tag; omitted during PR checks') + args = parser.parse_args() + root = Path(__file__).resolve().parents[1] + package = tomllib.loads((root / 'mcpp.toml').read_text(encoding="utf-8"))['package'] + assert package['namespace'] == 'mcpplibs.mwrap', package + assert re.fullmatch(r'[A-Za-z_][A-Za-z_0-9]*', package['name']), package['name'] + upstream = package['metadata']['mwrap']['upstream-version'] + version = package['version'] + assert version == upstream or re.fullmatch(re.escape(upstream) + r'\.[1-9][0-9]*', version), (version, upstream) + if args.tag: + assert args.tag == 'mwrap-' + version, (args.tag, version) + assert package['license'] == 'Apache-2.0', package['license'] + assert package['authors'] == ['mcpp-community'], package.get('authors') + license_text = (root / 'LICENSE').read_text(encoding='utf-8') + assert 'Apache License' in license_text and 'Version 2.0' in license_text + assert 'mcpp-community' in (root / 'NOTICE').read_text(encoding='utf-8') + documents = json.loads((root / 'upstream/documents.json').read_text(encoding='utf-8'))['documents'] + assert any(Path(d['path']).name.startswith('README') for d in documents) + assert any(Path(d['path']).name.startswith(('LICENSE', 'COPYING')) for d in documents) + deployed = {entry['from'] for entry in tomllib.loads((root / 'mcpp.toml').read_text(encoding='utf-8')).get('runtime', {}).get('deploy', [])} + assert {'LICENSE', 'NOTICE'} <= deployed + for document in documents: + path = Path(document['path']) + assert not path.is_absolute() and '..' not in path.parts and path.parts[0] == 'upstream' + assert hashlib.sha256((root / path).read_bytes()).hexdigest() == document['sha256'], path + assert document['source'] and path.as_posix() in deployed, path + identity = package['namespace'] + '.' + package['name'] + for name, other in [('README.md', 'README.zh.md'), ('README.zh.md', 'README.md')]: + text = (root / name).read_text(encoding="utf-8") + assert len(text.splitlines()) <= 150, name + assert '](' + other + ')' in '\n'.join(text.splitlines()[:12]), name + assert identity in text and version in text, name + assert text.count('```') % 2 == 0, name + print(identity + '@' + version) + + +if __name__ == '__main__': + main() diff --git a/tools/gen_exports.py b/tools/gen_exports.py new file mode 100644 index 0000000..d2feba2 --- /dev/null +++ b/tools/gen_exports.py @@ -0,0 +1,386 @@ +#!/usr/bin/env python3 +"""Write the modules from the pinned headers (tools/inventory.py). + + python3 tools/gen_exports.py write src/ + python3 tools/gen_exports.py --check exit 1 when src/ is not what this writes + + src/libvpx.cppm mcpplibs.mwrap.libvpx: libvpx's types, enumerators, + functions and codec interfaces (`export using ::name;`); + the typed controls adapted; re-exports the two below + src/control.cpp the typed controls' bodies (upstream's own) + src/webm.cppm mcpplibs.mwrap.libvpx.webm: libwebm's mkvmuxer, mkvparser + and libwebm namespaces + src/macros.cppm mcpplibs.mwrap.libvpx.macros: the macros as constants and + functions +""" +from pathlib import Path +import re +import sys + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from inventory import LIBVPX_HEADERS, LIBWEBM_HEADERS, c_surface, cpp_surface, exported_symbols, fetch # noqa: E402 + +# Declared by vp8cx.h and vp8dx.h, not built: compat.libvpx is configured +# --disable-vp8, so upstream exports neither (vpx/exports_* and vp9's lists). +NOT_BUILT = { + 'vpx_codec_vp8_cx': 'VP8 is not built (compat.libvpx: --disable-vp8)', + 'vpx_codec_vp8_dx': 'VP8 is not built (compat.libvpx: --disable-vp8)', + 'vpx_codec_vp8_cx_algo': 'VP8 is not built (compat.libvpx: --disable-vp8)', + 'vpx_codec_vp8_dx_algo': 'VP8 is not built (compat.libvpx: --disable-vp8)', +} + +# Macros that stay macros (in ), by reason. +STAY = { + 'VPX_FORCE_INLINE': 'an inlining annotation', + 'VPX_INLINE': 'an inlining annotation', + 'VPX_DEPRECATED': 'an attribute of declarations', + 'VPX_DECLSPEC_DEPRECATED': 'an attribute of declarations', + 'VPX_UNUSED': 'an attribute of declarations', + 'vpx_codec_control': 'token concatenation (vpx_codec_control_##id): the typed controls are adapted functions', + 'VPX_CTRL_USE_TYPE': 'defines the typed controls (adapted)', + 'VPX_CTRL_USE_TYPE_DEPRECATED': 'defines the typed controls (adapted)', + 'VPX_CTRL_VOID': 'defines the typed controls (adapted)', + 'LIBWEBM_DISALLOW_COPY_AND_ASSIGN': "a declaration helper of libwebm's classes", +} + +# Function-like macros mapped as functions in mcpplibs::mwrap::libvpx::macros. +FUNCTIONS = { + 'vpx_codec_enc_init': ('vpx_codec_err_t', 'vpx_codec_ctx_t* ctx, vpx_codec_iface_t* iface, const vpx_codec_enc_cfg_t* cfg, vpx_codec_flags_t flags', + 'ctx, iface, cfg, flags', 'An encoder for `iface` (vpx_codec_enc_init_ver with the ABI version the headers were written for).'), + 'vpx_codec_enc_init_multi': ('vpx_codec_err_t', 'vpx_codec_ctx_t* ctx, vpx_codec_iface_t* iface, vpx_codec_enc_cfg_t* cfg, int num_enc, vpx_codec_flags_t flags, vpx_rational_t* dsf', + 'ctx, iface, cfg, num_enc, flags, dsf', 'Several encoders at once (multi-resolution; VP8 only).'), + 'vpx_codec_dec_init': ('vpx_codec_err_t', 'vpx_codec_ctx_t* ctx, vpx_codec_iface_t* iface, const vpx_codec_dec_cfg_t* cfg, vpx_codec_flags_t flags', + 'ctx, iface, cfg, flags', 'A decoder for `iface` (vpx_codec_dec_init_ver with the ABI version the headers were written for).'), + 'vpx_codec_version_major': ('int', '', '', 'The library\'s version, its parts (from vpx_codec_version()).'), + 'vpx_codec_version_minor': ('int', '', '', ''), + 'vpx_codec_version_patch': ('int', '', '', ''), +} + + +# Function-like macros that compute: constexpr functions of the same name. +CONSTEXPR = { + 'VPX_VERSION_MAJOR': ('int', 'int v', 'v', 'The parts of a packed version (vpx_codec_version()).'), + 'VPX_VERSION_MINOR': ('int', 'int v', 'v', ''), + 'VPX_VERSION_PATCH': ('int', 'int v', 'v', ''), +} + + +def is_guard(name: str) -> bool: + return re.fullmatch(r'(VPX_VPX_\w+_H_|MKV\w+_H_|LIBWEBM_COMMON_\w+_H_|COMMON_WEBMIDS_H_)', name) is not None + + +def is_feature_test(name: str) -> bool: + """VPX_CTRL_: empty, defined beside each typed control so code can test for it with #ifdef.""" + return name.startswith('VPX_CTRL_') and name not in STAY + + +def constants(c) -> list[str]: + names = [m for m in c.macros if not is_guard(m) and m not in STAY and m not in FUNCTIONS and m not in CONSTEXPR and not is_feature_test(m)] + unknown = [m for m in names if m in c.function_like] + if unknown: + sys.exit('function-like macros with no mapping: ' + ', '.join(unknown)) + return names + + +HEAD = """// Generated by tools/gen_exports.py from libvpx 1.17.0's and libwebm 1.0.0.32's +// headers — do not edit by hand. +// +""" + + +def libvpx_module(c, functions, data) -> str: + out = [HEAD + """// mcpplibs.mwrap.libvpx — libvpx 1.17.0, the VP9 video codec, as a C++ module: +// every type, enumerator and function of the headers upstream installs +// (vpx_codec.h, vpx_image.h, vpx_frame_buffer.h, vpx_encoder.h with +// vpx_ext_ratectrl.h and vpx_tpl.h, vpx_decoder.h, vp8.h, vp8cx.h, vp8dx.h), +// the upstream globals unchanged: the encoder and decoder API, images and +// frame buffers, the control ids, the VP9 codec interfaces. The headers' +// typed controls (static functions made by VPX_CTRL_USE_TYPE) are adapted +// under the same names in namespace mcpplibs::mwrap::libvpx. It re-exports +// the WebM container (mcpplibs.mwrap.libvpx.webm: libwebm's mkvmuxer and +// mkvparser) and the macro mappings (mcpplibs.mwrap.libvpx.macros). The +// libraries are compat.libvpx and compat.libwebm, built from the same tags. +module; +#include + +export module mcpplibs.mwrap.libvpx; + +export import mcpplibs.mwrap.libvpx.webm; +export import mcpplibs.mwrap.libvpx.macros; +"""] + out.append(f'// types ({len(c.types)}: typedef names and struct and enum tags)') + out += [f'export using ::{t};' for t in c.types] + out.append(f'\n// enumerators ({len(c.enumerators)})') + out += [f'export using ::{e};' for e in c.enumerators] + out.append(f'\n// functions ({len(functions)}: what upstream exports; VP8\'s interfaces are not built)') + out += [f'export using ::{f};' for f in functions] + out.append(f'\n// the codec interfaces ({len(data)})') + out += [f'export using ::{d};' for d in data] + out.append(f""" +// The typed controls ({len(c.controls)}): `vpx_codec_control(ctx, ID, data)` in C +// is vpx_codec_control_ID(ctx, ID, data), a static function of the headers; +// here an exported one of the same name and signature. +export namespace mcpplibs::mwrap::libvpx {{ +""") + for k in c.controls: + dep = '[[deprecated]] ' if k.deprecated else '' + params = 'vpx_codec_ctx_t* ctx, int ctrl_id' + (f', {k.type} data' if k.type else '') + out.append(f'{dep}vpx_codec_err_t vpx_codec_control_{k.id}({params});') + out.append('\n} // namespace mcpplibs::mwrap::libvpx\n') + return '\n'.join(out) + + +def control_unit(c) -> str: + out = [HEAD + """// The typed controls' bodies: upstream's own (VPX_CTRL_USE_TYPE's), a call of +// the variadic vpx_codec_control_ with the typed argument. +module; +#include + +module mcpplibs.mwrap.libvpx; + +namespace mcpplibs::mwrap::libvpx { +"""] + for k in c.controls: + params = 'vpx_codec_ctx_t* ctx, int ctrl_id' + (f', {k.type} data' if k.type else '') + call = 'ctx, ctrl_id' + (', data' if k.type else '') + out.append(f'vpx_codec_err_t vpx_codec_control_{k.id}({params}) {{ return ::vpx_codec_control_({call}); }}') + out.append('\n} // namespace mcpplibs::mwrap::libvpx\n') + return '\n'.join(out) + + +def webm_module(w) -> str: + includes = '\n'.join(f'#include <{h}>' for h in LIBWEBM_HEADERS) + out = [HEAD + f"""// mcpplibs.mwrap.libvpx.webm — libwebm 1.0.0.32, the WebM (Matroska) container, +// as a C++ module: the classes, types, enumerators and functions of the +// headers of upstream's `webm` library in their own namespaces — mkvmuxer +// (Segment, the tracks, Frame, Colour, IMkvWriter and MkvWriter …), mkvparser +// (Segment, Tracks, Cluster, Block, IMkvReader and MkvReader …) and libwebm +// (the element ids, the colour copiers). The namespace-scope constants of the +// headers have internal linkage, so they are not exported by name: their +// values are under the same names in mcpplibs::mwrap::libvpx::mkvmuxer and +// mcpplibs::mwrap::libvpx::mkvparser. The library is compat.libwebm. +module; +{includes} + +export module mcpplibs.mwrap.libvpx.webm; +"""] + for name, ns in w.namespaces.items(): + out.append(f'export namespace {name} {{') + out.append(f'// types ({len(ns.classes)})') + out += [f'using ::{name}::{t};' for t in ns.classes] + if ns.enumerators: + out.append(f'// enumerators ({len(ns.enumerators)})') + out += [f'using ::{name}::{e};' for e in ns.enumerators] + out.append(f'// functions ({len(ns.functions)})') + out += [f'using ::{name}::{f};' for f in ns.functions] + out.append(f'}} // namespace {name}\n') + for name, ns in w.namespaces.items(): + if not ns.constants: + continue + out.append(f'export namespace mcpplibs::mwrap::libvpx::{name} {{') + for k, t in ns.constants.items(): + out.append(f'inline constexpr auto {k} = ::{name}::{k}; // {t}') + out.append(f'}} // namespace mcpplibs::mwrap::libvpx::{name}\n') + return '\n'.join(out) + + +def capture_header(c) -> str: + names = constants(c) + capture = '\n'.join(f'inline constexpr auto value_{m} = {m};' for m in names) + calls = '\n'.join(f'inline {ret} ({name})({params}) {{ return {name}({args}); }}' for name, (ret, params, args, _) in FUNCTIONS.items()) + computes = '\n'.join(f'constexpr {ret} ({name})({params}) {{ return {name}({args}); }}' for name, (ret, params, args, _) in CONSTEXPR.items()) + undef = '\n'.join(f'#undef {m}' for m in names + list(FUNCTIONS) + list(CONSTEXPR)) + return HEAD + f"""// The values of libvpx's macros, captured before src/macros.cppm undefines +// them (the global module fragment holds only what the preprocessor includes). +#pragma once +#include + +namespace mwrap_libvpx_capture {{ +{capture} +}} // namespace mwrap_libvpx_capture + +namespace mwrap_libvpx_function {{ +{calls} +{computes} +}} // namespace mwrap_libvpx_function + +{undef} +""" + + +def macros_module(c) -> str: + names = constants(c) + funcs = [] + for name, (ret, params, args, doc) in FUNCTIONS.items(): + if doc: + funcs.append(f'// {doc}') + funcs.append(f'{ret} {name}({params});') + return HEAD + f"""// mcpplibs.mwrap.libvpx.macros — the macros of libvpx's public headers (a named +// module cannot export a macro), under their original names in +// mcpplibs::mwrap::libvpx::macros: +// +// - the {len(names)} constants (the ABI versions, VPX_CODEC_CAP_*, VPX_CODEC_USE_*, +// VPX_FRAME_IS_*, VPX_EFLAG_*, VP8_EFLAG_*, VPX_DL_*, the layer limits, +// the image format and plane constants …) as `inline constexpr`, of the +// type the macro's expression has; +// - vpx_codec_enc_init, vpx_codec_enc_init_multi, vpx_codec_dec_init and +// vpx_codec_version_major/_minor/_patch as functions; +// - VPX_VERSION_MAJOR/_MINOR/_PATCH(v) as constexpr functions. +// +// What stays a macro (in ): vpx_codec_control (token +// concatenation; the typed controls are functions of the main module), +// VPX_CTRL_USE_TYPE*, VPX_CTRL_VOID, the empty VPX_CTRL_ feature tests +// for #ifdef, the declaration annotations and the include guards; +// tools/check_exports.py accounts for every macro. +module; +#include "macros_capture.hpp" + +export module mcpplibs.mwrap.libvpx.macros; + +export namespace mcpplibs::mwrap::libvpx::macros {{ + +{chr(10).join(f'inline constexpr auto {m} = mwrap_libvpx_capture::value_{m};' for m in names)} + +{chr(10).join(funcs)} + +{chr(10).join(('// ' + doc + chr(10) if doc else '') + f'constexpr {ret} {name}({params}) {{ return mwrap_libvpx_function::{name}({args}); }}' for name, (ret, params, args, doc) in CONSTEXPR.items())} + +}} // namespace mcpplibs::mwrap::libvpx::macros + +namespace mcpplibs::mwrap::libvpx::macros {{ +{chr(10).join(f'{ret} {name}({params}) {{ return mwrap_libvpx_function::{name}({args}); }}' for name, (ret, params, args, _) in FUNCTIONS.items())} +}} // namespace mcpplibs::mwrap::libvpx::macros +""" + + +def link_test(c, functions, data) -> str: + out = [HEAD + """// The address of every exported function and adapted control, and every codec +// interface, through the module only (no header): each is defined by +// compat.libvpx or by the module, so a program that calls any of them links. +import std; +import mcpplibs.mwrap.libvpx; + +using fn = void (*)(); + +int main() { + const fn functions[] = {"""] + out += [f' reinterpret_cast(&::{f}),' for f in functions] + out += [f' reinterpret_cast(&mcpplibs::mwrap::libvpx::vpx_codec_control_{k.id}),' for k in c.controls if not k.deprecated] + out.append(' };') + out.append(' const void* interfaces[] = {' + ', '.join(f'&::{d}' for d in data) + '};') + out.append(f""" int missing = 0; + for (fn f : functions) missing += f == nullptr; + for (const void* d : interfaces) missing += d == nullptr; + std::println("{{}} functions and controls, {{}} codec interfaces: {{}}", std::size(functions), std::size(interfaces), missing ? "MISSING" : "all linked"); + return missing; +}} +""") + return '\n'.join(out) + + +def macros_mix_test(c) -> str: + out = [HEAD + """// Every mapping against the raw headers, in one translation unit that includes +// beside the import: each constant has its macro's +// type and value (the macro is set aside with push_macro around the +// comparison: while it is defined it expands even inside `m::NAME`); each +// function-like mapping answers what its macro does (a function-like macro is +// not expanded when its name is followed by `)`, so `(m::NAME)(args)` calls +// the function beside the macro `NAME(args)`); the header's +// vpx_codec_control macro and the adapted typed control set the same thing. +#include + +import std; +import mcpplibs.mwrap.libvpx; + +namespace m = mcpplibs::mwrap::libvpx::macros; +"""] + for name in constants(c): + out.append(f'constexpr auto raw_{name} = {name};') + out.append(f'#pragma push_macro("{name}")\n#undef {name}') + out.append(f'static_assert(std::is_same_v && raw_{name} == m::{name});') + out.append(f'#pragma pop_macro("{name}")') + out.append(""" +static_assert((m::VPX_VERSION_MAJOR)(0x011100) == VPX_VERSION_MAJOR(0x011100) && (m::VPX_VERSION_MINOR)(0x011100) == VPX_VERSION_MINOR(0x011100) && + (m::VPX_VERSION_PATCH)(0x011102) == VPX_VERSION_PATCH(0x011102)); + +int main() { + int failures = 0; + auto check = [&](bool ok, const char* what) { + std::println("{:<64} {}", what, ok ? "ok" : "FAILED"); + failures += !ok; + }; + check((m::vpx_codec_version_major)() == vpx_codec_version_major() && (m::vpx_codec_version_minor)() == vpx_codec_version_minor() && + (m::vpx_codec_version_patch)() == vpx_codec_version_patch(), + "vpx_codec_version_major/_minor/_patch"); + vpx_codec_enc_cfg_t cfg; + check(vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &cfg, 0) == VPX_CODEC_OK, "a default configuration"); + cfg.g_w = 64; + cfg.g_h = 64; + vpx_codec_ctx_t a, b; + check((m::vpx_codec_enc_init)(&a, vpx_codec_vp9_cx(), &cfg, 0) == VPX_CODEC_OK && vpx_codec_enc_init(&b, vpx_codec_vp9_cx(), &cfg, 0) == VPX_CODEC_OK, + "the mapped vpx_codec_enc_init beside the macro"); + // The header's typed control through its macro, and the module's adapted one. + check(vpx_codec_control(&a, VP8E_SET_CPUUSED, 7) == VPX_CODEC_OK && + mcpplibs::mwrap::libvpx::vpx_codec_control_VP8E_SET_CPUUSED(&b, VP8E_SET_CPUUSED, 7) == VPX_CODEC_OK, + "vpx_codec_control (the macro) and the adapted control"); +#ifndef VPX_CTRL_VP9E_SET_ROW_MT + check(false, "the VPX_CTRL_ feature tests are defined"); +#endif + vpx_codec_destroy(&a); + vpx_codec_destroy(&b); + vpx_codec_dec_cfg_t dcfg{}; + vpx_codec_ctx_t d; + check((m::vpx_codec_dec_init)(&d, vpx_codec_vp9_dx(), &dcfg, 0) == VPX_CODEC_OK, "the mapped vpx_codec_dec_init"); + vpx_codec_destroy(&d); + std::println("{}", failures ? "FAILED" : "all passed"); + return failures; +} +""") + return '\n'.join(out) + + +MODULES = { + 'src/libvpx.cppm': None, + 'src/control.cpp': None, + 'src/webm.cppm': None, + 'src/macros.cppm': None, +} + + +def render(root: Path) -> dict[str, str]: + tops = fetch(root) + c = c_surface(tops['libvpx']) + text, data = exported_symbols(tops['libvpx']) + missing = (set(text) - set(c.functions)) | (set(data) - set(c.data)) + if missing: + sys.exit('exported by upstream, not declared by the headers: ' + ', '.join(sorted(missing))) + w = cpp_surface(tops['libwebm']) + return { + 'src/libvpx.cppm': libvpx_module(c, text, data), + 'src/control.cpp': control_unit(c), + 'src/webm.cppm': webm_module(w), + 'src/macros.cppm': macros_module(c), + 'src/macros_capture.hpp': capture_header(c), + 'tests/link_test.cpp': link_test(c, text, data), + 'tests/macros_mix_test.cpp': macros_mix_test(c), + } + + +def main(): + root = Path(__file__).resolve().parents[1] + files = render(root) + stale = [] + for name, text in files.items(): + path = root / name + if '--check' in sys.argv: + if not path.exists() or path.read_text(encoding='utf-8') != text: + stale.append(name) + else: + path.write_text(text, encoding='utf-8', newline='\n') + print('wrote', name) + if stale: + sys.exit('stale (python3 tools/gen_exports.py): ' + ', '.join(stale)) + + +if __name__ == '__main__': + main() diff --git a/tools/inventory.py b/tools/inventory.py new file mode 100644 index 0000000..08ddb47 --- /dev/null +++ b/tools/inventory.py @@ -0,0 +1,280 @@ +#!/usr/bin/env python3 +"""The public surface of libvpx's and libwebm's headers, from the pinned tags. + +Shared by tools/gen_exports.py (which writes the modules) and +tools/check_exports.py (which audits them against it). The headers come from +the upstream tag archives, downloaded once into target/upstream and checked +against upstream/documents.json: the same bytes compat.libvpx and +compat.libwebm build. + +libvpx (C): the headers upstream installs (libs.mk's INSTALL-LIBS for a VP9 +encoder and decoder build) — vpx/vpx_integer.h, vpx_codec.h, vpx_image.h, +vpx_frame_buffer.h, vpx_encoder.h with vpx_ext_ratectrl.h and vpx_tpl.h, +vpx_decoder.h, vp8.h, vp8cx.h, vp8dx.h. The functions are the symbols +upstream exports (vpx/exports_com, exports_enc, exports_dec and vp9's +exports_enc, exports_dec), which the headers declare. + +libwebm (C++): the headers of upstream's `webm` library — mkvmuxer/ +mkvmuxertypes.h, mkvmuxer.h, mkvmuxerutil.h, mkvwriter.h; mkvparser/ +mkvparser.h, mkvreader.h; common/webmids.h, hdr_util.h, file_util.h — by +namespace (mkvmuxer, mkvparser, libwebm). +""" +from dataclasses import dataclass, field +import hashlib +import json +from pathlib import Path +import re +import sys +import tarfile +import urllib.request + +LIBVPX_HEADERS = ['vpx/vpx_integer.h', 'vpx/vpx_codec.h', 'vpx/vpx_image.h', 'vpx/vpx_frame_buffer.h', 'vpx/vpx_ext_ratectrl.h', + 'vpx/vpx_tpl.h', 'vpx/vpx_encoder.h', 'vpx/vpx_decoder.h', 'vpx/vp8.h', 'vpx/vp8cx.h', 'vpx/vp8dx.h'] +LIBVPX_EXPORTS = ['vpx/exports_com', 'vpx/exports_enc', 'vpx/exports_dec', 'vp9/exports_enc', 'vp9/exports_dec'] +LIBWEBM_HEADERS = ['mkvmuxer/mkvmuxertypes.h', 'mkvmuxer/mkvmuxer.h', 'mkvmuxer/mkvmuxerutil.h', 'mkvmuxer/mkvwriter.h', + 'mkvparser/mkvparser.h', 'mkvparser/mkvreader.h', 'common/webmids.h', 'common/hdr_util.h', 'common/file_util.h'] + + +def fetch(root: Path) -> dict[str, Path]: + """The two extracted archives, by library.""" + pins = json.loads((root / 'upstream/documents.json').read_text(encoding='utf-8')) + cache = root / 'target/upstream' + cache.mkdir(parents=True, exist_ok=True) + out = {} + for lib, pin in pins['upstream'].items(): + archive = cache / f'{lib}.tar.gz' + if not archive.exists() or hashlib.sha256(archive.read_bytes()).hexdigest() != pin['sha256']: + print('+ fetch', pin['url'], flush=True, file=sys.stderr) + with urllib.request.urlopen(pin['url'], timeout=120) as response: + archive.write_bytes(response.read()) + digest = hashlib.sha256(archive.read_bytes()).hexdigest() + if digest != pin['sha256']: + sys.exit(f'{lib} archive checksum {digest} != {pin["sha256"]}') + with tarfile.open(archive) as tar: + top = tar.getmembers()[0].name.split('/')[0] + if not (cache / top).exists(): + tar.extractall(cache, filter='data') + out[lib] = cache / top + return out + + +def strip_comments(text: str) -> str: + text = re.sub(r'/\*.*?\*/', lambda m: '\n' * m.group(0).count('\n'), text, flags=re.S) + return re.sub(r'//[^\n]*', '', text) + + +def logical_lines(text: str) -> list[str]: + return strip_comments(text).replace('\\\n', ' ').split('\n') + + +def preprocess(text: str) -> tuple[str, list[str]]: + """The C text without preprocessor lines and without `#ifdef __cplusplus` + blocks (as a C compiler reads it), and the macros it defines.""" + out, macros, stack = [], [], [] + for line in logical_lines(text): + s = line.strip() + if s.startswith('#'): + d = re.match(r'#\s*(\w+)\s*(.*)', s) + word, rest = (d.group(1), d.group(2)) if d else ('', '') + if word in ('if', 'ifdef', 'ifndef'): + stack.append(word == 'ifdef' and rest.strip() == '__cplusplus' or word == 'if' and re.fullmatch(r'defined\s*\(?\s*__cplusplus\s*\)?', rest.strip()) is not None) + elif word in ('else', 'elif') and stack: + stack[-1] = False if stack[-1] else stack[-1] + elif word == 'endif' and stack: + stack.pop() + elif word == 'define' and not any(stack): + d2 = re.match(r'(\w+)(\()?', rest) + macros.append(d2.group(1) + ('()' if d2.group(2) else '')) + continue + if any(stack): + continue + out.append(line) + return '\n'.join(out), macros + + +def top_level(text: str) -> list[str]: + """Top-level statements: text up to a `;` at brace depth 0 (a struct's body + included), and macro invocations standing alone (`VPX_CTRL_USE_TYPE(…)`).""" + out, cur, depth = [], [], 0 + i = 0 + while i < len(text): + c = text[i] + if c == '{': + depth += 1 + elif c == '}': + depth -= 1 + cur.append(c) + if depth == 0 and c == ';': + out.append(''.join(cur).strip()) + cur = [] + elif depth == 0 and c == ')' and re.match(r'\s*VPX_CTRL_\w+\s*\(', ''.join(cur)): + out.append(''.join(cur).strip()) + cur = [] + i += 1 + return [s for s in out if s] + + +@dataclass +class Control: + id: str + type: str | None # None: VPX_CTRL_VOID + deprecated: bool + + +@dataclass +class CSurface: + types: list[str] = field(default_factory=list) # typedef names and struct/enum tags + enumerators: list[str] = field(default_factory=list) + functions: list[str] = field(default_factory=list) # declared, with external linkage + data: list[str] = field(default_factory=list) # extern variables + controls: list[Control] = field(default_factory=list) # VPX_CTRL_USE_TYPE / _DEPRECATED / VPX_CTRL_VOID + macros: dict[str, str] = field(default_factory=dict) # name -> header + function_like: set[str] = field(default_factory=set) # macros that take arguments + + +def c_surface(top: Path) -> CSurface: + s = CSurface() + for h in LIBVPX_HEADERS: + text, macros = preprocess((top / h).read_text(encoding='utf-8')) + for m in macros: + if m.endswith('()'): + m = m[:-2] + s.function_like.add(m) + s.macros.setdefault(m, h) + for st in top_level(text): + st = re.sub(r'^extern\s+"C"\s*\{', '', st).strip() + m = re.match(r'VPX_CTRL_(USE_TYPE|USE_TYPE_DEPRECATED|VOID)\s*\(\s*(\w+)\s*(?:,\s*(.+?))?\s*\)$', st, flags=re.S) + if m: + s.controls.append(Control(m.group(2), m.group(3) and ' '.join(m.group(3).split()), m.group(1) == 'USE_TYPE_DEPRECATED')) + continue + # Only what is at the statement's outermost level: in C++ a struct or + # enum defined inside another struct is that struct's member. + outer = re.match(r'(?:typedef\s+)?(struct|enum)\s*(\w*)\s*\{', st) + if outer: + if outer.group(2): + s.types.append(outer.group(2)) + if outer.group(1) == 'enum': + body = st[outer.end():st.rindex('}')] + for e in body.split(','): + e = e.strip() + if e: + s.enumerators.append(re.match(r'(\w+)', e).group(1)) + flat = ' '.join(re.sub(r'\{.*\}', ' { } ', st, flags=re.S).split()) + if flat.startswith('typedef'): + fp = re.search(r'\(\s*\*\s*(\w+)\s*\)\s*\(', flat) + name = fp.group(1) if fp else re.search(r'(\w+)\s*(?:\[[^\]]*\])?\s*;$', flat).group(1) + s.types.append(name) + elif flat.startswith('extern ') and '(' not in flat: + s.data.append(re.search(r'(\w+)\s*;$', flat).group(1)) + elif '(' in flat and not flat.startswith(('static', 'struct', 'enum')) and '{' not in flat: + fn = re.search(r'(\w+)\s*\([^()]*(?:\([^()]*\)[^()]*)*\)\s*(?:\w+\s*(?:\([^()]*\))?\s*)*;$', flat) + if fn: + s.functions.append(fn.group(1)) + s.types = sorted(set(s.types)) + s.enumerators = sorted(set(s.enumerators)) + s.functions = sorted(set(s.functions)) + s.data = sorted(set(s.data)) + return s + + +def exported_symbols(top: Path) -> tuple[list[str], list[str]]: + """What upstream's export lists name: functions (text) and data.""" + text, data = [], [] + for f in LIBVPX_EXPORTS: + for line in (top / f).read_text(encoding='utf-8').splitlines(): + kind, _, name = line.partition(' ') + (data if kind == 'data' else text).append(name.strip()) + return sorted(text), sorted(data) + + +# ── libwebm (C++) ──────────────────────────────────────────────────────── + + +@dataclass +class Namespace: + classes: list[str] = field(default_factory=list) # classes, structs, typedefs, enums + enumerators: list[str] = field(default_factory=list) # of unscoped namespace-level enums + functions: list[str] = field(default_factory=list) # free functions (and templates) + constants: dict[str, str] = field(default_factory=dict) # name -> type (namespace-scope const: internal linkage) + + +@dataclass +class CppSurface: + namespaces: dict[str, Namespace] = field(default_factory=dict) + macros: dict[str, str] = field(default_factory=dict) + + +def cpp_surface(top: Path) -> CppSurface: + s = CppSurface() + for h in LIBWEBM_HEADERS: + lines = logical_lines((top / h).read_text(encoding='utf-8')) + body = [] + for line in lines: + st = line.strip() + if st.startswith('#'): + d = re.match(r'#\s*define\s+(\w+)', st) + if d: + s.macros.setdefault(d.group(1), h) + continue + body.append(line) + text = '\n'.join(body) + # Namespace blocks at file level (libwebm does not nest them). + for m in re.finditer(r'\bnamespace\s+(\w+)\s*\{', text): + name = m.group(1) + depth, i = 1, m.end() + while depth and i < len(text): + depth += {'{': 1, '}': -1}.get(text[i], 0) + i += 1 + ns = s.namespaces.setdefault(name, Namespace()) + for st in top_level(text[m.end():i - 1]): + flat = ' '.join(re.sub(r'\{.*\}', ' { } ', st, flags=re.S).split()) + flat = re.sub(r'^template\s*<[^>]*>\s*', '', flat) + if re.match(r'(class|struct)\s+\w+\s*;$', flat): + continue # a forward declaration + c = re.match(r'(?:class|struct)\s+(\w+)\b', flat) + if c and '{' in flat: + ns.classes.append(c.group(1)) + continue + e = re.match(r'enum\s+(?:class\s+)?(\w+)\s*(?::\s*[\w:]+\s*)?\{', flat) + if e: + ns.classes.append(e.group(1)) + if not flat.startswith('enum class'): + inner = re.search(r'\benum\b[^{]*\{(.*)\}', st, flags=re.S).group(1) + for item in inner.split(','): + item = item.strip() + if item: + ns.enumerators.append(re.match(r'(\w+)', item).group(1)) + continue + if flat.startswith('typedef') or flat.startswith('using '): + t = re.search(r'(\w+)\s*;$', flat) if flat.startswith('typedef') else re.match(r'using\s+(\w+)\s*=', flat) + ns.classes.append(t.group(1)) + continue + k = re.match(r'(?:static\s+)?const(?:expr)?\s+([\w:]+)\s+(\w+)\s*=', flat) + if k: + ns.constants[k.group(2)] = k.group(1) + continue + if '(' in flat: + f = re.search(r'([\w~]+)\s*\(', re.sub(r'<[^<>]*>', '', flat)) + if f and not re.search(r'\b\w+::\w+\s*\(', flat): # not an out-of-class member definition + ns.functions.append(f.group(1)) + for ns in s.namespaces.values(): + ns.classes = sorted(set(ns.classes)) + ns.enumerators = sorted(set(ns.enumerators)) + ns.functions = sorted(set(ns.functions)) + return s + + +if __name__ == '__main__': + root = Path(__file__).resolve().parents[1] + tops = fetch(root) + c = c_surface(tops['libvpx']) + text, data = exported_symbols(tops['libvpx']) + print(f'libvpx: {len(c.types)} types, {len(c.enumerators)} enumerators, {len(c.functions)} functions, {len(c.data)} data, ' + f'{len(c.controls)} controls, {len(c.macros)} macros; exported: {len(text)} functions, {len(data)} data') + print(' declared, not exported:', sorted(set(c.functions) - set(text))) + print(' exported, not declared:', sorted(set(text) - set(c.functions))) + print(' data declared:', c.data) + w = cpp_surface(tops['libwebm']) + for name, ns in w.namespaces.items(): + print(f'libwebm {name}: {len(ns.classes)} types, {len(ns.enumerators)} enumerators, {len(ns.functions)} functions, constants {ns.constants}') + print(' macros:', sorted(w.macros)) diff --git a/tools/run_conformance.py b/tools/run_conformance.py new file mode 100644 index 0000000..4ee680c --- /dev/null +++ b/tools/run_conformance.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +"""Compare examples/vpxwebm, written against import mcpplibs.mwrap.libvpx, +with upstream's own vpxenc and vpxdec built from the same tag. + +The tag archives are downloaded once into target/upstream and checked against +upstream/documents.json. Upstream's vpxenc and vpxdec are built by its own +configure and makefiles (generic target, with the system C and C++ +compilers; vpxdec reads WebM through its own copy of libwebm in +third_party/); vpxwebm is built with mcpp over compat.libvpx and +compat.libwebm. Then, for a clip generated here (a moving gradient and a +square, 4:2:0): + +- for each setting (good quality at constant quality and at a bit rate, with + and without look-ahead, lossless, real time at a bit rate, four threads with + tile columns), the two encoders must write the same IVF file, byte for byte + (vpxenc with --passes=1: vpxwebm encodes in one pass); +- vpxdec must read the WebM file vpxwebm writes (mkvmuxer's), and the two + decoders must give the same frames from it and from vpxenc's WebM (vpxwebm + reading it with mkvparser), as raw I420. + +Needs `make` and C and C++ compilers: CI runs it on Linux and macOS. +""" +import argparse +import os +from pathlib import Path +import shutil +import subprocess +import sys + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from inventory import fetch # noqa: E402 + +SETTINGS = [ + ['--good', '--cpu-used', '4', '--end-usage', 'q', '--cq-level', '18', '--lag-in-frames', '0', '--threads', '1'], + ['--good', '--cpu-used', '2', '--end-usage', 'vbr', '--target-bitrate', '300', '--lag-in-frames', '0', '--threads', '1'], + ['--good', '--cpu-used', '3', '--end-usage', 'q', '--cq-level', '24', '--threads', '1'], + ['--good', '--cpu-used', '4', '--lossless', '--lag-in-frames', '0', '--threads', '1'], + ['--rt', '--cpu-used', '8', '--end-usage', 'cbr', '--target-bitrate', '400', '--lag-in-frames', '0', '--threads', '1'], + ['--good', '--cpu-used', '1', '--end-usage', 'q', '--cq-level', '30', '--lag-in-frames', '0', '--threads', '4', '--tile-columns', '2'], +] + + +def vpxenc_args(setting: list[str]) -> list[str]: + out, i = [], 0 + while i < len(setting): + s = setting[i] + if s in ('--good', '--rt'): + out.append(s) + elif s == '--lossless': + out.append('--lossless=1') + else: + out.append(f'{s}={setting[i + 1]}') + i += 1 + i += 1 + return out + + +def upstream_tools(upstream: Path, cc: str, cxx: str) -> tuple[Path, Path]: + build = upstream.parent / (upstream.name + '-build') + if not (build / 'vpxenc').exists() or not (build / 'vpxdec').exists(): + shutil.rmtree(build, ignore_errors=True) + build.mkdir(parents=True) + env = dict(os.environ, CC=cc, CXX=cxx, LD=cxx) + command = [str(upstream / 'configure'), '--target=generic-gnu', '--disable-vp8', '--enable-vp9', '--disable-unit-tests', '--disable-docs'] + print('+', ' '.join(command), flush=True) + subprocess.run(command, cwd=build, check=True, env=env, stdout=subprocess.DEVNULL) + command = ['make', '-s', f'-j{os.cpu_count() or 2}'] + print('+', ' '.join(command), flush=True) + subprocess.run(command, cwd=build, check=True, env=env, stdout=subprocess.DEVNULL) + return build / 'vpxenc', build / 'vpxdec' + + +def vpxwebm(example: Path, mcpp: str, toolchain: str | None, profile: str) -> Path: + command = [mcpp, 'build', '--profile', profile] + (['--toolchain', toolchain] if toolchain else []) + print('+', ' '.join(command), flush=True) + subprocess.run(command, cwd=example, check=True) + found = sorted((p for p in (example / 'target').rglob('vpxwebm*') if p.parent.name == 'bin' and p.is_file() and p.suffix in ('', '.exe')), + key=lambda p: p.stat().st_mtime) + if not found: + sys.exit('examples/vpxwebm was not built') + return found[-1] + + +def clip(path: Path, w: int = 176, h: int = 144, n: int = 40) -> None: + out = bytearray(f'YUV4MPEG2 W{w} H{h} F30:1 Ip A1:1 C420jpeg\n'.encode()) + for t in range(n): + out += b'FRAME\n' + for y in range(h): + out += bytes(235 if 30 + t * 3 <= x < 70 + t * 3 and 40 <= y < 90 else (x * 2 + y + t * 5) & 255 for x in range(w)) + for y in range(h // 2): + out += bytes(128 + ((x + t) & 31) - 16 for x in range(w // 2)) + for y in range(h // 2): + out += bytes(128 - ((y + t) & 31) + 16 for x in range(w // 2)) + path.write_bytes(out) + + +def run(command: list) -> None: + result = subprocess.run([str(c) for c in command], capture_output=True, text=True) + if result.returncode != 0: + sys.exit(f'{" ".join(map(str, command))} failed:\n{result.stdout}{result.stderr}') + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--mcpp', default='mcpp') + parser.add_argument('--toolchain') + parser.add_argument('--profile', default='release') + parser.add_argument('--cc', default=os.environ.get('CC', 'cc')) + parser.add_argument('--cxx', default=os.environ.get('CXX', 'c++')) + args = parser.parse_args() + root = Path(__file__).resolve().parents[1] + upstream = fetch(root)['libvpx'] + vpxenc, vpxdec = upstream_tools(upstream, args.cc, args.cxx) + ours = vpxwebm(root / 'examples/vpxwebm', args.mcpp, args.toolchain, args.profile) + work = root / 'target/conformance' + shutil.rmtree(work, ignore_errors=True) + work.mkdir(parents=True) + source = work / 'clip.y4m' + clip(source) + failures, checks = [], 0 + + def compare(a: Path, b: Path, what: str) -> None: + nonlocal checks + checks += 1 + if a.read_bytes() != b.read_bytes(): + failures.append(what) + print('DIFFERENT', what, flush=True) + + for k, setting in enumerate(SETTINGS): + label = ' '.join(setting) + theirs, mine = work / f'{k}.vpxenc.ivf', work / f'{k}.ours.ivf' + run([vpxenc, '--codec=vp9', '--passes=1', *vpxenc_args(setting), '--ivf', '-q', '-o', theirs, source]) + run([ours, 'encode', source, mine, *setting]) + compare(theirs, mine, f'encode {label}') + print(f'{label}: {theirs.stat().st_size} bytes', flush=True) + # The WebM files: ours read by vpxdec, vpxenc's read by ours, the same frames out. + ours_webm, their_webm = work / f'{k}.ours.webm', work / f'{k}.vpxenc.webm' + run([ours, 'encode', source, ours_webm, *setting]) + run([vpxenc, '--codec=vp9', '--passes=1', *vpxenc_args(setting), '-q', '-o', their_webm, source]) + for webm, who in ((ours_webm, 'vpxwebm'), (their_webm, 'vpxenc')): + a, b = work / f'{k}.{who}.vpxdec.yuv', work / f'{k}.{who}.ours.yuv' + run([vpxdec, '--i420', '--rawvideo', '-o', a, webm]) + run([ours, 'decode', webm, b]) + compare(a, b, f'decode {who}\'s WebM, {label}') + if failures: + sys.exit(f'{len(failures)} of {checks} comparisons differ from upstream vpxenc/vpxdec') + print(f'{checks} comparisons: every IVF file byte-identical to upstream vpxenc 1.17.0, every WebM read by both ' + f'(vpxdec and mkvparser) to the same frames ({args.cc})') + + +if __name__ == '__main__': + main() diff --git a/tools/test_template.py b/tools/test_template.py new file mode 100644 index 0000000..81ea805 --- /dev/null +++ b/tools/test_template.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +"""Render an installed candidate's default mcpp template, then build and run it.""" +import argparse +import json +import os +from pathlib import Path +import shutil +import subprocess +import tempfile +import tomllib + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--mcpp", default="mcpp") + parser.add_argument("--namespace", help="index namespace for legacy manifests without package.namespace") + parser.add_argument("--toolchain", default="llvm@23.1.3") + parser.add_argument("--template", help="named template; omit for the package default") + parser.add_argument("--run-arg", action="append", default=[]) + parser.add_argument("--index", type=Path, help="candidate SDK/wrapper index for unpublished feature dependencies") + args = parser.parse_args() + root = Path(__file__).resolve().parents[1] + metadata = tomllib.loads((root / "mcpp.toml").read_text())["package"] + namespace = metadata.get("namespace", args.namespace) + assert namespace, "Supply the published index namespace for this legacy manifest" + name, version = metadata["name"], metadata["version"] + identity = namespace + "." + name + with tempfile.TemporaryDirectory(prefix=name + "-template-") as directory: + temporary = Path(directory) + home = temporary / "home" + package = home / "registry/data/xpkgs" / (namespace + "-x-" + name) / version / (name + "-" + version) + shutil.copytree(root, package, ignore=shutil.ignore_patterns( + ".git", "target", ".mcpp", ".xlings", ".candidate-index", "__pycache__", "mcpp.lock", "compile_commands.json", "mcpp-2026.*")) + index = home / "indices/mcpplibs/pkgs/mwrap" + index.mkdir(parents=True) + entry = '{url="https://example.invalid/candidate.tar.gz",sha256="fixture"}' + descriptor = ('package={spec="1",namespace=' + json.dumps(namespace) + ',name=' + json.dumps(name) + + ',description="Installed template fixture",type="package",mcpp="mcpp.toml",xpm={') + descriptor += ",".join(platform + "={[" + json.dumps(version) + "]=" + entry + "}" + for platform in ("linux", "macosx", "windows")) + "}}\n" + (index / (identity + ".lua")).write_text(descriptor) + env = dict(os.environ, MCPP_HOME=str(home)) + selector = identity + "@" + version + for command in ([args.mcpp, "new", "--list-templates", selector], + [args.mcpp, "new", "quickstart", "--template", selector + (":" + args.template if args.template else "")]): + print("+", " ".join(command), flush=True) + subprocess.run(command, cwd=temporary, env=env, check=True) + project = temporary / "quickstart" + manifest = project / "mcpp.toml" + text = manifest.read_text() + dependency = tomllib.loads(text)["dependencies"][identity] + options = {"version": dependency} if isinstance(dependency, str) else dependency + assert options["version"] == version, options + replacement = '{ path = ' + json.dumps(package.as_posix()) + if options.get("features"): + replacement += ', features = ' + json.dumps(options["features"]) + replacement += ' }' + lines = text.splitlines() + matches = [i for i, line in enumerate(lines) if line.startswith(json.dumps(identity) + " =")] + assert len(matches) == 1, "Expected exactly one qualified dependency from self.name" + lines[matches[0]] = json.dumps(identity) + " = " + replacement + manifest.write_text("\n".join(lines) + "\n") + if args.index: + candidate_index = args.index.resolve() + subprocess.run([os.sys.executable, str(candidate_index / "tests/prepare_mwrap_indices.py")], + cwd=candidate_index, check=True) + with manifest.open("a") as out: + out.write("\n[indices]\n") + for namespace in ("compat", "mcpplibs.mwrap"): + view = candidate_index / "tests/mwrap/.indices" / namespace + out.write(json.dumps(namespace) + ' = { path = ' + json.dumps(view.as_posix()) + ' }\n') + command = [args.mcpp, "build", "--toolchain", args.toolchain, "--strict"] + print("+", " ".join(command), flush=True) + subprocess.run(command, cwd=project, check=True) + command = [args.mcpp, "run", "--toolchain", args.toolchain] + if args.run_arg: + command += ["--", *args.run_arg] + print("+", " ".join(command), flush=True) + subprocess.run(command, cwd=project, check=True) + + +if __name__ == "__main__": + main() diff --git a/upstream/documents.json b/upstream/documents.json new file mode 100644 index 0000000..7576766 --- /dev/null +++ b/upstream/documents.json @@ -0,0 +1,55 @@ +{ + "upstream": { + "libvpx": { + "url": "https://github.com/webmproject/libvpx/archive/refs/tags/v1.17.0.tar.gz", + "sha256": "1020f184046187baa2985dbde38e0691f49c44088bca7a1842b0236c6081dc0a" + }, + "libwebm": { + "url": "https://github.com/webmproject/libwebm/archive/refs/tags/libwebm-1.0.0.32.tar.gz", + "sha256": "7fd5e085bda9f8031cf2ad2a1e52d9b7b29cba9c0b96ad2ce794ce89e4249eb8" + } + }, + "pruning": "Nothing of libvpx or libwebm is vendored: the codec is compat.libvpx 1.17.0 and the container compat.libwebm 1.0.0.32, built from the same tag archives. Their READMEs, licenses, patent grants and authors are retained here.", + "documents": [ + { + "path": "upstream/libvpx/AUTHORS", + "source": "https://github.com/webmproject/libvpx/blob/v1.17.0/AUTHORS", + "sha256": "8a98b560e5314713550e6d3ba2d8acf3ef54dfc870fc448bef08a7c57648a0bd" + }, + { + "path": "upstream/libvpx/LICENSE", + "source": "https://github.com/webmproject/libvpx/blob/v1.17.0/LICENSE", + "sha256": "8267348d5af1262c11d1a08de2f5afc77457755f1ac658627dd9acf71011d615" + }, + { + "path": "upstream/libvpx/PATENTS", + "source": "https://github.com/webmproject/libvpx/blob/v1.17.0/PATENTS", + "sha256": "cc3273e0694ea5896145e0677699b53471b03ea43021ddc50e7923fbb9f5023c" + }, + { + "path": "upstream/libvpx/README", + "source": "https://github.com/webmproject/libvpx/blob/v1.17.0/README", + "sha256": "0791de8b892beac7123a01b3c8fb734b7cdbfc9572085eee36197fcc02acc79d" + }, + { + "path": "upstream/libwebm/AUTHORS.TXT", + "source": "https://github.com/webmproject/libwebm/blob/libwebm-1.0.0.32/AUTHORS.TXT", + "sha256": "3edb5f5496bfff3d6d4a62fa75fe996b814920d0d6e348c2e65c589e7ac5d177" + }, + { + "path": "upstream/libwebm/LICENSE.TXT", + "source": "https://github.com/webmproject/libwebm/blob/libwebm-1.0.0.32/LICENSE.TXT", + "sha256": "5aec868f669e384a22372a4e8a1a6cd7d44c64cd451f960ca69cc170d1e13acf" + }, + { + "path": "upstream/libwebm/PATENTS.TXT", + "source": "https://github.com/webmproject/libwebm/blob/libwebm-1.0.0.32/PATENTS.TXT", + "sha256": "cc3273e0694ea5896145e0677699b53471b03ea43021ddc50e7923fbb9f5023c" + }, + { + "path": "upstream/libwebm/README.libwebm", + "source": "https://github.com/webmproject/libwebm/blob/libwebm-1.0.0.32/README.libwebm", + "sha256": "e66fb88e4ebbfc3a5771ea4cff60ccbf111f40ccd0ba958f91d96cddc8fbfaf8" + } + ] +} diff --git a/upstream/libvpx/AUTHORS b/upstream/libvpx/AUTHORS new file mode 100644 index 0000000..f01bca4 --- /dev/null +++ b/upstream/libvpx/AUTHORS @@ -0,0 +1,263 @@ +# This file is automatically generated from the git commit history +# by tools/gen_authors.sh. + +Aaron Watry +Abo Talib Mahfoodh +Adam B. Goode +Adrian Grange +Ahmad Sharif +Aidan Welch +Aleksey Vasenev +Alexander Potapenko +Alexander Voronov +Alexandra Hájková +Aℓex Converse +Alex Davicenko +Alexis Ballier +Alok Ahuja +Alpha Lam +A.Mahfoodh +Ami Fischman +Andoni Morales Alastruey +Andres Calderon Jaramillo +Andres Mejia +Andrew Lewis +Andrew Russell +Andrew Salkeld +Andrey Filipenkov +Angie Chen +Angie Chiang +Anton Venema +Anupam Pandey +Aron Rosenberg +Attila Nagy +Birk Magnussen +Bohan Li +Brian Foley +Brion Vibber +Casey Smalley +changjun.yang +Charles 'Buck' Krasic +Cheng Chen +Chen Wang +Cherma Rajan A +Chi Yo Tsai +chm +Chris Cunningham +Christian Duvivier +Chunbo Hua +Chun-Min Chang +Clement Courbet +CodeByMoriarty +Daniel Cheng +Daniele Castagna +Daniel Kang +Daniel Sommermann +Dan Zhu +Darryl Pogue +David Benjamin +Deb Mukherjee +Deepa K G +Diksha Singh +Dim Temp +Dmitry Kovalev +Dragan Mrdjan +Ed Baker +Ehsan Akhgari +Elliott Karpilovsky +Erik Niemeyer +Fabio Pedretti +Feng, Feifei +Florian Mayer +Frank Galligan +Fredrik Söderquist +Fritz Koenig +Fyodor Kyslov +Gabriel Marin +Gaute Strokkenes +George Steed +Gerda Zsejke More +Geza Lore +Ghislain MARY +Giuseppe Scrivano +Gordana Cmiljanovic +Gregor Jasny +Guillaume Martres +Guillermo Ballester Valor +Hang Nguyen +Hangyu Kuang +Hanno Böck +Han Shen +Hao Chen +Hari Limaye +Harish Mahendrakar +Henrik Lundin +Hien Ho +Hirokazu Honda +Hui Su +Ilya Kurdyukov +Ivan Krasin +Ivan Maltz +Jacek Caban +Jacky Chen +James Berry +James Touton +James Yu +James Zern +Jan Gerber +Jan-Ivar Bruaroey +Jan Kratochvil +Janne Salonen +Jean-Yves Avenard +Jeff Faust +Jeff Muizelaar +Jeff Petkau +Jeremy Dorfman +Jeremy Leconte +Jerome Jiang +Jia Jia +Jianhui Dai +Jian Zhou +Jim Bankoski +jinbo +Jin Bo +Jingning Han +Joel Fernandes +Joey Parrish +Johann +Johann Koenig +John Koleszar +Johnny Klonaris +John Stark +Jonathan Wright +Jon Kunkee +Jorge E. Moreira +Joshua Bleecher Snyder +Joshua Litt +Julia Robson +Justin Clift +Justin Lebar +Kaustubh Raste +Kexy Biscuit +KO Myung-Hun +Konstantinos Margaritis +Kyle Siefring +Lawrence Velázquez +L. E. Segovia +Linfeng Zhang +Lin Zheng +Liu Peng +Li Zhang +Lou Quillio +Luca Barbato +Luc Trudeau +Lu Wang +Makoto Kato +Mans Rullgard +Marco Paniconi +Mark Mentovai +Martin Ettl +Martin Storsjö +Matthew Heaney +Matthias Räncker +Michael Horowitz +Michael Kohler +Michał Janiszewski +Mike Frysinger +Mike Hommey +Mikhal Shemer +Mikko Koivisto +Min Chen +Minghai Shang +Min Ye +Mirko Bonadei +Moriyoshi Koizumi +Morton Jonuschat +Nathan E. Egge +Neeraj Gadgil +Neil Birkbeck +Nico Weber +Niveditha Rau +Parag Salasakar +Pascal Massimino +Patrik Westin +Paul Wilkins +Pavol Rusnak +Paweł Hajdan +Pengchong Jin +Peter Boström +Peter Collingbourne +Peter de Rivaz +Peter Kasting +Philip Jägenstedt +Philippe Antoine +Philipp Hancke +Priit Laes +Rafael Ávila de Espíndola +Rafaël Carré +Rafael de Lucena Valle +Rahul Chaudhry +Ralph Giles +Ram Mohan M +Ranjit Kumar Tulabandu +Raphael Kubo da Costa +Ravi Chaudhary +Ritu Baldwa +Rob Bradford +Ronald S. Bultje +Rui Ueyama +Sai Deng +Salome Thirot +Sami Pietilä +Sam James +Sarah Parker +Sasi Inguva +Scott Graham +Scott LaVarnway +Sean McGovern +Sergey Kolomenkin +Sergey Silkin +Sergey Ulanov +Shimon Doodkin +Shiyou Yin +Shubham Tandle +Shunyao Li +Sreerenj Balachandran +Stefan Holmer +Suman Sunkara +Supradeep T R +Sylvestre Ledru +Taekhyun Kim +Takanori MATSUURA +Tamar Levy +Tao Bai +Tero Rintaluoma +Thijs Vermeir +Tim Kopp +Timothy B. Terriberry +Tom Finegan +Tristan Matthews +Urvang Joshi +Venkatarama NG. Avadhani +Vignesh Venkatasubramanian +Vitaly Buka +Vlad Tsyrklevich +Wan-Teh Chang +Wonkap Jang +Xiahong Bao +Xiwei Gu +Yaowu Xu +Yi Luo +Yongseok Jeon +Yongzhe Wang +yuanhecai +Yue Chen +Yun Liu +Yunqing Wang +Yury Gitman +Zoe Liu +Zoltan Kuscsik +zuxy +Google Inc. +The Mozilla Foundation +The Xiph.Org Foundation diff --git a/upstream/libvpx/LICENSE b/upstream/libvpx/LICENSE new file mode 100644 index 0000000..1ce4434 --- /dev/null +++ b/upstream/libvpx/LICENSE @@ -0,0 +1,31 @@ +Copyright (c) 2010, The WebM Project authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Google, nor the WebM Project, nor the names + of its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/upstream/libvpx/PATENTS b/upstream/libvpx/PATENTS new file mode 100644 index 0000000..caedf60 --- /dev/null +++ b/upstream/libvpx/PATENTS @@ -0,0 +1,23 @@ +Additional IP Rights Grant (Patents) +------------------------------------ + +"These implementations" means the copyrightable works that implement the WebM +codecs distributed by Google as part of the WebM Project. + +Google hereby grants to you a perpetual, worldwide, non-exclusive, no-charge, +royalty-free, irrevocable (except as stated in this section) patent license to +make, have made, use, offer to sell, sell, import, transfer, and otherwise +run, modify and propagate the contents of these implementations of WebM, where +such license applies only to those patent claims, both currently owned by +Google and acquired in the future, licensable by Google that are necessarily +infringed by these implementations of WebM. This grant does not include claims +that would be infringed only as a consequence of further modification of these +implementations. If you or your agent or exclusive licensee institute or order +or agree to the institution of patent litigation or any other patent +enforcement activity against any entity (including a cross-claim or +counterclaim in a lawsuit) alleging that any of these implementations of WebM +or any code incorporated within any of these implementations of WebM +constitute direct or contributory patent infringement, or inducement of +patent infringement, then any patent rights granted to you under this License +for these implementations of WebM shall terminate as of the date such +litigation is filed. diff --git a/upstream/libvpx/README b/upstream/libvpx/README new file mode 100644 index 0000000..7bcce8f --- /dev/null +++ b/upstream/libvpx/README @@ -0,0 +1,241 @@ +Welcome to the WebM VP8/VP9 Codec SDK! + +COMPILING THE APPLICATIONS/LIBRARIES: + The build system used is similar to autotools. Building generally consists of + "configuring" with your desired build options, then using GNU make to build + the application. + + 1. Prerequisites + + * All x86 targets require the NASM[0] or Yasm[1] assembler be installed[2]. + * All Windows builds require that Cygwin[3] or MSYS2[4] be installed. + * Building the documentation requires Doxygen[5]. If you do not + have this package, the install-docs option will be disabled. + * Downloading the data for the unit tests requires curl[6] and sha1sum. + sha1sum is provided via the GNU coreutils, installed by default on + many *nix platforms, as well as MinGW and Cygwin. If coreutils is not + available, a compatible version of sha1sum can be built from + source[7]. These requirements are optional if not running the unit + tests. + + [0]: https://www.nasm.us/ + [1]: http://www.tortall.net/projects/yasm + [2]: For Visual Studio the base yasm binary (not vsyasm) should be in the + PATH for Visual Studio. For VS2017 it is sufficient to rename + yasm--.exe to yasm.exe and place it in: + Program Files (x86)/Microsoft Visual Studio/2017//Common7/Tools/ + The MSYS2 version of the yasm binary can also be used and avoids an + issue caused by a missing Visual C++ Redistributable install (Visual + Studio 2010, MSVCR100.dll). + [3]: http://www.cygwin.com + [4]: http://www.msys2.org/ + [5]: http://www.doxygen.org + [6]: http://curl.haxx.se + [7]: http://www.microbrew.org/tools/md5sha1sum/ + + 2. Out-of-tree builds + Out of tree builds are a supported method of building the application. For + an out of tree build, the source tree is kept separate from the object + files produced during compilation. For instance: + + $ mkdir build + $ cd build + $ ../libvpx/configure + $ make + + 3. Configuration options + The 'configure' script supports a number of options. The --help option can be + used to get a list of supported options: + $ ../libvpx/configure --help + + 4. Compiler analyzers + Compilers have added sanitizers which instrument binaries with information + about address calculation, memory usage, threading, undefined behavior, and + other common errors. To simplify building libvpx with some of these features + use tools/set_analyzer_env.sh before running configure. It will set the + compiler and necessary flags for building as well as environment variables + read by the analyzer when testing the binaries. + $ source ../libvpx/tools/set_analyzer_env.sh address + + 5. Cross development + For cross development, the most notable option is the --target option. The + most up-to-date list of supported targets can be found at the bottom of the + --help output of the configure script. As of this writing, the list of + available targets is: + + arm64-android-gcc + arm64-darwin-gcc + arm64-darwin20-gcc + arm64-darwin21-gcc + arm64-darwin22-gcc + arm64-darwin23-gcc + arm64-darwin24-gcc + arm64-darwin25-gcc + arm64-linux-gcc + arm64-win64-gcc + arm64-win64-vs15 + arm64-win64-vs16 + arm64-win64-vs16-clangcl + arm64-win64-vs17 + arm64-win64-vs17-clangcl + arm64-win64-vs18 + arm64-win64-vs18-clangcl + armv7-android-gcc + armv7-darwin-gcc + armv7-linux-rvct + armv7-linux-gcc + armv7-none-rvct + armv7-win32-gcc + armv7-win32-vs14 + armv7-win32-vs15 + armv7-win32-vs16 + armv7-win32-vs17 + armv7s-darwin-gcc + armv8-linux-gcc + loongarch32-linux-gcc + loongarch64-linux-gcc + mips32-linux-gcc + mips64-linux-gcc + ppc64le-linux-gcc + sparc-solaris-gcc + x86-android-gcc + x86-darwin8-gcc + x86-darwin8-icc + x86-darwin9-gcc + x86-darwin9-icc + x86-darwin10-gcc + x86-darwin11-gcc + x86-darwin12-gcc + x86-darwin13-gcc + x86-darwin14-gcc + x86-darwin15-gcc + x86-darwin16-gcc + x86-darwin17-gcc + x86-iphonesimulator-gcc + x86-linux-gcc + x86-linux-icc + x86-os2-gcc + x86-solaris-gcc + x86-win32-gcc + x86-win32-vs14 + x86-win32-vs15 + x86-win32-vs16 + x86-win32-vs17 + x86-win32-vs18 + x86_64-android-gcc + x86_64-darwin9-gcc + x86_64-darwin10-gcc + x86_64-darwin11-gcc + x86_64-darwin12-gcc + x86_64-darwin13-gcc + x86_64-darwin14-gcc + x86_64-darwin15-gcc + x86_64-darwin16-gcc + x86_64-darwin17-gcc + x86_64-darwin18-gcc + x86_64-darwin19-gcc + x86_64-darwin20-gcc + x86_64-darwin21-gcc + x86_64-darwin22-gcc + x86_64-darwin23-gcc + x86_64-darwin24-gcc + x86_64-darwin25-gcc + x86_64-iphonesimulator-gcc + x86_64-linux-gcc + x86_64-linux-icc + x86_64-solaris-gcc + x86_64-win64-gcc + x86_64-win64-vs14 + x86_64-win64-vs15 + x86_64-win64-vs16 + x86_64-win64-vs17 + x86_64-win64-vs18 + generic-gnu + + The generic-gnu target, in conjunction with the CROSS environment variable, + can be used to cross compile architectures that aren't explicitly listed, if + the toolchain is a cross GNU (gcc/binutils) toolchain. Other POSIX toolchains + will likely work as well. For instance, to build using the mipsel-linux-uclibc + toolchain, the following command could be used (note, POSIX SH syntax, adapt + to your shell as necessary): + + $ CROSS=mipsel-linux-uclibc- ../libvpx/configure + + In addition, the executables to be invoked can be overridden by specifying the + environment variables: AR, AS, CC, CXX, LD, STRIP. Additional flags can be + passed to these executables with ASFLAGS, CFLAGS, CXXFLAGS, and LDFLAGS. + + 6. Configuration errors + If the configuration step fails, the first step is to look in the error log. + This defaults to config.log. This should give a good indication of what went + wrong. If not, contact us for support. + +VP8/VP9 TEST VECTORS: + The test vectors can be downloaded and verified using the build system after + running configure. To specify an alternate directory the + LIBVPX_TEST_DATA_PATH environment variable can be used. + + $ ./configure --enable-unit-tests + $ LIBVPX_TEST_DATA_PATH=../libvpx-test-data make testdata + +CODE STYLE: + The coding style used by this project is enforced with clang-format using the + configuration contained in the .clang-format file in the root of the + repository. + + Before pushing changes for review you can format your code with: + # Apply clang-format to modified .c, .h and .cc files + $ clang-format -i --style=file \ + $(git diff --name-only --diff-filter=ACMR '*.[hc]' '*.cc') + + Check the .clang-format file for the version used to generate it if there is + any difference between your local formatting and the review system. + + See also: http://clang.llvm.org/docs/ClangFormat.html + +PROFILE GUIDED OPTIMIZATION (PGO) + Profile Guided Optimization can be enabled for Clang builds using the + commands: + + $ export CC=clang + $ export CXX=clang++ + $ ../libvpx/configure --enable-profile + $ make + + Generate one or multiple PGO profile files by running vpxdec or vpxenc. For + example: + + $ ./vpxdec ../vpx/out_ful/vp90-2-sintel_1280x546_tile_1x4_1257kbps.webm \ + -o - > /dev/null + + To convert and merge the raw profile files, use the llvm-profdata tool: + + $ llvm-profdata merge -o perf.profdata default_8382761441159425451_0.profraw + + Then, rebuild the project with the new profile file: + + $ make clean + $ ../libvpx/configure --use-profile=perf.profdata + $ make + + Note: Always use the llvm-profdata from the toolchain that is used for + compiling the PGO-enabled binary. + + To observe the improvements from a PGO-enabled build, enable and compare the + list of failed optimizations by using the -Rpass-missed compiler flag. For + example, to list the failed loop vectorizations: + + $ ../libvpx/configure --use-profile=perf.profdata \ + --extra-cflags=-Rpass-missed=loop-vectorize + + For guidance on utilizing PGO files to identify potential optimization + opportunities, see: tools/README.pgo.md + +SUPPORT + This library is an open source project supported by its community. Please + email webm-discuss@webmproject.org for help. + +BUG REPORTS + Bug reports can be filed in the libvpx component of the issue tracker: + https://issues.webmproject.org/issues/new?component=1618984&template=2023833. + For security reports, select 'Security report' from the Template dropdown. diff --git a/upstream/libwebm/AUTHORS.TXT b/upstream/libwebm/AUTHORS.TXT new file mode 100644 index 0000000..59b648c --- /dev/null +++ b/upstream/libwebm/AUTHORS.TXT @@ -0,0 +1,5 @@ +# Names should be added to this file like so: +# Name or Organization + +Google Inc. +Elijah Cirioli diff --git a/upstream/libwebm/LICENSE.TXT b/upstream/libwebm/LICENSE.TXT new file mode 100644 index 0000000..7a6f995 --- /dev/null +++ b/upstream/libwebm/LICENSE.TXT @@ -0,0 +1,30 @@ +Copyright (c) 2010, Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Google nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/upstream/libwebm/PATENTS.TXT b/upstream/libwebm/PATENTS.TXT new file mode 100644 index 0000000..caedf60 --- /dev/null +++ b/upstream/libwebm/PATENTS.TXT @@ -0,0 +1,23 @@ +Additional IP Rights Grant (Patents) +------------------------------------ + +"These implementations" means the copyrightable works that implement the WebM +codecs distributed by Google as part of the WebM Project. + +Google hereby grants to you a perpetual, worldwide, non-exclusive, no-charge, +royalty-free, irrevocable (except as stated in this section) patent license to +make, have made, use, offer to sell, sell, import, transfer, and otherwise +run, modify and propagate the contents of these implementations of WebM, where +such license applies only to those patent claims, both currently owned by +Google and acquired in the future, licensable by Google that are necessarily +infringed by these implementations of WebM. This grant does not include claims +that would be infringed only as a consequence of further modification of these +implementations. If you or your agent or exclusive licensee institute or order +or agree to the institution of patent litigation or any other patent +enforcement activity against any entity (including a cross-claim or +counterclaim in a lawsuit) alleging that any of these implementations of WebM +or any code incorporated within any of these implementations of WebM +constitute direct or contributory patent infringement, or inducement of +patent infringement, then any patent rights granted to you under this License +for these implementations of WebM shall terminate as of the date such +litigation is filed. diff --git a/upstream/libwebm/README.libwebm b/upstream/libwebm/README.libwebm new file mode 100644 index 0000000..64c8111 --- /dev/null +++ b/upstream/libwebm/README.libwebm @@ -0,0 +1,148 @@ +Building Libwebm + +To build libwebm you must first create project files. To do this run cmake +and pass it the path to your libwebm repo. + +Makefile.unix can be used as a fallback on systems that cmake does not +support. + + +CMake Basics + +To generate project/make files for the default toolchain on your system simply +run cmake with the path to the libwebm repo: + +$ cmake path/to/libwebm + +On Windows the above command will produce Visual Studio project files for the +newest Visual Studio detected on the system. On Mac OS X and Linux systems, the +above command will produce a makefile. + +To control what types of projects are generated the -G parameter is added to +the cmake command line. This argument must be followed by the name of a +generator. Running cmake with the --help argument will list the available +generators for your system. + +On Mac OS X you would run the following command to generate Xcode projects: + +$ cmake path/to/libwebm -G Xcode + +On a Windows box you would run the following command to generate Visual Studio +2013 projects: + +$ cmake path/to/libwebm -G "Visual Studio 12" + +To generate 64-bit Windows Visual Studio 2013 projects: + +$ cmake path/to/libwebm "Visual Studio 12 Win64" + + +CMake Makefiles: Debugging and Optimization + +Unlike Visual Studio and Xcode projects, the build configuration for make builds +is controlled when you run cmake. The following examples demonstrate various +build configurations. + +Omitting the build type produces makefiles that use build flags containing +neither optimization nor debug flags: +$ cmake path/to/libwebm + +A makefile using release (optimized) flags is produced like this: +$ cmake path/to/libwebm -DCMAKE_BUILD_TYPE=release + +A release build with debug info can be produced as well: +$ cmake path/to/libwebm -DCMAKE_BUILD_TYPE=relwithdebinfo + +And your standard debug build will be produced using: +$ cmake path/to/libwebm -DCMAKE_BUILD_TYPE=debug + + +Tests + +To enable libwebm tests add -DENABLE_TESTS=ON CMake generation command line. For +example: + +$ cmake path/to/libwebm -G Xcode -DENABLE_TESTS=ON + +Libwebm tests depend on googletest. By default googletest is expected to be a +sibling directory of the Libwebm repository. To change that, update your CMake +command to be similar to the following: + +$ cmake path/to/libwebm -G Xcode -DENABLE_TESTS=ON \ + -DGTEST_SRC_DIR=/path/to/googletest + +The tests rely upon the LIBWEBM_TEST_DATA_PATH environment variable to locate +test input. The following example demonstrates running the muxer tests from the +build directory: + +$ LIBWEBM_TEST_DATA_PATH=path/to/libwebm/testing/testdata ./mkvmuxer_tests + +Note: Libwebm Googletest integration was built with googletest from + https://github.com/google/googletest.git at git revision + ddb8012eb48bc203aa93dcc2b22c1db516302b29. + + +CMake Include-what-you-use integration + +Include-what-you-use is an analysis tool that helps ensure libwebm includes the +C/C++ header files actually in use. To enable the integration support +ENABLE_IWYU must be turned on at cmake run time: + +$ cmake path/to/libwebm -G "Unix Makefiles" -DENABLE_IWYU=ON + +This adds the iwyu target to the build. To run include-what-you-use: + +$ make iwyu + +The following requirements must be met for ENABLE_IWYU to enable the iwyu +target: + +1. include-what-you-use and iwyu_tool.py must be in your PATH. +2. A python interpreter must be on the system and available to CMake. + +The values of the following variables are used to determine if the requirements +have been met. Values to the right of the equals sign are what a successful run +might look like: + iwyu_path=/path/to/iwyu_tool.py + iwyu_tool_path=/path/to/include-what-you-use + PYTHONINTERP_FOUND=TRUE + +An empty PYTHONINTERP_FOUND, or iwyu_path/iwyu_tool_path suffixed with NOTFOUND +are failures. + +For Include-what-you-use setup instructions, see: +https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/InstructionsForUsers.md + +If, when building the iwyu target, compile errors reporting failures loading +standard include files occur, one solution can be found here: +https://github.com/include-what-you-use/include-what-you-use/issues/100 + + +CMake cross compile +To cross compile libwebm for Windows using mingw-w64 run cmake with the +following arguments: + +$ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/libwebm/build/mingw-w64_toolchain.cmake \ + path/to/libwebm + +Note1: As of this writing googletest will not build via mingw-w64 without +disabling pthreads. +googletest hash: d225acc90bc3a8c420a9bcd1f033033c1ccd7fe0 + +To build with tests when using mingw-w64 use the following arguments when +running CMake: + +$ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/libwebm/build/mingw-w64_toolchain.cmake \ + -DENABLE_TESTS=ON -Dgtest_disable_pthreads=ON path/to/libwebm + +Note2: i686-w64-mingw32 is the default compiler. This can be controlled using +the MINGW_PREFIX variable: + +$ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/libwebm/build/mingw-w64_toolchain.cmake \ + -DMINGW_PREFIX=x86_64-w64-mingw32 path/to/libwebm + +Bug reports + +Bug reports can be filed in the libwebm issue tracker: +https://issues.webmproject.org/. +For security reports, select 'Security report' from the Template dropdown.