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, variant::Variant, MediaType, Result};
13#[cfg(feature = "video")]
14use num_rational::Rational64;
15
16#[cfg(feature = "audio")]
17#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
18#[repr(u16)]
19enum AudioCodecID {
20 AAC = 1,
21 Opus,
22}
23
24#[cfg(feature = "video")]
25#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
26#[repr(u16)]
27enum VideoCodecID {
28 H264 = 1,
29 HEVC,
30 VP8,
31 VP9,
32 AV1,
33}
34
35macro_rules! codec_id {
36 ($media_type:ident, $id_enum:ident, $id:ident) => {
37 ((MediaType::$media_type as u32) << 16) | ($id_enum::$id as u32)
38 };
39}
40
41#[repr(u32)]
42#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
43pub enum CodecID {
44 #[cfg(feature = "audio")]
46 AAC = codec_id!(Audio, AudioCodecID, AAC),
47 #[cfg(feature = "audio")]
48 Opus = codec_id!(Audio, AudioCodecID, Opus),
49 #[cfg(feature = "video")]
51 H264 = codec_id!(Video, VideoCodecID, H264),
52 #[cfg(feature = "video")]
53 HEVC = codec_id!(Video, VideoCodecID, HEVC),
54 #[cfg(feature = "video")]
55 VP8 = codec_id!(Video, VideoCodecID, VP8),
56 #[cfg(feature = "video")]
57 VP9 = codec_id!(Video, VideoCodecID, VP9),
58 #[cfg(feature = "video")]
59 AV1 = codec_id!(Video, VideoCodecID, AV1),
60}
61
62impl CodecID {
63 pub fn media_type(&self) -> MediaType {
64 match ((*self as u32) >> 16) as u16 {
65 #[cfg(feature = "audio")]
66 x if x == MediaType::Audio as u16 => MediaType::Audio,
67 #[cfg(feature = "video")]
68 x if x == MediaType::Video as u16 => MediaType::Video,
69 _ => unreachable!(),
70 }
71 }
72}
73
74#[derive(Clone, Copy, Debug, Eq, PartialEq)]
75pub enum CodecType {
76 Decoder,
77 Encoder,
78}
79
80pub trait CodecParameters: Clone + Send + Sync + 'static {
81 fn media_type() -> MediaType;
82 fn codec_type() -> CodecType;
83}
84
85pub trait CodecConfiguration: Clone + Send + Sync + 'static {
86 type Parameters: CodecParameters;
87
88 fn media_type() -> MediaType {
89 Self::Parameters::media_type()
90 }
91 fn codec_type() -> CodecType {
92 Self::Parameters::codec_type()
93 }
94 fn from_parameters(parameters: &Self::Parameters) -> Result<Self>;
95 fn configure(&mut self, parameters: &Self::Parameters) -> Result<()>;
96 fn configure_with_option(&mut self, key: &str, value: &Variant) -> Result<()>;
97}
98
99#[cfg(feature = "audio")]
100#[derive(Clone, Debug, Default)]
101pub struct AudioParameters {
102 pub format: Option<SampleFormat>,
103 pub samples: Option<NonZeroU32>,
104 pub sample_rate: Option<NonZeroU32>,
105 pub channel_layout: Option<ChannelLayout>,
106}
107
108#[cfg(feature = "audio")]
109impl AudioParameters {
110 pub(crate) fn update(&mut self, other: &AudioParameters) {
111 self.format = other.format.or(self.format);
112 self.samples = other.samples.or(self.samples);
113 self.sample_rate = other.sample_rate.or(self.sample_rate);
114 if other.channel_layout.is_some() {
115 self.channel_layout = other.channel_layout.clone();
116 }
117 }
118
119 pub(crate) fn update_with_option(&mut self, key: &str, value: &Variant) {
120 match key {
121 "sample_format" => self.format = value.get_uint32().and_then(|fmt| SampleFormat::try_from(fmt as usize).ok()),
122 "samples" => self.samples = value.get_uint32().and_then(NonZeroU32::new),
123 "sample_rate" => self.sample_rate = value.get_uint32().and_then(NonZeroU32::new),
124 "channels" => self.channel_layout = value.get_uint8().and_then(|c| ChannelLayout::default_from_channels(c).ok()),
125 _ => {}
126 }
127 }
128}
129
130#[cfg(feature = "video")]
131#[derive(Clone, Debug, Default)]
132pub struct VideoParameters {
133 pub format: Option<PixelFormat>,
134 pub width: Option<NonZeroU32>,
135 pub height: Option<NonZeroU32>,
136 pub color_range: Option<ColorRange>,
137 pub color_matrix: Option<ColorMatrix>,
138 pub color_primaries: Option<ColorPrimaries>,
139 pub color_transfer_characteristics: Option<ColorTransferCharacteristics>,
140 pub chroma_location: Option<ChromaLocation>,
141 pub frame_rate: Option<Rational64>,
142}
143
144#[cfg(feature = "video")]
145impl VideoParameters {
146 pub(crate) fn update(&mut self, other: &VideoParameters) {
147 self.format = other.format.or(self.format);
148 self.width = other.width.or(self.width);
149 self.height = other.height.or(self.height);
150 self.color_range = other.color_range.or(self.color_range);
151 self.color_matrix = other.color_matrix.or(self.color_matrix);
152 self.color_primaries = other.color_primaries.or(self.color_primaries);
153 self.color_transfer_characteristics = other.color_transfer_characteristics.or(self.color_transfer_characteristics);
154 self.chroma_location = other.chroma_location.or(self.chroma_location);
155 self.frame_rate = other.frame_rate.or(self.frame_rate);
156 }
157
158 pub(crate) fn update_with_option(&mut self, key: &str, value: &Variant) {
159 match key {
160 "pixel_format" => self.format = value.get_uint32().and_then(|f| PixelFormat::try_from(f as usize).ok()),
161 "width" => self.width = value.get_uint32().and_then(NonZeroU32::new),
162 "height" => self.height = value.get_uint32().and_then(NonZeroU32::new),
163 "color_range" => self.color_range = value.get_uint32().map(|v| ColorRange::from(v as usize)),
164 "color_matrix" => self.color_matrix = value.get_uint32().and_then(|v| ColorMatrix::try_from(v as usize).ok()),
165 "color_primaries" => self.color_primaries = value.get_uint32().and_then(|v| ColorPrimaries::try_from(v as usize).ok()),
166 "color_transfer_characteristics" => {
167 self.color_transfer_characteristics = value.get_uint32().and_then(|v| ColorTransferCharacteristics::try_from(v as usize).ok())
168 }
169 "chroma_location" => self.chroma_location = value.get_uint32().map(|v| ChromaLocation::from(v as usize)),
170 _ => {}
171 }
172 }
173}
174
175pub trait CodecInfomation {
176 fn id(&self) -> CodecID;
177 fn name(&self) -> &'static str;
178}
179
180pub trait Codec<T: CodecConfiguration>: CodecInfomation {
181 fn configure(&mut self, parameters: Option<&T::Parameters>, options: Option<&Variant>) -> Result<()>;
182 fn set_option(&mut self, key: &str, value: &Variant) -> Result<()>;
183}
184
185pub trait CodecBuilder<T: CodecConfiguration>: Any + Send + Sync {
186 fn id(&self) -> CodecID;
187 fn name(&self) -> &'static str;
188}
189
190pub(crate) struct CodecList<T: CodecConfiguration> {
191 pub(crate) codecs: HashMap<CodecID, Vec<Arc<dyn CodecBuilder<T>>>>,
192}
193
194pub(crate) type LazyCodecList<T> = LazyLock<RwLock<CodecList<T>>>;
195
196pub(crate) fn register_codec<T>(codec_list: &LazyCodecList<T>, builder: Arc<dyn CodecBuilder<T>>, default: bool) -> Result<()>
197where
198 T: CodecConfiguration,
199{
200 let mut codec_list = codec_list.write().map_err(|err| Error::Invalid(err.to_string()))?;
201 let entry = codec_list.codecs.entry(builder.id()).or_default();
202
203 if default {
204 entry.insert(0, builder);
205 } else {
206 entry.push(builder);
207 }
208
209 Ok(())
210}
211
212pub(crate) fn find_codec<T>(codec_list: &LazyCodecList<T>, id: CodecID) -> Result<Arc<dyn CodecBuilder<T>>>
213where
214 T: CodecConfiguration,
215{
216 let codec_list = codec_list.read().map_err(|err| Error::Invalid(err.to_string()))?;
217
218 if let Some(builders) = codec_list.codecs.get(&id) {
219 if let Some(builder) = builders.first() {
220 return Ok(builder.clone());
221 }
222 }
223
224 Err(Error::NotFound(format!("codec: {:?}", id)))
225}
226
227pub(crate) fn find_codec_by_name<T>(codec_list: &LazyCodecList<T>, name: &str) -> Result<Arc<dyn CodecBuilder<T>>>
228where
229 T: CodecConfiguration,
230{
231 let codec_list = codec_list.read().map_err(|err| Error::Invalid(err.to_string()))?;
232
233 for builders in codec_list.codecs.values() {
234 for builder in builders {
235 if builder.name() == name {
236 return Ok(builder.clone());
237 }
238 }
239 }
240
241 Err(Error::NotFound(format!("codec: {}", name)))
242}