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 fn with_flag(mut self, flag: MediaCodecSet, enabled: bool) -> Self {
53 if enabled {
54 self.enabled.insert(flag);
55 } else {
56 self.enabled.remove(flag);
57 }
58 self
59 }
60
61 media_codec_accessors!(
62 opus_enabled => with_opus => OPUS,
63 pcmu_enabled => with_pcmu => PCMU,
64 pcma_enabled => with_pcma => PCMA,
65 vp8_enabled => with_vp8 => VP8,
66 h264_enabled => with_h264 => H264,
67 h265_enabled => with_h265 => H265,
68 vp9_enabled => with_vp9 => VP9,
69 av1_enabled => with_av1 => AV1,
70 );
71}
72
73impl Default for MediaCodecFlags {
74 fn default() -> Self {
75 Self {
76 enabled: MediaCodecSet::OPUS | MediaCodecSet::VP8,
77 }
78 }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86pub enum AudioCodecPreference {
87 Opus,
89 Pcmu,
91 Pcma,
93}
94
95impl AudioCodecPreference {
96 #[must_use]
98 pub const fn wire_name(self) -> &'static str {
99 match self {
100 Self::Opus => codec_name::OPUS,
101 Self::Pcmu => codec_name::PCMU,
102 Self::Pcma => codec_name::PCMA,
103 }
104 }
105
106 #[must_use]
108 pub fn enabled_by(self, flags: MediaCodecFlags) -> bool {
109 match self {
110 Self::Opus => flags.opus_enabled(),
111 Self::Pcmu => flags.pcmu_enabled(),
112 Self::Pcma => flags.pcma_enabled(),
113 }
114 }
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum VideoCodecPreference {
123 Vp8,
125 H264,
127 H265,
129 Vp9,
131 Av1,
133}
134
135impl VideoCodecPreference {
136 #[must_use]
138 pub const fn wire_name(self) -> &'static str {
139 match self {
140 Self::Vp8 => codec_name::VP8,
141 Self::H264 => codec_name::H264,
142 Self::H265 => codec_name::H265,
143 Self::Vp9 => codec_name::VP9,
144 Self::Av1 => codec_name::AV1,
145 }
146 }
147
148 #[must_use]
150 pub fn enabled_by(self, flags: MediaCodecFlags) -> bool {
151 match self {
152 Self::Vp8 => flags.vp8_enabled(),
153 Self::H264 => flags.h264_enabled(),
154 Self::H265 => flags.h265_enabled(),
155 Self::Vp9 => flags.vp9_enabled(),
156 Self::Av1 => flags.av1_enabled(),
157 }
158 }
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq)]
166pub struct CodecPreferences {
167 audio: [AudioCodecPreference; 3],
168 video: [VideoCodecPreference; 5],
169}
170
171impl CodecPreferences {
172 pub const DEFAULT_AUDIO: [AudioCodecPreference; 3] = [
174 AudioCodecPreference::Opus,
175 AudioCodecPreference::Pcmu,
176 AudioCodecPreference::Pcma,
177 ];
178 pub const DEFAULT_VIDEO: [VideoCodecPreference; 5] = [
180 VideoCodecPreference::Vp8,
181 VideoCodecPreference::H264,
182 VideoCodecPreference::H265,
183 VideoCodecPreference::Vp9,
184 VideoCodecPreference::Av1,
185 ];
186
187 #[must_use]
192 pub const fn new(audio: [AudioCodecPreference; 3], video: [VideoCodecPreference; 5]) -> Self {
193 Self { audio, video }
194 }
195
196 #[must_use]
201 pub fn with_audio_order(self, preferred: &[AudioCodecPreference]) -> Self {
202 Self {
203 audio: complete_codec_order(preferred, Self::DEFAULT_AUDIO),
204 ..self
205 }
206 }
207
208 #[must_use]
213 pub fn with_video_order(self, preferred: &[VideoCodecPreference]) -> Self {
214 Self {
215 video: complete_codec_order(preferred, Self::DEFAULT_VIDEO),
216 ..self
217 }
218 }
219
220 #[must_use]
222 pub const fn audio_order(self) -> [AudioCodecPreference; 3] {
223 self.audio
224 }
225
226 #[must_use]
228 pub const fn video_order(self) -> [VideoCodecPreference; 5] {
229 self.video
230 }
231}
232
233impl Default for CodecPreferences {
234 fn default() -> Self {
235 Self::new(Self::DEFAULT_AUDIO, Self::DEFAULT_VIDEO)
236 }
237}
238
239fn complete_codec_order<T, const N: usize>(preferred: &[T], default: [T; N]) -> [T; N]
240where
241 T: Copy + Eq,
242{
243 let mut output = default;
244 let mut len = 0;
245 for codec in preferred.iter().copied().chain(default) {
248 if contains_codec(&output, len, codec) {
249 continue;
250 }
251 if let Some(slot) = output.get_mut(len) {
252 *slot = codec;
253 len += 1;
254 }
255 }
256 output
257}
258
259fn contains_codec<T, const N: usize>(codecs: &[T; N], len: usize, needle: T) -> bool
260where
261 T: Copy + Eq,
262{
263 codecs.iter().take(len).any(|codec| *codec == needle)
264}