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