Skip to main content

arcly_stream/codec/
mod.rs

1//! Codec bitstream helpers — turning wire bytes into engine metadata and
2//! [`MediaFrame`](crate::MediaFrame) classification.
3//!
4//! Gated behind the per-codec features (`codec-h264`, `codec-h265`, `codec-av1`,
5//! `codec-vp9`, `codec-vvc`, `codec-aac`; `codec` = H.264 + AAC; `codecs-all` =
6//! everything). Every parser is pure Rust with **no native dependencies**.
7//!
8//! All codecs share the [`CodecParser`] contract: extract [`VideoParams`] from a
9//! config header, and classify random-access points so the kernel's GOP cache,
10//! segmenter, eviction, and recorder — which key only on
11//! [`MediaFrame::is_keyframe`](crate::MediaFrame::is_keyframe) — work unchanged.
12//!
13//! ```
14//! # #[cfg(feature = "codec-h265")] {
15//! use arcly_stream::codec::dispatch;
16//! use arcly_stream::CodecId;
17//!
18//! let access_unit: &[u8] = &[/* … Annex-B HEVC bytes … */];
19//! // Codec-erased dispatch by CodecId (used at the ingest boundary):
20//! let is_rap = dispatch::is_random_access_point(CodecId::H265, access_unit);
21//! # let _ = is_rap;
22//! # }
23//! ```
24
25mod bitreader;
26pub mod parser;
27
28#[cfg(test)]
29pub(crate) mod testutil;
30
31pub use parser::{CodecConfig, CodecParser, VideoParams};
32
33#[cfg(feature = "_nal")]
34pub mod nal;
35
36#[cfg(feature = "codec-h264")]
37#[cfg_attr(docsrs, doc(cfg(feature = "codec-h264")))]
38pub mod h264;
39
40#[cfg(feature = "codec-h265")]
41#[cfg_attr(docsrs, doc(cfg(feature = "codec-h265")))]
42pub mod h265;
43
44#[cfg(feature = "codec-vvc")]
45#[cfg_attr(docsrs, doc(cfg(feature = "codec-vvc")))]
46pub mod vvc;
47
48#[cfg(feature = "codec-av1")]
49#[cfg_attr(docsrs, doc(cfg(feature = "codec-av1")))]
50pub mod av1;
51
52#[cfg(feature = "codec-av1")]
53pub(crate) mod obu;
54
55#[cfg(feature = "codec-vp9")]
56#[cfg_attr(docsrs, doc(cfg(feature = "codec-vp9")))]
57pub mod vp9;
58
59#[cfg(feature = "codec-aac")]
60#[cfg_attr(docsrs, doc(cfg(feature = "codec-aac")))]
61pub mod aac;
62
63// ── Back-compat flat re-exports (pre-split public surface) ──────────────────
64#[cfg(feature = "codec-aac")]
65pub use aac::{parse_adts, AdtsHeader};
66#[cfg(feature = "codec-h264")]
67pub use h264::{annexb_to_avcc, avcc_to_annexb, iter_nals_annexb, parse_sps, SpsInfo};
68
69/// Codec-erased dispatch over [`CodecId`](crate::CodecId).
70///
71/// Protocol handlers call these to classify an access unit without naming a
72/// concrete parser. Each returns a conservative default (`false` / `None`) for a
73/// codec whose feature is not enabled.
74pub mod dispatch {
75    use super::*;
76    use crate::CodecId;
77
78    /// Extract [`VideoParams`] from a config access unit for `codec`.
79    #[allow(unused_variables)]
80    pub fn parse_config(codec: CodecId, data: &[u8]) -> Option<VideoParams> {
81        match codec {
82            #[cfg(feature = "codec-h264")]
83            CodecId::H264 => <h264::H264 as CodecParser>::parse_config(data),
84            #[cfg(feature = "codec-h265")]
85            CodecId::H265 => <h265::H265 as CodecParser>::parse_config(data),
86            #[cfg(feature = "codec-vvc")]
87            CodecId::VVC => <vvc::Vvc as CodecParser>::parse_config(data),
88            #[cfg(feature = "codec-av1")]
89            CodecId::AV1 => <av1::Av1 as CodecParser>::parse_config(data),
90            #[cfg(feature = "codec-vp9")]
91            CodecId::VP9 => <vp9::Vp9 as CodecParser>::parse_config(data),
92            _ => None,
93        }
94    }
95
96    /// Whether `data` is a random-access point under `codec`.
97    #[allow(unused_variables)]
98    pub fn is_random_access_point(codec: CodecId, data: &[u8]) -> bool {
99        match codec {
100            #[cfg(feature = "codec-h264")]
101            CodecId::H264 => <h264::H264 as CodecParser>::is_random_access_point(data),
102            #[cfg(feature = "codec-h265")]
103            CodecId::H265 => <h265::H265 as CodecParser>::is_random_access_point(data),
104            #[cfg(feature = "codec-vvc")]
105            CodecId::VVC => <vvc::Vvc as CodecParser>::is_random_access_point(data),
106            #[cfg(feature = "codec-av1")]
107            CodecId::AV1 => <av1::Av1 as CodecParser>::is_random_access_point(data),
108            #[cfg(feature = "codec-vp9")]
109            CodecId::VP9 => <vp9::Vp9 as CodecParser>::is_random_access_point(data),
110            _ => false,
111        }
112    }
113
114    /// The HLS `CODECS` attribute for `codec` given parsed `params`.
115    #[allow(unused_variables)]
116    pub fn hls_codec_string(codec: CodecId, params: &VideoParams) -> Option<String> {
117        match codec {
118            #[cfg(feature = "codec-h264")]
119            CodecId::H264 => Some(<h264::H264 as CodecParser>::hls_codec_string(params)),
120            #[cfg(feature = "codec-h265")]
121            CodecId::H265 => Some(<h265::H265 as CodecParser>::hls_codec_string(params)),
122            #[cfg(feature = "codec-vvc")]
123            CodecId::VVC => Some(<vvc::Vvc as CodecParser>::hls_codec_string(params)),
124            #[cfg(feature = "codec-av1")]
125            CodecId::AV1 => Some(<av1::Av1 as CodecParser>::hls_codec_string(params)),
126            #[cfg(feature = "codec-vp9")]
127            CodecId::VP9 => Some(<vp9::Vp9 as CodecParser>::hls_codec_string(params)),
128            _ => None,
129        }
130    }
131}