// Take a look at the license at the top of the repository in the LICENSE file. use std::borrow::{Borrow, BorrowMut, ToOwned}; use std::ffi::CStr; use std::fmt; use std::mem; use std::ops; use std::ptr; use glib::translate::*; use crate::sdp_attribute::SDPAttribute; use crate::sdp_bandwidth::SDPBandwidth; use crate::sdp_connection::SDPConnection; use crate::sdp_key::SDPKey; use crate::sdp_media::SDPMedia; use crate::sdp_media::SDPMediaRef; use crate::sdp_origin::SDPOrigin; use crate::sdp_time::SDPTime; use crate::sdp_zone::SDPZone; glib::wrapper! { #[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SDPMessage(Boxed); match fn { copy => |ptr| { let mut copy = std::ptr::null_mut(); assert_eq!(ffi::gst_sdp_message_copy(ptr, &mut copy), ffi::GST_SDP_OK); copy }, free => |ptr| assert_eq!(ffi::gst_sdp_message_free(ptr), ffi::GST_SDP_OK), type_ => || ffi::gst_sdp_message_get_type(), } } unsafe impl Send for SDPMessage {} unsafe impl Sync for SDPMessage {} impl Default for SDPMessage { fn default() -> Self { Self::new() } } impl ops::Deref for SDPMessage { type Target = SDPMessageRef; fn deref(&self) -> &SDPMessageRef { unsafe { &*(self.to_glib_none().0 as *const SDPMessageRef) } } } impl ops::DerefMut for SDPMessage { fn deref_mut(&mut self) -> &mut SDPMessageRef { unsafe { &mut *(self.to_glib_none_mut().0 as *mut SDPMessageRef) } } } impl fmt::Debug for SDPMessage { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ::fmt(&*self, f) } } impl fmt::Display for SDPMessage { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ::fmt(&*self, f) } } impl SDPMessage { pub fn new() -> SDPMessage { assert_initialized_main_thread!(); unsafe { let mut msg = ptr::null_mut(); ffi::gst_sdp_message_new(&mut msg); from_glib_full(msg) } } pub fn parse_buffer(data: &[u8]) -> Result { assert_initialized_main_thread!(); unsafe { let size = data.len() as u32; let mut msg = ptr::null_mut(); ffi::gst_sdp_message_new(&mut msg); let result = ffi::gst_sdp_message_parse_buffer(data.to_glib_none().0, size, msg); match result { ffi::GST_SDP_OK => Ok(from_glib_full(msg)), _ => { ffi::gst_sdp_message_uninit(msg); Err(glib::bool_error!("Failed to parse buffer")) } } } } pub fn parse_uri(uri: &str) -> Result { assert_initialized_main_thread!(); unsafe { let mut msg = ptr::null_mut(); ffi::gst_sdp_message_new(&mut msg); let result = ffi::gst_sdp_message_parse_uri(uri.to_glib_none().0, msg); match result { ffi::GST_SDP_OK => Ok(from_glib_full(msg)), _ => { ffi::gst_sdp_message_uninit(msg); Err(glib::bool_error!("Failed to parse URI")) } } } } } #[repr(transparent)] pub struct SDPMessageRef(ffi::GstSDPMessage); impl fmt::Debug for SDPMessageRef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use std::cell::RefCell; struct DebugIter(RefCell); impl fmt::Debug for DebugIter where I::Item: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_list().entries(&mut *self.0.borrow_mut()).finish() } } f.debug_struct("SDPMessage") .field("connection", &self.connection()) .field("information", &self.information()) .field("key", &self.key()) .field("origin", &self.origin()) .field("session-name", &self.session_name()) .field("uri", &self.uri()) .field("version", &self.version()) .field("attributes", &DebugIter(RefCell::new(self.attributes()))) .field("bandwidths", &DebugIter(RefCell::new(self.bandwidths()))) .field("emails", &DebugIter(RefCell::new(self.emails()))) .field("medias", &DebugIter(RefCell::new(self.medias()))) .field("phones", &DebugIter(RefCell::new(self.phones()))) .field("times", &DebugIter(RefCell::new(self.times()))) .field("zones", &DebugIter(RefCell::new(self.zones()))) .finish() } } impl fmt::Display for SDPMessageRef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.as_text() { Ok(text) => f.write_str(text.as_str()), Err(_) => Err(fmt::Error), } } } unsafe impl Send for SDPMessageRef {} unsafe impl Sync for SDPMessageRef {} impl SDPMessageRef { pub fn add_attribute(&mut self, key: &str, value: Option<&str>) { unsafe { ffi::gst_sdp_message_add_attribute( &mut self.0, key.to_glib_none().0, value.to_glib_none().0, ); } } pub fn add_email(&mut self, email: &str) { unsafe { ffi::gst_sdp_message_add_email(&mut self.0, email.to_glib_none().0) }; } pub fn add_media(&mut self, media: SDPMedia) { unsafe { ffi::gst_sdp_message_add_media( &mut self.0, media.to_glib_none().0 as *mut ffi::GstSDPMedia, ); } } pub fn add_phone(&mut self, phone: &str) { unsafe { ffi::gst_sdp_message_add_phone(&mut self.0, phone.to_glib_none().0) }; } pub fn add_time(&mut self, start: &str, stop: &str, repeat: &[&str]) { unsafe { ffi::gst_sdp_message_add_time( &mut self.0, start.to_glib_none().0, stop.to_glib_none().0, repeat.to_glib_none().0, ) }; } pub fn add_zone(&mut self, adj_time: &str, typed_time: &str) { unsafe { ffi::gst_sdp_message_add_zone( &mut self.0, adj_time.to_glib_none().0, typed_time.to_glib_none().0, ) }; } pub fn as_text(&self) -> Result { unsafe { match from_glib_full(ffi::gst_sdp_message_as_text(&self.0)) { Some(s) => Ok(s), None => Err(glib::bool_error!( "Failed to convert the contents of message to a text string" )), } } } pub fn attributes_len(&self) -> u32 { unsafe { ffi::gst_sdp_message_attributes_len(&self.0) } } pub fn attributes_to_caps(&self, caps: &mut gst::CapsRef) -> Result<(), glib::BoolError> { let result = unsafe { ffi::gst_sdp_message_attributes_to_caps(&self.0, caps.as_mut_ptr()) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to store attributes in caps")), } } pub fn bandwidths_len(&self) -> u32 { unsafe { ffi::gst_sdp_message_bandwidths_len(&self.0) } } pub fn dump(&self) { unsafe { ffi::gst_sdp_message_dump(&self.0) }; } pub fn emails_len(&self) -> u32 { unsafe { ffi::gst_sdp_message_emails_len(&self.0) } } pub fn attribute(&self, idx: u32) -> Option<&SDPAttribute> { if idx >= self.attributes_len() { return None; } unsafe { let ptr = ffi::gst_sdp_message_get_attribute(&self.0, idx); if ptr.is_null() { None } else { Some(&*(ptr as *mut SDPAttribute)) } } } pub fn attribute_val(&self, key: &str) -> Option<&str> { unsafe { let ptr = ffi::gst_sdp_message_get_attribute_val(&self.0, key.to_glib_none().0); if ptr.is_null() { None } else { CStr::from_ptr(ptr).to_str().ok() } } } pub fn attribute_val_n(&self, key: &str, nth: u32) -> Option<&str> { unsafe { let ptr = ffi::gst_sdp_message_get_attribute_val_n(&self.0, key.to_glib_none().0, nth); if ptr.is_null() { None } else { CStr::from_ptr(ptr).to_str().ok() } } } pub fn bandwidth(&self, idx: u32) -> Option<&SDPBandwidth> { if idx >= self.bandwidths_len() { return None; } unsafe { let ptr = ffi::gst_sdp_message_get_bandwidth(&self.0, idx); if ptr.is_null() { None } else { Some(&*(ptr as *mut SDPBandwidth)) } } } pub fn connection(&self) -> Option<&SDPConnection> { unsafe { let ptr = ffi::gst_sdp_message_get_connection(&self.0); if ptr.is_null() { None } else { Some(&*(ptr as *mut SDPConnection)) } } } pub fn email(&self, idx: u32) -> Option<&str> { if idx >= self.emails_len() { return None; } unsafe { let ptr = ffi::gst_sdp_message_get_email(&self.0, idx); if ptr.is_null() { None } else { CStr::from_ptr(ptr).to_str().ok() } } } pub fn information(&self) -> Option<&str> { unsafe { let ptr = ffi::gst_sdp_message_get_information(&self.0); if ptr.is_null() { None } else { CStr::from_ptr(ptr).to_str().ok() } } } pub fn key(&self) -> Option<&SDPKey> { unsafe { let ptr = ffi::gst_sdp_message_get_key(&self.0); if ptr.is_null() { None } else { Some(&*(ptr as *mut SDPKey)) } } } pub fn media(&self, idx: u32) -> Option<&SDPMediaRef> { if idx >= self.medias_len() { return None; } unsafe { let ptr = ffi::gst_sdp_message_get_media(&self.0, idx); if ptr.is_null() { None } else { Some(&*(ptr as *const SDPMediaRef)) } } } pub fn media_mut(&mut self, idx: u32) -> Option<&mut SDPMediaRef> { if idx >= self.medias_len() { return None; } unsafe { let ptr = ffi::gst_sdp_message_get_media(&self.0, idx); if ptr.is_null() { None } else { Some(&mut *(ptr as *mut SDPMediaRef)) } } } pub fn origin(&self) -> Option<&SDPOrigin> { unsafe { let ptr = ffi::gst_sdp_message_get_origin(&self.0); if ptr.is_null() { None } else { Some(&*(ptr as *mut SDPOrigin)) } } } pub fn phone(&self, idx: u32) -> Option<&str> { if idx >= self.phones_len() { return None; } unsafe { let ptr = ffi::gst_sdp_message_get_phone(&self.0, idx); if ptr.is_null() { None } else { CStr::from_ptr(ptr).to_str().ok() } } } pub fn session_name(&self) -> Option<&str> { unsafe { let ptr = ffi::gst_sdp_message_get_session_name(&self.0); if ptr.is_null() { None } else { CStr::from_ptr(ptr).to_str().ok() } } } pub fn time(&self, idx: u32) -> Option<&SDPTime> { if idx >= self.times_len() { return None; } unsafe { let ptr = ffi::gst_sdp_message_get_time(&self.0, idx); if ptr.is_null() { None } else { Some(&*(ptr as *mut SDPTime)) } } } pub fn uri(&self) -> Option<&str> { unsafe { let ptr = ffi::gst_sdp_message_get_uri(&self.0); if ptr.is_null() { None } else { CStr::from_ptr(ptr).to_str().ok() } } } pub fn version(&self) -> Option<&str> { unsafe { let ptr = ffi::gst_sdp_message_get_version(&self.0); if ptr.is_null() { None } else { CStr::from_ptr(ptr).to_str().ok() } } } pub fn zone(&self, idx: u32) -> Option<&SDPZone> { if idx >= self.zones_len() { return None; } unsafe { let ptr = ffi::gst_sdp_message_get_zone(&self.0, idx); if ptr.is_null() { None } else { Some(&*(ptr as *mut SDPZone)) } } } pub fn insert_attribute( &mut self, idx: Option, attr: SDPAttribute, ) -> Result<(), glib::BoolError> { if let Some(idx) = idx { if idx >= self.attributes_len() { return Err(glib::bool_error!("Failed to insert attribute")); } } let idx = idx.map(|idx| idx as i32).unwrap_or(-1); let mut attr = mem::ManuallyDrop::new(attr); let result = unsafe { ffi::gst_sdp_message_insert_attribute(&mut self.0, idx, &mut attr.0) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to insert attribute")), } } pub fn insert_bandwidth( &mut self, idx: Option, bw: SDPBandwidth, ) -> Result<(), glib::BoolError> { if let Some(idx) = idx { if idx >= self.bandwidths_len() { return Err(glib::bool_error!("Failed to insert bandwidth")); } } let idx = idx.map(|idx| idx as i32).unwrap_or(-1); let mut bw = mem::ManuallyDrop::new(bw); let result = unsafe { ffi::gst_sdp_message_insert_bandwidth(&mut self.0, idx, &mut bw.0) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to insert bandwidth")), } } pub fn insert_email(&mut self, idx: Option, email: &str) -> Result<(), glib::BoolError> { if let Some(idx) = idx { if idx >= self.emails_len() { return Err(glib::bool_error!("Failed to insert email")); } } let idx = idx.map(|idx| idx as i32).unwrap_or(-1); let result = unsafe { ffi::gst_sdp_message_insert_email(&mut self.0, idx, email.to_glib_none().0) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to insert email")), } } pub fn insert_phone(&mut self, idx: Option, phone: &str) -> Result<(), glib::BoolError> { if let Some(idx) = idx { if idx >= self.phones_len() { return Err(glib::bool_error!("Failed to insert phone")); } } let idx = idx.map(|idx| idx as i32).unwrap_or(-1); let result = unsafe { ffi::gst_sdp_message_insert_phone(&mut self.0, idx, phone.to_glib_none().0) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to insert phone")), } } pub fn insert_time(&mut self, idx: Option, time: SDPTime) -> Result<(), glib::BoolError> { if let Some(idx) = idx { if idx >= self.times_len() { return Err(glib::bool_error!("Failed to insert time")); } } let idx = idx.map(|idx| idx as i32).unwrap_or(-1); let mut time = mem::ManuallyDrop::new(time); let result = unsafe { ffi::gst_sdp_message_insert_time(&mut self.0, idx, &mut time.0) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to insert time")), } } pub fn insert_zone(&mut self, idx: Option, zone: SDPZone) -> Result<(), glib::BoolError> { if let Some(idx) = idx { if idx >= self.zones_len() { return Err(glib::bool_error!("Failed to insert zone")); } } let idx = idx.map(|idx| idx as i32).unwrap_or(-1); let mut zone = mem::ManuallyDrop::new(zone); let result = unsafe { ffi::gst_sdp_message_insert_zone(&mut self.0, idx, &mut zone.0) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to insert zone")), } } pub fn medias_len(&self) -> u32 { unsafe { ffi::gst_sdp_message_medias_len(&self.0) } } pub fn phones_len(&self) -> u32 { unsafe { ffi::gst_sdp_message_phones_len(&self.0) } } pub fn remove_attribute(&mut self, idx: u32) -> Result<(), glib::BoolError> { if idx >= self.attributes_len() { return Err(glib::bool_error!("Failed to remove attribute")); } let result = unsafe { ffi::gst_sdp_message_remove_attribute(&mut self.0, idx) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to remove attribute")), } } pub fn remove_bandwidth(&mut self, idx: u32) -> Result<(), glib::BoolError> { if idx >= self.bandwidths_len() { return Err(glib::bool_error!("Failed to remove bandwidth")); } let result = unsafe { ffi::gst_sdp_message_remove_bandwidth(&mut self.0, idx) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to remove bandwidth")), } } pub fn remove_email(&mut self, idx: u32) -> Result<(), glib::BoolError> { if idx >= self.emails_len() { return Err(glib::bool_error!("Failed to remove email")); } let result = unsafe { ffi::gst_sdp_message_remove_email(&mut self.0, idx) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to remove email")), } } pub fn remove_phone(&mut self, idx: u32) -> Result<(), glib::BoolError> { if idx >= self.phones_len() { return Err(glib::bool_error!("Failed to remove phone")); } let result = unsafe { ffi::gst_sdp_message_remove_phone(&mut self.0, idx) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to remove phone")), } } pub fn remove_time(&mut self, idx: u32) -> Result<(), glib::BoolError> { if idx >= self.times_len() { return Err(glib::bool_error!("Failed to remove time")); } let result = unsafe { ffi::gst_sdp_message_remove_time(&mut self.0, idx) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to remove time")), } } pub fn remove_zone(&mut self, idx: u32) -> Result<(), glib::BoolError> { if idx >= self.zones_len() { return Err(glib::bool_error!("Failed to remove zone")); } let result = unsafe { ffi::gst_sdp_message_remove_zone(&mut self.0, idx) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to remove zone")), } } pub fn replace_attribute( &mut self, idx: u32, attr: SDPAttribute, ) -> Result<(), glib::BoolError> { if idx >= self.attributes_len() { return Err(glib::bool_error!("Failed to replace attribute")); } let mut attr = mem::ManuallyDrop::new(attr); let result = unsafe { ffi::gst_sdp_message_replace_attribute(&mut self.0, idx, &mut attr.0) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to replace attribute")), } } pub fn replace_bandwidth(&mut self, idx: u32, bw: SDPBandwidth) -> Result<(), glib::BoolError> { if idx >= self.bandwidths_len() { return Err(glib::bool_error!("Failed to replace bandwidth")); } let mut bw = mem::ManuallyDrop::new(bw); let result = unsafe { ffi::gst_sdp_message_replace_bandwidth(&mut self.0, idx, &mut bw.0) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to replace bandwidth")), } } pub fn replace_email(&mut self, idx: u32, email: &str) -> Result<(), glib::BoolError> { if idx >= self.emails_len() { return Err(glib::bool_error!("Failed to replace email")); } let result = unsafe { ffi::gst_sdp_message_replace_email(&mut self.0, idx, email.to_glib_none().0) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to replace email")), } } pub fn replace_phone(&mut self, idx: u32, phone: &str) -> Result<(), glib::BoolError> { if idx >= self.phones_len() { return Err(glib::bool_error!("Failed to replace phone")); } let result = unsafe { ffi::gst_sdp_message_replace_phone(&mut self.0, idx, phone.to_glib_none().0) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to replace phone")), } } pub fn replace_time(&mut self, idx: u32, time: SDPTime) -> Result<(), glib::BoolError> { if idx >= self.times_len() { return Err(glib::bool_error!("Failed to replace time")); } let mut time = mem::ManuallyDrop::new(time); let result = unsafe { ffi::gst_sdp_message_replace_time(&mut self.0, idx, &mut time.0) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to replace time")), } } pub fn replace_zone(&mut self, idx: u32, zone: SDPZone) -> Result<(), glib::BoolError> { if idx >= self.zones_len() { return Err(glib::bool_error!("Failed to replace zone")); } let mut zone = mem::ManuallyDrop::new(zone); let result = unsafe { ffi::gst_sdp_message_replace_zone(&mut self.0, idx, &mut zone.0) }; match result { ffi::GST_SDP_OK => Ok(()), _ => Err(glib::bool_error!("Failed to replace zone")), } } pub fn set_connection( &mut self, nettype: &str, addrtype: &str, address: &str, ttl: u32, addr_number: u32, ) { unsafe { ffi::gst_sdp_message_set_connection( &mut self.0, nettype.to_glib_none().0, addrtype.to_glib_none().0, address.to_glib_none().0, ttl, addr_number, ) }; } pub fn set_information(&mut self, information: &str) { unsafe { ffi::gst_sdp_message_set_information(&mut self.0, information.to_glib_none().0) }; } pub fn set_key(&mut self, type_: &str, data: &str) { unsafe { ffi::gst_sdp_message_set_key(&mut self.0, type_.to_glib_none().0, data.to_glib_none().0) }; } pub fn set_origin( &mut self, username: &str, sess_id: &str, sess_version: &str, nettype: &str, addrtype: &str, addr: &str, ) { unsafe { ffi::gst_sdp_message_set_origin( &mut self.0, username.to_glib_none().0, sess_id.to_glib_none().0, sess_version.to_glib_none().0, nettype.to_glib_none().0, addrtype.to_glib_none().0, addr.to_glib_none().0, ) }; } pub fn set_session_name(&mut self, session_name: &str) { unsafe { ffi::gst_sdp_message_set_session_name(&mut self.0, session_name.to_glib_none().0) }; } pub fn set_uri(&mut self, uri: &str) { unsafe { ffi::gst_sdp_message_set_uri(&mut self.0, uri.to_glib_none().0) }; } pub fn set_version(&mut self, version: &str) { unsafe { ffi::gst_sdp_message_set_version(&mut self.0, version.to_glib_none().0) }; } pub fn times_len(&self) -> u32 { unsafe { ffi::gst_sdp_message_times_len(&self.0) } } pub fn zones_len(&self) -> u32 { unsafe { ffi::gst_sdp_message_zones_len(&self.0) } } pub fn as_uri(&self, scheme: &str) -> Result { assert_initialized_main_thread!(); unsafe { match from_glib_full(ffi::gst_sdp_message_as_uri( scheme.to_glib_none().0, &self.0, )) { Some(s) => Ok(s), None => Err(glib::bool_error!("Failed to create an URI from message")), } } } pub fn attributes(&self) -> AttributesIter { AttributesIter::new(self) } pub fn bandwidths(&self) -> BandwidthsIter { BandwidthsIter::new(self) } pub fn emails(&self) -> EmailsIter { EmailsIter::new(self) } pub fn medias(&self) -> MediasIter { MediasIter::new(self) } pub fn medias_mut(&mut self) -> MediasIterMut { MediasIterMut::new(self) } pub fn phones(&self) -> PhonesIter { PhonesIter::new(self) } pub fn times(&self) -> TimesIter { TimesIter::new(self) } pub fn zones(&self) -> ZonesIter { ZonesIter::new(self) } } impl Borrow for SDPMessage { fn borrow(&self) -> &SDPMessageRef { &*self } } impl BorrowMut for SDPMessage { fn borrow_mut(&mut self) -> &mut SDPMessageRef { &mut *self } } impl ToOwned for SDPMessageRef { type Owned = SDPMessage; fn to_owned(&self) -> SDPMessage { unsafe { let mut ptr = ptr::null_mut(); ffi::gst_sdp_message_copy(&self.0, &mut ptr); from_glib_full(ptr) } } } impl glib::types::StaticType for SDPMessageRef { fn static_type() -> glib::types::Type { unsafe { from_glib(ffi::gst_sdp_message_get_type()) } } } impl<'a> glib::value::FromValueOptional<'a> for &'a SDPMessageRef { unsafe fn from_value_optional(v: &'a glib::Value) -> Option { let ptr = glib::gobject_ffi::g_value_get_boxed(v.to_glib_none().0); if ptr.is_null() { None } else { Some(&*(ptr as *const SDPMessageRef)) } } } impl glib::value::SetValue for SDPMessageRef { unsafe fn set_value(v: &mut glib::Value, s: &Self) { glib::gobject_ffi::g_value_set_boxed( v.to_glib_none_mut().0, s as *const SDPMessageRef as glib::ffi::gpointer, ); } } impl glib::value::SetValueOptional for SDPMessageRef { unsafe fn set_value_optional(v: &mut glib::Value, s: Option<&Self>) { if let Some(s) = s { glib::gobject_ffi::g_value_set_boxed( v.to_glib_none_mut().0, s as *const SDPMessageRef as glib::ffi::gpointer, ); } else { glib::gobject_ffi::g_value_set_boxed(v.to_glib_none_mut().0, ptr::null_mut()); } } } macro_rules! define_iter( ($name:ident, $typ:ty, $get_item:expr, $get_len:expr) => { #[derive(Debug)] pub struct $name<'a> { message: &'a SDPMessageRef, idx: u32, len: u32, } impl<'a> $name<'a> { fn new(message: &'a SDPMessageRef) -> $name<'a> { skip_assert_initialized!(); let len = $get_len(message); $name { message, idx: 0, len, } } } impl<'a> Iterator for $name<'a> { type Item = $typ; fn next(&mut self) -> Option { if self.idx >= self.len { return None; } let item = $get_item(self.message, self.idx)?; self.idx += 1; Some(item) } fn size_hint(&self) -> (usize, Option) { if self.idx == self.len { return (0, Some(0)) } let remaining = (self.len - self.idx) as usize; (remaining, Some(remaining)) } } impl<'a> DoubleEndedIterator for $name<'a> { fn next_back(&mut self) -> Option { if self.idx == self.len { return None; } self.len -= 1; $get_item(self.message, self.len) } } impl<'a> ExactSizeIterator for $name<'a> {} } ); macro_rules! define_iter_mut( ($name:ident, $typ:ty, $get_item:expr, $get_len:expr) => { #[derive(Debug)] pub struct $name<'a> { message: &'a mut SDPMessageRef, idx: u32, len: u32, } impl<'a> $name<'a> { fn new(message: &'a mut SDPMessageRef) -> $name<'a> { skip_assert_initialized!(); let len = $get_len(message); $name { message, idx: 0, len, } } } impl<'a> Iterator for $name<'a> { type Item = $typ; fn next(&mut self) -> Option { // The lifetime of the passed self is not 'a but we need to return a message reference // with that lifetime here. The reason here is that the compiler does not and can't // know that it's statically impossible to mutate the message between different calls // to next(), so it has to assume that every call to next() will invalidate any // returned references. // // In theory we could have a function on the iterator that mutates the message and for // example removes the message that was returned here at an earlier time. The compiler // would be correct to complain in that case, but we don't provide such a function. let message = unsafe { &mut *(self.message as *mut SDPMessageRef) }; if self.idx >= self.len { return None; } let item = $get_item(message, self.idx)?; self.idx += 1; Some(item) } fn size_hint(&self) -> (usize, Option) { if self.idx == self.len { return (0, Some(0)) } let remaining = (self.len - self.idx) as usize; (remaining, Some(remaining)) } } impl<'a> DoubleEndedIterator for $name<'a> { fn next_back(&mut self) -> Option { let message = unsafe { &mut *(self.message as *mut SDPMessageRef) }; if self.idx == self.len { return None; } self.len -= 1; $get_item(message, self.len) } } impl<'a> ExactSizeIterator for $name<'a> {} } ); define_iter!( AttributesIter, &'a SDPAttribute, |message: &'a SDPMessageRef, idx| message.get_attribute(idx), |message: &SDPMessageRef| message.attributes_len() ); define_iter!( BandwidthsIter, &'a SDPBandwidth, |message: &'a SDPMessageRef, idx| message.get_bandwidth(idx), |message: &SDPMessageRef| message.bandwidths_len() ); define_iter!( EmailsIter, &'a str, |message: &'a SDPMessageRef, idx| message.get_email(idx), |message: &SDPMessageRef| message.emails_len() ); define_iter!( MediasIter, &'a SDPMediaRef, |message: &'a SDPMessageRef, idx| message.get_media(idx), |message: &SDPMessageRef| message.medias_len() ); define_iter_mut!( MediasIterMut, &'a mut SDPMediaRef, |message: &'a mut SDPMessageRef, idx| message.get_media_mut(idx), |message: &SDPMessageRef| message.medias_len() ); define_iter!( PhonesIter, &'a str, |message: &'a SDPMessageRef, idx| message.get_phone(idx), |message: &SDPMessageRef| message.phones_len() ); define_iter!( TimesIter, &'a SDPTime, |message: &'a SDPMessageRef, idx| message.get_time(idx), |message: &SDPMessageRef| message.times_len() ); define_iter!( ZonesIter, &'a SDPZone, |message: &'a SDPMessageRef, idx| message.get_zone(idx), |message: &SDPMessageRef| message.zones_len() ); #[cfg(test)] mod tests { use crate::SDPMessage; fn init() { gst::init().unwrap(); } #[test] fn media_from_message() { init(); let sdp = "v=0\r\no=- 1938737043334325940 0 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\na=ice-options:trickle\r\nm=video 9 UDP/TLS/RTP/SAVPF 96\r\nc=IN IP4 0.0.0.0\r\na=setup:actpass\r\na=ice-ufrag:YZxU9JlWHzHcF6O2U09/q3PvBhbTPdZW\r\na=ice-pwd:fyrt730GWo5mFGc9m2z/vbUu3z1lewla\r\na=sendrecv\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 nack\r\na=rtcp-fb:96 nack pli\r\na=framerate:30\r\na=mid:video0\r\na=fingerprint:sha-256 DB:48:8F:18:13:F3:AA:13:31:B3:75:3D:1A:D3:BA:88:4A:ED:1B:56:14:C3:09:CD:BC:4D:18:42:B9:6A:5F:98\r\nm=audio 9 UDP/TLS/RTP/SAVPF 97\r\nc=IN IP4 0.0.0.0\r\na=setup:actpass\r\na=ice-ufrag:04KZM9qE2S4r06AN6A9CeXOM6mzO0LZY\r\na=ice-pwd:cJTSfHF6hHDAcsTJXZVJeuYCC6rKqBvW\r\na=sendrecv\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:97 OPUS/48000/2\r\na=rtcp-fb:97 nack\r\na=rtcp-fb:97 nack pli\r\na=mid:audio1\r\na=fingerprint:sha-256 DB:48:8F:18:13:F3:AA:13:31:B3:75:3D:1A:D3:BA:88:4A:ED:1B:56:14:C3:09:CD:BC:4D:18:42:B9:6A:5F:98\r\n"; let sdp = SDPMessage::parse_buffer(sdp.as_bytes()).unwrap(); let media = sdp.get_media(0).unwrap(); assert_eq!(media.formats_len(), 1); } #[test] fn debug_impl() { init(); let sdp = SDPMessage::new(); assert!(!format!("{:?}", sdp).is_empty()); } }