1use bitflags::bitflags;
7use o_sfu_rfc::rtp::codec_name;
8
9bitflags! {
10 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
11 struct MediaCodecSet: u16 {
12 const OPUS = 1 << 0;
13 const PCMU = 1 << 1;
14 const PCMA = 1 << 2;
15 const VP8 = 1 << 3;
16 const H264 = 1 << 4;
17 const H265 = 1 << 5;
18 const VP9 = 1 << 6;
19 const AV1 = 1 << 7;
20 }
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct MediaCodecFlags {
29 enabled: MediaCodecSet,
30}
31
32macro_rules! media_codec_accessors {
33 ($($enabled:ident => $with:ident => $flag:ident),+ $(,)?) => {
34 $(
35 #[doc = concat!("returns whether `", stringify!($flag), "` may enter the compiled RTP profile")]
36 #[must_use]
37 pub fn $enabled(self) -> bool {
38 self.enabled.contains(MediaCodecSet::$flag)
39 }
40
41 #[doc = concat!("returns a copy with `", stringify!($flag), "` enabled or disabled for profile compilation")]
42 #[must_use]
43 pub fn $with(self, enabled: bool) -> Self {
44 self.with_flag(MediaCodecSet::$flag, enabled)
45 }
46 )+
47 };
48}
49
50impl MediaCodecFlags {
51 #[must_use]
52 pub const fn empty() -> Self {
53 Self {
54 enabled: MediaCodecSet::empty(),
55 }
56 }
57
58 #[must_use]
59 fn with_flag(mut self, flag: MediaCodecSet, enabled: bool) -> Self {
60 if enabled {
61 self.enabled.insert(flag);
62 } else {
63 self.enabled.remove(flag);
64 }
65 self
66 }
67
68 media_codec_accessors!(
69 opus_enabled => with_opus => OPUS,
70 pcmu_enabled => with_pcmu => PCMU,
71 pcma_enabled => with_pcma => PCMA,
72 vp8_enabled => with_vp8 => VP8,
73 h264_enabled => with_h264 => H264,
74 h265_enabled => with_h265 => H265,
75 vp9_enabled => with_vp9 => VP9,
76 av1_enabled => with_av1 => AV1,
77 );
78}
79
80impl Default for MediaCodecFlags {
81 fn default() -> Self {
82 Self {
83 enabled: MediaCodecSet::OPUS | MediaCodecSet::VP8,
84 }
85 }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93pub enum AudioCodecPreference {
94 Opus,
96 Pcmu,
98 Pcma,
100}
101
102impl AudioCodecPreference {
103 #[must_use]
105 pub const fn wire_name(self) -> &'static str {
106 match self {
107 Self::Opus => codec_name::OPUS,
108 Self::Pcmu => codec_name::PCMU,
109 Self::Pcma => codec_name::PCMA,
110 }
111 }
112
113 #[must_use]
115 pub fn enabled_by(self, flags: MediaCodecFlags) -> bool {
116 match self {
117 Self::Opus => flags.opus_enabled(),
118 Self::Pcmu => flags.pcmu_enabled(),
119 Self::Pcma => flags.pcma_enabled(),
120 }
121 }
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129pub enum VideoCodecPreference {
130 Vp8,
132 H264,
134 H265,
136 Vp9,
138 Av1,
140}
141
142impl VideoCodecPreference {
143 #[must_use]
145 pub const fn wire_name(self) -> &'static str {
146 match self {
147 Self::Vp8 => codec_name::VP8,
148 Self::H264 => codec_name::H264,
149 Self::H265 => codec_name::H265,
150 Self::Vp9 => codec_name::VP9,
151 Self::Av1 => codec_name::AV1,
152 }
153 }
154
155 #[must_use]
157 pub fn enabled_by(self, flags: MediaCodecFlags) -> bool {
158 match self {
159 Self::Vp8 => flags.vp8_enabled(),
160 Self::H264 => flags.h264_enabled(),
161 Self::H265 => flags.h265_enabled(),
162 Self::Vp9 => flags.vp9_enabled(),
163 Self::Av1 => flags.av1_enabled(),
164 }
165 }
166}
167
168#[derive(Debug, Clone, Copy, PartialEq, Eq)]
173pub struct CodecPreferences {
174 audio: [AudioCodecPreference; 3],
175 video: [VideoCodecPreference; 5],
176}
177
178impl CodecPreferences {
179 pub const DEFAULT_AUDIO: [AudioCodecPreference; 3] = [
181 AudioCodecPreference::Opus,
182 AudioCodecPreference::Pcmu,
183 AudioCodecPreference::Pcma,
184 ];
185 pub const DEFAULT_VIDEO: [VideoCodecPreference; 5] = [
187 VideoCodecPreference::Vp8,
188 VideoCodecPreference::H264,
189 VideoCodecPreference::H265,
190 VideoCodecPreference::Vp9,
191 VideoCodecPreference::Av1,
192 ];
193
194 #[must_use]
199 pub const fn new(audio: [AudioCodecPreference; 3], video: [VideoCodecPreference; 5]) -> Self {
200 Self { audio, video }
201 }
202
203 #[must_use]
208 pub fn with_audio_order(self, preferred: &[AudioCodecPreference]) -> Self {
209 Self {
210 audio: complete_codec_order(preferred, Self::DEFAULT_AUDIO),
211 ..self
212 }
213 }
214
215 #[must_use]
220 pub fn with_video_order(self, preferred: &[VideoCodecPreference]) -> Self {
221 Self {
222 video: complete_codec_order(preferred, Self::DEFAULT_VIDEO),
223 ..self
224 }
225 }
226
227 #[must_use]
229 pub const fn audio_order(self) -> [AudioCodecPreference; 3] {
230 self.audio
231 }
232
233 #[must_use]
235 pub const fn video_order(self) -> [VideoCodecPreference; 5] {
236 self.video
237 }
238}
239
240impl Default for CodecPreferences {
241 fn default() -> Self {
242 Self::new(Self::DEFAULT_AUDIO, Self::DEFAULT_VIDEO)
243 }
244}
245
246fn complete_codec_order<T, const N: usize>(preferred: &[T], default: [T; N]) -> [T; N]
247where
248 T: Copy + Eq,
249{
250 let mut output = default;
251 let mut len = 0;
252 for codec in preferred.iter().copied().chain(default) {
255 if contains_codec(&output, len, codec) {
256 continue;
257 }
258 if let Some(slot) = output.get_mut(len) {
259 *slot = codec;
260 len += 1;
261 }
262 }
263 output
264}
265
266fn contains_codec<T, const N: usize>(codecs: &[T; N], len: usize, needle: T) -> bool
267where
268 T: Copy + Eq,
269{
270 codecs.iter().take(len).any(|codec| *codec == needle)
271}