Skip to main content

oxideav_bitstream/
lib.rs

1//! Minimal IDR / keyframe header parsing for H.264, HEVC and AV1.
2//!
3//! This crate is the parser layer that the oxideav HW-accel bridge
4//! crates (`oxideav-vaapi`, `oxideav-vdpau`, `oxideav-vulkan-video`)
5//! call before submitting parameter buffers to the GPU. It is **not**
6//! a full codec: there is no DCT, no entropy decode, no inverse
7//! transform, no motion compensation. Just enough syntax parsing to
8//! populate `VAPictureParameterBufferH264` / `VdpPictureInfoH264` /
9//! `VkVideoDecodeH264PictureInfoKHR` and the equivalents for HEVC and
10//! AV1.
11//!
12//! Each codec lives in its own module:
13//!
14//! - [`h264`] — Annex-B framing, SPS, PPS and minimal slice header.
15//! - [`hevc`] — Annex-B framing, VPS, SPS, PPS and minimal slice
16//!   segment header.
17//! - [`av1`] — leb128 reader, OBU walker, sequence header and
18//!   key-frame OBU parsing.
19//!
20//! There is no shared cross-codec abstraction in v0 — each module
21//! exposes its own `parse_*` entry points and result structs. The
22//! shared [`bit_reader::BitReader`] type provides `u(n)`, `ue(v)` and
23//! `se(v)` primitives.
24//!
25//! ## No `unsafe`
26//!
27//! This crate contains zero `unsafe` blocks. Bit reading is software,
28//! no FFI involved.
29//!
30//! ## Workspace clean-room policy
31//!
32//! Bitstream syntax is taken from the relevant ITU-T / Khronos /
33//! AOMedia spec PDFs only. We deliberately do not consult ffmpeg /
34//! libavcodec, x264, x265, libde265, libaom, dav1d, rav1e or libgav1
35//! source. ffmpeg / aomenc are used only as black-box CLI tools to
36//! generate test fixtures.
37
38#![forbid(unsafe_code)]
39#![deny(rust_2018_idioms)]
40
41pub mod av1;
42pub mod bit_reader;
43pub mod h264;
44pub mod hevc;
45
46/// Errors produced by the parsers in this crate.
47///
48/// We deliberately do not depend on `oxideav_core::Error` — keeping
49/// this crate dep-free means `oxideav-vdpau` / `oxideav-vaapi` /
50/// `oxideav-vulkan-video` can pull it in without tugging on the rest
51/// of the framework.
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub enum BitstreamError {
54    /// The byte stream ended in the middle of a header we expected
55    /// to be complete (NAL too short, OBU truncated, etc.).
56    UnexpectedEnd(String),
57
58    /// Syntax error inside an otherwise well-framed header (an
59    /// Exp-Golomb code with too many leading zeros, an out-of-range
60    /// field value, a forbidden bit set, …).
61    InvalidData(String),
62
63    /// The input parses successfully but uses a feature this crate
64    /// deliberately does not handle (scaling lists, FMO, AV1 film
65    /// grain, …). HW backends should reject the fixture rather than
66    /// submit a partial parameter buffer.
67    Unsupported(String),
68}
69
70impl core::fmt::Display for BitstreamError {
71    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
72        match self {
73            BitstreamError::UnexpectedEnd(msg) => write!(f, "unexpected end of stream: {msg}"),
74            BitstreamError::InvalidData(msg) => write!(f, "invalid bitstream data: {msg}"),
75            BitstreamError::Unsupported(msg) => write!(f, "unsupported bitstream feature: {msg}"),
76        }
77    }
78}
79
80impl std::error::Error for BitstreamError {}
81
82impl BitstreamError {
83    pub fn invalid(msg: impl Into<String>) -> Self {
84        BitstreamError::InvalidData(msg.into())
85    }
86
87    pub fn unsupported(msg: impl Into<String>) -> Self {
88        BitstreamError::Unsupported(msg.into())
89    }
90
91    pub fn unexpected_end(msg: impl Into<String>) -> Self {
92        BitstreamError::UnexpectedEnd(msg.into())
93    }
94}