media_codec/
codec.rs

1use std::{
2    any::Any,
3    collections::HashMap,
4    num::NonZeroU32,
5    sync::{Arc, LazyLock, RwLock},
6};
7
8#[cfg(feature = "audio")]
9use media_core::audio::{ChannelLayout, SampleFormat};
10#[cfg(feature = "video")]
11use media_core::video::{ChromaLocation, ColorMatrix, ColorPrimaries, ColorRange, ColorTransferCharacteristics, PixelFormat};
12use media_core::{error::Error, invalid_param_error, variant::Variant, MediaType, Result};
13#[cfg(feature = "video")]
14use num_rational::Rational64;
15
16use crate::{decoder::DecoderParameters, encoder::EncoderParameters};
17
18#[cfg(feature = "audio")]
19#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
20#[repr(u16)]
21enum AudioCodecID {
22    AAC = 1,
23    Opus,
24}
25
26#[cfg(feature = "video")]
27#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
28#[repr(u16)]
29enum VideoCodecID {
30    H264 = 1,
31    HEVC,
32    VP8,
33    VP9,
34    AV1,
35}
36
37macro_rules! codec_id {
38    ($media_type:ident, $id_enum:ident, $id:ident) => {
39        ((MediaType::$media_type as u32) << 16) | ($id_enum::$id as u32)
40    };
41}
42
43#[repr(u32)]
44#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
45pub enum CodecID {
46    // Audio codecs
47    #[cfg(feature = "audio")]
48    AAC  = codec_id!(Audio, AudioCodecID, AAC),
49    #[cfg(feature = "audio")]
50    Opus = codec_id!(Audio, AudioCodecID, Opus),
51    // Video codecs
52    #[cfg(feature = "video")]
53    H264 = codec_id!(Video, VideoCodecID, H264),
54    #[cfg(feature = "video")]
55    HEVC = codec_id!(Video, VideoCodecID, HEVC),
56    #[cfg(feature = "video")]
57    VP8  = codec_id!(Video, VideoCodecID, VP8),
58    #[cfg(feature = "video")]
59    VP9  = codec_id!(Video, VideoCodecID, VP9),
60    #[cfg(feature = "video")]
61    AV1  = codec_id!(Video, VideoCodecID, AV1),
62}
63
64impl CodecID {
65    pub fn media_type(&self) -> MediaType {
66        match ((*self as u32) >> 16) as u16 {
67            #[cfg(feature = "audio")]
68            x if x == MediaType::Audio as u16 => MediaType::Audio,
69            #[cfg(feature = "video")]
70            x if x == MediaType::Video as u16 => MediaType::Video,
71            _ => unreachable!(),
72        }
73    }
74}
75
76#[derive(Clone, Copy, Debug, Eq, PartialEq)]
77pub enum CodecType {
78    Decoder,
79    Encoder,
80}
81
82#[derive(Clone, Debug)]
83pub struct CodecParameters {
84    pub media: MediaParametersType,
85    pub codec: CodecParametersType,
86}
87
88impl CodecParameters {
89    pub fn new<M, C>(media_params: M, codec_params: C) -> Self
90    where
91        M: Into<MediaParametersType>,
92        C: Into<CodecParametersType>,
93    {
94        Self {
95            media: media_params.into(),
96            codec: codec_params.into(),
97        }
98    }
99}
100
101pub trait CodecConfig: Clone + Send + Sync + 'static {
102    fn media_type() -> MediaType;
103    fn codec_type() -> CodecType;
104    fn from_parameters(params: &CodecParameters) -> Result<Self>;
105    fn configure(&mut self, params: &CodecParameters) -> Result<()>;
106    fn configure_with_option(&mut self, key: &str, value: &Variant) -> Result<()>;
107}
108
109#[cfg(feature = "audio")]
110#[derive(Clone, Debug, Default)]
111pub struct AudioParameters {
112    pub format: Option<SampleFormat>,
113    pub samples: Option<NonZeroU32>,
114    pub sample_rate: Option<NonZeroU32>,
115    pub channel_layout: Option<ChannelLayout>,
116}
117
118#[cfg(feature = "audio")]
119impl AudioParameters {
120    pub(crate) fn update(&mut self, other: &AudioParameters) {
121        self.format = other.format.or(self.format);
122        self.samples = other.samples.or(self.samples);
123        self.sample_rate = other.sample_rate.or(self.sample_rate);
124        if other.channel_layout.is_some() {
125            self.channel_layout = other.channel_layout.clone();
126        }
127    }
128
129    pub(crate) fn update_with_option(&mut self, key: &str, value: &Variant) {
130        match key {
131            "sample_format" => self.format = value.get_uint32().and_then(|fmt| SampleFormat::try_from(fmt as usize).ok()),
132            "samples" => self.samples = value.get_uint32().and_then(NonZeroU32::new),
133            "sample_rate" => self.sample_rate = value.get_uint32().and_then(NonZeroU32::new),
134            "channels" => self.channel_layout = value.get_uint8().and_then(|c| ChannelLayout::default_from_channels(c).ok()),
135            _ => {}
136        }
137    }
138}
139
140#[cfg(feature = "audio")]
141#[allow(unreachable_patterns)]
142impl TryFrom<&MediaParametersType> for AudioParameters {
143    type Error = Error;
144
145    fn try_from(params: &MediaParametersType) -> Result<Self> {
146        match params {
147            MediaParametersType::Audio(params) => Ok(params.clone()),
148            _ => Err(invalid_param_error!(params)),
149        }
150    }
151}
152
153#[cfg(feature = "video")]
154#[derive(Clone, Debug, Default)]
155pub struct VideoParameters {
156    pub format: Option<PixelFormat>,
157    pub width: Option<NonZeroU32>,
158    pub height: Option<NonZeroU32>,
159    pub color_range: Option<ColorRange>,
160    pub color_matrix: Option<ColorMatrix>,
161    pub color_primaries: Option<ColorPrimaries>,
162    pub color_transfer_characteristics: Option<ColorTransferCharacteristics>,
163    pub chroma_location: Option<ChromaLocation>,
164    pub frame_rate: Option<Rational64>,
165}
166
167#[cfg(feature = "video")]
168impl VideoParameters {
169    pub(crate) fn update(&mut self, other: &VideoParameters) {
170        self.format = other.format.or(self.format);
171        self.width = other.width.or(self.width);
172        self.height = other.height.or(self.height);
173        self.color_range = other.color_range.or(self.color_range);
174        self.color_matrix = other.color_matrix.or(self.color_matrix);
175        self.color_primaries = other.color_primaries.or(self.color_primaries);
176        self.color_transfer_characteristics = other.color_transfer_characteristics.or(self.color_transfer_characteristics);
177        self.chroma_location = other.chroma_location.or(self.chroma_location);
178        self.frame_rate = other.frame_rate.or(self.frame_rate);
179    }
180
181    pub(crate) fn update_with_option(&mut self, key: &str, value: &Variant) {
182        match key {
183            "pixel_format" => self.format = value.get_uint32().and_then(|f| PixelFormat::try_from(f as usize).ok()),
184            "width" => self.width = value.get_uint32().and_then(NonZeroU32::new),
185            "height" => self.height = value.get_uint32().and_then(NonZeroU32::new),
186            "color_range" => self.color_range = value.get_uint32().map(|v| ColorRange::from(v as usize)),
187            "color_matrix" => self.color_matrix = value.get_uint32().and_then(|v| ColorMatrix::try_from(v as usize).ok()),
188            "color_primaries" => self.color_primaries = value.get_uint32().and_then(|v| ColorPrimaries::try_from(v as usize).ok()),
189            "color_transfer_characteristics" => {
190                self.color_transfer_characteristics = value.get_uint32().and_then(|v| ColorTransferCharacteristics::try_from(v as usize).ok())
191            }
192            "chroma_location" => self.chroma_location = value.get_uint32().map(|v| ChromaLocation::from(v as usize)),
193            _ => {}
194        }
195    }
196}
197
198#[cfg(feature = "video")]
199#[allow(unreachable_patterns)]
200impl TryFrom<&MediaParametersType> for VideoParameters {
201    type Error = Error;
202
203    fn try_from(params: &MediaParametersType) -> Result<Self> {
204        match params {
205            MediaParametersType::Video(params) => Ok(params.clone()),
206            _ => Err(invalid_param_error!(params)),
207        }
208    }
209}
210
211#[derive(Clone, Debug)]
212pub enum MediaParametersType {
213    #[cfg(feature = "audio")]
214    Audio(AudioParameters),
215    #[cfg(feature = "video")]
216    Video(VideoParameters),
217}
218
219#[cfg(feature = "audio")]
220impl From<AudioParameters> for MediaParametersType {
221    fn from(params: AudioParameters) -> Self {
222        MediaParametersType::Audio(params)
223    }
224}
225
226#[cfg(feature = "video")]
227impl From<VideoParameters> for MediaParametersType {
228    fn from(params: VideoParameters) -> Self {
229        MediaParametersType::Video(params)
230    }
231}
232
233#[derive(Clone, Debug)]
234pub enum CodecParametersType {
235    Decoder(DecoderParameters),
236    Encoder(EncoderParameters),
237}
238
239impl From<DecoderParameters> for CodecParametersType {
240    fn from(params: DecoderParameters) -> Self {
241        CodecParametersType::Decoder(params)
242    }
243}
244
245impl From<EncoderParameters> for CodecParametersType {
246    fn from(params: EncoderParameters) -> Self {
247        CodecParametersType::Encoder(params)
248    }
249}
250
251pub trait CodecInformation {
252    fn id(&self) -> CodecID;
253    fn name(&self) -> &'static str;
254}
255
256pub trait Codec<T: CodecConfig>: CodecInformation {
257    fn configure(&mut self, params: Option<&CodecParameters>, options: Option<&Variant>) -> Result<()>;
258    fn set_option(&mut self, key: &str, value: &Variant) -> Result<()>;
259}
260
261pub trait CodecBuilder<T: CodecConfig>: Any + Send + Sync {
262    fn id(&self) -> CodecID;
263    fn name(&self) -> &'static str;
264}
265
266pub(crate) struct CodecList<T: CodecConfig> {
267    pub(crate) codecs: HashMap<CodecID, Vec<Arc<dyn CodecBuilder<T>>>>,
268}
269
270pub(crate) type LazyCodecList<T> = LazyLock<RwLock<CodecList<T>>>;
271
272pub(crate) fn register_codec<T>(codec_list: &LazyCodecList<T>, builder: Arc<dyn CodecBuilder<T>>, default: bool) -> Result<()>
273where
274    T: CodecConfig,
275{
276    let mut codec_list = codec_list.write().map_err(|err| Error::Invalid(err.to_string()))?;
277    let entry = codec_list.codecs.entry(builder.id()).or_default();
278
279    if default {
280        entry.insert(0, builder);
281    } else {
282        entry.push(builder);
283    }
284
285    Ok(())
286}
287
288pub(crate) fn find_codec<T>(codec_list: &LazyCodecList<T>, id: CodecID) -> Result<Arc<dyn CodecBuilder<T>>>
289where
290    T: CodecConfig,
291{
292    let codec_list = codec_list.read().map_err(|err| Error::Invalid(err.to_string()))?;
293
294    if let Some(builders) = codec_list.codecs.get(&id) {
295        if let Some(builder) = builders.first() {
296            return Ok(builder.clone());
297        }
298    }
299
300    Err(Error::NotFound(format!("codec: {:?}", id)))
301}
302
303pub(crate) fn find_codec_by_name<T>(codec_list: &LazyCodecList<T>, name: &str) -> Result<Arc<dyn CodecBuilder<T>>>
304where
305    T: CodecConfig,
306{
307    let codec_list = codec_list.read().map_err(|err| Error::Invalid(err.to_string()))?;
308
309    for builders in codec_list.codecs.values() {
310        for builder in builders {
311            if builder.name() == name {
312                return Ok(builder.clone());
313            }
314        }
315    }
316
317    Err(Error::NotFound(format!("codec: {}", name)))
318}