media_codec/
codec.rs

1use std::{
2    collections::HashMap,
3    num::NonZeroU32,
4    sync::{Arc, LazyLock, RwLock},
5};
6
7use media_core::{
8    audio::{ChannelLayout, SampleFormat},
9    error::Error,
10    variant::Variant,
11    video::{ChromaLocation, ColorMatrix, ColorPrimaries, ColorRange, ColorTransferCharacteristics, PixelFormat},
12    MediaType, Result,
13};
14use num_rational::Rational64;
15
16#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
17#[repr(u16)]
18enum AudioCodecID {
19    AAC = 1,
20    Opus,
21}
22
23#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
24#[repr(u16)]
25enum VideoCodecID {
26    H264 = 1,
27    HEVC,
28    VP8,
29    VP9,
30    AV1,
31}
32
33macro_rules! codec_id {
34    ($media_type:ident, $id_enum:ident, $id:ident) => {
35        ((MediaType::$media_type as u32) << 16) | ($id_enum::$id as u32)
36    };
37}
38
39#[repr(u32)]
40#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
41pub enum CodecID {
42    // Audio codecs
43    AAC  = codec_id!(Audio, AudioCodecID, AAC),
44    Opus = codec_id!(Audio, AudioCodecID, Opus),
45    // Video codecs
46    H264 = codec_id!(Video, VideoCodecID, H264),
47    HEVC = codec_id!(Video, VideoCodecID, HEVC),
48    VP8  = codec_id!(Video, VideoCodecID, VP8),
49    VP9  = codec_id!(Video, VideoCodecID, VP9),
50    AV1  = codec_id!(Video, VideoCodecID, AV1),
51}
52
53impl CodecID {
54    pub fn media_type(&self) -> MediaType {
55        match ((*self as u32) >> 16) as u16 {
56            x if x == MediaType::Audio as u16 => MediaType::Audio,
57            x if x == MediaType::Video as u16 => MediaType::Video,
58            _ => unreachable!(),
59        }
60    }
61}
62
63#[derive(Clone, Debug, Default)]
64pub struct AudioCodecParameters {
65    pub format: Option<SampleFormat>,
66    pub samples: Option<NonZeroU32>,
67    pub sample_rate: Option<NonZeroU32>,
68    pub channel_layout: Option<ChannelLayout>,
69}
70
71#[derive(Clone, Debug, Default)]
72pub struct VideoCodecParameters {
73    pub format: Option<PixelFormat>,
74    pub width: Option<NonZeroU32>,
75    pub height: Option<NonZeroU32>,
76    pub color_range: Option<ColorRange>,
77    pub color_matrix: Option<ColorMatrix>,
78    pub color_primaries: Option<ColorPrimaries>,
79    pub color_transfer_characteristics: Option<ColorTransferCharacteristics>,
80    pub chroma_location: Option<ChromaLocation>,
81    pub frame_rate: Option<Rational64>,
82}
83
84#[derive(Clone, Debug)]
85pub enum CodecSpecificParameters {
86    Audio(AudioCodecParameters),
87    Video(VideoCodecParameters),
88}
89
90impl From<AudioCodecParameters> for CodecSpecificParameters {
91    fn from(params: AudioCodecParameters) -> Self {
92        CodecSpecificParameters::Audio(params)
93    }
94}
95
96impl From<VideoCodecParameters> for CodecSpecificParameters {
97    fn from(params: VideoCodecParameters) -> Self {
98        CodecSpecificParameters::Video(params)
99    }
100}
101
102impl CodecSpecificParameters {
103    pub fn media_type(&self) -> MediaType {
104        match self {
105            CodecSpecificParameters::Audio(_) => MediaType::Audio,
106            CodecSpecificParameters::Video(_) => MediaType::Video,
107        }
108    }
109}
110
111#[derive(Clone, Debug, Default)]
112pub struct CodecParameters {
113    pub id: Option<CodecID>,
114    pub specific: Option<CodecSpecificParameters>,
115}
116
117impl CodecParameters {
118    pub fn new<T>(id: CodecID, params: T) -> Self
119    where
120        T: Into<CodecSpecificParameters>,
121    {
122        Self {
123            id: Some(id),
124            specific: Some(params.into()),
125        }
126    }
127
128    pub fn audio(&self) -> Option<&AudioCodecParameters> {
129        self.specific.as_ref().and_then(|spec| match spec {
130            CodecSpecificParameters::Audio(params) => Some(params),
131            _ => None,
132        })
133    }
134
135    pub fn video(&self) -> Option<&VideoCodecParameters> {
136        self.specific.as_ref().and_then(|spec| match spec {
137            CodecSpecificParameters::Video(params) => Some(params),
138            _ => None,
139        })
140    }
141}
142
143pub trait Codec {
144    fn configure(&mut self, parameters: Option<&CodecParameters>, options: Option<&Variant>) -> Result<()>;
145    fn set_option(&mut self, key: &str, value: &Variant) -> Result<()>;
146}
147
148pub trait CodecBuilder: Send + Sync {
149    fn id(&self) -> CodecID;
150    fn name(&self) -> &'static str;
151}
152
153pub(crate) struct CodecList<T> {
154    pub(crate) codecs: HashMap<CodecID, Vec<T>>,
155}
156
157pub(crate) type LazyCodecList<T> = LazyLock<RwLock<CodecList<Arc<T>>>>;
158
159pub(crate) fn register_codec<T>(codec_list: &LazyCodecList<T>, builder: Arc<T>, default: bool) -> Result<()>
160where
161    T: CodecBuilder + ?Sized,
162{
163    let mut codec_list = codec_list.write().map_err(|err| Error::Invalid(err.to_string()))?;
164    let entry = codec_list.codecs.entry(builder.id()).or_default();
165
166    if default {
167        entry.insert(0, builder);
168    } else {
169        entry.push(builder);
170    }
171
172    Ok(())
173}
174
175pub(crate) fn find_codec<T>(codec_list: &LazyCodecList<T>, codec_id: CodecID) -> Result<Arc<T>>
176where
177    T: CodecBuilder + ?Sized,
178{
179    let codec_list = codec_list.read().map_err(|err| Error::Invalid(err.to_string()))?;
180
181    if let Some(builders) = codec_list.codecs.get(&codec_id) {
182        if let Some(builder) = builders.first() {
183            return Ok(builder.clone());
184        }
185    }
186
187    Err(Error::NotFound(format!("codec: {:?}", codec_id)))
188}
189
190pub(crate) fn find_codec_by_name<T>(codec_list: &LazyCodecList<T>, name: &str) -> Result<Arc<T>>
191where
192    T: CodecBuilder + ?Sized,
193{
194    let codec_list = codec_list.read().map_err(|err| Error::Invalid(err.to_string()))?;
195
196    for builders in codec_list.codecs.values() {
197        for builder in builders {
198            if builder.name() == name {
199                return Ok(builder.clone());
200            }
201        }
202    }
203
204    Err(Error::NotFound(format!("codec: {}", name)))
205}