Skip to main content

media_codec_types/
codec.rs

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