1use std::{
2 collections::HashMap,
3 num::NonZeroU32,
4 sync::{Arc, LazyLock, RwLock},
5};
6
7use media_core::{
8 audio::{ChannelLayout, SampleFormat},
9 error::Error,
10 variant::Variant,
11 video::{ChromaLocation, ColorMatrix, ColorPrimaries, ColorRange, ColorTransferCharacteristics, PixelFormat},
12 MediaType, Result,
13};
14use num_rational::Rational64;
15
16#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
17#[repr(u16)]
18enum AudioCodecID {
19 AAC = 1,
20 Opus,
21}
22
23#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
24#[repr(u16)]
25enum VideoCodecID {
26 H264 = 1,
27 HEVC,
28 VP8,
29 VP9,
30 AV1,
31}
32
33macro_rules! codec_id {
34 ($media_type:ident, $id_enum:ident, $id:ident) => {
35 ((MediaType::$media_type as u32) << 16) | ($id_enum::$id as u32)
36 };
37}
38
39#[repr(u32)]
40#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
41pub enum CodecID {
42 AAC = codec_id!(Audio, AudioCodecID, AAC),
44 Opus = codec_id!(Audio, AudioCodecID, Opus),
45 H264 = codec_id!(Video, VideoCodecID, H264),
47 HEVC = codec_id!(Video, VideoCodecID, HEVC),
48 VP8 = codec_id!(Video, VideoCodecID, VP8),
49 VP9 = codec_id!(Video, VideoCodecID, VP9),
50 AV1 = codec_id!(Video, VideoCodecID, AV1),
51}
52
53impl CodecID {
54 pub fn media_type(&self) -> MediaType {
55 match ((*self as u32) >> 16) as u16 {
56 x if x == MediaType::Audio as u16 => MediaType::Audio,
57 x if x == MediaType::Video as u16 => MediaType::Video,
58 _ => unreachable!(),
59 }
60 }
61}
62
63#[derive(Clone, Debug, Default)]
64pub struct AudioCodecParameters {
65 pub format: Option<SampleFormat>,
66 pub samples: Option<NonZeroU32>,
67 pub sample_rate: Option<NonZeroU32>,
68 pub channel_layout: Option<ChannelLayout>,
69}
70
71#[derive(Clone, Debug, Default)]
72pub struct VideoCodecParameters {
73 pub format: Option<PixelFormat>,
74 pub width: Option<NonZeroU32>,
75 pub height: Option<NonZeroU32>,
76 pub color_range: Option<ColorRange>,
77 pub color_matrix: Option<ColorMatrix>,
78 pub color_primaries: Option<ColorPrimaries>,
79 pub color_transfer_characteristics: Option<ColorTransferCharacteristics>,
80 pub chroma_location: Option<ChromaLocation>,
81 pub frame_rate: Option<Rational64>,
82}
83
84#[derive(Clone, Debug)]
85pub enum CodecSpecificParameters {
86 Audio(AudioCodecParameters),
87 Video(VideoCodecParameters),
88}
89
90impl From<AudioCodecParameters> for CodecSpecificParameters {
91 fn from(params: AudioCodecParameters) -> Self {
92 CodecSpecificParameters::Audio(params)
93 }
94}
95
96impl From<VideoCodecParameters> for CodecSpecificParameters {
97 fn from(params: VideoCodecParameters) -> Self {
98 CodecSpecificParameters::Video(params)
99 }
100}
101
102impl CodecSpecificParameters {
103 pub fn media_type(&self) -> MediaType {
104 match self {
105 CodecSpecificParameters::Audio(_) => MediaType::Audio,
106 CodecSpecificParameters::Video(_) => MediaType::Video,
107 }
108 }
109}
110
111#[derive(Clone, Debug, Default)]
112pub struct CodecParameters {
113 pub id: Option<CodecID>,
114 pub specific: Option<CodecSpecificParameters>,
115}
116
117impl CodecParameters {
118 pub fn new<T>(id: CodecID, params: T) -> Self
119 where
120 T: Into<CodecSpecificParameters>,
121 {
122 Self {
123 id: Some(id),
124 specific: Some(params.into()),
125 }
126 }
127
128 pub fn audio(&self) -> Option<&AudioCodecParameters> {
129 self.specific.as_ref().and_then(|spec| match spec {
130 CodecSpecificParameters::Audio(params) => Some(params),
131 _ => None,
132 })
133 }
134
135 pub fn video(&self) -> Option<&VideoCodecParameters> {
136 self.specific.as_ref().and_then(|spec| match spec {
137 CodecSpecificParameters::Video(params) => Some(params),
138 _ => None,
139 })
140 }
141}
142
143pub trait Codec {
144 fn configure(&mut self, parameters: Option<&CodecParameters>, options: Option<&Variant>) -> Result<()>;
145 fn set_option(&mut self, key: &str, value: &Variant) -> Result<()>;
146}
147
148pub trait CodecBuilder: Send + Sync {
149 fn id(&self) -> CodecID;
150 fn name(&self) -> &'static str;
151}
152
153pub(crate) struct CodecList<T> {
154 pub(crate) codecs: HashMap<CodecID, Vec<T>>,
155}
156
157pub(crate) type LazyCodecList<T> = LazyLock<RwLock<CodecList<Arc<T>>>>;
158
159pub(crate) fn register_codec<T>(codec_list: &LazyCodecList<T>, builder: Arc<T>, default: bool) -> Result<()>
160where
161 T: CodecBuilder + ?Sized,
162{
163 let mut codec_list = codec_list.write().map_err(|err| Error::Invalid(err.to_string()))?;
164 let entry = codec_list.codecs.entry(builder.id()).or_default();
165
166 if default {
167 entry.insert(0, builder);
168 } else {
169 entry.push(builder);
170 }
171
172 Ok(())
173}
174
175pub(crate) fn find_codec<T>(codec_list: &LazyCodecList<T>, codec_id: CodecID) -> Result<Arc<T>>
176where
177 T: CodecBuilder + ?Sized,
178{
179 let codec_list = codec_list.read().map_err(|err| Error::Invalid(err.to_string()))?;
180
181 if let Some(builders) = codec_list.codecs.get(&codec_id) {
182 if let Some(builder) = builders.first() {
183 return Ok(builder.clone());
184 }
185 }
186
187 Err(Error::NotFound(format!("codec: {:?}", codec_id)))
188}
189
190pub(crate) fn find_codec_by_name<T>(codec_list: &LazyCodecList<T>, name: &str) -> Result<Arc<T>>
191where
192 T: CodecBuilder + ?Sized,
193{
194 let codec_list = codec_list.read().map_err(|err| Error::Invalid(err.to_string()))?;
195
196 for builders in codec_list.codecs.values() {
197 for builder in builders {
198 if builder.name() == name {
199 return Ok(builder.clone());
200 }
201 }
202 }
203
204 Err(Error::NotFound(format!("codec: {}", name)))
205}