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.