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
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 CodecConfig: Clone + Send + Sync + 'static {
144    fn media_type() -> MediaType;
145    fn codec_type() -> CodecType;
146    fn from_parameters(params: &CodecParameters) -> Result<Self>;
147    fn configure(&mut self, params: &CodecParameters) -> Result<()>;
148    fn configure_with_option(&mut self, key: &str, value: &Variant) -> Result<()>;
149}
150
151#[cfg(feature = "audio")]
152#[derive(Clone, Debug, Default)]
153pub struct AudioParameters {
154    pub format: Option<SampleFormat>,
155    pub samples: Option<NonZeroU32>,
156    pub sample_rate: Option<NonZeroU32>,
157    pub channel_layout: Option<ChannelLayout>,
158}
159
160#[cfg(feature = "audio")]
161impl AudioParameters {
162    pub(crate) fn update(&mut self, other: &AudioParameters) {
163        self.format = other.format.or(self.format);
164        self.samples = other.samples.or(self.samples);
165        self.sample_rate = other.sample_rate.or(self.sample_rate);
166        if other.channel_layout.is_some() {
167            self.channel_layout = other.channel_layout.clone();
168        }
169    }
170
171    pub(crate) fn update_with_option(&mut self, key: &str, value: &Variant) {
172        match key {
173            "sample_format" => self.format = value.get_uint32().and_then(|fmt| SampleFormat::try_from(fmt as usize).ok()),
174            "samples" => self.samples = value.get_uint32().and_then(NonZeroU32::new),
175            "sample_rate" => self.sample_rate = value.get_uint32().and_then(NonZeroU32::new),
176            "channels" => self.channel_layout = value.get_uint8().and_then(|c| ChannelLayout::default_from_channels(c).ok()),
177            _ => {}
178        }
179    }
180}
181
182#[cfg(feature = "audio")]
183#[allow(unreachable_patterns)]
184impl TryFrom<&MediaParametersType> for AudioParameters {
185    type Error = Error;
186
187    fn try_from(params: &MediaParametersType) -> Result<Self> {
188        match params {
189            MediaParametersType::Audio(params) => Ok(params.clone()),
190            _ => Err(invalid_param_error!(params)),
191        }
192    }
193}
194
195#[cfg(feature = "video")]
196#[derive(Clone, Debug, Default)]
197pub struct VideoParameters {
198    pub format: Option<PixelFormat>,
199    pub width: Option<NonZeroU32>,
200    pub height: Option<NonZeroU32>,
201    pub color_range: Option<ColorRange>,
202    pub color_matrix: Option<ColorMatrix>,
203    pub color_primaries: Option<ColorPrimaries>,
204    pub color_transfer_characteristics: Option<ColorTransferCharacteristics>,
205    pub chroma_location: Option<ChromaLocation>,
206    pub frame_rate: Option<Rational64>,
207}
208
209#[cfg(feature = "video")]
210impl VideoParameters {
211    pub(crate) fn update(&mut self, other: &VideoParameters) {
212        self.format = other.format.or(self.format);
213        self.width = other.width.or(self.width);
214        self.height = other.height.or(self.height);
215        self.color_range = other.color_range.or(self.color_range);
216        self.color_matrix = other.color_matrix.or(self.color_matrix);
217        self.color_primaries = other.color_primaries.or(self.color_primaries);
218        self.color_transfer_characteristics = other.color_transfer_characteristics.or(self.color_transfer_characteristics);
219        self.chroma_location = other.chroma_location.or(self.chroma_location);
220        self.frame_rate = other.frame_rate.or(self.frame_rate);
221    }
222
223    pub(crate) fn update_with_option(&mut self, key: &str, value: &Variant) {
224        match key {
225            "pixel_format" => self.format = value.get_uint32().and_then(|f| PixelFormat::try_from(f as usize).ok()),
226            "width" => self.width = value.get_uint32().and_then(NonZeroU32::new),
227            "height" => self.height = value.get_uint32().and_then(NonZeroU32::new),
228            "color_range" => self.color_range = value.get_uint32().map(|v| ColorRange::from(v as usize)),
229            "color_matrix" => self.color_matrix = value.get_uint32().and_then(|v| ColorMatrix::try_from(v as usize).ok()),
230            "color_primaries" => self.color_primaries = value.get_uint32().and_then(|v| ColorPrimaries::try_from(v as usize).ok()),
231            "color_transfer_characteristics" => {
232                self.color_transfer_characteristics = value.get_uint32().and_then(|v| ColorTransferCharacteristics::try_from(v as usize).ok())
233            }
234            "chroma_location" => self.chroma_location = value.get_uint32().map(|v| ChromaLocation::from(v as usize)),
235            _ => {}
236        }
237    }
238}
239
240#[cfg(feature = "video")]
241#[allow(unreachable_patterns)]
242impl TryFrom<&MediaParametersType> for VideoParameters {
243    type Error = Error;
244
245    fn try_from(params: &MediaParametersType) -> Result<Self> {
246        match params {
247            MediaParametersType::Video(params) => Ok(params.clone()),
248            _ => Err(invalid_param_error!(params)),
249        }
250    }
251}
252
253#[derive(Clone, Debug)]
254pub enum MediaParametersType {
255    #[cfg(feature = "audio")]
256    Audio(AudioParameters),
257    #[cfg(feature = "video")]
258    Video(VideoParameters),
259}
260
261#[cfg(feature = "audio")]
262impl From<AudioParameters> for MediaParametersType {
263    fn from(params: AudioParameters) -> Self {
264        MediaParametersType::Audio(params)
265    }
266}
267
268#[cfg(feature = "video")]
269impl From<VideoParameters> for MediaParametersType {
270    fn from(params: VideoParameters) -> Self {
271        MediaParametersType::Video(params)
272    }
273}
274
275#[derive(Clone, Debug)]
276pub enum CodecParametersType {
277    Decoder(DecoderParameters),
278    Encoder(EncoderParameters),
279}
280
281impl From<DecoderParameters> for CodecParametersType {
282    fn from(params: DecoderParameters) -> Self {
283        CodecParametersType::Decoder(params)
284    }
285}
286
287impl From<EncoderParameters> for CodecParametersType {
288    fn from(params: EncoderParameters) -> Self {
289        CodecParametersType::Encoder(params)
290    }
291}
292
293pub trait CodecInformation {
294    fn id(&self) -> CodecID;
295    fn name(&self) -> &'static str;
296}
297
298pub trait Codec<T: CodecConfig>: CodecInformation {
299    fn configure(&mut self, params: Option<&CodecParameters>, options: Option<&Variant>) -> Result<()>;
300    fn set_option(&mut self, key: &str, value: &Variant) -> Result<()>;
301}
302
303pub trait CodecBuilder<T: CodecConfig>: Any + Send + Sync {
304    fn id(&self) -> CodecID;
305    fn name(&self) -> &'static str;
306}
307
308pub(crate) struct CodecList<T: CodecConfig> {
309    pub(crate) codecs: HashMap<CodecID, Vec<Arc<dyn CodecBuilder<T>>>>,
310}
311
312pub(crate) type LazyCodecList<T> = LazyLock<RwLock<CodecList<T>>>;
313
314pub(crate) fn register_codec<T>(codec_list: &LazyCodecList<T>, builder: Arc<dyn CodecBuilder<T>>, default: bool) -> Result<()>
315where
316    T: CodecConfig,
317{
318    let mut codec_list = codec_list.write().map_err(|err| Error::Invalid(err.to_string()))?;
319    let entry = codec_list.codecs.entry(builder.id()).or_default();
320
321    if default {
322        entry.insert(0, builder);
323    } else {
324        entry.push(builder);
325    }
326
327    Ok(())
328}
329
330pub(crate) fn find_codec<T>(codec_list: &LazyCodecList<T>, id: CodecID) -> Result<Arc<dyn CodecBuilder<T>>>
331where
332    T: CodecConfig,
333{
334    let codec_list = codec_list.read().map_err(|err| Error::Invalid(err.to_string()))?;
335
336    if let Some(builders) = codec_list.codecs.get(&id) {
337        if let Some(builder) = builders.first() {
338            return Ok(builder.clone());
339        }
340    }
341
342    Err(Error::NotFound(format!("codec: {:?}", id)))
343}
344
345pub(crate) fn find_codec_by_name<T>(codec_list: &LazyCodecList<T>, name: &str) -> Result<Arc<dyn CodecBuilder<T>>>
346where
347    T: CodecConfig,
348{
349    let codec_list = codec_list.read().map_err(|err| Error::Invalid(err.to_string()))?;
350
351    for builders in codec_list.codecs.values() {
352        for builder in builders {
353            if builder.name() == name {
354                return Ok(builder.clone());
355            }
356        }
357    }
358
359    Err(Error::NotFound(format!("codec: {}", name)))
360}