dav1d-0.11.1/.cargo_vcs_info.json0000644000000001360000000000100121130ustar { "git": { "sha1": "03477a36c3de4f2aacbab81a8951150ffdb9c4c3" }, "path_in_vcs": "" }dav1d-0.11.1/.github/dependabot.yml000064400000000000000000000003041046102023000150700ustar 00000000000000version: 2 updates: - package-ecosystem: cargo directory: "/" schedule: interval: daily open-pull-requests-limit: 10 ignore: - dependency-name: system-deps versions: - 3.0.0 dav1d-0.11.1/.github/workflows/dav1d.yml000064400000000000000000000052071046102023000160260ustar 00000000000000name: dav1d on: [push, pull_request] jobs: linux-tests: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - name: Install nasm uses: ilammy/setup-nasm@v1 - name: Install Rust stable uses: dtolnay/rust-toolchain@stable with: toolchain: stable - name: Install Python 3.9 uses: actions/setup-python@v4 with: python-version: '3.9' - name: Install pip packages run: | pip install -U pip pip install -U wheel setuptools pip install -U meson ninja - name: Build dav1d env: DAV1D_DIR: dav1d_dir LIB_PATH: lib/x86_64-linux-gnu run: | git clone --branch 1.5.0 --depth 1 https://code.videolan.org/videolan/dav1d.git cd dav1d meson build -Dprefix=$HOME/$DAV1D_DIR -Denable_tools=false -Denable_examples=false --buildtype release ninja -C build ninja -C build install echo "PKG_CONFIG_PATH=$HOME/$DAV1D_DIR/$LIB_PATH/pkgconfig" >> $GITHUB_ENV echo "LD_LIBRARY_PATH=$HOME/$DAV1D_DIR/$LIB_PATH" >> $GITHUB_ENV - name: Run tests run: | cargo test --workspace --all-features windows-tests-msvc: runs-on: windows-latest steps: - uses: actions/checkout@v3 - uses: ilammy/msvc-dev-cmd@v1 - name: Install nasm uses: ilammy/setup-nasm@v1 - name: Install Rust stable MSVC uses: dtolnay/rust-toolchain@stable with: toolchain: stable-x86_64-pc-windows-msvc - name: Install Python 3.9 uses: actions/setup-python@v4 with: python-version: '3.9' - name: Install pip packages run: | pip install -U pip pip install -U wheel setuptools pip install -U meson ninja - name: Setting up environment shell: bash run: | echo "PKG_CONFIG=c:\build\bin\pkg-config.exe" >> $GITHUB_ENV echo "PKG_CONFIG_PATH=C:\build\lib\pkgconfig" >> $GITHUB_ENV echo "C:\build\bin" >> $GITHUB_PATH - name: Build pkg-config run: | git clone --branch meson-glib-subproject --depth 1 https://gitlab.freedesktop.org/tpm/pkg-config.git cd pkg-config meson build -Dprefix=C:\build --buildtype release ninja -C build ninja -C build install - name: Build dav1d run: | git clone --branch 1.5.0 --depth 1 https://code.videolan.org/videolan/dav1d.git cd dav1d meson build -Dprefix=C:\build -Denable_tools=false -Denable_examples=false --buildtype release ninja -C build ninja -C build install - name: Run tests run: | cargo test --workspace --all-features dav1d-0.11.1/.gitignore000064400000000000000000000000361046102023000126720ustar 00000000000000target/ **/*.rs.bk Cargo.lock dav1d-0.11.1/Cargo.lock0000644000000207530000000000100100750ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "av-data" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fca67ba5d317924c02180c576157afd54babe48a76ebc66ce6d34bb8ba08308e" dependencies = [ "byte-slice-cast", "bytes", "num-derive", "num-rational", "num-traits", ] [[package]] name = "bitflags" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bitstream-io" version = "4.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" dependencies = [ "core2", ] [[package]] name = "byte-slice-cast" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" [[package]] name = "bytes" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "cfg-expr" version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9acd0bdbbf4b2612d09f52ba61da432140cb10930354079d0d53fafc12968726" dependencies = [ "smallvec", "target-lexicon", ] [[package]] name = "core2" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" dependencies = [ "memchr", ] [[package]] name = "dav1d" version = "0.11.1" dependencies = [ "av-data", "bitflags", "bitstream-io", "dav1d-sys", "static_assertions", ] [[package]] name = "dav1d-sys" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c91aea6668645415331133ed6f8ddf0e7f40160cd97a12d59e68716a58704b" dependencies = [ "libc", "system-deps", ] [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "indexmap" version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "libc" version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "num-bigint" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", ] [[package]] name = "num-derive" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "num-integer" version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ "num-traits", ] [[package]] name = "num-rational" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ "num-bigint", "num-integer", "num-traits", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "proc-macro2" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] [[package]] name = "serde_core" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_spanned" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" dependencies = [ "serde_core", ] [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "system-deps" version = "7.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c8f33736f986f16d69b6cb8b03f55ddcad5c41acc4ccc39dd88e84aa805e7f" dependencies = [ "cfg-expr", "heck", "pkg-config", "toml", "version-compare", ] [[package]] name = "target-lexicon" version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" [[package]] name = "toml" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ "indexmap", "serde_core", "serde_spanned", "toml_datetime", "toml_parser", "toml_writer", "winnow", ] [[package]] name = "toml_datetime" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" dependencies = [ "serde_core", ] [[package]] name = "toml_parser" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" dependencies = [ "winnow", ] [[package]] name = "toml_writer" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "version-compare" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" [[package]] name = "winnow" version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dav1d-0.11.1/Cargo.toml0000644000000021700000000000100101110ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.78" name = "dav1d" version = "0.11.1" authors = ["Luca Barbato "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "libdav1d bindings" readme = "README.md" keywords = [ "dav1d-rs", "av1", ] license = "MIT" repository = "https://github.com/rust-av/dav1d-rs" [features] [lib] name = "dav1d" path = "src/lib.rs" [dependencies.av-data] version = "0.4.2" [dependencies.bitflags] version = "2" [dependencies.dav1d-sys] version = "0.8.2" [dependencies.static_assertions] version = "1" [dev-dependencies.bitstream-io] version = "4.0" dav1d-0.11.1/Cargo.toml.orig000064400000000000000000000007751046102023000136030ustar 00000000000000[package] authors = ["Luca Barbato "] description = "libdav1d bindings" edition = "2021" keywords = ["dav1d-rs","av1"] license = "MIT" name = "dav1d" readme = "README.md" repository = "https://github.com/rust-av/dav1d-rs" version = "0.11.1" rust-version = "1.78" [dependencies] bitflags = "2" dav1d-sys = { version = "0.8.2", path = "dav1d-sys" } av-data = "0.4.2" static_assertions = "1" [dev-dependencies] bitstream-io = "4.0" [features] [workspace] members = ["dav1d-sys", "tools"] dav1d-0.11.1/LICENSE000064400000000000000000000020551046102023000117120ustar 00000000000000MIT License Copyright (c) 2018 Luca Barbato Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. dav1d-0.11.1/README.md000064400000000000000000000032431046102023000121640ustar 00000000000000# 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) It is a simple FFI binding and safe abstraction over [dav1d][1]. ## Building 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 supported operating systems. ### Overriding the dav1d library The bindings use [system-deps](https://docs.rs/system-deps) to find dav1d. You may override the `PKG_CONFIG_PATH` or direcly set the env vars `SYSTEM_DEPS_DAV1D_SEARCH_NATIVE` and/or `SYSTEM_DEPS_DAV1D_LIB`. ## Building with vcpkg for Windows x64 To build with [vcpkg](https://vcpkg.io/en/index.html), you need to follow these steps: 1. Install `pkg-config` through [chocolatey](https://chocolatey.org/) choco install pkgconfiglite 2. Install `dav1d` vcpkg install dav1d:x64-windows 3. Add to the `PKG_CONFIG_PATH` environment variable the path `$VCPKG_INSTALLATION_ROOT\installed\x64-windows\lib\pkgconfig` 4. Build code cargo build --workspace To speed up the computation, you can build your packages only in `Release` mode adding the `set(VCPKG_BUILD_TYPE release)` line to the `$VCPKG_INSTALLATION_ROOT\triplets\x64-windows.cmake` file. Building for Windows x86 is the same, just replace `x64` with `x86` in the steps above. ## Supported versions The bindings require dav1d>=1.3.0 ( Might not work for >1.5.0 ) ## TODO - [x] Simple bindings - [x] Safe abstraction - [ ] Examples [1]: https://code.videolan.org/videolan/dav1d dav1d-0.11.1/src/lib.rs000064400000000000000000001547101046102023000126160ustar 00000000000000use dav1d_sys::*; pub use av_data::pixel; use std::ffi::{c_int, c_void}; use std::fmt::{self, Debug}; use std::i64; use std::mem; use std::ptr; use std::sync::Arc; /// Error enum return by various `dav1d` operations. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[non_exhaustive] pub enum Error { /// Try again. /// /// If this is returned by [`Decoder::send_data`] or [`Decoder::send_pending_data`] then there /// are decoded frames pending that first have to be retrieved via [`Decoder::get_picture`] /// before processing any further pending data. /// /// If this is returned by [`Decoder::get_picture`] then no decoded frames are pending /// currently and more data needs to be sent to the decoder. Again, /// Invalid argument. /// /// One of the arguments passed to the function was invalid. InvalidArgument, /// Not enough memory. /// /// Not enough memory is currently available for performing this operation. NotEnoughMemory, /// Unsupported bitstream. /// /// The provided bitstream is not supported by `dav1d`. UnsupportedBitstream, /// Unknown error. UnknownError(i32), } impl From for Error { fn from(err: i32) -> Self { assert!(err < 0); match err { DAV1D_ERR_AGAIN => Error::Again, DAV1D_ERR_INVAL => Error::InvalidArgument, DAV1D_ERR_NOMEM => Error::NotEnoughMemory, DAV1D_ERR_NOPROTOOPT => Error::UnsupportedBitstream, _ => Error::UnknownError(err), } } } impl Error { pub const fn is_again(&self) -> bool { matches!(self, Error::Again) } } impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match self { Error::Again => write!(fmt, "Try again"), Error::InvalidArgument => write!(fmt, "Invalid argument"), Error::NotEnoughMemory => write!(fmt, "Not enough memory available"), Error::UnsupportedBitstream => write!(fmt, "Unsupported bitstream"), Error::UnknownError(err) => write!(fmt, "Unknown error {}", err), } } } impl std::error::Error for Error {} /// Picture parameters used for allocation. #[derive(Debug)] pub struct PictureParameters { pic: ptr::NonNull, } impl PictureParameters { /// Bit depth of the plane data. /// /// This returns 8 or 16 for the underlying integer type used for the plane data. /// /// Check [`PictureParameters::bits_per_component`] for the number of bits that are used. pub fn bit_depth(&self) -> usize { unsafe { self.pic.as_ref().p.bpc as usize } } /// Bits used per component of the plane data. /// /// Check [`PictureParameters::bit_depth`] for the number of storage bits. pub fn bits_per_component(&self) -> Option { unsafe { match (*self.pic.as_ref().seq_hdr).hbd { 0 => Some(BitsPerComponent(8)), 1 => Some(BitsPerComponent(10)), 2 => Some(BitsPerComponent(12)), _ => None, } } } /// Width of the frame. pub fn width(&self) -> u32 { unsafe { self.pic.as_ref().p.w as u32 } } /// Height of the frame. pub fn height(&self) -> u32 { unsafe { self.pic.as_ref().p.h as u32 } } /// Pixel layout of the frame. pub fn pixel_layout(&self) -> PixelLayout { unsafe { #[allow(non_upper_case_globals)] match self.pic.as_ref().p.layout { DAV1D_PIXEL_LAYOUT_I400 => PixelLayout::I400, DAV1D_PIXEL_LAYOUT_I420 => PixelLayout::I420, DAV1D_PIXEL_LAYOUT_I422 => PixelLayout::I422, DAV1D_PIXEL_LAYOUT_I444 => PixelLayout::I444, _ => unreachable!(), } } } /// Chromaticity coordinates of the source colour primaries. pub fn color_primaries(&self) -> pixel::ColorPrimaries { unsafe { #[allow(non_upper_case_globals)] match (*self.pic.as_ref().seq_hdr).pri { DAV1D_COLOR_PRI_BT709 => pixel::ColorPrimaries::BT709, DAV1D_COLOR_PRI_UNKNOWN => pixel::ColorPrimaries::Unspecified, DAV1D_COLOR_PRI_BT470M => pixel::ColorPrimaries::BT470M, DAV1D_COLOR_PRI_BT470BG => pixel::ColorPrimaries::BT470BG, DAV1D_COLOR_PRI_BT601 => pixel::ColorPrimaries::BT470BG, DAV1D_COLOR_PRI_SMPTE240 => pixel::ColorPrimaries::ST240M, DAV1D_COLOR_PRI_FILM => pixel::ColorPrimaries::Film, DAV1D_COLOR_PRI_BT2020 => pixel::ColorPrimaries::BT2020, DAV1D_COLOR_PRI_XYZ => pixel::ColorPrimaries::ST428, DAV1D_COLOR_PRI_SMPTE431 => pixel::ColorPrimaries::P3DCI, DAV1D_COLOR_PRI_SMPTE432 => pixel::ColorPrimaries::P3Display, DAV1D_COLOR_PRI_EBU3213 => pixel::ColorPrimaries::Tech3213, 23..=DAV1D_COLOR_PRI_RESERVED => pixel::ColorPrimaries::Unspecified, _ => unreachable!(), } } } /// Transfer characteristics function. pub fn transfer_characteristic(&self) -> pixel::TransferCharacteristic { unsafe { #[allow(non_upper_case_globals)] match (*self.pic.as_ref().seq_hdr).trc { DAV1D_TRC_BT709 => pixel::TransferCharacteristic::BT1886, DAV1D_TRC_UNKNOWN => pixel::TransferCharacteristic::Unspecified, DAV1D_TRC_BT470M => pixel::TransferCharacteristic::BT470M, DAV1D_TRC_BT470BG => pixel::TransferCharacteristic::BT470BG, DAV1D_TRC_BT601 => pixel::TransferCharacteristic::ST170M, DAV1D_TRC_SMPTE240 => pixel::TransferCharacteristic::ST240M, DAV1D_TRC_LINEAR => pixel::TransferCharacteristic::Linear, DAV1D_TRC_LOG100 => pixel::TransferCharacteristic::Logarithmic100, DAV1D_TRC_LOG100_SQRT10 => pixel::TransferCharacteristic::Logarithmic316, DAV1D_TRC_IEC61966 => pixel::TransferCharacteristic::SRGB, DAV1D_TRC_BT1361 => pixel::TransferCharacteristic::BT1886, DAV1D_TRC_SRGB => pixel::TransferCharacteristic::SRGB, DAV1D_TRC_BT2020_10BIT => pixel::TransferCharacteristic::BT2020Ten, DAV1D_TRC_BT2020_12BIT => pixel::TransferCharacteristic::BT2020Twelve, DAV1D_TRC_SMPTE2084 => pixel::TransferCharacteristic::PerceptualQuantizer, DAV1D_TRC_SMPTE428 => pixel::TransferCharacteristic::ST428, DAV1D_TRC_HLG => pixel::TransferCharacteristic::HybridLogGamma, 19..=DAV1D_TRC_RESERVED => pixel::TransferCharacteristic::Unspecified, _ => unreachable!(), } } } /// Matrix coefficients used in deriving luma and chroma signals from the /// green, blue and red or X, Y and Z primaries. pub fn matrix_coefficients(&self) -> pixel::MatrixCoefficients { unsafe { #[allow(non_upper_case_globals)] match (*self.pic.as_ref().seq_hdr).mtrx { DAV1D_MC_IDENTITY => pixel::MatrixCoefficients::Identity, DAV1D_MC_BT709 => pixel::MatrixCoefficients::BT709, DAV1D_MC_UNKNOWN => pixel::MatrixCoefficients::Unspecified, DAV1D_MC_FCC => pixel::MatrixCoefficients::BT470M, DAV1D_MC_BT470BG => pixel::MatrixCoefficients::BT470BG, DAV1D_MC_BT601 => pixel::MatrixCoefficients::BT470BG, DAV1D_MC_SMPTE240 => pixel::MatrixCoefficients::ST240M, DAV1D_MC_SMPTE_YCGCO => pixel::MatrixCoefficients::YCgCo, DAV1D_MC_BT2020_NCL => pixel::MatrixCoefficients::BT2020NonConstantLuminance, DAV1D_MC_BT2020_CL => pixel::MatrixCoefficients::BT2020ConstantLuminance, DAV1D_MC_SMPTE2085 => pixel::MatrixCoefficients::ST2085, DAV1D_MC_CHROMAT_NCL => { pixel::MatrixCoefficients::ChromaticityDerivedNonConstantLuminance } DAV1D_MC_CHROMAT_CL => { pixel::MatrixCoefficients::ChromaticityDerivedConstantLuminance } DAV1D_MC_ICTCP => pixel::MatrixCoefficients::ICtCp, 15..=DAV1D_MC_RESERVED => pixel::MatrixCoefficients::Unspecified, _ => unreachable!(), } } } /// YUV color range. pub fn color_range(&self) -> pixel::YUVRange { unsafe { match (*self.pic.as_ref().seq_hdr).color_range { 0 => pixel::YUVRange::Limited, _ => pixel::YUVRange::Full, } } } /// Sample position for subsampled chroma. pub fn chroma_location(&self) -> pixel::ChromaLocation { // According to y4m mapping declared in dav1d's output/y4m2.c and applied from FFmpeg's yuv4mpegdec.c unsafe { match (*self.pic.as_ref().seq_hdr).chr { DAV1D_CHR_UNKNOWN | DAV1D_CHR_COLOCATED => pixel::ChromaLocation::Center, DAV1D_CHR_VERTICAL => pixel::ChromaLocation::Left, _ => unreachable!(), } } } } // Number of bytes to align AND pad picture memory buffers by, so that SIMD // implementations can over-read by a few bytes, and use aligned read/write // instructions. pub const PICTURE_ALIGNMENT: usize = 64; /// Allocation for a picture. #[derive(Debug, PartialEq, Eq)] pub struct PictureAllocation { /// Pointers to planar image data. /// /// Y is `data[0]`, U is `data[1]`, V is `data[2]`. /// /// The data should be `u8` (for 8 bits) or `u16` (for 10 bits). /// /// The `data[0]`, `data[1]` and `data[2]` must be [`PICTURE_ALIGNMENT`] byte aligned and with a pixel /// width/height multiple of 128 pixels. Any allocated memory area should also be padded by /// [`PICTURE_ALIGNMENT`] bytes. /// /// `data[1]` and `data[2]` must share the same `stride[1]`. pub data: [*mut u8; 3], /// Number of bytes between 2 lines in `data[]` for luma in case of `stride[0]` or chroma in /// case of `stride[1]`. pub stride: [isize; 2], /// Allocator data that can be retrieved back from the picture later. pub allocator_data: D, } unsafe impl Send for PictureAllocation {} /// Picture allocator trait. /// /// # Safety /// /// See documentation of [`PictureAllocator::alloc_picture`] and /// [`PictureAllocator::release_picture`] for the requirements of this trait. pub unsafe trait PictureAllocator: Send + Sync + 'static { /// Allocator data that is stored together with the [`Picture`]. /// /// This can be retrieved from the picture via [`Picture::allocator_data()`]. type AllocatorData: Send + 'static; /// Allocate the picture buffer based on `pic_params`. /// /// See the [`PictureAllocation`] documentation for the requirements on the allocated memory. /// /// This function will be called on the main thread (the thread which calls /// [`Decoder::get_picture()`]). /// /// # Safety /// /// This function needs to allocate enough memory with the constraints outlined in the /// [`PictureAllocation`] documentation. unsafe fn alloc_picture( &self, pic_params: &PictureParameters, ) -> Result, Error>; /// Release the picture buffer. /// /// If frame threading is used, this function may be called by the main /// thread (the thread which calls [`Decoder::get_picture()`]) or any of the frame /// threads and thus must be thread-safe. If frame threading is not used, /// this function will only be called on the main thread. /// /// # Safety /// /// This function needs to release the memory in `allocation` and can assume that it /// corresponds to a previous call to [`PictureAllocator::alloc_picture()`]. unsafe fn release_picture(&self, allocation: PictureAllocation); } /// Default allocator. /// /// Note that this allocator can't be directly instantiated. #[derive(Debug)] pub struct DefaultAllocator(()); unsafe impl PictureAllocator for DefaultAllocator { type AllocatorData = (); unsafe fn alloc_picture( &self, _pic_params: &PictureParameters, ) -> Result, Error> { unimplemented!() } unsafe fn release_picture(&self, _allocation: PictureAllocation) { unimplemented!() } } /// Settings for creating a new [`Decoder`] instance. /// See documentation for native `Dav1dSettings` struct. #[derive(Debug)] pub struct Settings { dav1d_settings: Dav1dSettings, } unsafe impl Send for Settings {} unsafe impl Sync for Settings {} impl Default for Settings { fn default() -> Self { Self::new() } } impl Settings { /// Creates a new [`Settings`] instance with default settings. pub fn new() -> Self { unsafe { let mut dav1d_settings = mem::MaybeUninit::uninit(); dav1d_default_settings(dav1d_settings.as_mut_ptr()); Self { dav1d_settings: dav1d_settings.assume_init(), } } } pub fn set_n_threads(&mut self, n_threads: u32) { self.dav1d_settings.n_threads = n_threads as i32; } pub fn get_n_threads(&self) -> u32 { self.dav1d_settings.n_threads as u32 } pub fn set_max_frame_delay(&mut self, max_frame_delay: u32) { self.dav1d_settings.max_frame_delay = max_frame_delay as i32; } pub fn get_max_frame_delay(&self) -> u32 { self.dav1d_settings.max_frame_delay as u32 } pub fn set_apply_grain(&mut self, apply_grain: bool) { self.dav1d_settings.apply_grain = i32::from(apply_grain); } pub fn get_apply_grain(&self) -> bool { self.dav1d_settings.apply_grain != 0 } pub fn set_operating_point(&mut self, operating_point: u32) { self.dav1d_settings.operating_point = operating_point as i32; } pub fn get_operating_point(&self) -> u32 { self.dav1d_settings.operating_point as u32 } pub fn set_all_layers(&mut self, all_layers: bool) { self.dav1d_settings.all_layers = i32::from(all_layers); } pub fn get_all_layers(&self) -> bool { self.dav1d_settings.all_layers != 0 } pub fn set_frame_size_limit(&mut self, frame_size_limit: u32) { self.dav1d_settings.frame_size_limit = frame_size_limit; } pub fn get_frame_size_limit(&self) -> u32 { self.dav1d_settings.frame_size_limit } pub fn set_strict_std_compliance(&mut self, strict_std_compliance: bool) { self.dav1d_settings.strict_std_compliance = i32::from(strict_std_compliance); } pub fn get_strict_std_compliance(&self) -> bool { self.dav1d_settings.strict_std_compliance != 0 } pub fn set_output_invisible_frames(&mut self, output_invisible_frames: bool) { self.dav1d_settings.output_invisible_frames = i32::from(output_invisible_frames); } pub fn get_output_invisible_frames(&self) -> bool { self.dav1d_settings.output_invisible_frames != 0 } pub fn set_inloop_filters(&mut self, inloop_filters: InloopFilterType) { self.dav1d_settings.inloop_filters = inloop_filters.bits(); } pub fn get_inloop_filters(&self) -> InloopFilterType { InloopFilterType::from_bits_truncate(self.dav1d_settings.inloop_filters) } pub fn set_decode_frame_type(&mut self, decode_frame_type: DecodeFrameType) { self.dav1d_settings.decode_frame_type = decode_frame_type.into(); } pub fn get_decode_frame_type(&self) -> DecodeFrameType { DecodeFrameType::try_from(self.dav1d_settings.decode_frame_type) .expect("Invalid Dav1dDecodeFrameType") } } bitflags::bitflags! { #[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] pub struct InloopFilterType: u32 { const DEBLOCK = DAV1D_INLOOPFILTER_DEBLOCK; const CDEF = DAV1D_INLOOPFILTER_CDEF; const RESTORATION = DAV1D_INLOOPFILTER_RESTORATION; } } #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] pub enum DecodeFrameType { #[default] All, Reference, Intra, Key, } impl TryFrom for DecodeFrameType { type Error = TryFromEnumError; fn try_from(value: u32) -> Result { match value { DAV1D_DECODEFRAMETYPE_ALL => Ok(DecodeFrameType::All), DAV1D_DECODEFRAMETYPE_REFERENCE => Ok(DecodeFrameType::Reference), DAV1D_DECODEFRAMETYPE_INTRA => Ok(DecodeFrameType::Intra), DAV1D_DECODEFRAMETYPE_KEY => Ok(DecodeFrameType::Key), _ => Err(TryFromEnumError(())), } } } impl From for u32 { fn from(v: DecodeFrameType) -> u32 { match v { DecodeFrameType::All => DAV1D_DECODEFRAMETYPE_ALL, DecodeFrameType::Reference => DAV1D_DECODEFRAMETYPE_REFERENCE, DecodeFrameType::Intra => DAV1D_DECODEFRAMETYPE_INTRA, DecodeFrameType::Key => DAV1D_DECODEFRAMETYPE_KEY, } } } /// The error type returned when a conversion from a C enum fails. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct TryFromEnumError(()); impl std::fmt::Display for TryFromEnumError { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fmt.write_str("Invalid enum value") } } impl From for TryFromEnumError { fn from(x: std::convert::Infallible) -> TryFromEnumError { match x {} } } impl std::error::Error for TryFromEnumError {} /// A `dav1d` decoder instance. #[derive(Debug)] pub struct Decoder { dec: ptr::NonNull, pending_data: Option, allocator: Option>, } static_assertions::assert_impl_all!(Decoder: Send, Sync, Debug); unsafe extern "C" fn release_wrapped_data>(_data: *const u8, cookie: *mut c_void) { let buf = Box::from_raw(cookie as *mut T); drop(buf); } impl Decoder { /// Creates a new [`Decoder`] instance with given [`Settings`]. pub fn with_settings(settings: &Settings) -> Result { unsafe { let mut dec = mem::MaybeUninit::uninit(); let ret = dav1d_open(dec.as_mut_ptr(), &settings.dav1d_settings); if ret < 0 { return Err(Error::from(ret)); } Ok(Decoder { dec: ptr::NonNull::new(dec.assume_init()).unwrap(), pending_data: None, allocator: None, }) } } /// Creates a new [`Decoder`] instance with the default settings. pub fn new() -> Result { Self::with_settings(&Settings::default()) } } unsafe extern "C" fn alloc_picture_callback( pic: *mut Dav1dPicture, cookie: *mut c_void, ) -> c_int { let allocator = &*(cookie as *const A); let pic_parameters = PictureParameters { pic: ptr::NonNull::new_unchecked(pic), }; let res = allocator.alloc_picture(&pic_parameters); match res { Ok(allocation) => { (*pic).data[0] = allocation.data[0] as *mut c_void; (*pic).data[1] = allocation.data[1] as *mut c_void; (*pic).data[2] = allocation.data[2] as *mut c_void; (*pic).stride[0] = allocation.stride[0]; (*pic).stride[1] = allocation.stride[1]; (*pic).allocator_data = Box::into_raw(Box::new(allocation.allocator_data)) as *mut c_void; 0 } Err(err) => match err { Error::Again => DAV1D_ERR_AGAIN, Error::InvalidArgument => DAV1D_ERR_INVAL, Error::NotEnoughMemory => DAV1D_ERR_NOMEM, Error::UnsupportedBitstream => DAV1D_ERR_NOPROTOOPT, Error::UnknownError(err) => { assert!(err < 0); err } }, } } unsafe extern "C" fn release_picture_callback( pic: *mut Dav1dPicture, cookie: *mut c_void, ) { let allocator = &*(cookie as *const A); let allocator_data = Box::from_raw((*pic).allocator_data as *mut A::AllocatorData); let allocation = PictureAllocation { data: [ (*pic).data[0] as *mut u8, (*pic).data[1] as *mut u8, (*pic).data[2] as *mut u8, ], stride: (*pic).stride, allocator_data: *allocator_data, }; allocator.release_picture(allocation); } impl Decoder { /// Creates a new [`Decoder`] instance with given [`Settings`] and [`PictureAllocator`]. pub fn with_settings_and_allocator(settings: &Settings, allocator: A) -> Result { unsafe { let allocator = Arc::new(allocator); let mut dec = mem::MaybeUninit::uninit(); let settings = Dav1dSettings { allocator: Dav1dPicAllocator { cookie: &*allocator as *const A as *mut c_void, alloc_picture_callback: Some(alloc_picture_callback::), release_picture_callback: Some(release_picture_callback::), }, ..settings.dav1d_settings }; let ret = dav1d_open(dec.as_mut_ptr(), &settings); if ret < 0 { return Err(Error::from(ret)); } Ok(Decoder { dec: ptr::NonNull::new(dec.assume_init()).unwrap(), pending_data: None, allocator: Some(allocator), }) } } /// Creates a new [`Decoder`] instance with the default settings and the given /// [`PictureAllocator`]. pub fn with_allocator(allocator: A) -> Result { Self::with_settings_and_allocator(&Settings::default(), allocator) } } impl Decoder { /// Flush the decoder. /// /// This flushes all delayed frames in the decoder and clears the internal decoder state. /// /// All currently pending frames are available afterwards via [`Decoder::get_picture`]. pub fn flush(&mut self) { unsafe { dav1d_flush(self.dec.as_ptr()); if let Some(mut pending_data) = self.pending_data.take() { dav1d_data_unref(&mut pending_data); } } } /// Send new AV1 data to the decoder. /// /// After this returned `Ok(())` or `Err([Error::Again])` there might be decoded frames /// available via [`Decoder::get_picture`]. /// /// # Panics /// /// If a previous call returned [`Error::Again`] then this must not be called again until /// [`Decoder::send_pending_data`] has returned `Ok(())`. pub fn send_data + Send + 'static>( &mut self, buf: T, offset: Option, timestamp: Option, duration: Option, ) -> Result<(), Error> { assert!( self.pending_data.is_none(), "Have pending data that needs to be handled first" ); let buf = Box::new(buf); let slice = (*buf).as_ref(); let len = slice.len(); unsafe { let mut data: Dav1dData = mem::zeroed(); let _ret = dav1d_data_wrap( &mut data, slice.as_ptr(), len, Some(release_wrapped_data::), Box::into_raw(buf) as *mut c_void, ); if let Some(offset) = offset { data.m.offset = offset; } if let Some(timestamp) = timestamp { data.m.timestamp = timestamp; } if let Some(duration) = duration { data.m.duration = duration; } let ret = dav1d_send_data(self.dec.as_ptr(), &mut data); if ret < 0 { let ret = Error::from(ret); if ret.is_again() { self.pending_data = Some(data); } else { dav1d_data_unref(&mut data); } return Err(ret); } if data.sz > 0 { self.pending_data = Some(data); return Err(Error::Again); } Ok(()) } } /// Sends any pending data to the decoder. /// /// This has to be called after [`Decoder::send_data`] has returned `Err([Error::Again])` to /// consume any futher pending data. /// /// After this returned `Ok(())` or `Err([Error::Again])` there might be decoded frames /// available via [`Decoder::get_picture`]. pub fn send_pending_data(&mut self) -> Result<(), Error> { let mut data = match self.pending_data.take() { None => { return Ok(()); } Some(data) => data, }; unsafe { let ret = dav1d_send_data(self.dec.as_ptr(), &mut data); if ret < 0 { let ret = Error::from(ret); if ret.is_again() { self.pending_data = Some(data); } else { dav1d_data_unref(&mut data); } return Err(ret); } if data.sz > 0 { self.pending_data = Some(data); return Err(Error::Again); } Ok(()) } } /// Get the next decoded frame from the decoder. /// /// If this returns `Err([Error::Again])` then further data has to be sent to the decoder /// before further decoded frames become available. /// /// To make most use of frame threading this function should only be called once per submitted /// input frame and not until it returns `Err([Error::Again])`. Calling it in a loop should /// only be done to drain all pending frames at the end. pub fn get_picture(&mut self) -> Result, Error> { unsafe { let mut pic: Dav1dPicture = mem::zeroed(); let ret = dav1d_get_picture(self.dec.as_ptr(), &mut pic); if ret < 0 { Err(Error::from(ret)) } else { let inner = InnerPicture { pic }; Ok(Picture { inner: Arc::new(inner), allocator: self.allocator.clone(), }) } } } /// Get the decoder delay. pub fn get_frame_delay(&self) -> Result { unsafe { let ret = dav1d_get_frame_delay(self.dec.as_ptr()); if ret < 0 { Err(Error::from(ret)) } else { Ok(ret as u32) } } } } impl Drop for Decoder { fn drop(&mut self) { unsafe { if let Some(mut pending_data) = self.pending_data.take() { dav1d_data_unref(&mut pending_data); } let mut dec = self.dec.as_ptr(); dav1d_close(&mut dec); }; } } unsafe impl Send for Decoder {} unsafe impl Sync for Decoder {} #[derive(Debug)] struct InnerPicture { pub pic: Dav1dPicture, } /// A decoded frame. #[derive(Debug)] pub struct Picture { inner: Arc, allocator: Option>, } impl Clone for Picture { fn clone(&self) -> Self { Picture { inner: self.inner.clone(), allocator: self.allocator.clone(), } } } /// Pixel layout of a frame. #[derive(Debug, Eq, PartialEq, Copy, Clone)] pub enum PixelLayout { /// Monochrome. I400, /// 4:2:0 planar. I420, /// 4:2:2 planar. I422, /// 4:4:4 planar. I444, } /// Frame component. #[derive(Eq, PartialEq, Copy, Clone, Debug)] pub enum PlanarImageComponent { /// Y component. Y, /// U component. U, /// V component. V, } impl From for PlanarImageComponent { fn from(index: usize) -> Self { match index { 0 => PlanarImageComponent::Y, 1 => PlanarImageComponent::U, 2 => PlanarImageComponent::V, _ => panic!("Invalid YUV index: {}", index), } } } impl From for usize { fn from(component: PlanarImageComponent) -> Self { match component { PlanarImageComponent::Y => 0, PlanarImageComponent::U => 1, PlanarImageComponent::V => 2, } } } /// A single plane of a decoded frame. /// /// This can be used like a `&[u8]`. #[derive(Debug)] pub struct Plane(Picture, PlanarImageComponent); impl Clone for Plane { fn clone(&self) -> Self { Plane(self.0.clone(), self.1) } } impl AsRef<[u8]> for Plane { fn as_ref(&self) -> &[u8] { let (stride, height) = self.0.plane_data_geometry(self.1); let data = self.0.plane_data_ptr(self.1) as *const u8; if stride == 0 || data.is_null() { return &[]; } unsafe { std::slice::from_raw_parts(data, stride as usize * height as usize) } } } impl std::ops::Deref for Plane { type Target = [u8]; fn deref(&self) -> &Self::Target { self.as_ref() } } static_assertions::assert_impl_all!(Plane: Send, Sync, Clone, Debug); /// Number of bits per component. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct BitsPerComponent(pub usize); impl Picture { /// Stride in pixels of the `component` for the decoded frame. pub fn stride(&self, component: PlanarImageComponent) -> u32 { let s = match component { PlanarImageComponent::Y => 0, _ => 1, }; self.inner.pic.stride[s] as u32 } /// Raw pointer to the data of the `component` for the decoded frame. pub fn plane_data_ptr(&self, component: PlanarImageComponent) -> *mut c_void { let index: usize = component.into(); self.inner.pic.data[index] } /// Plane geometry of the `component` for the decoded frame. /// /// This returns the stride and height. pub fn plane_data_geometry(&self, component: PlanarImageComponent) -> (u32, u32) { let height = match component { PlanarImageComponent::Y => self.height(), _ => match self.pixel_layout() { PixelLayout::I420 => (self.height() + 1) / 2, PixelLayout::I400 | PixelLayout::I422 | PixelLayout::I444 => self.height(), }, }; (self.stride(component), height) } /// Plane data of the `component` for the decoded frame. pub fn plane(&self, component: PlanarImageComponent) -> Plane { Plane(self.clone(), component) } /// Bit depth of the plane data. /// /// This returns 8 or 16 for the underlying integer type used for the plane data. /// /// Check [`Picture::bits_per_component`] for the number of bits that are used. pub fn bit_depth(&self) -> usize { self.inner.pic.p.bpc as usize } /// Bits used per component of the plane data. /// /// Check [`Picture::bit_depth`] for the number of storage bits. pub fn bits_per_component(&self) -> Option { unsafe { match (*self.inner.pic.seq_hdr).hbd { 0 => Some(BitsPerComponent(8)), 1 => Some(BitsPerComponent(10)), 2 => Some(BitsPerComponent(12)), _ => None, } } } /// Width of the frame. pub fn width(&self) -> u32 { self.inner.pic.p.w as u32 } /// Height of the frame. pub fn height(&self) -> u32 { self.inner.pic.p.h as u32 } /// Pixel layout of the frame. pub fn pixel_layout(&self) -> PixelLayout { #[allow(non_upper_case_globals)] match self.inner.pic.p.layout { DAV1D_PIXEL_LAYOUT_I400 => PixelLayout::I400, DAV1D_PIXEL_LAYOUT_I420 => PixelLayout::I420, DAV1D_PIXEL_LAYOUT_I422 => PixelLayout::I422, DAV1D_PIXEL_LAYOUT_I444 => PixelLayout::I444, _ => unreachable!(), } } /// Timestamp of the frame. /// /// This is the same timestamp as the one provided to [`Decoder::send_data`]. pub fn timestamp(&self) -> Option { let ts = self.inner.pic.m.timestamp; if ts == i64::MIN { None } else { Some(ts) } } /// Duration of the frame. /// /// This is the same duration as the one provided to [`Decoder::send_data`] or `0` if none was /// provided. pub fn duration(&self) -> i64 { self.inner.pic.m.duration } /// Offset of the frame. /// /// This is the same offset as the one provided to [`Decoder::send_data`] or `-1` if none was /// provided. pub fn offset(&self) -> i64 { self.inner.pic.m.offset } /// Chromaticity coordinates of the source colour primaries. pub fn color_primaries(&self) -> pixel::ColorPrimaries { unsafe { #[allow(non_upper_case_globals)] match (*self.inner.pic.seq_hdr).pri { DAV1D_COLOR_PRI_BT709 => pixel::ColorPrimaries::BT709, DAV1D_COLOR_PRI_UNKNOWN => pixel::ColorPrimaries::Unspecified, DAV1D_COLOR_PRI_BT470M => pixel::ColorPrimaries::BT470M, DAV1D_COLOR_PRI_BT470BG => pixel::ColorPrimaries::BT470BG, DAV1D_COLOR_PRI_BT601 => pixel::ColorPrimaries::BT470BG, DAV1D_COLOR_PRI_SMPTE240 => pixel::ColorPrimaries::ST240M, DAV1D_COLOR_PRI_FILM => pixel::ColorPrimaries::Film, DAV1D_COLOR_PRI_BT2020 => pixel::ColorPrimaries::BT2020, DAV1D_COLOR_PRI_XYZ => pixel::ColorPrimaries::ST428, DAV1D_COLOR_PRI_SMPTE431 => pixel::ColorPrimaries::P3DCI, DAV1D_COLOR_PRI_SMPTE432 => pixel::ColorPrimaries::P3Display, DAV1D_COLOR_PRI_EBU3213 => pixel::ColorPrimaries::Tech3213, 23..=DAV1D_COLOR_PRI_RESERVED => pixel::ColorPrimaries::Unspecified, _ => unreachable!(), } } } /// Transfer characteristics function. pub fn transfer_characteristic(&self) -> pixel::TransferCharacteristic { unsafe { #[allow(non_upper_case_globals)] match (*self.inner.pic.seq_hdr).trc { DAV1D_TRC_BT709 => pixel::TransferCharacteristic::BT1886, DAV1D_TRC_UNKNOWN => pixel::TransferCharacteristic::Unspecified, DAV1D_TRC_BT470M => pixel::TransferCharacteristic::BT470M, DAV1D_TRC_BT470BG => pixel::TransferCharacteristic::BT470BG, DAV1D_TRC_BT601 => pixel::TransferCharacteristic::ST170M, DAV1D_TRC_SMPTE240 => pixel::TransferCharacteristic::ST240M, DAV1D_TRC_LINEAR => pixel::TransferCharacteristic::Linear, DAV1D_TRC_LOG100 => pixel::TransferCharacteristic::Logarithmic100, DAV1D_TRC_LOG100_SQRT10 => pixel::TransferCharacteristic::Logarithmic316, DAV1D_TRC_IEC61966 => pixel::TransferCharacteristic::SRGB, DAV1D_TRC_BT1361 => pixel::TransferCharacteristic::BT1886, DAV1D_TRC_SRGB => pixel::TransferCharacteristic::SRGB, DAV1D_TRC_BT2020_10BIT => pixel::TransferCharacteristic::BT2020Ten, DAV1D_TRC_BT2020_12BIT => pixel::TransferCharacteristic::BT2020Twelve, DAV1D_TRC_SMPTE2084 => pixel::TransferCharacteristic::PerceptualQuantizer, DAV1D_TRC_SMPTE428 => pixel::TransferCharacteristic::ST428, DAV1D_TRC_HLG => pixel::TransferCharacteristic::HybridLogGamma, 19..=DAV1D_TRC_RESERVED => pixel::TransferCharacteristic::Unspecified, _ => unreachable!(), } } } /// Matrix coefficients used in deriving luma and chroma signals from the /// green, blue and red or X, Y and Z primaries. pub fn matrix_coefficients(&self) -> pixel::MatrixCoefficients { unsafe { #[allow(non_upper_case_globals)] match (*self.inner.pic.seq_hdr).mtrx { DAV1D_MC_IDENTITY => pixel::MatrixCoefficients::Identity, DAV1D_MC_BT709 => pixel::MatrixCoefficients::BT709, DAV1D_MC_UNKNOWN => pixel::MatrixCoefficients::Unspecified, DAV1D_MC_FCC => pixel::MatrixCoefficients::BT470M, DAV1D_MC_BT470BG => pixel::MatrixCoefficients::BT470BG, DAV1D_MC_BT601 => pixel::MatrixCoefficients::BT470BG, DAV1D_MC_SMPTE240 => pixel::MatrixCoefficients::ST240M, DAV1D_MC_SMPTE_YCGCO => pixel::MatrixCoefficients::YCgCo, DAV1D_MC_BT2020_NCL => pixel::MatrixCoefficients::BT2020NonConstantLuminance, DAV1D_MC_BT2020_CL => pixel::MatrixCoefficients::BT2020ConstantLuminance, DAV1D_MC_SMPTE2085 => pixel::MatrixCoefficients::ST2085, DAV1D_MC_CHROMAT_NCL => { pixel::MatrixCoefficients::ChromaticityDerivedNonConstantLuminance } DAV1D_MC_CHROMAT_CL => { pixel::MatrixCoefficients::ChromaticityDerivedConstantLuminance } DAV1D_MC_ICTCP => pixel::MatrixCoefficients::ICtCp, 15..=DAV1D_MC_RESERVED => pixel::MatrixCoefficients::Unspecified, _ => unreachable!(), } } } /// YUV color range. pub fn color_range(&self) -> pixel::YUVRange { unsafe { match (*self.inner.pic.seq_hdr).color_range { 0 => pixel::YUVRange::Limited, _ => pixel::YUVRange::Full, } } } /// Sample position for subsampled chroma. pub fn chroma_location(&self) -> pixel::ChromaLocation { // According to y4m mapping declared in dav1d's output/y4m2.c and applied from FFmpeg's yuv4mpegdec.c unsafe { match (*self.inner.pic.seq_hdr).chr { DAV1D_CHR_UNKNOWN | DAV1D_CHR_COLOCATED => pixel::ChromaLocation::Center, DAV1D_CHR_VERTICAL => pixel::ChromaLocation::Left, _ => unreachable!(), } } } /// Allocator data of the picture. pub fn allocator_data(&self) -> Option<&A::AllocatorData> { unsafe { if self.inner.pic.allocator_data.is_null() { None } else { Some(&*(self.inner.pic.allocator_data as *const A::AllocatorData)) } } } /// Content light level information. pub fn content_light(&self) -> Option { unsafe { if self.inner.pic.content_light.is_null() { None } else { Some(ContentLightLevel { max_content_light_level: (*self.inner.pic.content_light) .max_content_light_level, max_frame_average_light_level: (*self.inner.pic.content_light) .max_frame_average_light_level, }) } } } ///Mastering display information. pub fn mastering_display(&self) -> Option { unsafe { if self.inner.pic.mastering_display.is_null() { None } else { Some(MasteringDisplay { primaries: (*self.inner.pic.mastering_display).primaries, white_point: (*self.inner.pic.mastering_display).white_point, max_luminance: (*self.inner.pic.mastering_display).max_luminance, min_luminance: (*self.inner.pic.mastering_display).min_luminance, }) } } } } static_assertions::assert_impl_all!(Picture: Send, Sync, Clone, Debug); unsafe impl Send for InnerPicture {} unsafe impl Sync for InnerPicture {} impl Drop for InnerPicture { fn drop(&mut self) { unsafe { dav1d_picture_unref(&mut self.pic); } } } #[cfg(test)] mod test { use std::{ collections::HashSet, fmt, ptr, sync::{self, atomic}, }; mod ivf { use bitstream_io::{ByteRead, ByteReader, LittleEndian}; use std::io; #[derive(Debug, PartialEq, Eq)] pub struct Header { pub tag: [u8; 4], pub w: u16, pub h: u16, pub timebase_num: u32, pub timebase_den: u32, } pub fn read_header(r: &mut dyn io::Read) -> io::Result
{ let mut br = ByteReader::endian(r, LittleEndian); let mut signature = [0u8; 4]; let mut tag = [0u8; 4]; br.read_bytes(&mut signature)?; let _v0 = br.read::()?; let _v1 = br.read::()?; br.read_bytes(&mut tag)?; let w = br.read::()?; let h = br.read::()?; let timebase_den = br.read::()?; let timebase_num = br.read::()?; let _ = br.read::()?; let _ = br.read::()?; Ok(Header { tag, w, h, timebase_num, timebase_den, }) } pub struct Packet { pub data: Box<[u8]>, pub pts: u64, } pub fn read_packet(r: &mut dyn io::Read) -> io::Result { let mut br = ByteReader::endian(r, LittleEndian); let len = br.read::()?; let pts = br.read::()?; let mut buf = vec![0u8; len as usize]; br.read_bytes(&mut buf)?; Ok(Packet { data: buf.into_boxed_slice(), pts, }) } } static TEST_FILE_420_8: &[u8] = include_bytes!("../test-420-8.ivf"); static TEST_FILE_420_12: &[u8] = include_bytes!("../test-420-12.ivf"); fn handle_pending_pictures( dec: &mut super::Decoder, pictures: &mut Vec>, drain: bool, ) { loop { match dec.get_picture() { Ok(p) => { println!("{:?}", p); pictures.push(p); } // Need to send more data to the decoder before it can decode new pictures Err(e) if e.is_again() => return, Err(e) => { panic!("Error getting decoded pictures: {}", e); } } if !drain { break; } } } fn check_pictures(pictures: &[super::Picture], bpp: usize) { assert_eq!(pictures.len(), 5); let pts = [0, 33, 67, 100, 133]; for (i, picture) in pictures.iter().enumerate() { assert_eq!(picture.width(), 320); assert_eq!(picture.height(), 240); assert_eq!(picture.bit_depth(), bpp); assert_eq!( picture.bits_per_component(), Some(super::BitsPerComponent(bpp)) ); assert_eq!(picture.pixel_layout(), super::PixelLayout::I420); assert_eq!( picture.color_primaries(), super::pixel::ColorPrimaries::BT709 ); assert_eq!( picture.transfer_characteristic(), super::pixel::TransferCharacteristic::BT1886 ); assert_eq!( picture.matrix_coefficients(), super::pixel::MatrixCoefficients::BT709, ); assert_eq!( picture.chroma_location(), super::pixel::ChromaLocation::Center, ); assert_eq!(picture.timestamp(), Some(pts[i])); assert_eq!(picture.offset(), i as i64); let stride_mult = if bpp == 8 { 1 } else { 2 }; assert!(picture.stride(super::PlanarImageComponent::Y) >= 320 * stride_mult); assert!(picture.stride(super::PlanarImageComponent::U) >= 160 * stride_mult); assert!(picture.stride(super::PlanarImageComponent::V) >= 160 * stride_mult); assert_eq!( picture .plane_data_geometry(super::PlanarImageComponent::Y) .1, 240 ); assert_eq!( picture .plane_data_geometry(super::PlanarImageComponent::U) .1, 120 ); assert_eq!( picture .plane_data_geometry(super::PlanarImageComponent::V) .1, 120 ); assert_eq!( picture.plane(super::PlanarImageComponent::Y).len(), picture.stride(super::PlanarImageComponent::Y) as usize * 240 ); assert_eq!( picture.plane(super::PlanarImageComponent::U).len(), picture.stride(super::PlanarImageComponent::U) as usize * 120 ); assert_eq!( picture.plane(super::PlanarImageComponent::V).len(), picture.stride(super::PlanarImageComponent::V) as usize * 120 ); } } fn decode_file( file: &[u8], mut dec: super::Decoder, pictures: &mut Vec>, ) { use std::io; let mut r = io::BufReader::new(file); let header = ivf::read_header(&mut r).unwrap(); println!("{:?}", header); let mut idx = 0; while let Ok(packet) = ivf::read_packet(&mut r) { println!("Packet {}", packet.pts); // Let's use millisecond timestamps let pts = 1000 * packet.pts as i64 * header.timebase_num as i64 / header.timebase_den as i64; // Send packet to the decoder match dec.send_data(packet.data, Some(idx), Some(pts), None) { Err(e) if e.is_again() => { // If the decoder did not consume all data, output all // pending pictures and send pending data to the decoder // until it is all used up. loop { handle_pending_pictures(&mut dec, pictures, false); match dec.send_pending_data() { Err(e) if e.is_again() => continue, Err(e) => { panic!("Error sending pending data to the decoder: {}", e); } _ => break, } } } Err(e) => { panic!("Error sending data to the decoder: {}", e); } _ => (), } // Handle all pending pictures before sending the next data. handle_pending_pictures(&mut dec, pictures, false); idx += 1; } // Handle all pending pictures that were not output yet. handle_pending_pictures(&mut dec, pictures, true); } #[test] fn test_basic_420_8() { let dec = super::Decoder::new().expect("failed to create decoder instance"); let mut pictures = vec![]; decode_file(TEST_FILE_420_8, dec, &mut pictures); check_pictures(&pictures, 8); } #[test] fn test_basic_420_12() { let dec = super::Decoder::new().expect("failed to create decoder instance"); let mut pictures = vec![]; decode_file(TEST_FILE_420_12, dec, &mut pictures); check_pictures(&pictures, 12); } #[derive(Debug)] struct TestAllocator { counter: atomic::AtomicUsize, allocated: sync::Arc, } impl TestAllocator { fn new(allocated: &sync::Arc) -> Self { TestAllocator { counter: atomic::AtomicUsize::new(0), allocated: allocated.clone(), } } } unsafe impl super::PictureAllocator for TestAllocator { type AllocatorData = (usize, [std::alloc::Layout; 2]); unsafe fn alloc_picture( &self, pic_params: &crate::PictureParameters, ) -> Result, crate::Error> { fn align(x: usize) -> usize { (x + 128 - 1) & !(128 - 1) } let stride_mult = if pic_params.bit_depth() == 8 { 1 } else { 2 }; let (stride, height) = match pic_params.pixel_layout() { crate::PixelLayout::I400 => ( [align(pic_params.width() as usize) * stride_mult, 0], [align(pic_params.height() as usize), 0], ), crate::PixelLayout::I420 => ( [ align(pic_params.width() as usize) * stride_mult, align((pic_params.width() as usize + 1) / 2) * stride_mult, ], [ align(pic_params.height() as usize), align((pic_params.height() as usize + 1) / 2), ], ), crate::PixelLayout::I422 => ( [ align(pic_params.width() as usize) * stride_mult, align((pic_params.width() as usize + 1) / 2) * stride_mult, ], [ align(pic_params.height() as usize), align(pic_params.height() as usize), ], ), crate::PixelLayout::I444 => ( [ align(pic_params.width() as usize) * stride_mult, align(pic_params.width() as usize) * stride_mult, ], [ align(pic_params.height() as usize), align(pic_params.height() as usize), ], ), }; let layout_0 = std::alloc::Layout::from_size_align( height[0] * stride[0], super::PICTURE_ALIGNMENT, ) .unwrap(); let data_0 = std::alloc::alloc(layout_0); let layout_1; let data_1; let data_2; if stride[1] > 0 { layout_1 = std::alloc::Layout::from_size_align( height[1] * stride[1], super::PICTURE_ALIGNMENT, ) .unwrap(); data_1 = std::alloc::alloc(layout_1); data_2 = std::alloc::alloc(layout_1); } else { layout_1 = layout_0; data_1 = ptr::null_mut(); data_2 = ptr::null_mut(); } self.allocated.fetch_add(1, atomic::Ordering::SeqCst); Ok(super::PictureAllocation { data: [data_0, data_1, data_2], stride: [stride[0] as isize, stride[1] as isize], allocator_data: ( self.counter.fetch_add(1, atomic::Ordering::SeqCst), [layout_0, layout_1], ), }) } unsafe fn release_picture( &self, allocation: crate::PictureAllocation, ) { let prev = self.allocated.fetch_sub(1, atomic::Ordering::SeqCst); assert!(prev > 0); std::alloc::dealloc(allocation.data[0], allocation.allocator_data.1[0]); if !allocation.data[1].is_null() { std::alloc::dealloc(allocation.data[1], allocation.allocator_data.1[1]); std::alloc::dealloc(allocation.data[2], allocation.allocator_data.1[1]); } } } #[test] fn test_allocator_420_8() { let allocated = sync::Arc::new(atomic::AtomicUsize::new(0)); let dec = super::Decoder::with_allocator(TestAllocator::new(&allocated)) .expect("failed to create decoder instance"); let mut pictures = vec![]; decode_file(TEST_FILE_420_8, dec, &mut pictures); check_pictures(&pictures, 8); let mut indices = HashSet::new(); for picture in &pictures { let allocator_data = picture.allocator_data().unwrap(); assert!(indices.insert(allocator_data.0)); } assert_eq!(indices.len(), 5); assert_eq!(allocated.load(atomic::Ordering::SeqCst), 5); drop(pictures); assert_eq!(allocated.load(atomic::Ordering::SeqCst), 0); } #[test] fn test_allocator_420_12() { let allocated = sync::Arc::new(atomic::AtomicUsize::new(0)); let dec = super::Decoder::with_allocator(TestAllocator::new(&allocated)) .expect("failed to create decoder instance"); let mut pictures = vec![]; decode_file(TEST_FILE_420_12, dec, &mut pictures); check_pictures(&pictures, 12); let mut indices = HashSet::new(); for picture in &pictures { let allocator_data = picture.allocator_data().unwrap(); assert!(indices.insert(allocator_data.0)); } assert_eq!(indices.len(), 5); assert_eq!(allocated.load(atomic::Ordering::SeqCst), 5); drop(pictures); assert_eq!(allocated.load(atomic::Ordering::SeqCst), 0); } } /// Content light level information as specified in CEA-861.3, Appendix A. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ContentLightLevel { /// Maximum content light level (MaxCLL) in candela per square metre. pub max_content_light_level: u16, /// Maximum frame average light level (MaxFLL) in candela per square metre. pub max_frame_average_light_level: u16, } /// Mastering display information as specified in SMPTE ST 2086. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct MasteringDisplay { /// Red/green/blue XY coordinates of primaries in CIE 1931 color space as 0.16 fixed-point number. pub primaries: [[u16; 2usize]; 3usize], /// XY coordinates of white point in CIE 1931 color space as 0.16 fixed-point number. pub white_point: [u16; 2usize], /// Maximum luminance in candela per square metre as 24.8 fixed-point number. pub max_luminance: u32, /// Minimum luminance in candela per square metre as 18.14 fixed-point number. pub min_luminance: u32, } dav1d-0.11.1/test-420-12.ivf000064400000000000000000000004471046102023000131200ustar 00000000000000DKIF AV01@D @! 20#bAU@bԷ|KSϜs`_!2(.E~:XAU :Cˎˠ2( 6}wXAUP8^ $@20 @}n08^ $@Cd20 Id}n0bdav1d-0.11.1/test-420-8.ivf000064400000000000000000000004341046102023000130410ustar 00000000000000DKIF AV01@@ ! 2,"GbzXAU@bԷKhv$>" Y!2(0EyvAU +2( 8^ *I٦ 20 @m0I٦ Cd20 Idm0}3