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//! - [`h266`] — Annex-B framing, 2-byte NAL header decode, VCL /
18//! IRAP / parameter-set classifiers. Parameter-set parsing
19//! deferred to future rounds.
20//! - [`av1`] — leb128 reader/writer, OBU walker + emitter, sequence
21//! header and key-frame OBU parsing.
22//!
23//! There is no shared cross-codec abstraction in v0 — each module
24//! exposes its own `parse_*` entry points and result structs. The
25//! shared [`bit_reader::BitReader`] type provides `u(n)`, `ue(v)` and
26//! `se(v)` primitives; [`bit_writer::BitWriter`] is its MSB-first
27//! inverse for emitting the same fields back out.
28//!
29//! ## No `unsafe`
30//!
31//! This crate contains zero `unsafe` blocks. Bit reading is software,
32//! no FFI involved.
33//!
34//! ## Workspace clean-room policy
35//!
36//! Bitstream syntax is taken from the relevant ITU-T / Khronos /
37//! AOMedia spec PDFs only. We deliberately do not consult ffmpeg /
38//! libavcodec, x264, x265, libde265, libaom, dav1d, rav1e or libgav1
39//! source. ffmpeg / aomenc are used only as black-box CLI tools to
40//! generate test fixtures.
41
42#![forbid(unsafe_code)]
43#![deny(rust_2018_idioms)]
44
45pub mod av1;
46pub mod bit_reader;
47pub mod bit_writer;
48pub mod h264;
49pub mod h266;
50pub mod hevc;
51pub mod ivf;
52pub mod mpeg2;
53pub mod vc1;
54pub mod vp8;
55pub mod vp9;
56
57/// Errors produced by the parsers in this crate.
58///
59/// We deliberately do not depend on `oxideav_core::Error` — keeping
60/// this crate dep-free means `oxideav-vdpau` / `oxideav-vaapi` /
61/// `oxideav-vulkan-video` can pull it in without tugging on the rest
62/// of the framework.
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub enum BitstreamError {
65 /// The byte stream ended in the middle of a header we expected
66 /// to be complete (NAL too short, OBU truncated, etc.).
67 UnexpectedEnd(String),
68
69 /// Syntax error inside an otherwise well-framed header (an
70 /// Exp-Golomb code with too many leading zeros, an out-of-range
71 /// field value, a forbidden bit set, …).
72 InvalidData(String),
73
74 /// The input parses successfully but uses a feature this crate
75 /// deliberately does not handle (scaling lists, FMO, AV1 film
76 /// grain, …). HW backends should reject the fixture rather than
77 /// submit a partial parameter buffer.
78 Unsupported(String),
79}
80
81impl core::fmt::Display for BitstreamError {
82 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
83 match self {
84 BitstreamError::UnexpectedEnd(msg) => write!(f, "unexpected end of stream: {msg}"),
85 BitstreamError::InvalidData(msg) => write!(f, "invalid bitstream data: {msg}"),
86 BitstreamError::Unsupported(msg) => write!(f, "unsupported bitstream feature: {msg}"),
87 }
88 }
89}
90
91impl std::error::Error for BitstreamError {}
92
93impl BitstreamError {
94 pub fn invalid(msg: impl Into<String>) -> Self {
95 BitstreamError::InvalidData(msg.into())
96 }
97
98 pub fn unsupported(msg: impl Into<String>) -> Self {
99 BitstreamError::Unsupported(msg.into())
100 }
101
102 pub fn unexpected_end(msg: impl Into<String>) -> Self {
103 BitstreamError::UnexpectedEnd(msg.into())
104 }
105}