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#[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#[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}