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}