Skip to main content

o_sfu/config/
codec_preferences.rs

1use anyhow::{Result, anyhow, ensure};
2use o_sfu_core::prelude::{AudioCodecPreference, CodecPreferences, VideoCodecPreference};
3
4use super::env::Env;
5
6pub(super) fn load_codec_preferences(env: &Env<'_>) -> Result<CodecPreferences> {
7    let audio = match env.var::<String>("CODEC_AUDIO_PREFERENCE").optional()? {
8        Some(value) => parse_codec_list(&value, "CODEC_AUDIO_PREFERENCE", audio_codec_preference)?,
9        None => Vec::new(),
10    };
11    let video = match env.var::<String>("CODEC_VIDEO_PREFERENCE").optional()? {
12        Some(value) => parse_codec_list(&value, "CODEC_VIDEO_PREFERENCE", video_codec_preference)?,
13        None => Vec::new(),
14    };
15    Ok(CodecPreferences::default()
16        .with_audio_order(&audio)
17        .with_video_order(&video))
18}
19
20fn parse_codec_list<T>(
21    value: &str,
22    env_key: &str,
23    parse_codec: impl Fn(&str) -> Option<T>,
24) -> Result<Vec<T>>
25where
26    T: Copy + PartialEq,
27{
28    let mut codecs = Vec::new();
29    for raw_codec in value.split(',') {
30        let codec_name = raw_codec.trim();
31        ensure!(
32            !codec_name.is_empty(),
33            "{env_key} cannot contain empty entries"
34        );
35        let codec = parse_codec(codec_name)
36            .ok_or_else(|| anyhow!("{env_key} contains unsupported codec `{codec_name}`"))?;
37        ensure!(
38            !codecs.contains(&codec),
39            "{env_key} cannot contain duplicate codec `{codec_name}`"
40        );
41        codecs.push(codec);
42    }
43    ensure!(!codecs.is_empty(), "{env_key} cannot be empty");
44    Ok(codecs)
45}
46
47fn audio_codec_preference(codec_name: &str) -> Option<AudioCodecPreference> {
48    if codec_name.eq_ignore_ascii_case(AudioCodecPreference::Opus.wire_name()) {
49        Some(AudioCodecPreference::Opus)
50    } else if codec_name.eq_ignore_ascii_case(AudioCodecPreference::Pcmu.wire_name()) {
51        Some(AudioCodecPreference::Pcmu)
52    } else if codec_name.eq_ignore_ascii_case(AudioCodecPreference::Pcma.wire_name()) {
53        Some(AudioCodecPreference::Pcma)
54    } else {
55        None
56    }
57}
58
59fn video_codec_preference(codec_name: &str) -> Option<VideoCodecPreference> {
60    if codec_name.eq_ignore_ascii_case(VideoCodecPreference::Vp8.wire_name()) {
61        Some(VideoCodecPreference::Vp8)
62    } else if codec_name.eq_ignore_ascii_case(VideoCodecPreference::H264.wire_name()) {
63        Some(VideoCodecPreference::H264)
64    } else if codec_name.eq_ignore_ascii_case(VideoCodecPreference::H265.wire_name()) {
65        Some(VideoCodecPreference::H265)
66    } else if codec_name.eq_ignore_ascii_case(VideoCodecPreference::Vp9.wire_name()) {
67        Some(VideoCodecPreference::Vp9)
68    } else if codec_name.eq_ignore_ascii_case(VideoCodecPreference::Av1.wire_name()) {
69        Some(VideoCodecPreference::Av1)
70    } else {
71        None
72    }
73}
74
75#[cfg(test)]
76#[path = "TESTS/codec_preferences.rs"]
77mod tests;