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 if other.format.is_some() {
112 self.format = other.format;
113 }
114 if other.samples.is_some() {
115 self.samples = other.samples;
116 }
117 if other.sample_rate.is_some() {
118 self.sample_rate = other.sample_rate;
119 }
120 if other.channel_layout.is_some() {
121 self.channel_layout = other.channel_layout.clone();
122 }
123 }
124
125 pub(crate) fn update_with_option(&mut self, key: &str, value: &Variant) {
126 match key {
127 "sample_format" => self.format = value.get_uint32().and_then(|fmt| SampleFormat::try_from(fmt as usize).ok()),
128 "samples" => self.samples = value.get_uint32().and_then(NonZeroU32::new),
129 "sample_rate" => self.sample_rate = value.get_uint32().and_then(NonZeroU32::new),
130 "channels" => self.channel_layout = value.get_uint8().and_then(|c| ChannelLayout::default_from_channels(c).ok()),
131 _ => {}
132 }
133 }
134}
135
136#[cfg(feature = "video")]
137#[derive(Clone, Debug, Default)]
138pub struct VideoParameters {
139 pub format: Option<PixelFormat>,
140 pub width: Option<NonZeroU32>,
141 pub height: Option<NonZeroU32>,
142 pub color_range: Option<ColorRange>,
143 pub color_matrix: Option<ColorMatrix>,
144 pub color_primaries: Option<ColorPrimaries>,
145 pub color_transfer_characteristics: Option<ColorTransferCharacteristics>,
146 pub chroma_location: Option<ChromaLocation>,
147 pub frame_rate: Option<Rational64>,
148}
149
150#[cfg(feature = "video")]
151impl VideoParameters {
152 pub(crate) fn update(&mut self, other: &VideoParameters) {
153 if other.format.is_some() {
154 self.format = other.format;
155 }
156 if other.width.is_some() {
157 self.width = other.width;
158 }
159 if other.height.is_some() {
160 self.height = other.height;
161 }
162 if other.color_range.is_some() {
163 self.color_range = other.color_range;
164 }
165 if other.color_matrix.is_some() {
166 self.color_matrix = other.color_matrix;
167 }
168 if other.color_primaries.is_some() {
169 self.color_primaries = other.color_primaries;
170 }
171 if other.color_transfer_characteristics.is_some() {
172 self.color_transfer_characteristics = other.color_transfer_characteristics;
173 }
174 if other.chroma_location.is_some() {
175 self.chroma_location = other.chroma_location;
176 }
177 if other.frame_rate.is_some() {
178 self.frame_rate = other.frame_rate;
179 }
180 }
181
182 pub(crate) fn update_with_option(&mut self, key: &str, value: &Variant) {
183 match key {
184 "pixel_format" => self.format = value.get_uint32().and_then(|f| PixelFormat::try_from(f as usize).ok()),
185 "width" => self.width = value.get_uint32().and_then(NonZeroU32::new),
186 "height" => self.height = value.get_uint32().and_then(NonZeroU32::new),
187 "color_range" => self.color_range = value.get_uint32().map(|v| ColorRange::from(v as usize)),
188 "color_matrix" => self.color_matrix = value.get_uint32().and_then(|v| ColorMatrix::try_from(v as usize).ok()),
189 "color_primaries" => self.color_primaries = value.get_uint32().and_then(|v| ColorPrimaries::try_from(v as usize).ok()),
190 "color_transfer_characteristics" => {
191 self.color_transfer_characteristics = value.get_uint32().and_then(|v| ColorTransferCharacteristics::try_from(v as usize).ok())
192 }
193 "chroma_location" => self.chroma_location = value.get_uint32().map(|v| ChromaLocation::from(v as usize)),
194 _ => {}
195 }
196 }
197}
198
199pub trait CodecInfomation {
200 fn id(&self) -> CodecID;
201 fn name(&self) -> &'static str;
202}
203
204pub trait Codec<T: CodecConfiguration>: CodecInfomation {
205 fn configure(&mut self, parameters: Option<&T::Parameters>, options: Option<&Variant>) -> Result<()>;
206 fn set_option(&mut self, key: &str, value: &Variant) -> Result<()>;
207}
208
209pub trait CodecBuilder<T: CodecConfiguration>: Any + Send + Sync {
210 fn id(&self) -> CodecID;
211 fn name(&self) -> &'static str;
212}
213
214pub(crate) struct CodecList<T: CodecConfiguration> {
215 pub(crate) codecs: HashMap<CodecID, Vec<Arc<dyn CodecBuilder<T>>>>,
216}
217
218pub(crate) type LazyCodecList<T> = LazyLock<RwLock<CodecList<T>>>;
219
220pub(crate) fn register_codec<T>(codec_list: &LazyCodecList<T>, builder: Arc<dyn CodecBuilder<T>>, default: bool) -> Result<()>
221where
222 T: CodecConfiguration,
223{
224 let mut codec_list = codec_list.write().map_err(|err| Error::Invalid(err.to_string()))?;
225 let entry = codec_list.codecs.entry(builder.id()).or_default();
226
227 if default {
228 entry.insert(0, builder);
229 } else {
230 entry.push(builder);
231 }
232
233 Ok(())
234}
235
236pub(crate) fn find_codec<T>(codec_list: &LazyCodecList<T>, id: CodecID) -> Result<Arc<dyn CodecBuilder<T>>>
237where
238 T: CodecConfiguration,
239{
240 let codec_list = codec_list.read().map_err(|err| Error::Invalid(err.to_string()))?;
241
242 if let Some(builders) = codec_list.codecs.get(&id) {
243 if let Some(builder) = builders.first() {
244 return Ok(builder.clone());
245 }
246 }
247
248 Err(Error::NotFound(format!("codec: {:?}", id)))
249}
250
251pub(crate) fn find_codec_by_name<T>(codec_list: &LazyCodecList<T>, name: &str) -> Result<Arc<dyn CodecBuilder<T>>>
252where
253 T: CodecConfiguration,
254{
255 let codec_list = codec_list.read().map_err(|err| Error::Invalid(err.to_string()))?;
256
257 for builders in codec_list.codecs.values() {
258 for builder in builders {
259 if builder.name() == name {
260 return Ok(builder.clone());
261 }
262 }
263 }
264
265 Err(Error::NotFound(format!("codec: {}", name)))
266}