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