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