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    pub const AVC: CodecID = CodecID::H264;
150    pub const HEVC: CodecID = CodecID::H265;
151    pub const VVC: CodecID = CodecID::H266;
152
153    pub fn media_type(&self) -> MediaType {
154        match ((self.0) >> 16) as u16 {
155            #[cfg(feature = "audio")]
156            x if x == MediaType::Audio as u16 => MediaType::Audio,
157            #[cfg(feature = "video")]
158            x if x == MediaType::Video as u16 => MediaType::Video,
159            _ => unreachable!(),
160        }
161    }
162}
163
164#[derive(Clone, Copy, Debug, Eq, PartialEq)]
165pub enum CodecType {
166    Decoder,
167    Encoder,
168}
169
170#[derive(Clone, Debug)]
171pub struct CodecParameters {
172    pub media: MediaParametersType,
173    pub codec: CodecParametersType,
174}
175
176impl CodecParameters {
177    pub fn new<M, C>(media_params: M, codec_params: C) -> Self
178    where
179        M: Into<MediaParametersType>,
180        C: Into<CodecParametersType>,
181    {
182        Self {
183            media: media_params.into(),
184            codec: codec_params.into(),
185        }
186    }
187}
188
189pub trait CodecSpec: Clone + Send + Sync + 'static {
190    type FrameDescriptor: FrameDescriptorSpec;
191
192    fn media_type() -> MediaType;
193    fn codec_type() -> CodecType;
194    fn from_parameters(params: &CodecParameters) -> Result<Self>;
195    fn configure(&mut self, params: &CodecParameters) -> Result<()>;
196    fn configure_with_option(&mut self, key: &str, value: &Variant) -> Result<()>;
197}
198
199#[cfg(feature = "audio")]
200#[derive(Clone, Debug, Default)]
201pub struct AudioParameters {
202    pub format: Option<SampleFormat>,
203    pub samples: Option<NonZeroU32>,
204    pub sample_rate: Option<NonZeroU32>,
205    pub channel_layout: Option<ChannelLayout>,
206}
207
208#[cfg(feature = "audio")]
209impl AudioParameters {
210    pub(crate) fn update(&mut self, other: &AudioParameters) {
211        self.format = other.format.or(self.format);
212        self.samples = other.samples.or(self.samples);
213        self.sample_rate = other.sample_rate.or(self.sample_rate);
214        if other.channel_layout.is_some() {
215            self.channel_layout = other.channel_layout.clone();
216        }
217    }
218
219    pub(crate) fn update_with_option(&mut self, key: &str, value: &Variant) {
220        match key {
221            "sample_format" => self.format = value.get_uint32().and_then(|fmt| SampleFormat::try_from(fmt as usize).ok()),
222            "samples" => self.samples = value.get_uint32().and_then(NonZeroU32::new),
223            "sample_rate" => self.sample_rate = value.get_uint32().and_then(NonZeroU32::new),
224            "channels" => self.channel_layout = value.get_uint8().and_then(|c| ChannelLayout::default_from_channels(c).ok()),
225            _ => {}
226        }
227    }
228}
229
230#[cfg(feature = "audio")]
231#[allow(unreachable_patterns)]
232impl TryFrom<&MediaParametersType> for AudioParameters {
233    type Error = Error;
234
235    fn try_from(params: &MediaParametersType) -> Result<Self> {
236        match params {
237            MediaParametersType::Audio(params) => Ok(params.clone()),
238            _ => Err(invalid_param_error!(params)),
239        }
240    }
241}
242
243#[cfg(feature = "video")]
244#[derive(Clone, Debug, Default)]
245pub struct VideoParameters {
246    pub format: Option<PixelFormat>,
247    pub width: Option<NonZeroU32>,
248    pub height: Option<NonZeroU32>,
249    pub color_range: Option<ColorRange>,
250    pub color_matrix: Option<ColorMatrix>,
251    pub color_primaries: Option<ColorPrimaries>,
252    pub color_transfer_characteristics: Option<ColorTransferCharacteristics>,
253    pub chroma_location: Option<ChromaLocation>,
254    pub frame_rate: Option<Rational64>,
255}
256
257#[cfg(feature = "video")]
258impl VideoParameters {
259    pub(crate) fn update(&mut self, other: &VideoParameters) {
260        self.format = other.format.or(self.format);
261        self.width = other.width.or(self.width);
262        self.height = other.height.or(self.height);
263        self.color_range = other.color_range.or(self.color_range);
264        self.color_matrix = other.color_matrix.or(self.color_matrix);
265        self.color_primaries = other.color_primaries.or(self.color_primaries);
266        self.color_transfer_characteristics = other.color_transfer_characteristics.or(self.color_transfer_characteristics);
267        self.chroma_location = other.chroma_location.or(self.chroma_location);
268        self.frame_rate = other.frame_rate.or(self.frame_rate);
269    }
270
271    pub(crate) fn update_with_option(&mut self, key: &str, value: &Variant) {
272        match key {
273            "pixel_format" => self.format = value.get_uint32().and_then(|f| PixelFormat::try_from(f as usize).ok()),
274            "width" => self.width = value.get_uint32().and_then(NonZeroU32::new),
275            "height" => self.height = value.get_uint32().and_then(NonZeroU32::new),
276            "color_range" => self.color_range = value.get_uint32().map(|v| ColorRange::from(v as usize)),
277            "color_matrix" => self.color_matrix = value.get_uint32().and_then(|v| ColorMatrix::try_from(v as usize).ok()),
278            "color_primaries" => self.color_primaries = value.get_uint32().and_then(|v| ColorPrimaries::try_from(v as usize).ok()),
279            "color_transfer_characteristics" => {
280                self.color_transfer_characteristics = value.get_uint32().and_then(|v| ColorTransferCharacteristics::try_from(v as usize).ok())
281            }
282            "chroma_location" => self.chroma_location = value.get_uint32().map(|v| ChromaLocation::from(v as usize)),
283            _ => {}
284        }
285    }
286}
287
288#[cfg(feature = "video")]
289#[allow(unreachable_patterns)]
290impl TryFrom<&MediaParametersType> for VideoParameters {
291    type Error = Error;
292
293    fn try_from(params: &MediaParametersType) -> Result<Self> {
294        match params {
295            MediaParametersType::Video(params) => Ok(params.clone()),
296            _ => Err(invalid_param_error!(params)),
297        }
298    }
299}
300
301#[derive(Clone, Debug)]
302pub enum MediaParametersType {
303    #[cfg(feature = "audio")]
304    Audio(AudioParameters),
305    #[cfg(feature = "video")]
306    Video(VideoParameters),
307}
308
309#[cfg(feature = "audio")]
310impl From<AudioParameters> for MediaParametersType {
311    fn from(params: AudioParameters) -> Self {
312        MediaParametersType::Audio(params)
313    }
314}
315
316#[cfg(feature = "video")]
317impl From<VideoParameters> for MediaParametersType {
318    fn from(params: VideoParameters) -> Self {
319        MediaParametersType::Video(params)
320    }
321}
322
323#[derive(Clone, Debug)]
324pub enum CodecParametersType {
325    #[cfg(feature = "decoder")]
326    Decoder(DecoderParameters),
327    #[cfg(feature = "encoder")]
328    Encoder(EncoderParameters),
329}
330
331#[cfg(feature = "decoder")]
332impl From<DecoderParameters> for CodecParametersType {
333    fn from(params: DecoderParameters) -> Self {
334        CodecParametersType::Decoder(params)
335    }
336}
337
338#[cfg(feature = "encoder")]
339impl From<EncoderParameters> for CodecParametersType {
340    fn from(params: EncoderParameters) -> Self {
341        CodecParametersType::Encoder(params)
342    }
343}
344
345pub trait CodecInformation {
346    fn id(&self) -> CodecID;
347    fn name(&self) -> &'static str;
348}
349
350pub trait Codec<T: CodecSpec>: CodecInformation {
351    fn configure(&mut self, params: Option<&CodecParameters>, options: Option<&Variant>) -> Result<()>;
352    fn set_option(&mut self, key: &str, value: &Variant) -> Result<()>;
353}
354
355pub trait CodecBuilder<T: CodecSpec>: Any + Send + Sync {
356    fn ids(&self) -> &[CodecID];
357    fn name(&self) -> &'static str;
358}
359
360#[macro_export]
361macro_rules! define_codec_builder {
362    (
363        $builder:ident<$decoder_type:ty> {
364            name: $codec_name:expr,
365            ids: [$($id:ident),* $(,)?]
366        }
367    ) => {
368        pub struct $builder;
369
370        impl $crate::codec::CodecBuilder<$decoder_type> for $builder {
371            fn ids(&self) -> &[$crate::codec::CodecID] {
372                const IDS: &[$crate::codec::CodecID] = &[$($crate::codec::CodecID::$id),*];
373                IDS
374            }
375            fn name(&self) -> &'static str {
376                $codec_name
377            }
378        }
379    };
380}