├── .github ├── dependabot.yml └── workflows │ └── dav1d.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── dav1d-sys ├── .gitignore ├── Cargo.toml ├── LICENSE ├── build.rs └── src │ └── lib.rs ├── src └── lib.rs ├── test-420-12.ivf ├── test-420-8.ivf └── tools ├── Cargo.toml └── src └── main.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | ignore: 9 | - dependency-name: system-deps 10 | versions: 11 | - 3.0.0 12 | -------------------------------------------------------------------------------- /.github/workflows/dav1d.yml: -------------------------------------------------------------------------------- 1 | name: dav1d 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | 7 | linux-tests: 8 | 9 | runs-on: ubuntu-22.04 10 | 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - name: Install nasm 15 | uses: ilammy/setup-nasm@v1 16 | 17 | - name: Install Rust stable 18 | uses: dtolnay/rust-toolchain@stable 19 | with: 20 | toolchain: stable 21 | 22 | - name: Install Python 3.9 23 | uses: actions/setup-python@v4 24 | with: 25 | python-version: '3.9' 26 | 27 | - name: Install pip packages 28 | run: | 29 | pip install -U pip 30 | pip install -U wheel setuptools 31 | pip install -U meson ninja 32 | 33 | - name: Build dav1d 34 | env: 35 | DAV1D_DIR: dav1d_dir 36 | LIB_PATH: lib/x86_64-linux-gnu 37 | run: | 38 | git clone --branch 1.5.0 --depth 1 https://code.videolan.org/videolan/dav1d.git 39 | cd dav1d 40 | meson build -Dprefix=$HOME/$DAV1D_DIR -Denable_tools=false -Denable_examples=false --buildtype release 41 | ninja -C build 42 | ninja -C build install 43 | echo "PKG_CONFIG_PATH=$HOME/$DAV1D_DIR/$LIB_PATH/pkgconfig" >> $GITHUB_ENV 44 | echo "LD_LIBRARY_PATH=$HOME/$DAV1D_DIR/$LIB_PATH" >> $GITHUB_ENV 45 | 46 | - name: Run tests 47 | run: | 48 | cargo test --workspace --all-features 49 | 50 | windows-tests-msvc: 51 | 52 | runs-on: windows-latest 53 | 54 | steps: 55 | - uses: actions/checkout@v3 56 | - uses: ilammy/msvc-dev-cmd@v1 57 | 58 | - name: Install nasm 59 | uses: ilammy/setup-nasm@v1 60 | 61 | - name: Install Rust stable MSVC 62 | uses: dtolnay/rust-toolchain@stable 63 | with: 64 | toolchain: stable-x86_64-pc-windows-msvc 65 | 66 | - name: Install Python 3.9 67 | uses: actions/setup-python@v4 68 | with: 69 | python-version: '3.9' 70 | 71 | - name: Install pip packages 72 | run: | 73 | pip install -U pip 74 | pip install -U wheel setuptools 75 | pip install -U meson ninja 76 | 77 | - name: Setting up environment 78 | shell: bash 79 | run: | 80 | echo "PKG_CONFIG=c:\build\bin\pkg-config.exe" >> $GITHUB_ENV 81 | echo "PKG_CONFIG_PATH=C:\build\lib\pkgconfig" >> $GITHUB_ENV 82 | echo "C:\build\bin" >> $GITHUB_PATH 83 | 84 | - name: Build pkg-config 85 | run: | 86 | git clone --branch meson-glib-subproject --depth 1 https://gitlab.freedesktop.org/tpm/pkg-config.git 87 | cd pkg-config 88 | meson build -Dprefix=C:\build --buildtype release 89 | ninja -C build 90 | ninja -C build install 91 | 92 | - name: Build dav1d 93 | run: | 94 | git clone --branch 1.5.0 --depth 1 https://code.videolan.org/videolan/dav1d.git 95 | cd dav1d 96 | meson build -Dprefix=C:\build -Denable_tools=false -Denable_examples=false --buildtype release 97 | ninja -C build 98 | ninja -C build install 99 | 100 | - name: Run tests 101 | run: | 102 | cargo test --workspace --all-features 103 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Luca Barbato "] 3 | description = "libdav1d bindings" 4 | edition = "2021" 5 | keywords = ["dav1d-rs","av1"] 6 | license = "MIT" 7 | name = "dav1d" 8 | readme = "README.md" 9 | repository = "https://github.com/rust-av/dav1d-rs" 10 | version = "0.11.0" 11 | rust-version = "1.78" 12 | 13 | [dependencies] 14 | bitflags = "2" 15 | dav1d-sys = { version = "0.8.2", path = "dav1d-sys" } 16 | av-data = "0.4.2" 17 | static_assertions = "1" 18 | 19 | [dev-dependencies] 20 | bitstream-io = "4.0" 21 | 22 | [features] 23 | 24 | [workspace] 25 | members = ["dav1d-sys", "tools"] 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Luca Barbato 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libdav1d bindings [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![Actions Status](https://github.com/rust-av/dav1d-rs/workflows/dav1d/badge.svg)](https://github.com/rust-av/dav1d-rs/actions) 2 | 3 | It is a simple FFI binding and safe abstraction over [dav1d][1]. 4 | 5 | 6 | ## Building 7 | 8 | To build the code, always have a look at [CI](https://github.com/rust-av/dav1d-rs/blob/master/.github/workflows/dav1d.yml) to install the necessary dependencies on all 9 | supported operating systems. 10 | 11 | ### Overriding the dav1d library 12 | 13 | The bindings use [system-deps](https://docs.rs/system-deps) to find dav1d. You may override the `PKG_CONFIG_PATH` or 14 | direcly set the env vars `SYSTEM_DEPS_DAV1D_SEARCH_NATIVE` and/or `SYSTEM_DEPS_DAV1D_LIB`. 15 | 16 | ## Building with vcpkg for Windows x64 17 | 18 | To build with [vcpkg](https://vcpkg.io/en/index.html), you need to follow these 19 | steps: 20 | 21 | 1. Install `pkg-config` through [chocolatey](https://chocolatey.org/) 22 | 23 | choco install pkgconfiglite 24 | 25 | 2. Install `dav1d` 26 | 27 | vcpkg install dav1d:x64-windows 28 | 29 | 3. Add to the `PKG_CONFIG_PATH` environment variable the path `$VCPKG_INSTALLATION_ROOT\installed\x64-windows\lib\pkgconfig` 30 | 31 | 4. Build code 32 | 33 | cargo build --workspace 34 | 35 | To speed up the computation, you can build your packages only in `Release` mode 36 | adding the `set(VCPKG_BUILD_TYPE release)` line to the 37 | `$VCPKG_INSTALLATION_ROOT\triplets\x64-windows.cmake` file. 38 | 39 | Building for Windows x86 is the same, just replace `x64` with `x86` in the 40 | steps above. 41 | 42 | ## Supported versions 43 | 44 | The bindings require dav1d>=1.3.0 ( Might not work for >1.5.0 ) 45 | 46 | ## TODO 47 | - [x] Simple bindings 48 | - [x] Safe abstraction 49 | - [ ] Examples 50 | 51 | [1]: https://code.videolan.org/videolan/dav1d 52 | -------------------------------------------------------------------------------- /dav1d-sys/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /dav1d-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dav1d-sys" 3 | version = "0.8.3" 4 | authors = ["Luca Barbato "] 5 | license = "MIT" 6 | description = "FFI bindings to dav1d" 7 | repository = "https://github.com/rust-av/dav1d-rs" 8 | edition = "2021" 9 | readme = "../README.md" 10 | rust-version = "1.78" 11 | 12 | build = "build.rs" 13 | 14 | [build-dependencies] 15 | system-deps = "7" 16 | 17 | [dependencies] 18 | libc = "0.2" 19 | 20 | [features] 21 | 22 | [package.metadata.system-deps.dav1d] 23 | name = "dav1d" 24 | version = "1.3.0" 25 | -------------------------------------------------------------------------------- /dav1d-sys/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Luca Barbato 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dav1d-sys/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::PathBuf; 3 | 4 | mod build { 5 | use super::*; 6 | use std::path::Path; 7 | use std::process::{Command, Stdio}; 8 | 9 | const REPO: &str = "https://code.videolan.org/videolan/dav1d.git"; 10 | const TAG: &str = "1.5.0"; 11 | 12 | macro_rules! runner { 13 | ($cmd:expr, $($arg:expr),*) => { 14 | Command::new($cmd) 15 | $(.arg($arg))* 16 | .stderr(Stdio::inherit()) 17 | .stdout(Stdio::inherit()) 18 | .output() 19 | .expect(concat!($cmd, " failed")); 20 | 21 | }; 22 | } 23 | 24 | pub fn build_from_src( 25 | lib: &str, 26 | _version: &str, 27 | ) -> Result { 28 | let build_dir = "build"; 29 | let release_dir = "release"; 30 | 31 | let source = PathBuf::from(env::var("OUT_DIR").unwrap()).join("dav1d"); 32 | let build_path = source.join(build_dir); 33 | let release_path = source.join(release_dir); 34 | 35 | if !Path::new(&source.join(".git")).exists() { 36 | runner!("git", "clone", "--depth", "1", "-b", TAG, REPO, &source); 37 | } else { 38 | runner!( 39 | "git", 40 | "-C", 41 | source.to_str().unwrap(), 42 | "fetch", 43 | "--depth", 44 | "1", 45 | "origin", 46 | TAG 47 | ); 48 | runner!( 49 | "git", 50 | "-C", 51 | source.to_str().unwrap(), 52 | "checkout", 53 | "FETCH_HEAD" 54 | ); 55 | } 56 | 57 | runner!( 58 | "meson", 59 | "setup", 60 | "-Ddefault_library=static", 61 | "--prefix", 62 | release_path.to_str().unwrap(), 63 | build_path.to_str().unwrap(), 64 | source.to_str().unwrap() 65 | ); 66 | runner!("ninja", "-C", build_path.to_str().unwrap()); 67 | runner!("meson", "install", "-C", build_path.to_str().unwrap()); 68 | 69 | let pkg_dir = build_path.join("meson-private"); 70 | system_deps::Library::from_internal_pkg_config(pkg_dir, lib, TAG) 71 | } 72 | } 73 | 74 | fn main() { 75 | if std::env::var("DOCS_RS").is_ok() { 76 | return; 77 | } 78 | 79 | system_deps::Config::new() 80 | .add_build_internal("dav1d", build::build_from_src) 81 | .probe() 82 | .unwrap(); 83 | } 84 | -------------------------------------------------------------------------------- /dav1d-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | // TODO: Use core::ffi once we depend on Rust >= 1.64 2 | use std::os::raw::{c_char, c_int, c_uint, c_void}; 3 | 4 | pub const DAV1D_OBU_SEQ_HDR: Dav1dObuType = 1; 5 | pub const DAV1D_OBU_TD: Dav1dObuType = 2; 6 | pub const DAV1D_OBU_FRAME_HDR: Dav1dObuType = 3; 7 | pub const DAV1D_OBU_TILE_GRP: Dav1dObuType = 4; 8 | pub const DAV1D_OBU_METADATA: Dav1dObuType = 5; 9 | pub const DAV1D_OBU_FRAME: Dav1dObuType = 6; 10 | pub const DAV1D_OBU_REDUNDANT_FRAME_HDR: Dav1dObuType = 7; 11 | pub const DAV1D_OBU_PADDING: Dav1dObuType = 15; 12 | pub type Dav1dObuType = c_uint; 13 | 14 | pub const DAV1D_TX_4X4_ONLY: Dav1dTxfmMode = 0; 15 | pub const DAV1D_TX_LARGEST: Dav1dTxfmMode = 1; 16 | pub const DAV1D_TX_SWITCHABLE: Dav1dTxfmMode = 2; 17 | pub const DAV1D_N_TX_MODES: Dav1dTxfmMode = 3; 18 | pub type Dav1dTxfmMode = c_uint; 19 | 20 | pub const DAV1D_FILTER_8TAP_REGULAR: Dav1dFilterMode = 0; 21 | pub const DAV1D_FILTER_8TAP_SMOOTH: Dav1dFilterMode = 1; 22 | pub const DAV1D_FILTER_8TAP_SHARP: Dav1dFilterMode = 2; 23 | pub const DAV1D_N_SWITCHABLE_FILTERS: Dav1dFilterMode = 3; 24 | pub const DAV1D_FILTER_BILINEAR: Dav1dFilterMode = 3; 25 | pub const DAV1D_N_FILTERS: Dav1dFilterMode = 4; 26 | pub const DAV1D_FILTER_SWITCHABLE: Dav1dFilterMode = 4; 27 | pub type Dav1dFilterMode = c_uint; 28 | 29 | pub const DAV1D_OFF: Dav1dAdaptiveBoolean = 0; 30 | pub const DAV1D_ON: Dav1dAdaptiveBoolean = 1; 31 | pub const DAV1D_ADAPTIVE: Dav1dAdaptiveBoolean = 2; 32 | pub type Dav1dAdaptiveBoolean = c_uint; 33 | 34 | pub const DAV1D_RESTORATION_NONE: Dav1dRestorationType = 0; 35 | pub const DAV1D_RESTORATION_SWITCHABLE: Dav1dRestorationType = 1; 36 | pub const DAV1D_RESTORATION_WIENER: Dav1dRestorationType = 2; 37 | pub const DAV1D_RESTORATION_SGRPROJ: Dav1dRestorationType = 3; 38 | pub type Dav1dRestorationType = c_uint; 39 | 40 | pub const DAV1D_WM_TYPE_IDENTITY: Dav1dWarpedMotionType = 0; 41 | pub const DAV1D_WM_TYPE_TRANSLATION: Dav1dWarpedMotionType = 1; 42 | pub const DAV1D_WM_TYPE_ROT_ZOOM: Dav1dWarpedMotionType = 2; 43 | pub const DAV1D_WM_TYPE_AFFINE: Dav1dWarpedMotionType = 3; 44 | pub type Dav1dWarpedMotionType = c_uint; 45 | 46 | pub const DAV1D_PIXEL_LAYOUT_I400: Dav1dPixelLayout = 0; 47 | pub const DAV1D_PIXEL_LAYOUT_I420: Dav1dPixelLayout = 1; 48 | pub const DAV1D_PIXEL_LAYOUT_I422: Dav1dPixelLayout = 2; 49 | pub const DAV1D_PIXEL_LAYOUT_I444: Dav1dPixelLayout = 3; 50 | pub type Dav1dPixelLayout = c_uint; 51 | 52 | pub const DAV1D_FRAME_TYPE_KEY: Dav1dFrameType = 0; 53 | pub const DAV1D_FRAME_TYPE_INTER: Dav1dFrameType = 1; 54 | pub const DAV1D_FRAME_TYPE_INTRA: Dav1dFrameType = 2; 55 | pub const DAV1D_FRAME_TYPE_SWITCH: Dav1dFrameType = 3; 56 | pub type Dav1dFrameType = c_uint; 57 | 58 | pub const DAV1D_COLOR_PRI_BT709: Dav1dColorPrimaries = 1; 59 | pub const DAV1D_COLOR_PRI_UNKNOWN: Dav1dColorPrimaries = 2; 60 | pub const DAV1D_COLOR_PRI_BT470M: Dav1dColorPrimaries = 4; 61 | pub const DAV1D_COLOR_PRI_BT470BG: Dav1dColorPrimaries = 5; 62 | pub const DAV1D_COLOR_PRI_BT601: Dav1dColorPrimaries = 6; 63 | pub const DAV1D_COLOR_PRI_SMPTE240: Dav1dColorPrimaries = 7; 64 | pub const DAV1D_COLOR_PRI_FILM: Dav1dColorPrimaries = 8; 65 | pub const DAV1D_COLOR_PRI_BT2020: Dav1dColorPrimaries = 9; 66 | pub const DAV1D_COLOR_PRI_XYZ: Dav1dColorPrimaries = 10; 67 | pub const DAV1D_COLOR_PRI_SMPTE431: Dav1dColorPrimaries = 11; 68 | pub const DAV1D_COLOR_PRI_SMPTE432: Dav1dColorPrimaries = 12; 69 | pub const DAV1D_COLOR_PRI_EBU3213: Dav1dColorPrimaries = 22; 70 | pub const DAV1D_COLOR_PRI_RESERVED: Dav1dColorPrimaries = 255; 71 | pub type Dav1dColorPrimaries = c_uint; 72 | 73 | pub const DAV1D_TRC_BT709: Dav1dTransferCharacteristics = 1; 74 | pub const DAV1D_TRC_UNKNOWN: Dav1dTransferCharacteristics = 2; 75 | pub const DAV1D_TRC_BT470M: Dav1dTransferCharacteristics = 4; 76 | pub const DAV1D_TRC_BT470BG: Dav1dTransferCharacteristics = 5; 77 | pub const DAV1D_TRC_BT601: Dav1dTransferCharacteristics = 6; 78 | pub const DAV1D_TRC_SMPTE240: Dav1dTransferCharacteristics = 7; 79 | pub const DAV1D_TRC_LINEAR: Dav1dTransferCharacteristics = 8; 80 | pub const DAV1D_TRC_LOG100: Dav1dTransferCharacteristics = 9; 81 | pub const DAV1D_TRC_LOG100_SQRT10: Dav1dTransferCharacteristics = 10; 82 | pub const DAV1D_TRC_IEC61966: Dav1dTransferCharacteristics = 11; 83 | pub const DAV1D_TRC_BT1361: Dav1dTransferCharacteristics = 12; 84 | pub const DAV1D_TRC_SRGB: Dav1dTransferCharacteristics = 13; 85 | pub const DAV1D_TRC_BT2020_10BIT: Dav1dTransferCharacteristics = 14; 86 | pub const DAV1D_TRC_BT2020_12BIT: Dav1dTransferCharacteristics = 15; 87 | pub const DAV1D_TRC_SMPTE2084: Dav1dTransferCharacteristics = 16; 88 | pub const DAV1D_TRC_SMPTE428: Dav1dTransferCharacteristics = 17; 89 | pub const DAV1D_TRC_HLG: Dav1dTransferCharacteristics = 18; 90 | pub const DAV1D_TRC_RESERVED: Dav1dTransferCharacteristics = 255; 91 | pub type Dav1dTransferCharacteristics = c_uint; 92 | 93 | pub const DAV1D_MC_IDENTITY: Dav1dMatrixCoefficients = 0; 94 | pub const DAV1D_MC_BT709: Dav1dMatrixCoefficients = 1; 95 | pub const DAV1D_MC_UNKNOWN: Dav1dMatrixCoefficients = 2; 96 | pub const DAV1D_MC_FCC: Dav1dMatrixCoefficients = 4; 97 | pub const DAV1D_MC_BT470BG: Dav1dMatrixCoefficients = 5; 98 | pub const DAV1D_MC_BT601: Dav1dMatrixCoefficients = 6; 99 | pub const DAV1D_MC_SMPTE240: Dav1dMatrixCoefficients = 7; 100 | pub const DAV1D_MC_SMPTE_YCGCO: Dav1dMatrixCoefficients = 8; 101 | pub const DAV1D_MC_BT2020_NCL: Dav1dMatrixCoefficients = 9; 102 | pub const DAV1D_MC_BT2020_CL: Dav1dMatrixCoefficients = 10; 103 | pub const DAV1D_MC_SMPTE2085: Dav1dMatrixCoefficients = 11; 104 | pub const DAV1D_MC_CHROMAT_NCL: Dav1dMatrixCoefficients = 12; 105 | pub const DAV1D_MC_CHROMAT_CL: Dav1dMatrixCoefficients = 13; 106 | pub const DAV1D_MC_ICTCP: Dav1dMatrixCoefficients = 14; 107 | pub const DAV1D_MC_RESERVED: Dav1dMatrixCoefficients = 255; 108 | pub type Dav1dMatrixCoefficients = c_uint; 109 | 110 | pub const DAV1D_CHR_UNKNOWN: Dav1dChromaSamplePosition = 0; 111 | pub const DAV1D_CHR_VERTICAL: Dav1dChromaSamplePosition = 1; 112 | pub const DAV1D_CHR_COLOCATED: Dav1dChromaSamplePosition = 2; 113 | pub type Dav1dChromaSamplePosition = c_uint; 114 | 115 | pub const DAV1D_INLOOPFILTER_NONE: Dav1dInloopFilterType = 0; 116 | pub const DAV1D_INLOOPFILTER_DEBLOCK: Dav1dInloopFilterType = 1; 117 | pub const DAV1D_INLOOPFILTER_CDEF: Dav1dInloopFilterType = 2; 118 | pub const DAV1D_INLOOPFILTER_RESTORATION: Dav1dInloopFilterType = 4; 119 | pub const DAV1D_INLOOPFILTER_ALL: Dav1dInloopFilterType = 7; 120 | pub type Dav1dInloopFilterType = c_uint; 121 | 122 | pub const DAV1D_EVENT_FLAG_NEW_SEQUENCE: Dav1dEventFlags = 1; 123 | pub const DAV1D_EVENT_FLAG_NEW_OP_PARAMS_INFO: Dav1dEventFlags = 2; 124 | pub type Dav1dEventFlags = c_uint; 125 | 126 | pub const DAV1D_MAX_THREADS: c_int = 256; 127 | pub const DAV1D_MAX_FRAME_DELAY: c_int = 256; 128 | 129 | pub const DAV1D_MAX_CDEF_STRENGTHS: usize = 8; 130 | pub const DAV1D_MAX_OPERATING_POINTS: usize = 32; 131 | pub const DAV1D_MAX_TILE_COLS: usize = 64; 132 | pub const DAV1D_MAX_TILE_ROWS: usize = 64; 133 | pub const DAV1D_MAX_SEGMENTS: usize = 8; 134 | pub const DAV1D_NUM_REF_FRAMES: usize = 8; 135 | pub const DAV1D_PRIMARY_REF_NONE: usize = 7; 136 | pub const DAV1D_REFS_PER_FRAME: usize = 7; 137 | pub const DAV1D_TOTAL_REFS_PER_FRAME: usize = DAV1D_REFS_PER_FRAME + 1; 138 | 139 | pub const DAV1D_DECODEFRAMETYPE_ALL: Dav1dDecodeFrameType = 0; 140 | pub const DAV1D_DECODEFRAMETYPE_REFERENCE: Dav1dDecodeFrameType = 1; 141 | pub const DAV1D_DECODEFRAMETYPE_INTRA: Dav1dDecodeFrameType = 2; 142 | pub const DAV1D_DECODEFRAMETYPE_KEY: Dav1dDecodeFrameType = 3; 143 | pub type Dav1dDecodeFrameType = c_uint; 144 | 145 | // Conversion of the C DAV1D_ERR macro 146 | pub const fn dav1d_err(errno: c_int) -> c_int { 147 | if libc::EPERM < 0 { 148 | errno 149 | } else { 150 | -errno 151 | } 152 | } 153 | 154 | pub const DAV1D_ERR_AGAIN: c_int = dav1d_err(libc::EAGAIN); 155 | pub const DAV1D_ERR_INVAL: c_int = dav1d_err(libc::EINVAL); 156 | pub const DAV1D_ERR_NOMEM: c_int = dav1d_err(libc::ENOMEM); 157 | pub const DAV1D_ERR_NOPROTOOPT: c_int = dav1d_err(libc::ENOPROTOOPT); 158 | 159 | #[repr(C)] 160 | #[derive(Debug, Copy, Clone)] 161 | pub struct Dav1dUserData { 162 | pub data: *const u8, 163 | pub ref_: *mut Dav1dRef, 164 | } 165 | 166 | #[repr(C)] 167 | #[derive(Debug, Copy, Clone)] 168 | pub struct Dav1dDataProps { 169 | pub timestamp: i64, 170 | pub duration: i64, 171 | pub offset: i64, 172 | pub size: usize, 173 | pub user_data: Dav1dUserData, 174 | } 175 | 176 | #[repr(C)] 177 | #[derive(Copy, Clone)] 178 | pub struct Dav1dWarpedMotionParams { 179 | pub type_: Dav1dWarpedMotionType, 180 | pub matrix: [i32; 6usize], 181 | pub u: Dav1dWarpedMotionParamsU, 182 | } 183 | 184 | #[repr(C)] 185 | #[derive(Copy, Clone)] 186 | pub union Dav1dWarpedMotionParamsU { 187 | pub p: Dav1dWarpedMotionParamsUP, 188 | pub abcd: [i16; 4usize], 189 | } 190 | 191 | #[repr(C)] 192 | #[derive(Debug, Copy, Clone)] 193 | pub struct Dav1dWarpedMotionParamsUP { 194 | pub alpha: i16, 195 | pub beta: i16, 196 | pub gamma: i16, 197 | pub delta: i16, 198 | } 199 | 200 | #[repr(C)] 201 | #[derive(Debug, Copy, Clone)] 202 | pub struct Dav1dContentLightLevel { 203 | pub max_content_light_level: u16, 204 | pub max_frame_average_light_level: u16, 205 | } 206 | 207 | #[repr(C)] 208 | #[derive(Debug, Copy, Clone)] 209 | pub struct Dav1dMasteringDisplay { 210 | pub primaries: [[u16; 2usize]; 3usize], 211 | pub white_point: [u16; 2usize], 212 | pub max_luminance: u32, 213 | pub min_luminance: u32, 214 | } 215 | 216 | #[repr(C)] 217 | #[derive(Debug, Copy, Clone)] 218 | pub struct Dav1dITUTT35 { 219 | pub country_code: u8, 220 | pub country_code_extension_byte: u8, 221 | pub payload_size: usize, 222 | pub payload: *mut u8, 223 | } 224 | 225 | #[repr(C)] 226 | #[derive(Debug, Copy, Clone)] 227 | pub struct Dav1dSequenceHeader { 228 | pub profile: u8, 229 | pub max_width: c_int, 230 | pub max_height: c_int, 231 | pub layout: Dav1dPixelLayout, 232 | pub pri: Dav1dColorPrimaries, 233 | pub trc: Dav1dTransferCharacteristics, 234 | pub mtrx: Dav1dMatrixCoefficients, 235 | pub chr: Dav1dChromaSamplePosition, 236 | pub hbd: u8, 237 | pub color_range: u8, 238 | pub num_operating_points: u8, 239 | pub operating_points: [Dav1dSequenceHeaderOperatingPoint; DAV1D_MAX_OPERATING_POINTS], 240 | pub still_picture: u8, 241 | pub reduced_still_picture_header: u8, 242 | pub timing_info_present: u8, 243 | pub num_units_in_tick: u32, 244 | pub time_scale: u32, 245 | pub equal_picture_interval: u8, 246 | pub num_ticks_per_picture: u32, 247 | pub decoder_model_info_present: u8, 248 | pub encoder_decoder_buffer_delay_length: u8, 249 | pub num_units_in_decoding_tick: u32, 250 | pub buffer_removal_delay_length: u8, 251 | pub frame_presentation_delay_length: u8, 252 | pub display_model_info_present: u8, 253 | pub width_n_bits: u8, 254 | pub height_n_bits: u8, 255 | pub frame_id_numbers_present: u8, 256 | pub delta_frame_id_n_bits: u8, 257 | pub frame_id_n_bits: u8, 258 | pub sb128: u8, 259 | pub filter_intra: u8, 260 | pub intra_edge_filter: u8, 261 | pub inter_intra: u8, 262 | pub masked_compound: u8, 263 | pub warped_motion: u8, 264 | pub dual_filter: u8, 265 | pub order_hint: u8, 266 | pub jnt_comp: u8, 267 | pub ref_frame_mvs: u8, 268 | pub screen_content_tools: Dav1dAdaptiveBoolean, 269 | pub force_integer_mv: Dav1dAdaptiveBoolean, 270 | pub order_hint_n_bits: u8, 271 | pub super_res: u8, 272 | pub cdef: u8, 273 | pub restoration: u8, 274 | pub ss_hor: u8, 275 | pub ss_ver: u8, 276 | pub monochrome: u8, 277 | pub color_description_present: u8, 278 | pub separate_uv_delta_q: u8, 279 | pub film_grain_present: u8, 280 | pub operating_parameter_info: 281 | [Dav1dSequenceHeaderOperatingParameterInfo; DAV1D_MAX_OPERATING_POINTS], 282 | } 283 | 284 | #[repr(C)] 285 | #[derive(Debug, Copy, Clone)] 286 | pub struct Dav1dSequenceHeaderOperatingPoint { 287 | pub major_level: u8, 288 | pub minor_level: u8, 289 | pub initial_display_delay: u16, 290 | pub idc: u8, 291 | pub tier: u8, 292 | pub decoder_model_param_present: u8, 293 | pub display_model_param_present: u8, 294 | } 295 | 296 | #[repr(C)] 297 | #[derive(Debug, Copy, Clone)] 298 | pub struct Dav1dSequenceHeaderOperatingParameterInfo { 299 | pub decoder_buffer_delay: u32, 300 | pub encoder_buffer_delay: u32, 301 | pub low_delay_mode: u8, 302 | } 303 | 304 | #[repr(C)] 305 | #[derive(Debug, Copy, Clone)] 306 | pub struct Dav1dSegmentationData { 307 | pub delta_q: i16, 308 | pub delta_lf_y_v: i8, 309 | pub delta_lf_y_h: i8, 310 | pub delta_lf_u: i8, 311 | pub delta_lf_v: i8, 312 | pub ref_: i8, 313 | pub skip: u8, 314 | pub globalmv: u8, 315 | } 316 | 317 | #[repr(C)] 318 | #[derive(Debug, Copy, Clone)] 319 | pub struct Dav1dSegmentationDataSet { 320 | pub d: [Dav1dSegmentationData; DAV1D_MAX_SEGMENTS], 321 | pub preskip: u8, 322 | pub last_active_segid: i8, 323 | } 324 | 325 | #[repr(C)] 326 | #[derive(Debug, Copy, Clone)] 327 | pub struct Dav1dLoopfilterModeRefDeltas { 328 | pub mode_delta: [i8; 2usize], 329 | pub ref_delta: [i8; DAV1D_TOTAL_REFS_PER_FRAME], 330 | } 331 | 332 | #[repr(C)] 333 | #[derive(Debug, Copy, Clone)] 334 | pub struct Dav1dFilmGrainData { 335 | pub seed: c_uint, 336 | pub num_y_points: c_int, 337 | pub y_points: [[u8; 2usize]; 14usize], 338 | pub chroma_scaling_from_luma: c_int, 339 | pub num_uv_points: [c_int; 2usize], 340 | pub uv_points: [[[u8; 2usize]; 10usize]; 2usize], 341 | pub scaling_shift: c_int, 342 | pub ar_coeff_lag: c_int, 343 | pub ar_coeffs_y: [i8; 24usize], 344 | pub ar_coeffs_uv: [[i8; 28usize]; 2usize], 345 | pub ar_coeff_shift: u64, 346 | pub grain_scale_shift: c_int, 347 | pub uv_mult: [c_int; 2usize], 348 | pub uv_luma_mult: [c_int; 2usize], 349 | pub uv_offset: [c_int; 2usize], 350 | pub overlap_flag: c_int, 351 | pub clip_to_restricted_range: c_int, 352 | } 353 | 354 | #[repr(C)] 355 | #[derive(Copy, Clone)] 356 | pub struct Dav1dFrameHeader { 357 | pub film_grain: Dav1dFrameHeaderFilmGrain, 358 | pub frame_type: Dav1dFrameType, 359 | pub width: [c_int; 2usize], 360 | pub height: c_int, 361 | pub frame_offset: u8, 362 | pub temporal_id: u8, 363 | pub spatial_id: u8, 364 | pub show_existing_frame: u8, 365 | pub existing_frame_idx: u8, 366 | pub frame_id: u32, 367 | pub frame_presentation_delay: u32, 368 | pub show_frame: u8, 369 | pub showable_frame: u8, 370 | pub error_resilient_mode: u8, 371 | pub disable_cdf_update: u8, 372 | pub allow_screen_content_tools: u8, 373 | pub force_integer_mv: u8, 374 | pub frame_size_override: u8, 375 | pub primary_ref_frame: u8, 376 | pub buffer_removal_time_present: u8, 377 | pub operating_points: [Dav1dFrameHeaderOperatingPoint; DAV1D_MAX_OPERATING_POINTS], 378 | pub refresh_frame_flags: u8, 379 | pub render_width: c_int, 380 | pub render_height: c_int, 381 | pub super_res: Dav1dFrameHeaderSuperRes, 382 | pub have_render_size: u8, 383 | pub allow_intrabc: u8, 384 | pub frame_ref_short_signaling: u8, 385 | pub refidx: [i8; DAV1D_REFS_PER_FRAME], 386 | pub hp: u8, 387 | pub subpel_filter_mode: Dav1dFilterMode, 388 | pub switchable_motion_mode: u8, 389 | pub use_ref_frame_mvs: u8, 390 | pub refresh_context: u8, 391 | pub tiling: Dav1dFrameHeaderTiling, 392 | pub quant: Dav1dFrameHeaderQuant, 393 | pub segmentation: Dav1dFrameHeaderSegmentation, 394 | pub delta: Dav1dFrameHeaderDelta, 395 | pub all_lossless: u8, 396 | pub loopfilter: Dav1dFrameHeaderLoopfilter, 397 | pub cdef: Dav1dFrameHeaderCDef, 398 | pub restoration: Dav1dFrameHeaderRestoration, 399 | pub txfm_mode: Dav1dTxfmMode, 400 | pub switchable_comp_refs: u8, 401 | pub skip_mode_allowed: u8, 402 | pub skip_mode_enabled: u8, 403 | pub skip_mode_refs: [i8; 2usize], 404 | pub warp_motion: u8, 405 | pub reduced_txtp_set: u8, 406 | pub gmv: [Dav1dWarpedMotionParams; DAV1D_REFS_PER_FRAME], 407 | } 408 | 409 | #[repr(C)] 410 | #[derive(Debug, Copy, Clone)] 411 | pub struct Dav1dFrameHeaderFilmGrain { 412 | pub data: Dav1dFilmGrainData, 413 | pub present: u8, 414 | pub update: u8, 415 | } 416 | 417 | #[repr(C)] 418 | #[derive(Debug, Copy, Clone)] 419 | pub struct Dav1dFrameHeaderOperatingPoint { 420 | pub buffer_removal_time: u32, 421 | } 422 | 423 | #[repr(C)] 424 | #[derive(Debug, Copy, Clone)] 425 | pub struct Dav1dFrameHeaderSuperRes { 426 | pub width_scale_denominator: u8, 427 | pub enabled: u8, 428 | } 429 | 430 | #[repr(C)] 431 | #[derive(Debug, Copy, Clone)] 432 | pub struct Dav1dFrameHeaderTiling { 433 | pub uniform: u8, 434 | pub n_bytes: u8, 435 | pub min_log2_cols: u8, 436 | pub max_log2_cols: u8, 437 | pub log2_cols: u8, 438 | pub cols: u8, 439 | pub min_log2_rows: u8, 440 | pub max_log2_rows: u8, 441 | pub log2_rows: u8, 442 | pub rows: u8, 443 | pub col_start_sb: [u16; DAV1D_MAX_TILE_COLS + 1], 444 | pub row_start_sb: [u16; DAV1D_MAX_TILE_ROWS + 1], 445 | pub update: u16, 446 | } 447 | 448 | #[repr(C)] 449 | #[derive(Debug, Copy, Clone)] 450 | pub struct Dav1dFrameHeaderQuant { 451 | pub yac: u8, 452 | pub ydc_delta: i8, 453 | pub udc_delta: i8, 454 | pub uac_delta: i8, 455 | pub vdc_delta: i8, 456 | pub vac_delta: i8, 457 | pub qm: u8, 458 | pub qm_y: u8, 459 | pub qm_u: u8, 460 | pub qm_v: u8, 461 | } 462 | 463 | #[repr(C)] 464 | #[derive(Debug, Copy, Clone)] 465 | pub struct Dav1dFrameHeaderSegmentation { 466 | pub enabled: u8, 467 | pub update_map: u8, 468 | pub temporal: u8, 469 | pub update_data: u8, 470 | pub seg_data: Dav1dSegmentationDataSet, 471 | pub lossless: [u8; DAV1D_MAX_SEGMENTS], 472 | pub qidx: [u8; DAV1D_MAX_SEGMENTS], 473 | } 474 | 475 | #[repr(C)] 476 | #[derive(Debug, Copy, Clone)] 477 | pub struct Dav1dFrameHeaderDelta { 478 | pub q: Dav1dDeltaQ, 479 | pub lf: Dav1dDeltaLF, 480 | } 481 | 482 | #[repr(C)] 483 | #[derive(Debug, Copy, Clone)] 484 | pub struct Dav1dDeltaQ { 485 | pub present: u8, 486 | pub res_log2: u8, 487 | } 488 | 489 | #[repr(C)] 490 | #[derive(Debug, Copy, Clone)] 491 | pub struct Dav1dDeltaLF { 492 | pub present: u8, 493 | pub res_log2: u8, 494 | pub multi: u8, 495 | } 496 | 497 | #[repr(C)] 498 | #[derive(Debug, Copy, Clone)] 499 | pub struct Dav1dFrameHeaderLoopfilter { 500 | pub level_y: [u8; 2usize], 501 | pub level_u: u8, 502 | pub level_v: u8, 503 | pub mode_ref_delta_enabled: u8, 504 | pub mode_ref_delta_update: u8, 505 | pub mode_ref_deltas: Dav1dLoopfilterModeRefDeltas, 506 | pub sharpness: u8, 507 | } 508 | 509 | #[repr(C)] 510 | #[derive(Debug, Copy, Clone)] 511 | pub struct Dav1dFrameHeaderCDef { 512 | pub damping: u8, 513 | pub n_bits: u8, 514 | pub y_strength: [u8; DAV1D_MAX_CDEF_STRENGTHS], 515 | pub uv_strength: [u8; DAV1D_MAX_CDEF_STRENGTHS], 516 | } 517 | 518 | #[repr(C)] 519 | #[derive(Debug, Copy, Clone)] 520 | pub struct Dav1dFrameHeaderRestoration { 521 | pub type_: [Dav1dRestorationType; 3usize], 522 | pub unit_size: [u8; 2usize], 523 | } 524 | 525 | #[repr(C)] 526 | #[derive(Debug, Copy, Clone)] 527 | pub struct Dav1dPictureParameters { 528 | pub w: c_int, 529 | pub h: c_int, 530 | pub layout: Dav1dPixelLayout, 531 | pub bpc: c_int, 532 | } 533 | 534 | #[repr(C)] 535 | #[derive(Debug, Copy, Clone)] 536 | pub struct Dav1dPicture { 537 | pub seq_hdr: *mut Dav1dSequenceHeader, 538 | pub frame_hdr: *mut Dav1dFrameHeader, 539 | pub data: [*mut c_void; 3usize], 540 | pub stride: [isize; 2usize], 541 | pub p: Dav1dPictureParameters, 542 | pub m: Dav1dDataProps, 543 | pub content_light: *mut Dav1dContentLightLevel, 544 | pub mastering_display: *mut Dav1dMasteringDisplay, 545 | pub itut_t35: *mut Dav1dITUTT35, 546 | pub reserved: [usize; 4usize], 547 | pub frame_hdr_ref: *mut Dav1dRef, 548 | pub seq_hdr_ref: *mut Dav1dRef, 549 | pub content_light_ref: *mut Dav1dRef, 550 | pub mastering_display_ref: *mut Dav1dRef, 551 | pub itut_t35_ref: *mut Dav1dRef, 552 | pub n_itut_t34: usize, 553 | pub reserved_ref: [usize; 4usize], 554 | pub ref_: *mut Dav1dRef, 555 | pub allocator_data: *mut c_void, 556 | } 557 | 558 | #[repr(C)] 559 | #[derive(Debug, Copy, Clone)] 560 | pub struct Dav1dPicAllocator { 561 | pub cookie: *mut c_void, 562 | pub alloc_picture_callback: 563 | Option c_int>, 564 | pub release_picture_callback: 565 | Option, 566 | } 567 | 568 | #[repr(C)] 569 | #[derive(Debug, Copy, Clone)] 570 | pub struct Dav1dData { 571 | pub data: *const u8, 572 | pub sz: usize, 573 | pub ref_: *mut Dav1dRef, 574 | pub m: Dav1dDataProps, 575 | } 576 | 577 | #[repr(transparent)] 578 | #[derive(Debug)] 579 | pub struct Dav1dContext(c_void); 580 | 581 | #[repr(C)] 582 | #[derive(Debug, Copy, Clone)] 583 | pub struct Dav1dLogger { 584 | pub cookie: *mut c_void, 585 | pub callback: *mut c_void, 586 | // FIXME: Use the following once std::ffi::VaList is stable 587 | // Option< 588 | // unsafe extern "C" fn( 589 | // cookie: *mut c_void, 590 | // format: *const c_char, 591 | // ap: *mut ::std::ffi::VaList, 592 | // ), 593 | // >, 594 | } 595 | 596 | #[repr(C)] 597 | #[derive(Debug, Copy, Clone)] 598 | pub struct Dav1dSettings { 599 | pub n_threads: c_int, 600 | pub max_frame_delay: c_int, 601 | pub apply_grain: c_int, 602 | pub operating_point: c_int, 603 | pub all_layers: c_int, 604 | pub frame_size_limit: c_uint, 605 | pub allocator: Dav1dPicAllocator, 606 | pub logger: Dav1dLogger, 607 | pub strict_std_compliance: c_int, 608 | pub output_invisible_frames: c_int, 609 | pub inloop_filters: Dav1dInloopFilterType, 610 | pub decode_frame_type: Dav1dDecodeFrameType, 611 | pub reserved: [u8; 16usize], 612 | } 613 | 614 | #[repr(transparent)] 615 | #[derive(Debug)] 616 | pub struct Dav1dRef(c_void); 617 | 618 | extern "C" { 619 | pub fn dav1d_version() -> *const c_char; 620 | 621 | pub fn dav1d_version_api() -> u32; 622 | 623 | pub fn dav1d_default_settings(s: *mut Dav1dSettings); 624 | 625 | pub fn dav1d_parse_sequence_header( 626 | out: *mut Dav1dSequenceHeader, 627 | buf: *const u8, 628 | sz: usize, 629 | ) -> c_int; 630 | 631 | pub fn dav1d_open(c_out: *mut *mut Dav1dContext, s: *const Dav1dSettings) -> c_int; 632 | 633 | pub fn dav1d_send_data(c: *mut Dav1dContext, in_: *mut Dav1dData) -> c_int; 634 | 635 | pub fn dav1d_flush(c: *mut Dav1dContext); 636 | 637 | pub fn dav1d_get_picture(c: *mut Dav1dContext, out: *mut Dav1dPicture) -> c_int; 638 | 639 | pub fn dav1d_get_decode_error_data_props( 640 | c: *mut Dav1dContext, 641 | out: *mut Dav1dDataProps, 642 | ) -> c_int; 643 | 644 | pub fn dav1d_get_frame_delay(c: *mut Dav1dContext) -> c_int; 645 | 646 | pub fn dav1d_apply_grain( 647 | c: *mut Dav1dContext, 648 | out: *mut Dav1dPicture, 649 | in_: *const Dav1dPicture, 650 | ) -> c_int; 651 | 652 | pub fn dav1d_get_event_flags(c: *mut Dav1dContext, flags: *mut Dav1dEventFlags) -> c_int; 653 | 654 | pub fn dav1d_close(c_out: *mut *mut Dav1dContext); 655 | 656 | pub fn dav1d_picture_unref(p: *mut Dav1dPicture); 657 | 658 | pub fn dav1d_data_props_unref(props: *mut Dav1dDataProps); 659 | 660 | pub fn dav1d_data_create(data: *mut Dav1dData, sz: usize) -> *mut u8; 661 | 662 | pub fn dav1d_data_wrap( 663 | data: *mut Dav1dData, 664 | buf: *const u8, 665 | sz: usize, 666 | free_callback: Option, 667 | cookie: *mut c_void, 668 | ) -> c_int; 669 | 670 | pub fn dav1d_data_wrap_user_data( 671 | data: *mut Dav1dData, 672 | user_data: *const u8, 673 | free_callback: Option, 674 | cookie: *mut c_void, 675 | ) -> c_int; 676 | 677 | pub fn dav1d_data_unref(data: *mut Dav1dData); 678 | } 679 | 680 | #[cfg(test)] 681 | mod tests { 682 | use super::*; 683 | use std::ffi::CStr; 684 | 685 | macro_rules! assert_size ( 686 | ($t:ty, $sz:expr) => ( 687 | assert_eq!(::std::mem::size_of::<$t>(), $sz); 688 | ); 689 | ); 690 | 691 | #[test] 692 | fn size() { 693 | #[cfg(target_pointer_width = "64")] 694 | assert_size!(Dav1dSettings, 96); 695 | #[cfg(target_pointer_width = "32")] 696 | assert_size!(Dav1dSettings, 76); 697 | } 698 | 699 | #[test] 700 | fn version() { 701 | println!("{}", unsafe { 702 | CStr::from_ptr(dav1d_version()).to_string_lossy() 703 | }); 704 | } 705 | } 706 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use dav1d_sys::*; 2 | 3 | pub use av_data::pixel; 4 | use std::ffi::{c_int, c_void}; 5 | use std::fmt::{self, Debug}; 6 | use std::i64; 7 | use std::mem; 8 | use std::ptr; 9 | use std::sync::Arc; 10 | 11 | /// Error enum return by various `dav1d` operations. 12 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 13 | #[non_exhaustive] 14 | pub enum Error { 15 | /// Try again. 16 | /// 17 | /// If this is returned by [`Decoder::send_data`] or [`Decoder::send_pending_data`] then there 18 | /// are decoded frames pending that first have to be retrieved via [`Decoder::get_picture`] 19 | /// before processing any further pending data. 20 | /// 21 | /// If this is returned by [`Decoder::get_picture`] then no decoded frames are pending 22 | /// currently and more data needs to be sent to the decoder. 23 | Again, 24 | /// Invalid argument. 25 | /// 26 | /// One of the arguments passed to the function was invalid. 27 | InvalidArgument, 28 | /// Not enough memory. 29 | /// 30 | /// Not enough memory is currently available for performing this operation. 31 | NotEnoughMemory, 32 | /// Unsupported bitstream. 33 | /// 34 | /// The provided bitstream is not supported by `dav1d`. 35 | UnsupportedBitstream, 36 | /// Unknown error. 37 | UnknownError(i32), 38 | } 39 | 40 | impl From for Error { 41 | fn from(err: i32) -> Self { 42 | assert!(err < 0); 43 | 44 | match err { 45 | DAV1D_ERR_AGAIN => Error::Again, 46 | DAV1D_ERR_INVAL => Error::InvalidArgument, 47 | DAV1D_ERR_NOMEM => Error::NotEnoughMemory, 48 | DAV1D_ERR_NOPROTOOPT => Error::UnsupportedBitstream, 49 | _ => Error::UnknownError(err), 50 | } 51 | } 52 | } 53 | 54 | impl Error { 55 | pub const fn is_again(&self) -> bool { 56 | matches!(self, Error::Again) 57 | } 58 | } 59 | 60 | impl fmt::Display for Error { 61 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 62 | match self { 63 | Error::Again => write!(fmt, "Try again"), 64 | Error::InvalidArgument => write!(fmt, "Invalid argument"), 65 | Error::NotEnoughMemory => write!(fmt, "Not enough memory available"), 66 | Error::UnsupportedBitstream => write!(fmt, "Unsupported bitstream"), 67 | Error::UnknownError(err) => write!(fmt, "Unknown error {}", err), 68 | } 69 | } 70 | } 71 | 72 | impl std::error::Error for Error {} 73 | 74 | /// Picture parameters used for allocation. 75 | #[derive(Debug)] 76 | pub struct PictureParameters { 77 | pic: ptr::NonNull, 78 | } 79 | 80 | impl PictureParameters { 81 | /// Bit depth of the plane data. 82 | /// 83 | /// This returns 8 or 16 for the underlying integer type used for the plane data. 84 | /// 85 | /// Check [`PictureParameters::bits_per_component`] for the number of bits that are used. 86 | pub fn bit_depth(&self) -> usize { 87 | unsafe { self.pic.as_ref().p.bpc as usize } 88 | } 89 | 90 | /// Bits used per component of the plane data. 91 | /// 92 | /// Check [`PictureParameters::bit_depth`] for the number of storage bits. 93 | pub fn bits_per_component(&self) -> Option { 94 | unsafe { 95 | match (*self.pic.as_ref().seq_hdr).hbd { 96 | 0 => Some(BitsPerComponent(8)), 97 | 1 => Some(BitsPerComponent(10)), 98 | 2 => Some(BitsPerComponent(12)), 99 | _ => None, 100 | } 101 | } 102 | } 103 | 104 | /// Width of the frame. 105 | pub fn width(&self) -> u32 { 106 | unsafe { self.pic.as_ref().p.w as u32 } 107 | } 108 | 109 | /// Height of the frame. 110 | pub fn height(&self) -> u32 { 111 | unsafe { self.pic.as_ref().p.h as u32 } 112 | } 113 | 114 | /// Pixel layout of the frame. 115 | pub fn pixel_layout(&self) -> PixelLayout { 116 | unsafe { 117 | #[allow(non_upper_case_globals)] 118 | match self.pic.as_ref().p.layout { 119 | DAV1D_PIXEL_LAYOUT_I400 => PixelLayout::I400, 120 | DAV1D_PIXEL_LAYOUT_I420 => PixelLayout::I420, 121 | DAV1D_PIXEL_LAYOUT_I422 => PixelLayout::I422, 122 | DAV1D_PIXEL_LAYOUT_I444 => PixelLayout::I444, 123 | _ => unreachable!(), 124 | } 125 | } 126 | } 127 | 128 | /// Chromaticity coordinates of the source colour primaries. 129 | pub fn color_primaries(&self) -> pixel::ColorPrimaries { 130 | unsafe { 131 | #[allow(non_upper_case_globals)] 132 | match (*self.pic.as_ref().seq_hdr).pri { 133 | DAV1D_COLOR_PRI_BT709 => pixel::ColorPrimaries::BT709, 134 | DAV1D_COLOR_PRI_UNKNOWN => pixel::ColorPrimaries::Unspecified, 135 | DAV1D_COLOR_PRI_BT470M => pixel::ColorPrimaries::BT470M, 136 | DAV1D_COLOR_PRI_BT470BG => pixel::ColorPrimaries::BT470BG, 137 | DAV1D_COLOR_PRI_BT601 => pixel::ColorPrimaries::BT470BG, 138 | DAV1D_COLOR_PRI_SMPTE240 => pixel::ColorPrimaries::ST240M, 139 | DAV1D_COLOR_PRI_FILM => pixel::ColorPrimaries::Film, 140 | DAV1D_COLOR_PRI_BT2020 => pixel::ColorPrimaries::BT2020, 141 | DAV1D_COLOR_PRI_XYZ => pixel::ColorPrimaries::ST428, 142 | DAV1D_COLOR_PRI_SMPTE431 => pixel::ColorPrimaries::P3DCI, 143 | DAV1D_COLOR_PRI_SMPTE432 => pixel::ColorPrimaries::P3Display, 144 | DAV1D_COLOR_PRI_EBU3213 => pixel::ColorPrimaries::Tech3213, 145 | 23..=DAV1D_COLOR_PRI_RESERVED => pixel::ColorPrimaries::Unspecified, 146 | _ => unreachable!(), 147 | } 148 | } 149 | } 150 | 151 | /// Transfer characteristics function. 152 | pub fn transfer_characteristic(&self) -> pixel::TransferCharacteristic { 153 | unsafe { 154 | #[allow(non_upper_case_globals)] 155 | match (*self.pic.as_ref().seq_hdr).trc { 156 | DAV1D_TRC_BT709 => pixel::TransferCharacteristic::BT1886, 157 | DAV1D_TRC_UNKNOWN => pixel::TransferCharacteristic::Unspecified, 158 | DAV1D_TRC_BT470M => pixel::TransferCharacteristic::BT470M, 159 | DAV1D_TRC_BT470BG => pixel::TransferCharacteristic::BT470BG, 160 | DAV1D_TRC_BT601 => pixel::TransferCharacteristic::ST170M, 161 | DAV1D_TRC_SMPTE240 => pixel::TransferCharacteristic::ST240M, 162 | DAV1D_TRC_LINEAR => pixel::TransferCharacteristic::Linear, 163 | DAV1D_TRC_LOG100 => pixel::TransferCharacteristic::Logarithmic100, 164 | DAV1D_TRC_LOG100_SQRT10 => pixel::TransferCharacteristic::Logarithmic316, 165 | DAV1D_TRC_IEC61966 => pixel::TransferCharacteristic::SRGB, 166 | DAV1D_TRC_BT1361 => pixel::TransferCharacteristic::BT1886, 167 | DAV1D_TRC_SRGB => pixel::TransferCharacteristic::SRGB, 168 | DAV1D_TRC_BT2020_10BIT => pixel::TransferCharacteristic::BT2020Ten, 169 | DAV1D_TRC_BT2020_12BIT => pixel::TransferCharacteristic::BT2020Twelve, 170 | DAV1D_TRC_SMPTE2084 => pixel::TransferCharacteristic::PerceptualQuantizer, 171 | DAV1D_TRC_SMPTE428 => pixel::TransferCharacteristic::ST428, 172 | DAV1D_TRC_HLG => pixel::TransferCharacteristic::HybridLogGamma, 173 | 19..=DAV1D_TRC_RESERVED => pixel::TransferCharacteristic::Unspecified, 174 | _ => unreachable!(), 175 | } 176 | } 177 | } 178 | 179 | /// Matrix coefficients used in deriving luma and chroma signals from the 180 | /// green, blue and red or X, Y and Z primaries. 181 | pub fn matrix_coefficients(&self) -> pixel::MatrixCoefficients { 182 | unsafe { 183 | #[allow(non_upper_case_globals)] 184 | match (*self.pic.as_ref().seq_hdr).mtrx { 185 | DAV1D_MC_IDENTITY => pixel::MatrixCoefficients::Identity, 186 | DAV1D_MC_BT709 => pixel::MatrixCoefficients::BT709, 187 | DAV1D_MC_UNKNOWN => pixel::MatrixCoefficients::Unspecified, 188 | DAV1D_MC_FCC => pixel::MatrixCoefficients::BT470M, 189 | DAV1D_MC_BT470BG => pixel::MatrixCoefficients::BT470BG, 190 | DAV1D_MC_BT601 => pixel::MatrixCoefficients::BT470BG, 191 | DAV1D_MC_SMPTE240 => pixel::MatrixCoefficients::ST240M, 192 | DAV1D_MC_SMPTE_YCGCO => pixel::MatrixCoefficients::YCgCo, 193 | DAV1D_MC_BT2020_NCL => pixel::MatrixCoefficients::BT2020NonConstantLuminance, 194 | DAV1D_MC_BT2020_CL => pixel::MatrixCoefficients::BT2020ConstantLuminance, 195 | DAV1D_MC_SMPTE2085 => pixel::MatrixCoefficients::ST2085, 196 | DAV1D_MC_CHROMAT_NCL => { 197 | pixel::MatrixCoefficients::ChromaticityDerivedNonConstantLuminance 198 | } 199 | DAV1D_MC_CHROMAT_CL => { 200 | pixel::MatrixCoefficients::ChromaticityDerivedConstantLuminance 201 | } 202 | DAV1D_MC_ICTCP => pixel::MatrixCoefficients::ICtCp, 203 | 15..=DAV1D_MC_RESERVED => pixel::MatrixCoefficients::Unspecified, 204 | _ => unreachable!(), 205 | } 206 | } 207 | } 208 | 209 | /// YUV color range. 210 | pub fn color_range(&self) -> pixel::YUVRange { 211 | unsafe { 212 | match (*self.pic.as_ref().seq_hdr).color_range { 213 | 0 => pixel::YUVRange::Limited, 214 | _ => pixel::YUVRange::Full, 215 | } 216 | } 217 | } 218 | 219 | /// Sample position for subsampled chroma. 220 | pub fn chroma_location(&self) -> pixel::ChromaLocation { 221 | // According to y4m mapping declared in dav1d's output/y4m2.c and applied from FFmpeg's yuv4mpegdec.c 222 | unsafe { 223 | match (*self.pic.as_ref().seq_hdr).chr { 224 | DAV1D_CHR_UNKNOWN | DAV1D_CHR_COLOCATED => pixel::ChromaLocation::Center, 225 | DAV1D_CHR_VERTICAL => pixel::ChromaLocation::Left, 226 | _ => unreachable!(), 227 | } 228 | } 229 | } 230 | } 231 | 232 | // Number of bytes to align AND pad picture memory buffers by, so that SIMD 233 | // implementations can over-read by a few bytes, and use aligned read/write 234 | // instructions. 235 | pub const PICTURE_ALIGNMENT: usize = 64; 236 | 237 | /// Allocation for a picture. 238 | #[derive(Debug, PartialEq, Eq)] 239 | pub struct PictureAllocation { 240 | /// Pointers to planar image data. 241 | /// 242 | /// Y is `data[0]`, U is `data[1]`, V is `data[2]`. 243 | /// 244 | /// The data should be `u8` (for 8 bits) or `u16` (for 10 bits). 245 | /// 246 | /// The `data[0]`, `data[1]` and `data[2]` must be [`PICTURE_ALIGNMENT`] byte aligned and with a pixel 247 | /// width/height multiple of 128 pixels. Any allocated memory area should also be padded by 248 | /// [`PICTURE_ALIGNMENT`] bytes. 249 | /// 250 | /// `data[1]` and `data[2]` must share the same `stride[1]`. 251 | pub data: [*mut u8; 3], 252 | 253 | /// Number of bytes between 2 lines in `data[]` for luma in case of `stride[0]` or chroma in 254 | /// case of `stride[1]`. 255 | pub stride: [isize; 2], 256 | 257 | /// Allocator data that can be retrieved back from the picture later. 258 | pub allocator_data: D, 259 | } 260 | 261 | unsafe impl Send for PictureAllocation {} 262 | 263 | /// Picture allocator trait. 264 | /// 265 | /// # Safety 266 | /// 267 | /// See documentation of [`PictureAllocator::alloc_picture`] and 268 | /// [`PictureAllocator::release_picture`] for the requirements of this trait. 269 | pub unsafe trait PictureAllocator: Send + Sync + 'static { 270 | /// Allocator data that is stored together with the [`Picture`]. 271 | /// 272 | /// This can be retrieved from the picture via [`Picture::allocator_data()`]. 273 | type AllocatorData: Send + 'static; 274 | 275 | /// Allocate the picture buffer based on `pic_params`. 276 | /// 277 | /// See the [`PictureAllocation`] documentation for the requirements on the allocated memory. 278 | /// 279 | /// This function will be called on the main thread (the thread which calls 280 | /// [`Decoder::get_picture()`]). 281 | /// 282 | /// # Safety 283 | /// 284 | /// This function needs to allocate enough memory with the constraints outlined in the 285 | /// [`PictureAllocation`] documentation. 286 | unsafe fn alloc_picture( 287 | &self, 288 | pic_params: &PictureParameters, 289 | ) -> Result, Error>; 290 | 291 | /// Release the picture buffer. 292 | /// 293 | /// If frame threading is used, this function may be called by the main 294 | /// thread (the thread which calls [`Decoder::get_picture()`]) or any of the frame 295 | /// threads and thus must be thread-safe. If frame threading is not used, 296 | /// this function will only be called on the main thread. 297 | /// 298 | /// # Safety 299 | /// 300 | /// This function needs to release the memory in `allocation` and can assume that it 301 | /// corresponds to a previous call to [`PictureAllocator::alloc_picture()`]. 302 | unsafe fn release_picture(&self, allocation: PictureAllocation); 303 | } 304 | 305 | /// Default allocator. 306 | /// 307 | /// Note that this allocator can't be directly instantiated. 308 | #[derive(Debug)] 309 | pub struct DefaultAllocator(()); 310 | 311 | unsafe impl PictureAllocator for DefaultAllocator { 312 | type AllocatorData = (); 313 | 314 | unsafe fn alloc_picture( 315 | &self, 316 | _pic_params: &PictureParameters, 317 | ) -> Result, Error> { 318 | unimplemented!() 319 | } 320 | 321 | unsafe fn release_picture(&self, _allocation: PictureAllocation) { 322 | unimplemented!() 323 | } 324 | } 325 | 326 | /// Settings for creating a new [`Decoder`] instance. 327 | /// See documentation for native `Dav1dSettings` struct. 328 | #[derive(Debug)] 329 | pub struct Settings { 330 | dav1d_settings: Dav1dSettings, 331 | } 332 | 333 | unsafe impl Send for Settings {} 334 | unsafe impl Sync for Settings {} 335 | 336 | impl Default for Settings { 337 | fn default() -> Self { 338 | Self::new() 339 | } 340 | } 341 | 342 | impl Settings { 343 | /// Creates a new [`Settings`] instance with default settings. 344 | pub fn new() -> Self { 345 | unsafe { 346 | let mut dav1d_settings = mem::MaybeUninit::uninit(); 347 | 348 | dav1d_default_settings(dav1d_settings.as_mut_ptr()); 349 | 350 | Self { 351 | dav1d_settings: dav1d_settings.assume_init(), 352 | } 353 | } 354 | } 355 | 356 | pub fn set_n_threads(&mut self, n_threads: u32) { 357 | self.dav1d_settings.n_threads = n_threads as i32; 358 | } 359 | 360 | pub fn get_n_threads(&self) -> u32 { 361 | self.dav1d_settings.n_threads as u32 362 | } 363 | 364 | pub fn set_max_frame_delay(&mut self, max_frame_delay: u32) { 365 | self.dav1d_settings.max_frame_delay = max_frame_delay as i32; 366 | } 367 | 368 | pub fn get_max_frame_delay(&self) -> u32 { 369 | self.dav1d_settings.max_frame_delay as u32 370 | } 371 | 372 | pub fn set_apply_grain(&mut self, apply_grain: bool) { 373 | self.dav1d_settings.apply_grain = i32::from(apply_grain); 374 | } 375 | 376 | pub fn get_apply_grain(&self) -> bool { 377 | self.dav1d_settings.apply_grain != 0 378 | } 379 | 380 | pub fn set_operating_point(&mut self, operating_point: u32) { 381 | self.dav1d_settings.operating_point = operating_point as i32; 382 | } 383 | 384 | pub fn get_operating_point(&self) -> u32 { 385 | self.dav1d_settings.operating_point as u32 386 | } 387 | 388 | pub fn set_all_layers(&mut self, all_layers: bool) { 389 | self.dav1d_settings.all_layers = i32::from(all_layers); 390 | } 391 | 392 | pub fn get_all_layers(&self) -> bool { 393 | self.dav1d_settings.all_layers != 0 394 | } 395 | 396 | pub fn set_frame_size_limit(&mut self, frame_size_limit: u32) { 397 | self.dav1d_settings.frame_size_limit = frame_size_limit; 398 | } 399 | 400 | pub fn get_frame_size_limit(&self) -> u32 { 401 | self.dav1d_settings.frame_size_limit 402 | } 403 | 404 | pub fn set_strict_std_compliance(&mut self, strict_std_compliance: bool) { 405 | self.dav1d_settings.strict_std_compliance = i32::from(strict_std_compliance); 406 | } 407 | 408 | pub fn get_strict_std_compliance(&self) -> bool { 409 | self.dav1d_settings.strict_std_compliance != 0 410 | } 411 | 412 | pub fn set_output_invisible_frames(&mut self, output_invisible_frames: bool) { 413 | self.dav1d_settings.output_invisible_frames = i32::from(output_invisible_frames); 414 | } 415 | 416 | pub fn get_output_invisible_frames(&self) -> bool { 417 | self.dav1d_settings.output_invisible_frames != 0 418 | } 419 | 420 | pub fn set_inloop_filters(&mut self, inloop_filters: InloopFilterType) { 421 | self.dav1d_settings.inloop_filters = inloop_filters.bits(); 422 | } 423 | 424 | pub fn get_inloop_filters(&self) -> InloopFilterType { 425 | InloopFilterType::from_bits_truncate(self.dav1d_settings.inloop_filters) 426 | } 427 | 428 | pub fn set_decode_frame_type(&mut self, decode_frame_type: DecodeFrameType) { 429 | self.dav1d_settings.decode_frame_type = decode_frame_type.into(); 430 | } 431 | 432 | pub fn get_decode_frame_type(&self) -> DecodeFrameType { 433 | DecodeFrameType::try_from(self.dav1d_settings.decode_frame_type) 434 | .expect("Invalid Dav1dDecodeFrameType") 435 | } 436 | } 437 | 438 | bitflags::bitflags! { 439 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] 440 | pub struct InloopFilterType: u32 { 441 | const DEBLOCK = DAV1D_INLOOPFILTER_DEBLOCK; 442 | const CDEF = DAV1D_INLOOPFILTER_CDEF; 443 | const RESTORATION = DAV1D_INLOOPFILTER_RESTORATION; 444 | } 445 | } 446 | 447 | #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] 448 | pub enum DecodeFrameType { 449 | #[default] 450 | All, 451 | Reference, 452 | Intra, 453 | Key, 454 | } 455 | 456 | impl TryFrom for DecodeFrameType { 457 | type Error = TryFromEnumError; 458 | 459 | fn try_from(value: u32) -> Result { 460 | match value { 461 | DAV1D_DECODEFRAMETYPE_ALL => Ok(DecodeFrameType::All), 462 | DAV1D_DECODEFRAMETYPE_REFERENCE => Ok(DecodeFrameType::Reference), 463 | DAV1D_DECODEFRAMETYPE_INTRA => Ok(DecodeFrameType::Intra), 464 | DAV1D_DECODEFRAMETYPE_KEY => Ok(DecodeFrameType::Key), 465 | _ => Err(TryFromEnumError(())), 466 | } 467 | } 468 | } 469 | 470 | impl From for u32 { 471 | fn from(v: DecodeFrameType) -> u32 { 472 | match v { 473 | DecodeFrameType::All => DAV1D_DECODEFRAMETYPE_ALL, 474 | DecodeFrameType::Reference => DAV1D_DECODEFRAMETYPE_REFERENCE, 475 | DecodeFrameType::Intra => DAV1D_DECODEFRAMETYPE_INTRA, 476 | DecodeFrameType::Key => DAV1D_DECODEFRAMETYPE_KEY, 477 | } 478 | } 479 | } 480 | 481 | /// The error type returned when a conversion from a C enum fails. 482 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 483 | pub struct TryFromEnumError(()); 484 | 485 | impl std::fmt::Display for TryFromEnumError { 486 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 487 | fmt.write_str("Invalid enum value") 488 | } 489 | } 490 | 491 | impl From for TryFromEnumError { 492 | fn from(x: std::convert::Infallible) -> TryFromEnumError { 493 | match x {} 494 | } 495 | } 496 | 497 | impl std::error::Error for TryFromEnumError {} 498 | 499 | /// A `dav1d` decoder instance. 500 | #[derive(Debug)] 501 | pub struct Decoder { 502 | dec: ptr::NonNull, 503 | pending_data: Option, 504 | allocator: Option>, 505 | } 506 | 507 | static_assertions::assert_impl_all!(Decoder: Send, Sync, Debug); 508 | 509 | unsafe extern "C" fn release_wrapped_data>(_data: *const u8, cookie: *mut c_void) { 510 | let buf = Box::from_raw(cookie as *mut T); 511 | drop(buf); 512 | } 513 | 514 | impl Decoder { 515 | /// Creates a new [`Decoder`] instance with given [`Settings`]. 516 | pub fn with_settings(settings: &Settings) -> Result { 517 | unsafe { 518 | let mut dec = mem::MaybeUninit::uninit(); 519 | 520 | let ret = dav1d_open(dec.as_mut_ptr(), &settings.dav1d_settings); 521 | 522 | if ret < 0 { 523 | return Err(Error::from(ret)); 524 | } 525 | 526 | Ok(Decoder { 527 | dec: ptr::NonNull::new(dec.assume_init()).unwrap(), 528 | pending_data: None, 529 | allocator: None, 530 | }) 531 | } 532 | } 533 | 534 | /// Creates a new [`Decoder`] instance with the default settings. 535 | pub fn new() -> Result { 536 | Self::with_settings(&Settings::default()) 537 | } 538 | } 539 | 540 | unsafe extern "C" fn alloc_picture_callback( 541 | pic: *mut Dav1dPicture, 542 | cookie: *mut c_void, 543 | ) -> c_int { 544 | let allocator = &*(cookie as *const A); 545 | 546 | let pic_parameters = PictureParameters { 547 | pic: ptr::NonNull::new_unchecked(pic), 548 | }; 549 | 550 | let res = allocator.alloc_picture(&pic_parameters); 551 | match res { 552 | Ok(allocation) => { 553 | (*pic).data[0] = allocation.data[0] as *mut c_void; 554 | (*pic).data[1] = allocation.data[1] as *mut c_void; 555 | (*pic).data[2] = allocation.data[2] as *mut c_void; 556 | (*pic).stride[0] = allocation.stride[0]; 557 | (*pic).stride[1] = allocation.stride[1]; 558 | (*pic).allocator_data = 559 | Box::into_raw(Box::new(allocation.allocator_data)) as *mut c_void; 560 | 561 | 0 562 | } 563 | Err(err) => match err { 564 | Error::Again => DAV1D_ERR_AGAIN, 565 | Error::InvalidArgument => DAV1D_ERR_INVAL, 566 | Error::NotEnoughMemory => DAV1D_ERR_NOMEM, 567 | Error::UnsupportedBitstream => DAV1D_ERR_NOPROTOOPT, 568 | Error::UnknownError(err) => { 569 | assert!(err < 0); 570 | err 571 | } 572 | }, 573 | } 574 | } 575 | 576 | unsafe extern "C" fn release_picture_callback( 577 | pic: *mut Dav1dPicture, 578 | cookie: *mut c_void, 579 | ) { 580 | let allocator = &*(cookie as *const A); 581 | let allocator_data = Box::from_raw((*pic).allocator_data as *mut A::AllocatorData); 582 | let allocation = PictureAllocation { 583 | data: [ 584 | (*pic).data[0] as *mut u8, 585 | (*pic).data[1] as *mut u8, 586 | (*pic).data[2] as *mut u8, 587 | ], 588 | stride: (*pic).stride, 589 | allocator_data: *allocator_data, 590 | }; 591 | allocator.release_picture(allocation); 592 | } 593 | 594 | impl Decoder { 595 | /// Creates a new [`Decoder`] instance with given [`Settings`] and [`PictureAllocator`]. 596 | pub fn with_settings_and_allocator(settings: &Settings, allocator: A) -> Result { 597 | unsafe { 598 | let allocator = Arc::new(allocator); 599 | 600 | let mut dec = mem::MaybeUninit::uninit(); 601 | 602 | let settings = Dav1dSettings { 603 | allocator: Dav1dPicAllocator { 604 | cookie: &*allocator as *const A as *mut c_void, 605 | alloc_picture_callback: Some(alloc_picture_callback::), 606 | release_picture_callback: Some(release_picture_callback::), 607 | }, 608 | ..settings.dav1d_settings 609 | }; 610 | let ret = dav1d_open(dec.as_mut_ptr(), &settings); 611 | 612 | if ret < 0 { 613 | return Err(Error::from(ret)); 614 | } 615 | 616 | Ok(Decoder { 617 | dec: ptr::NonNull::new(dec.assume_init()).unwrap(), 618 | pending_data: None, 619 | allocator: Some(allocator), 620 | }) 621 | } 622 | } 623 | 624 | /// Creates a new [`Decoder`] instance with the default settings and the given 625 | /// [`PictureAllocator`]. 626 | pub fn with_allocator(allocator: A) -> Result { 627 | Self::with_settings_and_allocator(&Settings::default(), allocator) 628 | } 629 | } 630 | 631 | impl Decoder { 632 | /// Flush the decoder. 633 | /// 634 | /// This flushes all delayed frames in the decoder and clears the internal decoder state. 635 | /// 636 | /// All currently pending frames are available afterwards via [`Decoder::get_picture`]. 637 | pub fn flush(&mut self) { 638 | unsafe { 639 | dav1d_flush(self.dec.as_ptr()); 640 | if let Some(mut pending_data) = self.pending_data.take() { 641 | dav1d_data_unref(&mut pending_data); 642 | } 643 | } 644 | } 645 | 646 | /// Send new AV1 data to the decoder. 647 | /// 648 | /// After this returned `Ok(())` or `Err([Error::Again])` there might be decoded frames 649 | /// available via [`Decoder::get_picture`]. 650 | /// 651 | /// # Panics 652 | /// 653 | /// If a previous call returned [`Error::Again`] then this must not be called again until 654 | /// [`Decoder::send_pending_data`] has returned `Ok(())`. 655 | pub fn send_data + Send + 'static>( 656 | &mut self, 657 | buf: T, 658 | offset: Option, 659 | timestamp: Option, 660 | duration: Option, 661 | ) -> Result<(), Error> { 662 | assert!( 663 | self.pending_data.is_none(), 664 | "Have pending data that needs to be handled first" 665 | ); 666 | 667 | let buf = Box::new(buf); 668 | let slice = (*buf).as_ref(); 669 | let len = slice.len(); 670 | 671 | unsafe { 672 | let mut data: Dav1dData = mem::zeroed(); 673 | let _ret = dav1d_data_wrap( 674 | &mut data, 675 | slice.as_ptr(), 676 | len, 677 | Some(release_wrapped_data::), 678 | Box::into_raw(buf) as *mut c_void, 679 | ); 680 | if let Some(offset) = offset { 681 | data.m.offset = offset; 682 | } 683 | if let Some(timestamp) = timestamp { 684 | data.m.timestamp = timestamp; 685 | } 686 | if let Some(duration) = duration { 687 | data.m.duration = duration; 688 | } 689 | 690 | let ret = dav1d_send_data(self.dec.as_ptr(), &mut data); 691 | if ret < 0 { 692 | let ret = Error::from(ret); 693 | 694 | if ret.is_again() { 695 | self.pending_data = Some(data); 696 | } else { 697 | dav1d_data_unref(&mut data); 698 | } 699 | 700 | return Err(ret); 701 | } 702 | 703 | if data.sz > 0 { 704 | self.pending_data = Some(data); 705 | return Err(Error::Again); 706 | } 707 | 708 | Ok(()) 709 | } 710 | } 711 | 712 | /// Sends any pending data to the decoder. 713 | /// 714 | /// This has to be called after [`Decoder::send_data`] has returned `Err([Error::Again])` to 715 | /// consume any futher pending data. 716 | /// 717 | /// After this returned `Ok(())` or `Err([Error::Again])` there might be decoded frames 718 | /// available via [`Decoder::get_picture`]. 719 | pub fn send_pending_data(&mut self) -> Result<(), Error> { 720 | let mut data = match self.pending_data.take() { 721 | None => { 722 | return Ok(()); 723 | } 724 | Some(data) => data, 725 | }; 726 | 727 | unsafe { 728 | let ret = dav1d_send_data(self.dec.as_ptr(), &mut data); 729 | if ret < 0 { 730 | let ret = Error::from(ret); 731 | 732 | if ret.is_again() { 733 | self.pending_data = Some(data); 734 | } else { 735 | dav1d_data_unref(&mut data); 736 | } 737 | 738 | return Err(ret); 739 | } 740 | 741 | if data.sz > 0 { 742 | self.pending_data = Some(data); 743 | return Err(Error::Again); 744 | } 745 | 746 | Ok(()) 747 | } 748 | } 749 | 750 | /// Get the next decoded frame from the decoder. 751 | /// 752 | /// If this returns `Err([Error::Again])` then further data has to be sent to the decoder 753 | /// before further decoded frames become available. 754 | /// 755 | /// To make most use of frame threading this function should only be called once per submitted 756 | /// input frame and not until it returns `Err([Error::Again])`. Calling it in a loop should 757 | /// only be done to drain all pending frames at the end. 758 | pub fn get_picture(&mut self) -> Result, Error> { 759 | unsafe { 760 | let mut pic: Dav1dPicture = mem::zeroed(); 761 | let ret = dav1d_get_picture(self.dec.as_ptr(), &mut pic); 762 | 763 | if ret < 0 { 764 | Err(Error::from(ret)) 765 | } else { 766 | let inner = InnerPicture { pic }; 767 | Ok(Picture { 768 | inner: Arc::new(inner), 769 | allocator: self.allocator.clone(), 770 | }) 771 | } 772 | } 773 | } 774 | 775 | /// Get the decoder delay. 776 | pub fn get_frame_delay(&self) -> Result { 777 | unsafe { 778 | let ret = dav1d_get_frame_delay(self.dec.as_ptr()); 779 | if ret < 0 { 780 | Err(Error::from(ret)) 781 | } else { 782 | Ok(ret as u32) 783 | } 784 | } 785 | } 786 | } 787 | 788 | impl Drop for Decoder { 789 | fn drop(&mut self) { 790 | unsafe { 791 | if let Some(mut pending_data) = self.pending_data.take() { 792 | dav1d_data_unref(&mut pending_data); 793 | } 794 | let mut dec = self.dec.as_ptr(); 795 | dav1d_close(&mut dec); 796 | }; 797 | } 798 | } 799 | 800 | unsafe impl Send for Decoder {} 801 | unsafe impl Sync for Decoder {} 802 | 803 | #[derive(Debug)] 804 | struct InnerPicture { 805 | pub pic: Dav1dPicture, 806 | } 807 | 808 | /// A decoded frame. 809 | #[derive(Debug)] 810 | pub struct Picture { 811 | inner: Arc, 812 | allocator: Option>, 813 | } 814 | 815 | impl Clone for Picture { 816 | fn clone(&self) -> Self { 817 | Picture { 818 | inner: self.inner.clone(), 819 | allocator: self.allocator.clone(), 820 | } 821 | } 822 | } 823 | 824 | /// Pixel layout of a frame. 825 | #[derive(Debug, Eq, PartialEq, Copy, Clone)] 826 | pub enum PixelLayout { 827 | /// Monochrome. 828 | I400, 829 | /// 4:2:0 planar. 830 | I420, 831 | /// 4:2:2 planar. 832 | I422, 833 | /// 4:4:4 planar. 834 | I444, 835 | } 836 | 837 | /// Frame component. 838 | #[derive(Eq, PartialEq, Copy, Clone, Debug)] 839 | pub enum PlanarImageComponent { 840 | /// Y component. 841 | Y, 842 | /// U component. 843 | U, 844 | /// V component. 845 | V, 846 | } 847 | 848 | impl From for PlanarImageComponent { 849 | fn from(index: usize) -> Self { 850 | match index { 851 | 0 => PlanarImageComponent::Y, 852 | 1 => PlanarImageComponent::U, 853 | 2 => PlanarImageComponent::V, 854 | _ => panic!("Invalid YUV index: {}", index), 855 | } 856 | } 857 | } 858 | 859 | impl From for usize { 860 | fn from(component: PlanarImageComponent) -> Self { 861 | match component { 862 | PlanarImageComponent::Y => 0, 863 | PlanarImageComponent::U => 1, 864 | PlanarImageComponent::V => 2, 865 | } 866 | } 867 | } 868 | 869 | /// A single plane of a decoded frame. 870 | /// 871 | /// This can be used like a `&[u8]`. 872 | #[derive(Debug)] 873 | pub struct Plane(Picture, PlanarImageComponent); 874 | 875 | impl Clone for Plane { 876 | fn clone(&self) -> Self { 877 | Plane(self.0.clone(), self.1) 878 | } 879 | } 880 | 881 | impl AsRef<[u8]> for Plane { 882 | fn as_ref(&self) -> &[u8] { 883 | let (stride, height) = self.0.plane_data_geometry(self.1); 884 | unsafe { 885 | std::slice::from_raw_parts( 886 | self.0.plane_data_ptr(self.1) as *const u8, 887 | (stride * height) as usize, 888 | ) 889 | } 890 | } 891 | } 892 | 893 | impl std::ops::Deref for Plane { 894 | type Target = [u8]; 895 | 896 | fn deref(&self) -> &Self::Target { 897 | self.as_ref() 898 | } 899 | } 900 | 901 | static_assertions::assert_impl_all!(Plane: Send, Sync, Clone, Debug); 902 | 903 | /// Number of bits per component. 904 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 905 | pub struct BitsPerComponent(pub usize); 906 | 907 | impl Picture { 908 | /// Stride in pixels of the `component` for the decoded frame. 909 | pub fn stride(&self, component: PlanarImageComponent) -> u32 { 910 | let s = match component { 911 | PlanarImageComponent::Y => 0, 912 | _ => 1, 913 | }; 914 | self.inner.pic.stride[s] as u32 915 | } 916 | 917 | /// Raw pointer to the data of the `component` for the decoded frame. 918 | pub fn plane_data_ptr(&self, component: PlanarImageComponent) -> *mut c_void { 919 | let index: usize = component.into(); 920 | self.inner.pic.data[index] 921 | } 922 | 923 | /// Plane geometry of the `component` for the decoded frame. 924 | /// 925 | /// This returns the stride and height. 926 | pub fn plane_data_geometry(&self, component: PlanarImageComponent) -> (u32, u32) { 927 | let height = match component { 928 | PlanarImageComponent::Y => self.height(), 929 | _ => match self.pixel_layout() { 930 | PixelLayout::I420 => (self.height() + 1) / 2, 931 | PixelLayout::I400 | PixelLayout::I422 | PixelLayout::I444 => self.height(), 932 | }, 933 | }; 934 | (self.stride(component), height) 935 | } 936 | 937 | /// Plane data of the `component` for the decoded frame. 938 | pub fn plane(&self, component: PlanarImageComponent) -> Plane { 939 | Plane(self.clone(), component) 940 | } 941 | 942 | /// Bit depth of the plane data. 943 | /// 944 | /// This returns 8 or 16 for the underlying integer type used for the plane data. 945 | /// 946 | /// Check [`Picture::bits_per_component`] for the number of bits that are used. 947 | pub fn bit_depth(&self) -> usize { 948 | self.inner.pic.p.bpc as usize 949 | } 950 | 951 | /// Bits used per component of the plane data. 952 | /// 953 | /// Check [`Picture::bit_depth`] for the number of storage bits. 954 | pub fn bits_per_component(&self) -> Option { 955 | unsafe { 956 | match (*self.inner.pic.seq_hdr).hbd { 957 | 0 => Some(BitsPerComponent(8)), 958 | 1 => Some(BitsPerComponent(10)), 959 | 2 => Some(BitsPerComponent(12)), 960 | _ => None, 961 | } 962 | } 963 | } 964 | 965 | /// Width of the frame. 966 | pub fn width(&self) -> u32 { 967 | self.inner.pic.p.w as u32 968 | } 969 | 970 | /// Height of the frame. 971 | pub fn height(&self) -> u32 { 972 | self.inner.pic.p.h as u32 973 | } 974 | 975 | /// Pixel layout of the frame. 976 | pub fn pixel_layout(&self) -> PixelLayout { 977 | #[allow(non_upper_case_globals)] 978 | match self.inner.pic.p.layout { 979 | DAV1D_PIXEL_LAYOUT_I400 => PixelLayout::I400, 980 | DAV1D_PIXEL_LAYOUT_I420 => PixelLayout::I420, 981 | DAV1D_PIXEL_LAYOUT_I422 => PixelLayout::I422, 982 | DAV1D_PIXEL_LAYOUT_I444 => PixelLayout::I444, 983 | _ => unreachable!(), 984 | } 985 | } 986 | 987 | /// Timestamp of the frame. 988 | /// 989 | /// This is the same timestamp as the one provided to [`Decoder::send_data`]. 990 | pub fn timestamp(&self) -> Option { 991 | let ts = self.inner.pic.m.timestamp; 992 | if ts == i64::MIN { 993 | None 994 | } else { 995 | Some(ts) 996 | } 997 | } 998 | 999 | /// Duration of the frame. 1000 | /// 1001 | /// This is the same duration as the one provided to [`Decoder::send_data`] or `0` if none was 1002 | /// provided. 1003 | pub fn duration(&self) -> i64 { 1004 | self.inner.pic.m.duration 1005 | } 1006 | 1007 | /// Offset of the frame. 1008 | /// 1009 | /// This is the same offset as the one provided to [`Decoder::send_data`] or `-1` if none was 1010 | /// provided. 1011 | pub fn offset(&self) -> i64 { 1012 | self.inner.pic.m.offset 1013 | } 1014 | 1015 | /// Chromaticity coordinates of the source colour primaries. 1016 | pub fn color_primaries(&self) -> pixel::ColorPrimaries { 1017 | unsafe { 1018 | #[allow(non_upper_case_globals)] 1019 | match (*self.inner.pic.seq_hdr).pri { 1020 | DAV1D_COLOR_PRI_BT709 => pixel::ColorPrimaries::BT709, 1021 | DAV1D_COLOR_PRI_UNKNOWN => pixel::ColorPrimaries::Unspecified, 1022 | DAV1D_COLOR_PRI_BT470M => pixel::ColorPrimaries::BT470M, 1023 | DAV1D_COLOR_PRI_BT470BG => pixel::ColorPrimaries::BT470BG, 1024 | DAV1D_COLOR_PRI_BT601 => pixel::ColorPrimaries::BT470BG, 1025 | DAV1D_COLOR_PRI_SMPTE240 => pixel::ColorPrimaries::ST240M, 1026 | DAV1D_COLOR_PRI_FILM => pixel::ColorPrimaries::Film, 1027 | DAV1D_COLOR_PRI_BT2020 => pixel::ColorPrimaries::BT2020, 1028 | DAV1D_COLOR_PRI_XYZ => pixel::ColorPrimaries::ST428, 1029 | DAV1D_COLOR_PRI_SMPTE431 => pixel::ColorPrimaries::P3DCI, 1030 | DAV1D_COLOR_PRI_SMPTE432 => pixel::ColorPrimaries::P3Display, 1031 | DAV1D_COLOR_PRI_EBU3213 => pixel::ColorPrimaries::Tech3213, 1032 | 23..=DAV1D_COLOR_PRI_RESERVED => pixel::ColorPrimaries::Unspecified, 1033 | _ => unreachable!(), 1034 | } 1035 | } 1036 | } 1037 | 1038 | /// Transfer characteristics function. 1039 | pub fn transfer_characteristic(&self) -> pixel::TransferCharacteristic { 1040 | unsafe { 1041 | #[allow(non_upper_case_globals)] 1042 | match (*self.inner.pic.seq_hdr).trc { 1043 | DAV1D_TRC_BT709 => pixel::TransferCharacteristic::BT1886, 1044 | DAV1D_TRC_UNKNOWN => pixel::TransferCharacteristic::Unspecified, 1045 | DAV1D_TRC_BT470M => pixel::TransferCharacteristic::BT470M, 1046 | DAV1D_TRC_BT470BG => pixel::TransferCharacteristic::BT470BG, 1047 | DAV1D_TRC_BT601 => pixel::TransferCharacteristic::ST170M, 1048 | DAV1D_TRC_SMPTE240 => pixel::TransferCharacteristic::ST240M, 1049 | DAV1D_TRC_LINEAR => pixel::TransferCharacteristic::Linear, 1050 | DAV1D_TRC_LOG100 => pixel::TransferCharacteristic::Logarithmic100, 1051 | DAV1D_TRC_LOG100_SQRT10 => pixel::TransferCharacteristic::Logarithmic316, 1052 | DAV1D_TRC_IEC61966 => pixel::TransferCharacteristic::SRGB, 1053 | DAV1D_TRC_BT1361 => pixel::TransferCharacteristic::BT1886, 1054 | DAV1D_TRC_SRGB => pixel::TransferCharacteristic::SRGB, 1055 | DAV1D_TRC_BT2020_10BIT => pixel::TransferCharacteristic::BT2020Ten, 1056 | DAV1D_TRC_BT2020_12BIT => pixel::TransferCharacteristic::BT2020Twelve, 1057 | DAV1D_TRC_SMPTE2084 => pixel::TransferCharacteristic::PerceptualQuantizer, 1058 | DAV1D_TRC_SMPTE428 => pixel::TransferCharacteristic::ST428, 1059 | DAV1D_TRC_HLG => pixel::TransferCharacteristic::HybridLogGamma, 1060 | 19..=DAV1D_TRC_RESERVED => pixel::TransferCharacteristic::Unspecified, 1061 | _ => unreachable!(), 1062 | } 1063 | } 1064 | } 1065 | 1066 | /// Matrix coefficients used in deriving luma and chroma signals from the 1067 | /// green, blue and red or X, Y and Z primaries. 1068 | pub fn matrix_coefficients(&self) -> pixel::MatrixCoefficients { 1069 | unsafe { 1070 | #[allow(non_upper_case_globals)] 1071 | match (*self.inner.pic.seq_hdr).mtrx { 1072 | DAV1D_MC_IDENTITY => pixel::MatrixCoefficients::Identity, 1073 | DAV1D_MC_BT709 => pixel::MatrixCoefficients::BT709, 1074 | DAV1D_MC_UNKNOWN => pixel::MatrixCoefficients::Unspecified, 1075 | DAV1D_MC_FCC => pixel::MatrixCoefficients::BT470M, 1076 | DAV1D_MC_BT470BG => pixel::MatrixCoefficients::BT470BG, 1077 | DAV1D_MC_BT601 => pixel::MatrixCoefficients::BT470BG, 1078 | DAV1D_MC_SMPTE240 => pixel::MatrixCoefficients::ST240M, 1079 | DAV1D_MC_SMPTE_YCGCO => pixel::MatrixCoefficients::YCgCo, 1080 | DAV1D_MC_BT2020_NCL => pixel::MatrixCoefficients::BT2020NonConstantLuminance, 1081 | DAV1D_MC_BT2020_CL => pixel::MatrixCoefficients::BT2020ConstantLuminance, 1082 | DAV1D_MC_SMPTE2085 => pixel::MatrixCoefficients::ST2085, 1083 | DAV1D_MC_CHROMAT_NCL => { 1084 | pixel::MatrixCoefficients::ChromaticityDerivedNonConstantLuminance 1085 | } 1086 | DAV1D_MC_CHROMAT_CL => { 1087 | pixel::MatrixCoefficients::ChromaticityDerivedConstantLuminance 1088 | } 1089 | DAV1D_MC_ICTCP => pixel::MatrixCoefficients::ICtCp, 1090 | 15..=DAV1D_MC_RESERVED => pixel::MatrixCoefficients::Unspecified, 1091 | _ => unreachable!(), 1092 | } 1093 | } 1094 | } 1095 | 1096 | /// YUV color range. 1097 | pub fn color_range(&self) -> pixel::YUVRange { 1098 | unsafe { 1099 | match (*self.inner.pic.seq_hdr).color_range { 1100 | 0 => pixel::YUVRange::Limited, 1101 | _ => pixel::YUVRange::Full, 1102 | } 1103 | } 1104 | } 1105 | 1106 | /// Sample position for subsampled chroma. 1107 | pub fn chroma_location(&self) -> pixel::ChromaLocation { 1108 | // According to y4m mapping declared in dav1d's output/y4m2.c and applied from FFmpeg's yuv4mpegdec.c 1109 | unsafe { 1110 | match (*self.inner.pic.seq_hdr).chr { 1111 | DAV1D_CHR_UNKNOWN | DAV1D_CHR_COLOCATED => pixel::ChromaLocation::Center, 1112 | DAV1D_CHR_VERTICAL => pixel::ChromaLocation::Left, 1113 | _ => unreachable!(), 1114 | } 1115 | } 1116 | } 1117 | 1118 | /// Allocator data of the picture. 1119 | pub fn allocator_data(&self) -> Option<&A::AllocatorData> { 1120 | unsafe { 1121 | if self.inner.pic.allocator_data.is_null() { 1122 | None 1123 | } else { 1124 | Some(&*(self.inner.pic.allocator_data as *const A::AllocatorData)) 1125 | } 1126 | } 1127 | } 1128 | 1129 | /// Content light level information. 1130 | pub fn content_light(&self) -> Option { 1131 | unsafe { 1132 | if self.inner.pic.content_light.is_null() { 1133 | None 1134 | } else { 1135 | Some(ContentLightLevel { 1136 | max_content_light_level: (*self.inner.pic.content_light) 1137 | .max_content_light_level, 1138 | max_frame_average_light_level: (*self.inner.pic.content_light) 1139 | .max_frame_average_light_level, 1140 | }) 1141 | } 1142 | } 1143 | } 1144 | 1145 | ///Mastering display information. 1146 | pub fn mastering_display(&self) -> Option { 1147 | unsafe { 1148 | if self.inner.pic.mastering_display.is_null() { 1149 | None 1150 | } else { 1151 | Some(MasteringDisplay { 1152 | primaries: (*self.inner.pic.mastering_display).primaries, 1153 | white_point: (*self.inner.pic.mastering_display).white_point, 1154 | max_luminance: (*self.inner.pic.mastering_display).max_luminance, 1155 | min_luminance: (*self.inner.pic.mastering_display).min_luminance, 1156 | }) 1157 | } 1158 | } 1159 | } 1160 | } 1161 | 1162 | static_assertions::assert_impl_all!(Picture: Send, Sync, Clone, Debug); 1163 | 1164 | unsafe impl Send for InnerPicture {} 1165 | unsafe impl Sync for InnerPicture {} 1166 | 1167 | impl Drop for InnerPicture { 1168 | fn drop(&mut self) { 1169 | unsafe { 1170 | dav1d_picture_unref(&mut self.pic); 1171 | } 1172 | } 1173 | } 1174 | 1175 | #[cfg(test)] 1176 | mod test { 1177 | use std::{ 1178 | collections::HashSet, 1179 | fmt, ptr, 1180 | sync::{self, atomic}, 1181 | }; 1182 | 1183 | mod ivf { 1184 | use bitstream_io::{ByteRead, ByteReader, LittleEndian}; 1185 | use std::io; 1186 | 1187 | #[derive(Debug, PartialEq, Eq)] 1188 | pub struct Header { 1189 | pub tag: [u8; 4], 1190 | pub w: u16, 1191 | pub h: u16, 1192 | pub timebase_num: u32, 1193 | pub timebase_den: u32, 1194 | } 1195 | 1196 | pub fn read_header(r: &mut dyn io::Read) -> io::Result
{ 1197 | let mut br = ByteReader::endian(r, LittleEndian); 1198 | 1199 | let mut signature = [0u8; 4]; 1200 | let mut tag = [0u8; 4]; 1201 | 1202 | br.read_bytes(&mut signature)?; 1203 | let _v0 = br.read::()?; 1204 | let _v1 = br.read::()?; 1205 | br.read_bytes(&mut tag)?; 1206 | 1207 | let w = br.read::()?; 1208 | let h = br.read::()?; 1209 | 1210 | let timebase_den = br.read::()?; 1211 | let timebase_num = br.read::()?; 1212 | 1213 | let _ = br.read::()?; 1214 | let _ = br.read::()?; 1215 | 1216 | Ok(Header { 1217 | tag, 1218 | w, 1219 | h, 1220 | timebase_num, 1221 | timebase_den, 1222 | }) 1223 | } 1224 | 1225 | pub struct Packet { 1226 | pub data: Box<[u8]>, 1227 | pub pts: u64, 1228 | } 1229 | 1230 | pub fn read_packet(r: &mut dyn io::Read) -> io::Result { 1231 | let mut br = ByteReader::endian(r, LittleEndian); 1232 | 1233 | let len = br.read::()?; 1234 | let pts = br.read::()?; 1235 | let mut buf = vec![0u8; len as usize]; 1236 | 1237 | br.read_bytes(&mut buf)?; 1238 | 1239 | Ok(Packet { 1240 | data: buf.into_boxed_slice(), 1241 | pts, 1242 | }) 1243 | } 1244 | } 1245 | 1246 | static TEST_FILE_420_8: &[u8] = include_bytes!("../test-420-8.ivf"); 1247 | static TEST_FILE_420_12: &[u8] = include_bytes!("../test-420-12.ivf"); 1248 | 1249 | fn handle_pending_pictures( 1250 | dec: &mut super::Decoder, 1251 | pictures: &mut Vec>, 1252 | drain: bool, 1253 | ) { 1254 | loop { 1255 | match dec.get_picture() { 1256 | Ok(p) => { 1257 | println!("{:?}", p); 1258 | pictures.push(p); 1259 | } 1260 | // Need to send more data to the decoder before it can decode new pictures 1261 | Err(e) if e.is_again() => return, 1262 | Err(e) => { 1263 | panic!("Error getting decoded pictures: {}", e); 1264 | } 1265 | } 1266 | 1267 | if !drain { 1268 | break; 1269 | } 1270 | } 1271 | } 1272 | 1273 | fn check_pictures(pictures: &[super::Picture], bpp: usize) { 1274 | assert_eq!(pictures.len(), 5); 1275 | 1276 | let pts = [0, 33, 67, 100, 133]; 1277 | 1278 | for (i, picture) in pictures.iter().enumerate() { 1279 | assert_eq!(picture.width(), 320); 1280 | assert_eq!(picture.height(), 240); 1281 | assert_eq!(picture.bit_depth(), bpp); 1282 | assert_eq!( 1283 | picture.bits_per_component(), 1284 | Some(super::BitsPerComponent(bpp)) 1285 | ); 1286 | assert_eq!(picture.pixel_layout(), super::PixelLayout::I420); 1287 | assert_eq!( 1288 | picture.color_primaries(), 1289 | super::pixel::ColorPrimaries::BT709 1290 | ); 1291 | assert_eq!( 1292 | picture.transfer_characteristic(), 1293 | super::pixel::TransferCharacteristic::BT1886 1294 | ); 1295 | assert_eq!( 1296 | picture.matrix_coefficients(), 1297 | super::pixel::MatrixCoefficients::BT709, 1298 | ); 1299 | assert_eq!( 1300 | picture.chroma_location(), 1301 | super::pixel::ChromaLocation::Center, 1302 | ); 1303 | assert_eq!(picture.timestamp(), Some(pts[i])); 1304 | assert_eq!(picture.offset(), i as i64); 1305 | 1306 | let stride_mult = if bpp == 8 { 1 } else { 2 }; 1307 | 1308 | assert!(picture.stride(super::PlanarImageComponent::Y) >= 320 * stride_mult); 1309 | assert!(picture.stride(super::PlanarImageComponent::U) >= 160 * stride_mult); 1310 | assert!(picture.stride(super::PlanarImageComponent::V) >= 160 * stride_mult); 1311 | 1312 | assert_eq!( 1313 | picture 1314 | .plane_data_geometry(super::PlanarImageComponent::Y) 1315 | .1, 1316 | 240 1317 | ); 1318 | assert_eq!( 1319 | picture 1320 | .plane_data_geometry(super::PlanarImageComponent::U) 1321 | .1, 1322 | 120 1323 | ); 1324 | assert_eq!( 1325 | picture 1326 | .plane_data_geometry(super::PlanarImageComponent::V) 1327 | .1, 1328 | 120 1329 | ); 1330 | 1331 | assert_eq!( 1332 | picture.plane(super::PlanarImageComponent::Y).len(), 1333 | picture.stride(super::PlanarImageComponent::Y) as usize * 240 1334 | ); 1335 | 1336 | assert_eq!( 1337 | picture.plane(super::PlanarImageComponent::U).len(), 1338 | picture.stride(super::PlanarImageComponent::U) as usize * 120 1339 | ); 1340 | 1341 | assert_eq!( 1342 | picture.plane(super::PlanarImageComponent::V).len(), 1343 | picture.stride(super::PlanarImageComponent::V) as usize * 120 1344 | ); 1345 | } 1346 | } 1347 | 1348 | fn decode_file( 1349 | file: &[u8], 1350 | mut dec: super::Decoder, 1351 | pictures: &mut Vec>, 1352 | ) { 1353 | use std::io; 1354 | 1355 | let mut r = io::BufReader::new(file); 1356 | let header = ivf::read_header(&mut r).unwrap(); 1357 | println!("{:?}", header); 1358 | 1359 | let mut idx = 0; 1360 | 1361 | while let Ok(packet) = ivf::read_packet(&mut r) { 1362 | println!("Packet {}", packet.pts); 1363 | 1364 | // Let's use millisecond timestamps 1365 | let pts = 1366 | 1000 * packet.pts as i64 * header.timebase_num as i64 / header.timebase_den as i64; 1367 | 1368 | // Send packet to the decoder 1369 | match dec.send_data(packet.data, Some(idx), Some(pts), None) { 1370 | Err(e) if e.is_again() => { 1371 | // If the decoder did not consume all data, output all 1372 | // pending pictures and send pending data to the decoder 1373 | // until it is all used up. 1374 | loop { 1375 | handle_pending_pictures(&mut dec, pictures, false); 1376 | 1377 | match dec.send_pending_data() { 1378 | Err(e) if e.is_again() => continue, 1379 | Err(e) => { 1380 | panic!("Error sending pending data to the decoder: {}", e); 1381 | } 1382 | _ => break, 1383 | } 1384 | } 1385 | } 1386 | Err(e) => { 1387 | panic!("Error sending data to the decoder: {}", e); 1388 | } 1389 | _ => (), 1390 | } 1391 | 1392 | // Handle all pending pictures before sending the next data. 1393 | handle_pending_pictures(&mut dec, pictures, false); 1394 | 1395 | idx += 1; 1396 | } 1397 | 1398 | // Handle all pending pictures that were not output yet. 1399 | handle_pending_pictures(&mut dec, pictures, true); 1400 | } 1401 | 1402 | #[test] 1403 | fn test_basic_420_8() { 1404 | let dec = super::Decoder::new().expect("failed to create decoder instance"); 1405 | let mut pictures = vec![]; 1406 | decode_file(TEST_FILE_420_8, dec, &mut pictures); 1407 | check_pictures(&pictures, 8); 1408 | } 1409 | 1410 | #[test] 1411 | fn test_basic_420_12() { 1412 | let dec = super::Decoder::new().expect("failed to create decoder instance"); 1413 | let mut pictures = vec![]; 1414 | decode_file(TEST_FILE_420_12, dec, &mut pictures); 1415 | check_pictures(&pictures, 12); 1416 | } 1417 | 1418 | #[derive(Debug)] 1419 | struct TestAllocator { 1420 | counter: atomic::AtomicUsize, 1421 | allocated: sync::Arc, 1422 | } 1423 | 1424 | impl TestAllocator { 1425 | fn new(allocated: &sync::Arc) -> Self { 1426 | TestAllocator { 1427 | counter: atomic::AtomicUsize::new(0), 1428 | allocated: allocated.clone(), 1429 | } 1430 | } 1431 | } 1432 | 1433 | unsafe impl super::PictureAllocator for TestAllocator { 1434 | type AllocatorData = (usize, [std::alloc::Layout; 2]); 1435 | 1436 | unsafe fn alloc_picture( 1437 | &self, 1438 | pic_params: &crate::PictureParameters, 1439 | ) -> Result, crate::Error> { 1440 | fn align(x: usize) -> usize { 1441 | (x + 128 - 1) & !(128 - 1) 1442 | } 1443 | 1444 | let stride_mult = if pic_params.bit_depth() == 8 { 1 } else { 2 }; 1445 | 1446 | let (stride, height) = match pic_params.pixel_layout() { 1447 | crate::PixelLayout::I400 => ( 1448 | [align(pic_params.width() as usize) * stride_mult, 0], 1449 | [align(pic_params.height() as usize), 0], 1450 | ), 1451 | crate::PixelLayout::I420 => ( 1452 | [ 1453 | align(pic_params.width() as usize) * stride_mult, 1454 | align((pic_params.width() as usize + 1) / 2) * stride_mult, 1455 | ], 1456 | [ 1457 | align(pic_params.height() as usize), 1458 | align((pic_params.height() as usize + 1) / 2), 1459 | ], 1460 | ), 1461 | crate::PixelLayout::I422 => ( 1462 | [ 1463 | align(pic_params.width() as usize) * stride_mult, 1464 | align((pic_params.width() as usize + 1) / 2) * stride_mult, 1465 | ], 1466 | [ 1467 | align(pic_params.height() as usize), 1468 | align(pic_params.height() as usize), 1469 | ], 1470 | ), 1471 | crate::PixelLayout::I444 => ( 1472 | [ 1473 | align(pic_params.width() as usize) * stride_mult, 1474 | align(pic_params.width() as usize) * stride_mult, 1475 | ], 1476 | [ 1477 | align(pic_params.height() as usize), 1478 | align(pic_params.height() as usize), 1479 | ], 1480 | ), 1481 | }; 1482 | 1483 | let layout_0 = std::alloc::Layout::from_size_align( 1484 | height[0] * stride[0], 1485 | super::PICTURE_ALIGNMENT, 1486 | ) 1487 | .unwrap(); 1488 | 1489 | let data_0 = std::alloc::alloc(layout_0); 1490 | 1491 | let layout_1; 1492 | let data_1; 1493 | let data_2; 1494 | if stride[1] > 0 { 1495 | layout_1 = std::alloc::Layout::from_size_align( 1496 | height[1] * stride[1], 1497 | super::PICTURE_ALIGNMENT, 1498 | ) 1499 | .unwrap(); 1500 | data_1 = std::alloc::alloc(layout_1); 1501 | data_2 = std::alloc::alloc(layout_1); 1502 | } else { 1503 | layout_1 = layout_0; 1504 | data_1 = ptr::null_mut(); 1505 | data_2 = ptr::null_mut(); 1506 | } 1507 | 1508 | self.allocated.fetch_add(1, atomic::Ordering::SeqCst); 1509 | 1510 | Ok(super::PictureAllocation { 1511 | data: [data_0, data_1, data_2], 1512 | stride: [stride[0] as isize, stride[1] as isize], 1513 | allocator_data: ( 1514 | self.counter.fetch_add(1, atomic::Ordering::SeqCst), 1515 | [layout_0, layout_1], 1516 | ), 1517 | }) 1518 | } 1519 | 1520 | unsafe fn release_picture( 1521 | &self, 1522 | allocation: crate::PictureAllocation, 1523 | ) { 1524 | let prev = self.allocated.fetch_sub(1, atomic::Ordering::SeqCst); 1525 | assert!(prev > 0); 1526 | 1527 | std::alloc::dealloc(allocation.data[0], allocation.allocator_data.1[0]); 1528 | if !allocation.data[1].is_null() { 1529 | std::alloc::dealloc(allocation.data[1], allocation.allocator_data.1[1]); 1530 | std::alloc::dealloc(allocation.data[2], allocation.allocator_data.1[1]); 1531 | } 1532 | } 1533 | } 1534 | 1535 | #[test] 1536 | fn test_allocator_420_8() { 1537 | let allocated = sync::Arc::new(atomic::AtomicUsize::new(0)); 1538 | 1539 | let dec = super::Decoder::with_allocator(TestAllocator::new(&allocated)) 1540 | .expect("failed to create decoder instance"); 1541 | 1542 | let mut pictures = vec![]; 1543 | decode_file(TEST_FILE_420_8, dec, &mut pictures); 1544 | check_pictures(&pictures, 8); 1545 | 1546 | let mut indices = HashSet::new(); 1547 | for picture in &pictures { 1548 | let allocator_data = picture.allocator_data().unwrap(); 1549 | assert!(indices.insert(allocator_data.0)); 1550 | } 1551 | assert_eq!(indices.len(), 5); 1552 | 1553 | assert_eq!(allocated.load(atomic::Ordering::SeqCst), 5); 1554 | drop(pictures); 1555 | assert_eq!(allocated.load(atomic::Ordering::SeqCst), 0); 1556 | } 1557 | 1558 | #[test] 1559 | fn test_allocator_420_12() { 1560 | let allocated = sync::Arc::new(atomic::AtomicUsize::new(0)); 1561 | 1562 | let dec = super::Decoder::with_allocator(TestAllocator::new(&allocated)) 1563 | .expect("failed to create decoder instance"); 1564 | 1565 | let mut pictures = vec![]; 1566 | decode_file(TEST_FILE_420_12, dec, &mut pictures); 1567 | check_pictures(&pictures, 12); 1568 | 1569 | let mut indices = HashSet::new(); 1570 | for picture in &pictures { 1571 | let allocator_data = picture.allocator_data().unwrap(); 1572 | assert!(indices.insert(allocator_data.0)); 1573 | } 1574 | assert_eq!(indices.len(), 5); 1575 | 1576 | assert_eq!(allocated.load(atomic::Ordering::SeqCst), 5); 1577 | drop(pictures); 1578 | assert_eq!(allocated.load(atomic::Ordering::SeqCst), 0); 1579 | } 1580 | } 1581 | 1582 | /// Content light level information as specified in CEA-861.3, Appendix A. 1583 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 1584 | pub struct ContentLightLevel { 1585 | /// Maximum content light level (MaxCLL) in candela per square metre. 1586 | pub max_content_light_level: u16, 1587 | /// Maximum frame average light level (MaxFLL) in candela per square metre. 1588 | pub max_frame_average_light_level: u16, 1589 | } 1590 | 1591 | /// Mastering display information as specified in SMPTE ST 2086. 1592 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 1593 | pub struct MasteringDisplay { 1594 | /// Red/green/blue XY coordinates of primaries in CIE 1931 color space as 0.16 fixed-point number. 1595 | pub primaries: [[u16; 2usize]; 3usize], 1596 | /// XY coordinates of white point in CIE 1931 color space as 0.16 fixed-point number. 1597 | pub white_point: [u16; 2usize], 1598 | /// Maximum luminance in candela per square metre as 24.8 fixed-point number. 1599 | pub max_luminance: u32, 1600 | /// Minimum luminance in candela per square metre as 18.14 fixed-point number. 1601 | pub min_luminance: u32, 1602 | } 1603 | -------------------------------------------------------------------------------- /test-420-12.ivf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-av/dav1d-rs/9f62f346c6841d1af4ab3eb8267c73041c8c4ee6/test-420-12.ivf -------------------------------------------------------------------------------- /test-420-8.ivf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-av/dav1d-rs/9f62f346c6841d1af4ab3eb8267c73041c8c4ee6/test-420-8.ivf -------------------------------------------------------------------------------- /tools/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dav1d-tools" 3 | version = "0.1.0" 4 | authors = ["Luca Barbato "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | dav1d = { path = "..", version = "0.11" } 9 | bitstream-io = "4.0" 10 | log = "0.4" 11 | structopt = "0.3" 12 | -------------------------------------------------------------------------------- /tools/src/main.rs: -------------------------------------------------------------------------------- 1 | mod ivf { 2 | use bitstream_io::{ByteRead, ByteReader, LittleEndian}; 3 | use std::io; 4 | 5 | #[derive(Debug, PartialEq, Eq)] 6 | pub struct Header { 7 | pub tag: [u8; 4], 8 | pub w: u16, 9 | pub h: u16, 10 | pub timebase_num: u32, 11 | pub timebase_den: u32, 12 | } 13 | 14 | pub fn read_header(r: &mut dyn io::Read) -> io::Result
{ 15 | let mut br = ByteReader::endian(r, LittleEndian); 16 | 17 | let mut signature = [0u8; 4]; 18 | let mut tag = [0u8; 4]; 19 | 20 | br.read_bytes(&mut signature)?; 21 | let v0 = br.read::()?; 22 | let v1 = br.read::()?; 23 | br.read_bytes(&mut tag)?; 24 | 25 | log::debug!("sign {:?} version {} {} tag {:?}", &signature, v0, v1, &tag); 26 | 27 | let w = br.read::()?; 28 | let h = br.read::()?; 29 | 30 | let timebase_den = br.read::()?; 31 | let timebase_num = br.read::()?; 32 | 33 | let _ = br.read::()?; 34 | let _ = br.read::()?; 35 | 36 | Ok(Header { 37 | tag, 38 | w, 39 | h, 40 | timebase_num, 41 | timebase_den, 42 | }) 43 | } 44 | 45 | pub struct Packet { 46 | pub data: Box<[u8]>, 47 | pub pts: u64, 48 | } 49 | 50 | pub fn read_packet(r: &mut dyn io::Read) -> io::Result { 51 | let mut br = ByteReader::endian(r, LittleEndian); 52 | 53 | let len = br.read::()?; 54 | let pts = br.read::()?; 55 | let mut buf = vec![0u8; len as usize]; 56 | 57 | br.read_bytes(&mut buf)?; 58 | 59 | Ok(Packet { 60 | data: buf.into_boxed_slice(), 61 | pts, 62 | }) 63 | } 64 | } 65 | 66 | use structopt::*; 67 | 68 | #[derive(StructOpt, Debug)] 69 | struct Opt { 70 | #[structopt(name = "FILE", parse(from_os_str))] 71 | input: std::path::PathBuf, 72 | } 73 | 74 | use std::fs::File; 75 | use std::io::BufReader; 76 | 77 | fn handle_pending_pictures(dec: &mut dav1d::Decoder, drain: bool) { 78 | loop { 79 | match dec.get_picture() { 80 | Ok(p) => println!("{:?}", p), 81 | // Need to send more data to the decoder before it can decode new pictures 82 | Err(e) if e.is_again() => return, 83 | Err(e) => { 84 | panic!("Error getting decoded pictures: {}", e); 85 | } 86 | } 87 | 88 | if !drain { 89 | break; 90 | } 91 | } 92 | } 93 | 94 | fn main() -> std::io::Result<()> { 95 | let opt = Opt::from_args(); 96 | 97 | let file = File::open(opt.input)?; 98 | let mut r = BufReader::new(file); 99 | let header = ivf::read_header(&mut r)?; 100 | println!("{:?}", header); 101 | 102 | let mut dec = dav1d::Decoder::new().expect("failed to create decoder instance"); 103 | 104 | while let Ok(packet) = ivf::read_packet(&mut r) { 105 | println!("Packet {}", packet.pts); 106 | 107 | // Send packet to the decoder 108 | match dec.send_data(packet.data, None, None, None) { 109 | Err(e) if e.is_again() => { 110 | // If the decoder did not consume all data, output all 111 | // pending pictures and send pending data to the decoder 112 | // until it is all used up. 113 | loop { 114 | handle_pending_pictures(&mut dec, false); 115 | 116 | match dec.send_pending_data() { 117 | Err(e) if e.is_again() => continue, 118 | Err(e) => { 119 | panic!("Error sending pending data to the decoder: {}", e); 120 | } 121 | _ => break, 122 | } 123 | } 124 | } 125 | Err(e) => { 126 | panic!("Error sending data to the decoder: {}", e); 127 | } 128 | _ => (), 129 | } 130 | 131 | // Handle all pending pictures before sending the next data. 132 | handle_pending_pictures(&mut dec, false); 133 | } 134 | 135 | // Handle all pending pictures that were not output yet. 136 | handle_pending_pictures(&mut dec, true); 137 | 138 | Ok(()) 139 | } 140 | --------------------------------------------------------------------------------