1use glib_sys;
4use libc;
5use janus_plugin_sys as ffi;
6use serde::de::{self, Deserialize, Deserializer, Unexpected, Visitor};
7use serde::ser::{Serialize, Serializer};
8pub use ffi::sdp::janus_sdp_generate_answer as generate_answer;
9pub use ffi::sdp::janus_sdp_generate_offer as generate_offer;
10use std::collections::HashMap;
11use std::error::Error;
12use std::fmt;
13use std::ffi::{CStr, CString};
14use std::ops::Deref;
15use std::str;
16use crate::utils::GLibString;
17
18pub type RawSdp = ffi::sdp::janus_sdp;
19pub type RawMLine = ffi::sdp::janus_sdp_mline;
20pub type RawAttribute = ffi::sdp::janus_sdp_attribute;
21pub use ffi::sdp::janus_sdp_mtype as MediaType;
22pub use ffi::sdp::janus_sdp_mdirection as MediaDirection;
23
24macro_rules! c_str {
27 ($lit:expr) => {
28 unsafe {
29 ::std::ffi::CStr::from_ptr(concat!($lit, "\0").as_ptr() as *const ::std::os::raw::c_char)
30 }
31 }
32}
33
34static MEDIA_PAYLOAD_ATTRIBUTES: [&str; 3] = ["rtpmap", "fmtp", "rtcp-fb"];
36
37#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
38pub enum AudioCodec {
40 Opus,
41 Pcmu,
42 Pcma,
43 G722,
44 Isac16,
45 Isac32,
46}
47
48impl AudioCodec {
49 pub fn to_str(self) -> &'static str {
50 self.to_cstr().to_str().unwrap()
51 }
52 pub fn to_cstr(self) -> &'static CStr {
53 match self {
54 AudioCodec::Opus => c_str!("opus"),
55 AudioCodec::Pcmu => c_str!("pcmu"),
56 AudioCodec::Pcma => c_str!("pcma"),
57 AudioCodec::G722 => c_str!("g722"),
58 AudioCodec::Isac16 => c_str!("isac16"),
59 AudioCodec::Isac32 => c_str!("isac32"),
60 }
61 }
62}
63
64#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
65pub enum VideoCodec {
67 Vp8,
68 Vp9,
69 H264,
70 Av1,
71 H265,
72}
73
74impl VideoCodec {
75 pub fn to_str(self) -> &'static str {
76 self.to_cstr().to_str().unwrap()
77 }
78 pub fn to_cstr(self) -> &'static CStr {
79 match self {
80 VideoCodec::Vp8 => c_str!("vp8"),
81 VideoCodec::Vp9 => c_str!("vp9"),
82 VideoCodec::H264 => c_str!("h264"),
83 VideoCodec::Av1 => c_str!("av1"),
84 VideoCodec::H265 => c_str!("h265"),
85 }
86 }
87}
88
89#[repr(i32)]
90#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
91pub enum OfferAnswerParameters {
94 Done = 0,
96 Audio = 1,
98 Video = 2,
100 Data = 3,
102 AudioDirection = 4,
104 VideoDirection = 5,
106 AudioCodec = 6,
108 VideoCodec = 7,
110 Vp9Profile = 8,
112 H264Profile = 9,
114 AudioPayloadType = 10,
116 VideoPayloadType = 11,
118 AudioDtmf = 12,
120 AudioFmtp = 13,
122 VideoFmtp = 14,
125 VideoRtcpfbDefaults = 15,
127 DataLegacy = 16,
128 AudioExtension = 17,
129 VideoExtension = 18,
130 AcceptExtmap = 19,
131}
132
133pub struct Sdp {
135 pub ptr: *mut RawSdp, }
137
138#[derive(Debug, Clone)]
140pub struct SdpParseError {
141 buffer: Vec<u8>
142}
143
144impl Error for SdpParseError {}
145
146impl fmt::Display for SdpParseError {
147 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148 f.write_str(str::from_utf8(&self.buffer).unwrap_or("SDP parsing failed, but the error was not valid UTF-8 :("))
149 }
150}
151
152impl Sdp {
153 pub unsafe fn new(ptr: *mut RawSdp) -> Option<Self> {
154 ptr.as_mut().map(|p| Self { ptr: p })
155 }
156
157 pub fn parse(offer: &CStr) -> Result<Self, SdpParseError> {
159 let mut error_buffer = Vec::with_capacity(512);
160 let error_ptr = error_buffer.as_mut_ptr() as *mut _;
161 unsafe {
162 let result = ffi::sdp::janus_sdp_parse(offer.as_ptr(), error_ptr, error_buffer.capacity());
163 Sdp::new(result).ok_or_else(|| {
164 error_buffer.set_len(libc::strlen(error_ptr));
165 SdpParseError { buffer: error_buffer }
166 })
167 }
168 }
169
170 pub fn get_payload_type(&self, codec_name: &CStr) -> Option<i32> {
172 unsafe {
173 match ffi::sdp::janus_sdp_get_codec_pt(self.ptr, codec_name.as_ptr()) {
174 err if err < 0 => None,
175 n => Some(n),
176 }
177 }
178 }
179
180 pub fn get_payload_type_full(&self, codec_name: &CStr, profile: &CStr) -> Option<i32> {
182 unsafe {
183 match ffi::sdp::janus_sdp_get_codec_pt_full(self.ptr, codec_name.as_ptr(), profile.as_ptr()) {
184 err if err < 0 => None,
185 n => Some(n),
186 }
187 }
188 }
189
190 pub fn add_attribute(&mut self, pt: i32, name: &CStr, contents: &CStr) {
192 for (_media, m_lines) in self.get_mlines() {
193 unsafe {
194 for m_line in m_lines {
195 if !glib_sys::g_list_find(m_line.ptypes, pt as *const _).is_null() {
196 let attr = ffi::sdp::janus_sdp_attribute_create(name.as_ptr(), contents.as_ptr());
197 ffi::sdp::janus_sdp_attribute_add_to_mline(m_line as *mut _, attr as *mut _);
198 }
199 }
200 }
201 }
202 }
203
204 pub fn rewrite_payload_type(&mut self, from: i32, to: i32) {
207 let from_pt_string = from.to_string();
208 let to_pt_string = to.to_string();
209 for (_media, m_lines) in self.get_mlines() {
210 unsafe {
211 for m_line in m_lines {
212 if !glib_sys::g_list_find(m_line.ptypes, from as *const _).is_null() {
214 m_line.ptypes = glib_sys::g_list_remove(m_line.ptypes, from as *const _);
216 m_line.ptypes = glib_sys::g_list_prepend(m_line.ptypes, to as *mut _);
217 }
218 let mut attr_node = m_line.attributes;
220 while let Some(node) = attr_node.as_ref() {
221 let next = node.next; let data = node.data as *mut RawAttribute;
223 let attr = data.as_ref().expect("Null data in SDP attribute node :(");
224 let name = CStr::from_ptr(attr.name).to_str().expect("Invalid attribute name in SDP :(");
225 if MEDIA_PAYLOAD_ATTRIBUTES.contains(&name) {
226 let value = CStr::from_ptr(attr.value).to_str().expect("Invalid attribute value in SDP :(");
229 if value.starts_with(&from_pt_string) {
230 let new_val = CString::new(value.replacen(&from_pt_string, &to_pt_string, 1)).unwrap();
232 let new_attr = ffi::sdp::janus_sdp_attribute_create(attr.name, new_val.as_ptr());
233 m_line.attributes = glib_sys::g_list_prepend(m_line.attributes, new_attr as *mut _);
234 m_line.attributes = glib_sys::g_list_delete_link(m_line.attributes, attr_node);
235 ffi::sdp::janus_sdp_attribute_destroy(data);
236 }
237 }
238 attr_node = next;
239 }
240 }
241 }
242 }
243 }
244
245 pub fn get_mlines(&self) -> HashMap<MediaType, Vec<&mut RawMLine>> {
247 let mut result = HashMap::new();
248 unsafe {
249 let mut ml_node = (*self.ptr).m_lines;
250 while let Some(node) = ml_node.as_ref() {
251 let ml = (node.data as *mut RawMLine).as_mut().expect("Null data in SDP media node :(");
252 result.entry(ml.type_).or_insert_with(Vec::new).push(ml);
253 ml_node = node.next;
254 }
255 result
256 }
257 }
258
259 pub fn to_glibstring(&self) -> GLibString {
261 unsafe {
262 let sdp = ffi::sdp::janus_sdp_write(self.ptr);
263 GLibString::from_chars(sdp).expect("Mysterious error writing SDP to string :(")
264 }
265 }
266}
267
268impl Deref for Sdp {
269 type Target = RawSdp;
270
271 fn deref(&self) -> &RawSdp {
272 unsafe { &*self.ptr }
273 }
274}
275
276impl Drop for Sdp {
277 fn drop(&mut self) {
278 unsafe {
279 ffi::sdp::janus_sdp_destroy(self.ptr);
280 }
281 }
282}
283
284impl fmt::Debug for Sdp {
285 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
286 write!(f, "Sdp {{ {} }}", self.to_glibstring().to_string_lossy())
287 }
288}
289
290impl Serialize for Sdp {
291 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
292 self.to_glibstring().serialize(serializer)
293 }
294}
295
296impl<'de> Deserialize<'de> for Sdp {
297 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
298 struct SdpVisitor;
299 impl<'de> Visitor<'de> for SdpVisitor {
300 type Value = Sdp;
301
302 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
303 formatter.write_str("an SDP string")
304 }
305
306 fn visit_str<E>(self, value: &str) -> Result<Sdp, E> where E: de::Error {
307 if let Ok(cs_value) = CString::new(value) {
308 if let Ok(sdp) = Sdp::parse(&cs_value) {
309 return Ok(sdp)
310 }
311 }
312 Err(E::invalid_value(Unexpected::Str(value), &self))
313 }
314 }
315 deserializer.deserialize_str(SdpVisitor)
316 }
317}
318
319unsafe impl Send for Sdp {}
320
321#[macro_export]
322macro_rules! answer_sdp {
325 ($sdp:expr $(, $param:expr, $value:expr)* $(,)*) => {
326 unsafe {
327 let result = $crate::sdp::generate_answer(
328 $sdp.ptr,
329 $($param, $value,)*
330 $crate::sdp::OfferAnswerParameters::Done
331 );
332 $crate::sdp::Sdp::new(result).expect("Mysterious error generating SDP answer :(")
333 }
334 }
335}
336
337#[macro_export]
338macro_rules! offer_sdp {
341 ($name:expr, $address:expr $(, $param:expr, $value:expr)* $(,)*) => {
342 unsafe {
343 let result = $crate::sdp::generate_offer(
344 $name,
345 $address,
346 $($param, $value,)*
347 $crate::sdp::OfferAnswerParameters::Done
348 );
349 $crate::sdp::Sdp::new(result).expect("Mysterious error generating SDP offer :(")
350 }
351 }
352}