oddity_sdp_protocol/
codec.rs1use std::fmt::Write;
2
3use base64::engine::Engine;
4
5pub use super::{fmt::FMT_RTP_PAYLOAD_DYNAMIC, Tag};
6
7pub trait MediaAttributes {
8 fn media_attributes(&self) -> Vec<Tag>;
9}
10
11pub enum CodecInfo<'params> {
12 H264(H264CodecParameters<'params>),
13}
14
15impl<'params> CodecInfo<'params> {
16 pub fn h264(
17 sps: &'params [u8],
18 pps: &'params [&'params [u8]],
19 packetization_mode: usize,
20 ) -> Self {
21 Self::H264(H264CodecParameters {
22 sps,
23 pps,
24 packetization_mode,
25 })
26 }
27}
28
29pub struct H264CodecParameters<'params> {
30 sps: &'params [u8],
31 pps: &'params [&'params [u8]],
32 packetization_mode: usize,
33}
34
35impl MediaAttributes for CodecInfo<'_> {
36 fn media_attributes(&self) -> Vec<Tag> {
37 match self {
38 CodecInfo::H264(params) => vec![
39 h264_rtpmap(),
40 h264_fmtp(params.packetization_mode, params.sps, params.pps),
41 ],
42 }
43 }
44}
45
46fn h264_rtpmap() -> Tag {
47 Tag::Value(
48 "rtpmap".to_string(),
49 format!("{} H264/90000", FMT_RTP_PAYLOAD_DYNAMIC),
50 )
51}
52
53fn h264_fmtp(packetization_mode: usize, sps: &[u8], pps: &[&[u8]]) -> Tag {
54 let profile_level_id_bytes = &sps[1..4];
55 let profile_level_id = profile_level_id_bytes
56 .iter()
57 .fold(String::new(), |mut output, b| {
58 let _ = write!(output, "{b:02X}");
59 output
60 });
61
62 let mut parameter_sets = Vec::with_capacity(1 + pps.len());
63 parameter_sets.push(base64::engine::general_purpose::STANDARD.encode(sps));
64 parameter_sets.extend(
65 pps.iter()
66 .map(|pps| base64::engine::general_purpose::STANDARD.encode(pps)),
67 );
68 let sprop_parameter_sets = parameter_sets.join(",");
69
70 Tag::Value(
71 "fmtp".to_string(),
72 format!(
73 "{} packetization-mode={}; profile-level-id={}; sprop-parameter-sets={}",
74 FMT_RTP_PAYLOAD_DYNAMIC, packetization_mode, profile_level_id, sprop_parameter_sets,
75 ),
76 )
77}