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, FrameDescriptorSpec, MediaType, Result};
13#[cfg(feature = "video")]
14use num_rational::Rational64;
15
16use crate::{decoder::DecoderParameters, encoder::EncoderParameters};
17
18macro_rules! codecs {
19    (@$media_type:ident: $($name:ident),+ $(,)?) => {
20        codecs!(@impl $media_type, 1, $($name),+);
21    };
22
23    (@impl $media_type:ident, $id:expr, $name:ident) => {
24        pub const $name: CodecID = CodecID(((MediaType::$media_type as u32) << 16) | $id);
25    };
26
27    (@impl $media_type:ident, $id:expr, $name:ident, $($rest:ident),+) => {
28        pub const $name: CodecID = CodecID(((MediaType::$media_type as u32) << 16) | $id);
29        codecs!(@impl $media_type, $id + 1, $($rest),+);
30    };
31}
32
33#[repr(transparent)]
34#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
35pub struct CodecID(u32);
36
37// Audio codecs
38#[cfg(feature = "audio")]
39impl CodecID {
40    codecs! {
41        @Audio:
42        MP1,
43        MP2,
44        MP3,
45        AAC,
46        AC3,
47        EAC3,
48        DTS,
49        FLAC,
50        ALAC,
51        G723_1,
52        G729,
53        VORBIS,
54        OPUS,
55        WMA1,
56        WMA2,
57        WMAVOICE,
58        WMAPRO,
59        WMALOSSLESS,
60    }
61}
62
63// Video codecs
64#[cfg(feature = "video")]
65impl CodecID {
66    codecs! {
67        @Video:
68        MPEG1,
69        MPEG2,
70        MPEG4,
71        MJPEG,
72        H261,
73        H263,
74        H264,
75        HEVC,
76        VVC,
77        VP8,
78        VP9,
79        AV1,
80        RV10,
81        RV20,
82        RV30,
83        RV40,
84        RV60,
85        FLV1,
86        WMV1,
87        WMV2,
88        WMV3,
89        VC1,
90        AVS,
91        CAVS,
92        AVS2,
93        AVS3,
94        BMP,
95        PNG,
96        APNG,
97        GIF,
98        TIFF,
99        WEBP,
100        JPEGXL,
101        JPEG2000,
102        PRORES,
103    }
104}
105
106impl CodecID {
107    pub fn media_type(&self) -> MediaType {
108        match ((self.0) >> 16) as u16 {
109            #[cfg(feature = "audio")]
110            x if x == MediaType::Audio as u16 => MediaType::Audio,
111            #[cfg(feature = "video")]
112            x if x == MediaType::Video as u16 => MediaType::Video,
113            _ => unreachable!(),
114        }
115    }
116}
117
118#[derive(Clone, Copy, Debug, Eq, PartialEq)]
119pub enum CodecType {
120    Decoder,
121    Encoder,
122}
123
124#[derive(Clone, Debug)]
125pub struct CodecParameters {
126    pub media: MediaParametersType,
127    pub codec: CodecParametersType,
128}
129
130impl CodecParameters {
131    pub fn new<M, C>(media_params: M, codec_params: C) -> Self
132    where
133        M: Into<MediaParametersType>,
134        C: Into<CodecParametersType>,
135    {
136        Self {
137            media: media_params.into(),
138            codec: codec_params.into(),
139        }
140    }
141}
142
143pub trait CodecSpec: Clone + Send + Sync + 'static {
144    type FrameDescriptor: FrameDescriptorSpec;
145
146    fn media_type() -> MediaType;
147    fn codec_type() -> CodecType;
148    fn from_parameters(params: &CodecParameters) -> Result<Self>;
149    fn configure(&mut self, params: &CodecParameters) -> Result<()>;
150    fn configure_with_option(&mut self, key: &str, value: &Variant) -> Result<()>;
151}
152
153#[cfg(feature = "audio")]
154#[derive(Clone, Debug, Default)]
155pub struct AudioParameters {
156    pub format: Option<SampleFormat>,
157    pub samples: Option<NonZeroU32>,
158    pub sample_rate: Option<NonZeroU32>,
159    pub channel_layout: Option<ChannelLayout>,
160}
161
162#[cfg(feature = "audio")]
163impl AudioParameters {
164    pub(crate) fn update(&mut self, other: &AudioParameters) {
165        self.format = other.format.or(self.format);
166        self.samples = other.samples.or(self.samples);
167        self.sample_rate = other.sample_rate.or(self.sample_rate);
168        if other.channel_layout.is_some() {
169            self.channel_layout = other.channel_layout.clone();
170        }
171    }
172
173    pub(crate) fn update_with_option(&mut self, key: &str, value: &Variant) {
174        match key {
175            "sample_format" => self.format = value.get_uint32().and_then(|fmt| SampleFormat::try_from(fmt as usize).ok()),
176            "samples" => self.samples = value.get_uint32().and_then(NonZeroU32::new),
177            "sample_rate" => self.sample_rate = value.get_uint32().and_then(NonZeroU32::new),
178            "channels" => self.channel_layout = value.get_uint8().and_then(|c| ChannelLayout::default_from_channels(c).ok()),
179            _ => {}
180        }
181    }
182}
183
184#[cfg(feature = "audio")]
185#[allow(unreachable_patterns)]
186impl TryFrom<&MediaParametersType> for AudioParameters {
187    type Error = Error;
188
189    fn try_from(params: &MediaParametersType) -> Result<Self> {
190        match params {
191            MediaParametersType::Audio(params) => Ok(params.clone()),
192            _ => Err(invalid_param_error!(params)),
193        }
194    }
195}
196
197#[cfg(feature = "video")]
198#[derive(Clone, Debug, Default)]
199pub struct VideoParameters {
200    pub format: Option<PixelFormat>,
201    pub width: Option<NonZeroU32>,
202    pub height: Option<NonZeroU32>,
203    pub color_range: Option<ColorRange>,
204    pub color_matrix: Option<ColorMatrix>,
205    pub color_primaries: Option<ColorPrimaries>,
206    pub color_transfer_characteristics: Option<ColorTransferCharacteristics>,
207    pub chroma_location: Option<ChromaLocation>,
208    pub frame_rate: Option<Rational64>,
209}
210
211#[cfg(feature = "video")]
212impl VideoParameters {
213    pub(crate) fn update(&mut self, other: &VideoParameters) {
214        self.format = other.format.or(self.format);
215        self.width = other.width.or(self.width);
216        self.height = other.height.or(self.height);
217        self.color_range = other.color_range.or(self.color_range);
218        self.color_matrix = other.color_matrix.or(self.color_matrix);
219        self.color_primaries = other.color_primaries.or(self.color_primaries);
220        self.color_transfer_characteristics = other.color_transfer_characteristics.or(self.color_transfer_characteristics);
221        self.chroma_location = other.chroma_location.or(self.chroma_location);
222        self.frame_rate = other.frame_rate.or(self.frame_rate);
223    }
224
225    pub(crate) fn update_with_option(&mut self, key: &str, value: &Variant) {
226        match key {
227            "pixel_format" => self.format = value.get_uint32().and_then(|f| PixelFormat::try_from(f as usize).ok()),
228            "width" => self.width = value.get_uint32().and_then(NonZeroU32::new),
229            "height" => self.height = value.get_uint32().and_then(NonZeroU32::new),
230            "color_range" => self.color_range = value.get_uint32().map(|v| ColorRange::from(v as usize)),
231            "color_matrix" => self.color_matrix = value.get_uint32().and_then(|v| ColorMatrix::try_from(v as usize).ok()),
232            "color_primaries" => self.color_primaries = value.get_uint32().and_then(|v| ColorPrimaries::try_from(v as usize).ok()),
233            "color_transfer_characteristics" => {
234                self.color_transfer_characteristics = value.get_uint32().and_then(|v| ColorTransferCharacteristics::try_from(v as usize).ok())
235            }
236            "chroma_location" => self.chroma_location = value.get_uint32().map(|v| ChromaLocation::from(v as usize)),
237            _ => {}
238        }
239    }
240}
241
242#[cfg(feature = "video")]
243#[allow(unreachable_patterns)]
244impl TryFrom<&MediaParametersType> for VideoParameters {
245    type Error = Error;
246
247    fn try_from(params: &MediaParametersType) -> Result<Self> {
248        match params {
249            MediaParametersType::Video(params) => Ok(params.clone()),
250            _ => Err(invalid_param_error!(params)),
251        }
252    }
253}
254
255#[derive(Clone, Debug)]
256pub enum MediaParametersType {
257    #[cfg(feature = "audio")]
258    Audio(AudioParameters),
259    #[cfg(feature = "video")]
260    Video(VideoParameters),
261}
262
263#[cfg(feature = "audio")]
264impl From<AudioParameters> for MediaParametersType {
265    fn from(params: AudioParameters) -> Self {
266        MediaParametersType::Audio(params)
267    }
268}
269
270#[cfg(feature = "video")]
271impl From<VideoParameters> for MediaParametersType {
272    fn from(params: VideoParameters) -> Self {
273        MediaParametersType::Video(params)
274    }
275}
276
277#[derive(Clone, Debug)]
278pub enum CodecParametersType {
279    Decoder(DecoderParameters),
280    Encoder(EncoderParameters),
281}
282
283impl From<DecoderParameters> for CodecParametersType {
284    fn from(params: DecoderParameters) -> Self {
285        CodecParametersType::Decoder(params)
286    }
287}
288
289impl From<EncoderParameters> for CodecParametersType {
290    fn from(params: EncoderParameters) -> Self {
291        CodecParametersType::Encoder(params)
292    }
293}
294
295pub trait CodecInformation {
296    fn id(&self) -> CodecID;
297    fn name(&self) -> &'static str;
298}
299
300pub trait Codec<T: CodecSpec>: CodecInformation {
301    fn configure(&mut self, params: Option<&CodecParameters>, options: Option<&Variant>) -> Result<()>;
302    fn set_option(&mut self, key: &str, value: &Variant) -> Result<()>;
303}
304
305pub trait CodecBuilder<T: CodecSpec>: Any + Send + Sync {
306    fn id(&self) -> CodecID;
307    fn name(&self) -> &'static str;
308}
309
310pub(crate) struct CodecList<T: CodecSpec> {
311    pub(crate) codecs: HashMap<CodecID, Vec<Arc<dyn CodecBuilder<T>>>>,
312}
313
314pub(crate) type LazyCodecList<T> = LazyLock<RwLock<CodecList<T>>>;
315
316pub(crate) fn register_codec<T>(codec_list: &LazyCodecList<T>, builder: Arc<dyn CodecBuilder<T>>, default: bool) -> Result<()>
317where
318    T: CodecSpec,
319{
320    let mut codec_list = codec_list.write().map_err(|err| Error::Invalid(err.to_string()))?;
321    let entry = codec_list.codecs.entry(builder.id()).or_default();
322
323    if default {
324        entry.insert(0, builder);
325    } else {
326        entry.push(builder);
327    }
328
329    Ok(())
330}
331
332pub(crate) fn find_codec<T>(codec_list: &LazyCodecList<T>, id: CodecID) -> Result<Arc<dyn CodecBuilder<T>>>
333where
334    T: CodecSpec,
335{
336    let codec_list = codec_list.read().map_err(|err| Error::Invalid(err.to_string()))?;
337
338    if let Some(builders) = codec_list.codecs.get(&id) {
339        if let Some(builder) = builders.first() {
340            return Ok(builder.clone());
341        }
342    }
343
344    Err(Error::NotFound(format!("codec: {:?}", id)))
345}
346
347pub(crate) fn find_codec_by_name<T>(codec_list: &LazyCodecList<T>, name: &str) -> Result<Arc<dyn CodecBuilder<T>>>
348where
349    T: CodecSpec,
350{
351    let codec_list = codec_list.read().map_err(|err| Error::Invalid(err.to_string()))?;
352
353    for builders in codec_list.codecs.values() {
354        for builder in builders {
355            if builder.name() == name {
356                return Ok(builder.clone());
357            }
358        }
359    }
360
361    Err(Error::NotFound(format!("codec: {}", name)))
362}