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
18#[cfg(feature = "audio")]
19#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
20#[repr(u16)]
21enum AudioCodecID {
22 AAC = 1,
23 Opus,
24}
25
26#[cfg(feature = "video")]
27#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
28#[repr(u16)]
29enum VideoCodecID {
30 H264 = 1,
31 HEVC,
32 VP8,
33 VP9,
34 AV1,
35}
36
37macro_rules! codec_id {
38 ($media_type:ident, $id_enum:ident, $id:ident) => {
39 ((MediaType::$media_type as u32) << 16) | ($id_enum::$id as u32)
40 };
41}
42
43#[repr(u32)]
44#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
45pub enum CodecID {
46 #[cfg(feature = "audio")]
48 AAC = codec_id!(Audio, AudioCodecID, AAC),
49 #[cfg(feature = "audio")]
50 Opus = codec_id!(Audio, AudioCodecID, Opus),
51 #[cfg(feature = "video")]
53 H264 = codec_id!(Video, VideoCodecID, H264),
54 #[cfg(feature = "video")]
55 HEVC = codec_id!(Video, VideoCodecID, HEVC),
56 #[cfg(feature = "video")]
57 VP8 = codec_id!(Video, VideoCodecID, VP8),
58 #[cfg(feature = "video")]
59 VP9 = codec_id!(Video, VideoCodecID, VP9),
60 #[cfg(feature = "video")]
61 AV1 = codec_id!(Video, VideoCodecID, AV1),
62}
63
64impl CodecID {
65 pub fn media_type(&self) -> MediaType {
66 match ((*self as u32) >> 16) as u16 {
67 #[cfg(feature = "audio")]
68 x if x == MediaType::Audio as u16 => MediaType::Audio,
69 #[cfg(feature = "video")]
70 x if x == MediaType::Video as u16 => MediaType::Video,
71 _ => unreachable!(),
72 }
73 }
74}
75
76#[derive(Clone, Copy, Debug, Eq, PartialEq)]
77pub enum CodecType {
78 Decoder,
79 Encoder,
80}
81
82#[derive(Clone, Debug)]
83pub struct CodecParameters {
84 pub media: MediaParametersType,
85 pub codec: CodecParametersType,
86}
87
88impl CodecParameters {
89 pub fn new<M, C>(media_params: M, codec_params: C) -> Self
90 where
91 M: Into<MediaParametersType>,
92 C: Into<CodecParametersType>,
93 {
94 Self {
95 media: media_params.into(),
96 codec: codec_params.into(),
97 }
98 }
99}
100
101pub trait CodecConfig: Clone + Send + Sync + 'static {
102 fn media_type() -> MediaType;
103 fn codec_type() -> CodecType;
104 fn from_parameters(params: &CodecParameters) -> Result<Self>;
105 fn configure(&mut self, params: &CodecParameters) -> Result<()>;
106 fn configure_with_option(&mut self, key: &str, value: &Variant) -> Result<()>;
107}
108
109#[cfg(feature = "audio")]
110#[derive(Clone, Debug, Default)]
111pub struct AudioParameters {
112 pub format: Option<SampleFormat>,
113 pub samples: Option<NonZeroU32>,
114 pub sample_rate: Option<NonZeroU32>,
115 pub channel_layout: Option<ChannelLayout>,
116}
117
118#[cfg(feature = "audio")]
119impl AudioParameters {
120 pub(crate) fn update(&mut self, other: &AudioParameters) {
121 self.format = other.format.or(self.format);
122 self.samples = other.samples.or(self.samples);
123 self.sample_rate = other.sample_rate.or(self.sample_rate);
124 if other.channel_layout.is_some() {
125 self.channel_layout = other.channel_layout.clone();
126 }
127 }
128
129 pub(crate) fn update_with_option(&mut self, key: &str, value: &Variant) {
130 match key {
131 "sample_format" => self.format = value.get_uint32().and_then(|fmt| SampleFormat::try_from(fmt as usize).ok()),
132 "samples" => self.samples = value.get_uint32().and_then(NonZeroU32::new),
133 "sample_rate" => self.sample_rate = value.get_uint32().and_then(NonZeroU32::new),
134 "channels" => self.channel_layout = value.get_uint8().and_then(|c| ChannelLayout::default_from_channels(c).ok()),
135 _ => {}
136 }
137 }
138}
139
140#[cfg(feature = "audio")]
141#[allow(unreachable_patterns)]
142impl TryFrom<&MediaParametersType> for AudioParameters {
143 type Error = Error;
144
145 fn try_from(params: &MediaParametersType) -> Result<Self> {
146 match params {
147 MediaParametersType::Audio(params) => Ok(params.clone()),
148 _ => Err(invalid_param_error!(params)),
149 }
150 }
151}
152
153#[cfg(feature = "video")]
154#[derive(Clone, Debug, Default)]
155pub struct VideoParameters {
156 pub format: Option<PixelFormat>,
157 pub width: Option<NonZeroU32>,
158 pub height: Option<NonZeroU32>,
159 pub color_range: Option<ColorRange>,
160 pub color_matrix: Option<ColorMatrix>,
161 pub color_primaries: Option<ColorPrimaries>,
162 pub color_transfer_characteristics: Option<ColorTransferCharacteristics>,
163 pub chroma_location: Option<ChromaLocation>,
164 pub frame_rate: Option<Rational64>,
165}
166
167#[cfg(feature = "video")]
168impl VideoParameters {
169 pub(crate) fn update(&mut self, other: &VideoParameters) {
170 self.format = other.format.or(self.format);
171 self.width = other.width.or(self.width);
172 self.height = other.height.or(self.height);
173 self.color_range = other.color_range.or(self.color_range);
174 self.color_matrix = other.color_matrix.or(self.color_matrix);
175 self.color_primaries = other.color_primaries.or(self.color_primaries);
176 self.color_transfer_characteristics = other.color_transfer_characteristics.or(self.color_transfer_characteristics);
177 self.chroma_location = other.chroma_location.or(self.chroma_location);
178 self.frame_rate = other.frame_rate.or(self.frame_rate);
179 }
180
181 pub(crate) fn update_with_option(&mut self, key: &str, value: &Variant) {
182 match key {
183 "pixel_format" => self.format = value.get_uint32().and_then(|f| PixelFormat::try_from(f as usize).ok()),
184 "width" => self.width = value.get_uint32().and_then(NonZeroU32::new),
185 "height" => self.height = value.get_uint32().and_then(NonZeroU32::new),
186 "color_range" => self.color_range = value.get_uint32().map(|v| ColorRange::from(v as usize)),
187 "color_matrix" => self.color_matrix = value.get_uint32().and_then(|v| ColorMatrix::try_from(v as usize).ok()),
188 "color_primaries" => self.color_primaries = value.get_uint32().and_then(|v| ColorPrimaries::try_from(v as usize).ok()),
189 "color_transfer_characteristics" => {
190 self.color_transfer_characteristics = value.get_uint32().and_then(|v| ColorTransferCharacteristics::try_from(v as usize).ok())
191 }
192 "chroma_location" => self.chroma_location = value.get_uint32().map(|v| ChromaLocation::from(v as usize)),
193 _ => {}
194 }
195 }
196}
197
198#[cfg(feature = "video")]
199#[allow(unreachable_patterns)]
200impl TryFrom<&MediaParametersType> for VideoParameters {
201 type Error = Error;
202
203 fn try_from(params: &MediaParametersType) -> Result<Self> {
204 match params {
205 MediaParametersType::Video(params) => Ok(params.clone()),
206 _ => Err(invalid_param_error!(params)),
207 }
208 }
209}
210
211#[derive(Clone, Debug)]
212pub enum MediaParametersType {
213 #[cfg(feature = "audio")]
214 Audio(AudioParameters),
215 #[cfg(feature = "video")]
216 Video(VideoParameters),
217}
218
219#[cfg(feature = "audio")]
220impl From<AudioParameters> for MediaParametersType {
221 fn from(params: AudioParameters) -> Self {
222 MediaParametersType::Audio(params)
223 }
224}
225
226#[cfg(feature = "video")]
227impl From<VideoParameters> for MediaParametersType {
228 fn from(params: VideoParameters) -> Self {
229 MediaParametersType::Video(params)
230 }
231}
232
233#[derive(Clone, Debug)]
234pub enum CodecParametersType {
235 Decoder(DecoderParameters),
236 Encoder(EncoderParameters),
237}
238
239impl From<DecoderParameters> for CodecParametersType {
240 fn from(params: DecoderParameters) -> Self {
241 CodecParametersType::Decoder(params)
242 }
243}
244
245impl From<EncoderParameters> for CodecParametersType {
246 fn from(params: EncoderParameters) -> Self {
247 CodecParametersType::Encoder(params)
248 }
249}
250
251pub trait CodecInformation {
252 fn id(&self) -> CodecID;
253 fn name(&self) -> &'static str;
254}
255
256pub trait Codec<T: CodecConfig>: CodecInformation {
257 fn configure(&mut self, params: Option<&CodecParameters>, options: Option<&Variant>) -> Result<()>;
258 fn set_option(&mut self, key: &str, value: &Variant) -> Result<()>;
259}
260
261pub trait CodecBuilder<T: CodecConfig>: Any + Send + Sync {
262 fn id(&self) -> CodecID;
263 fn name(&self) -> &'static str;
264}
265
266pub(crate) struct CodecList<T: CodecConfig> {
267 pub(crate) codecs: HashMap<CodecID, Vec<Arc<dyn CodecBuilder<T>>>>,
268}
269
270pub(crate) type LazyCodecList<T> = LazyLock<RwLock<CodecList<T>>>;
271
272pub(crate) fn register_codec<T>(codec_list: &LazyCodecList<T>, builder: Arc<dyn CodecBuilder<T>>, default: bool) -> Result<()>
273where
274 T: CodecConfig,
275{
276 let mut codec_list = codec_list.write().map_err(|err| Error::Invalid(err.to_string()))?;
277 let entry = codec_list.codecs.entry(builder.id()).or_default();
278
279 if default {
280 entry.insert(0, builder);
281 } else {
282 entry.push(builder);
283 }
284
285 Ok(())
286}
287
288pub(crate) fn find_codec<T>(codec_list: &LazyCodecList<T>, id: CodecID) -> Result<Arc<dyn CodecBuilder<T>>>
289where
290 T: CodecConfig,
291{
292 let codec_list = codec_list.read().map_err(|err| Error::Invalid(err.to_string()))?;
293
294 if let Some(builders) = codec_list.codecs.get(&id) {
295 if let Some(builder) = builders.first() {
296 return Ok(builder.clone());
297 }
298 }
299
300 Err(Error::NotFound(format!("codec: {:?}", id)))
301}
302
303pub(crate) fn find_codec_by_name<T>(codec_list: &LazyCodecList<T>, name: &str) -> Result<Arc<dyn CodecBuilder<T>>>
304where
305 T: CodecConfig,
306{
307 let codec_list = codec_list.read().map_err(|err| Error::Invalid(err.to_string()))?;
308
309 for builders in codec_list.codecs.values() {
310 for builder in builders {
311 if builder.name() == name {
312 return Ok(builder.clone());
313 }
314 }
315 }
316
317 Err(Error::NotFound(format!("codec: {}", name)))
318}