media_codec/
codec.rs

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