diff --git a/gstreamer-rtp/src/rtp_buffer.rs b/gstreamer-rtp/src/rtp_buffer.rs index ffcfa7360..19849830c 100644 --- a/gstreamer-rtp/src/rtp_buffer.rs +++ b/gstreamer-rtp/src/rtp_buffer.rs @@ -1,7 +1,9 @@ -use glib::translate::{from_glib, FromGlibPtrFull}; +use glib::translate::{from_glib, from_glib_full, FromGlibPtrFull, ToGlib}; use std::fmt; use std::marker::PhantomData; use std::mem; +use std::ptr; +use std::slice; use gst::MiniObject; use gst_rtp_sys; @@ -89,24 +91,226 @@ impl<'a> RTPBuffer<'a, Writable> { } } + pub fn set_ssrc(&mut self, ssrc: u32) { + unsafe { gst_rtp_sys::gst_rtp_buffer_set_ssrc(&mut self.rtp_buffer, ssrc) } + } + + pub fn set_csrc(&mut self, idx: u8, ssrc: u32) { + unsafe { gst_rtp_sys::gst_rtp_buffer_set_csrc(&mut self.rtp_buffer, idx, ssrc) } + } + pub fn set_timestamp(&mut self, rtptime: u32) { unsafe { gst_rtp_sys::gst_rtp_buffer_set_timestamp(&mut self.rtp_buffer, rtptime); } } + + pub fn set_extension(&mut self, extension: bool) { + unsafe { + gst_rtp_sys::gst_rtp_buffer_set_extension(&mut self.rtp_buffer, extension.to_glib()) + } + } + + pub fn add_extension_onebyte_header( + &mut self, + id: u8, + data: &[u8], + ) -> Result<(), glib::BoolError> { + assert!( + id >= 1 && id <= 14, + "id should be between 1 and 14 (inclusive)" + ); + assert!( + !data.is_empty() && data.len() <= 16, + "data size should be between 1 and 16 (inclusive" + ); + unsafe { + let result: bool = from_glib(gst_rtp_sys::gst_rtp_buffer_add_extension_onebyte_header( + &mut self.rtp_buffer, + id, + data.as_ptr() as glib_sys::gconstpointer, + data.len() as u32, + )); + if result { + Ok(()) + } else { + Err(glib_bool_error!("Failed to add onebyte header extension")) + } + } + } + + pub fn add_extension_twobytes_header( + &mut self, + appbits: u8, + id: u8, + data: &[u8], + ) -> Result<(), glib::BoolError> { + assert_eq!( + appbits & 0xF0, + 0, + "appbits must use only 4 bits (max value is 15)" + ); + assert!(data.len() < 256, "data size should be smaller than 256"); + unsafe { + let result: bool = + from_glib(gst_rtp_sys::gst_rtp_buffer_add_extension_twobytes_header( + &mut self.rtp_buffer, + appbits, + id, + data.as_ptr() as glib_sys::gconstpointer, + data.len() as u32, + )); + if result { + Ok(()) + } else { + Err(glib_bool_error!("Failed to add twobytes header extension")) + } + } + } } impl<'a, T> RTPBuffer<'a, T> { - pub fn get_seq(&mut self) -> u16 { - unsafe { gst_rtp_sys::gst_rtp_buffer_get_seq(&mut self.rtp_buffer) } + pub fn get_seq(&self) -> u16 { + unsafe { + gst_rtp_sys::gst_rtp_buffer_get_seq(glib::translate::mut_override(&self.rtp_buffer)) + } } - pub fn get_payload_type(&mut self) -> u8 { - unsafe { gst_rtp_sys::gst_rtp_buffer_get_payload_type(&mut self.rtp_buffer) } + pub fn get_payload_type(&self) -> u8 { + unsafe { + gst_rtp_sys::gst_rtp_buffer_get_payload_type(glib::translate::mut_override( + &self.rtp_buffer, + )) + } } - pub fn get_timestamp(&mut self) -> u32 { - unsafe { gst_rtp_sys::gst_rtp_buffer_get_timestamp(&mut self.rtp_buffer) } + pub fn get_ssrc(&self) -> u32 { + unsafe { + gst_rtp_sys::gst_rtp_buffer_get_ssrc(glib::translate::mut_override(&self.rtp_buffer)) + } + } + + pub fn get_timestamp(&self) -> u32 { + unsafe { + gst_rtp_sys::gst_rtp_buffer_get_timestamp(glib::translate::mut_override( + &self.rtp_buffer, + )) + } + } + + pub fn get_csrc(&self, idx: u8) -> Option { + if idx < self.get_csrc_count() { + unsafe { + Some(gst_rtp_sys::gst_rtp_buffer_get_csrc( + glib::translate::mut_override(&self.rtp_buffer), + idx, + )) + } + } else { + None + } + } + + pub fn get_csrc_count(&self) -> u8 { + unsafe { + gst_rtp_sys::gst_rtp_buffer_get_csrc_count(glib::translate::mut_override( + &self.rtp_buffer, + )) + } + } + + pub fn get_payload_size(&self) -> u32 { + unsafe { + gst_rtp_sys::gst_rtp_buffer_get_payload_len(glib::translate::mut_override( + &self.rtp_buffer, + )) as u32 + } + } + + pub fn get_payload(&self) -> Result<&[u8], glib::error::BoolError> { + let size = self.get_payload_size(); + if size == 0 { + return Ok(&[]); + } + unsafe { + let pointer = gst_rtp_sys::gst_rtp_buffer_get_payload(glib::translate::mut_override( + &self.rtp_buffer, + )); + if pointer.is_null() { + Err(glib_bool_error!("Failed to get payload data")) + } else { + Ok(slice::from_raw_parts(pointer as *const u8, size as usize)) + } + } + } + + pub fn get_extension(&self) -> bool { + unsafe { + from_glib(gst_rtp_sys::gst_rtp_buffer_get_extension( + glib::translate::mut_override(&self.rtp_buffer), + )) + } + } + + pub fn get_extension_bytes(&self) -> Option<(u16, glib::Bytes)> { + unsafe { + let mut bits: u16 = 0; + match from_glib_full(gst_rtp_sys::gst_rtp_buffer_get_extension_bytes( + glib::translate::mut_override(&self.rtp_buffer), + &mut bits, + )) { + Some(bytes) => Some((bits, bytes)), + None => None, + } + } + } + + pub fn get_extension_onebyte_header(&self, id: u8, nth: u32) -> Option<&[u8]> { + unsafe { + let mut data = ptr::null_mut(); + // FIXME: Workaround for gstreamer-rtp-sys having the wrong type for this parameter + let data_ptr = &mut data as *mut *mut u8 as *mut u8; + let mut size: u32 = 0; + let result: bool = from_glib(gst_rtp_sys::gst_rtp_buffer_get_extension_onebyte_header( + glib::translate::mut_override(&self.rtp_buffer), + id, + nth, + data_ptr, + &mut size, + )); + if result { + Some(slice::from_raw_parts(data as *const u8, size as usize)) + } else { + None + } + } + } + + pub fn get_extension_twobytes_header(&self, id: u8, nth: u32) -> Option<(u8, &[u8])> { + unsafe { + let mut data = ptr::null_mut(); + // FIXME: Workaround for gstreamer-rtp-sys having the wrong type for this parameter + let data_ptr = &mut data as *mut *mut u8 as *mut u8; + let mut size: u32 = 0; + let mut appbits = 0; + let result: bool = + from_glib(gst_rtp_sys::gst_rtp_buffer_get_extension_twobytes_header( + glib::translate::mut_override(&self.rtp_buffer), + &mut appbits, + id, + nth, + data_ptr, + &mut size, + )); + if result { + Some(( + appbits, + slice::from_raw_parts(data as *const u8, size as usize), + )) + } else { + None + } + } } } @@ -155,7 +359,9 @@ mod tests { fn test_map() { gst::init().unwrap(); - let mut buffer = gst::Buffer::new_rtp_with_sizes(16, 4, 0).unwrap(); + let csrc_count = 2; + let payload_size = 16; + let mut buffer = gst::Buffer::new_rtp_with_sizes(payload_size, 4, csrc_count).unwrap(); let mut rtp_buffer = RTPBuffer::from_buffer_writable(&mut buffer).unwrap(); rtp_buffer.set_seq(42); @@ -166,5 +372,122 @@ mod tests { rtp_buffer.set_timestamp(44); assert_eq!(rtp_buffer.get_timestamp(), 44); + + rtp_buffer.set_ssrc(45); + assert_eq!(rtp_buffer.get_ssrc(), 45); + + assert_eq!(rtp_buffer.get_payload_size(), payload_size); + let payload = rtp_buffer.get_payload(); + assert!(payload.is_ok()); + let payload = payload.unwrap(); + assert_eq!(payload.len(), payload_size as usize); + + assert_eq!(rtp_buffer.get_csrc_count(), csrc_count); + rtp_buffer.set_csrc(0, 12); + rtp_buffer.set_csrc(1, 15); + assert_eq!(rtp_buffer.get_csrc(0).unwrap(), 12); + assert_eq!(rtp_buffer.get_csrc(1).unwrap(), 15); + assert!(rtp_buffer.get_csrc(2).is_none()); + + rtp_buffer.set_extension(true); + assert_eq!(rtp_buffer.get_extension(), true); + + assert_eq!(rtp_buffer.get_extension_bytes(), None); + } + + #[test] + fn test_empty_payload() { + gst::init().unwrap(); + + let csrc_count = 0; + let payload_size = 0; + let buffer = gst::Buffer::new_rtp_with_sizes(payload_size, 4, csrc_count).unwrap(); + let rtp_buffer = RTPBuffer::from_buffer_readable(&buffer).unwrap(); + + assert_eq!(rtp_buffer.get_payload_size(), payload_size); + let payload = rtp_buffer.get_payload(); + assert!(payload.is_ok()); + assert_eq!(payload.unwrap().len(), payload_size as usize); + } + + #[test] + fn test_extension_header_onebyte() { + gst::init().unwrap(); + + let mut buffer = gst::Buffer::new_rtp_with_sizes(16, 4, 0).unwrap(); + let mut rtp_buffer = RTPBuffer::from_buffer_writable(&mut buffer).unwrap(); + + assert_eq!(rtp_buffer.get_extension_bytes(), None); + + let extension_data: [u8; 4] = [100, 101, 102, 103]; + let result = rtp_buffer.add_extension_onebyte_header(1, &extension_data); + assert!(result.is_ok()); + + let bytes_option = rtp_buffer.get_extension_bytes(); + assert!(bytes_option.is_some()); + let (bits, bytes) = bytes_option.unwrap(); + // 0xBEDE is the onebyte extension header marker: https://tools.ietf.org/html/rfc5285 (4.2) + assert_eq!(bits, 0xbede); + /* + * bytes is: + * * id (4 bits) + * * size-1 (4 bits) + * * data (with padded length to multiples of 4) + */ + assert_eq!(bytes[0] >> 4, 1); + assert_eq!(bytes[0] & 0xF, 3); + for i in 0..extension_data.len() { + assert_eq!(bytes[i + 1], extension_data[i]); + } + + let result = rtp_buffer.get_extension_onebyte_header(2, 0); + assert!(result.is_none()); + + let result = rtp_buffer.get_extension_onebyte_header(1, 0); + assert!(result.is_some()); + assert_eq!(result.unwrap(), &extension_data); + } + + #[test] + fn test_extension_header_twobytes() { + gst::init().unwrap(); + + let mut buffer = gst::Buffer::new_rtp_with_sizes(16, 4, 0).unwrap(); + let mut rtp_buffer = RTPBuffer::from_buffer_writable(&mut buffer).unwrap(); + + assert_eq!(rtp_buffer.get_extension_bytes(), None); + + let extension_data: [u8; 4] = [100, 101, 102, 103]; + let appbits = 5; + let id = 1; + let result = rtp_buffer.add_extension_twobytes_header(appbits, id, &extension_data); + assert!(result.is_ok()); + + let bytes_option = rtp_buffer.get_extension_bytes(); + assert!(bytes_option.is_some()); + let (bits, bytes) = bytes_option.unwrap(); + // 0x100 + appbits is the twobyte extension header marker: + // https://tools.ietf.org/html/rfc5285 (4.3) + assert_eq!(bits, 0x1000 | appbits as u16); + /* + * bytes is: + * * id (1 byte) + * * size-2 (1 byte) + * * data (with padded length to multiples of 4) + */ + assert_eq!(bytes[0], id); + assert_eq!(bytes[1], extension_data.len() as u8); + for i in 0..extension_data.len() { + assert_eq!(bytes[i + 2], extension_data[i]); + } + + let result = rtp_buffer.get_extension_twobytes_header(2, 0); + assert!(result.is_none()); + + let result = rtp_buffer.get_extension_twobytes_header(id, 0); + assert!(result.is_some()); + let (extracted_appbits, data) = result.unwrap(); + assert_eq!(appbits, extracted_appbits); + assert_eq!(data, &extension_data); } }