Add `RtcpPacket`
							parent
							
								
									0b71263817
								
							
						
					
					
						commit
						0a6553f965
					
				|  | @ -11,3 +11,4 @@ RFC | ||||||
| - AVP: https://tools.ietf.org/html/rfc3551 | - AVP: https://tools.ietf.org/html/rfc3551 | ||||||
| - AVPF: https://tools.ietf.org/html/rfc4585 | - AVPF: https://tools.ietf.org/html/rfc4585 | ||||||
| - SAVPF: https://tools.ietf.org/html/rfc5124 | - SAVPF: https://tools.ietf.org/html/rfc5124 | ||||||
|  | - Multiplexing RTP and RTCP: https://tools.ietf.org/html/rfc5761 | ||||||
|  |  | ||||||
|  | @ -19,8 +19,11 @@ pub mod types { | ||||||
|     pub type U7 = u8; |     pub type U7 = u8; | ||||||
|     pub type U24 = u32; |     pub type U24 = u32; | ||||||
|     pub type RtpTimestamp = u32; |     pub type RtpTimestamp = u32; | ||||||
|  |     pub type NtpTimestamp = u64; | ||||||
|  |     pub type NtpMiddleTimetamp = u32; | ||||||
|     pub type Ssrc = u32; |     pub type Ssrc = u32; | ||||||
|     pub type Csrc = u32; |     pub type Csrc = u32; | ||||||
|  |     pub type SsrcOrCsrc = u32; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub mod constants { | pub mod constants { | ||||||
|  |  | ||||||
|  | @ -1,3 +1,14 @@ | ||||||
| pub use self::rtp::{RtpPacket, RtpFixedHeader, RtpHeaderExtension}; | pub use self::rtp::{RtpPacket, RtpFixedHeader, RtpHeaderExtension}; | ||||||
| 
 | 
 | ||||||
|  | pub use self::rtcp::{RtcpPacket, RtcpSenderReport, RtcpReceiverReport}; | ||||||
|  | pub use self::rtcp::{RtcpSourceDescription, RtcpGoodbye, RtcpApplicationDefined}; | ||||||
|  | pub use self::rtcp::{ReceptionReport, SdesChunk, SdesItem}; | ||||||
|  | 
 | ||||||
|  | pub use self::rtcp::{RTCP_PACKET_TYPE_SR, RTCP_PACKET_TYPE_RR, RTCP_PACKET_TYPE_SDES}; | ||||||
|  | pub use self::rtcp::{RTCP_PACKET_TYPE_BYE, RTCP_PACKET_TYPE_APP}; | ||||||
|  | pub use self::rtcp::{SDES_ITEM_TYPE_END, SDES_ITEM_TYPE_CNAME, SDES_ITEM_TYPE_NAME}; | ||||||
|  | pub use self::rtcp::{SDES_ITEM_TYPE_EMAIL, SDES_ITEM_TYPE_PHONE, SDES_ITEM_TYPE_LOC}; | ||||||
|  | pub use self::rtcp::{SDES_ITEM_TYPE_TOOL, SDES_ITEM_TYPE_NOTE, SDES_ITEM_TYPE_PRIV}; | ||||||
|  | 
 | ||||||
| mod rtp; | mod rtp; | ||||||
|  | mod rtcp; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,578 @@ | ||||||
|  | use std::io::{Read, Write}; | ||||||
|  | use handy_async::sync_io::{ReadExt, WriteExt}; | ||||||
|  | 
 | ||||||
|  | use {Result, ErrorKind}; | ||||||
|  | use io::{ReadFrom, WriteTo}; | ||||||
|  | use packet::Packet; | ||||||
|  | use types::{U5, U24, RtpTimestamp, NtpTimestamp, NtpMiddleTimetamp, Ssrc, SsrcOrCsrc}; | ||||||
|  | use constants::RTP_VERSION; | ||||||
|  | 
 | ||||||
|  | pub const RTCP_PACKET_TYPE_SR: u8 = 200; | ||||||
|  | pub const RTCP_PACKET_TYPE_RR: u8 = 201; | ||||||
|  | pub const RTCP_PACKET_TYPE_SDES: u8 = 202; | ||||||
|  | pub const RTCP_PACKET_TYPE_BYE: u8 = 203; | ||||||
|  | pub const RTCP_PACKET_TYPE_APP: u8 = 204; | ||||||
|  | 
 | ||||||
|  | pub const SDES_ITEM_TYPE_END: u8 = 0; | ||||||
|  | pub const SDES_ITEM_TYPE_CNAME: u8 = 1; | ||||||
|  | pub const SDES_ITEM_TYPE_NAME: u8 = 2; | ||||||
|  | pub const SDES_ITEM_TYPE_EMAIL: u8 = 3; | ||||||
|  | pub const SDES_ITEM_TYPE_PHONE: u8 = 4; | ||||||
|  | pub const SDES_ITEM_TYPE_LOC: u8 = 5; | ||||||
|  | pub const SDES_ITEM_TYPE_TOOL: u8 = 6; | ||||||
|  | pub const SDES_ITEM_TYPE_NOTE: u8 = 7; | ||||||
|  | pub const SDES_ITEM_TYPE_PRIV: u8 = 8; | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, PartialEq, Eq)] | ||||||
|  | pub enum RtcpPacket { | ||||||
|  |     Sr(RtcpSenderReport), | ||||||
|  |     Rr(RtcpReceiverReport), | ||||||
|  |     Sdes(RtcpSourceDescription), | ||||||
|  |     Bye(RtcpGoodbye), | ||||||
|  |     App(RtcpApplicationDefined), | ||||||
|  | } | ||||||
|  | impl Packet for RtcpPacket {} | ||||||
|  | impl ReadFrom for RtcpPacket { | ||||||
|  |     fn read_from<R: Read>(reader: &mut R) -> Result<Self> { | ||||||
|  |         let mut buf = [0; 2]; | ||||||
|  |         track_try!(reader.read_exact(&mut buf)); | ||||||
|  | 
 | ||||||
|  |         let reader = &mut (&buf[..]).chain(reader); | ||||||
|  |         let packet_type = buf[1]; | ||||||
|  |         match packet_type { | ||||||
|  |             RTCP_PACKET_TYPE_SR => track_err!(RtcpSenderReport::read_from(reader).map(From::from)), | ||||||
|  |             RTCP_PACKET_TYPE_RR => { | ||||||
|  |                 track_err!(RtcpReceiverReport::read_from(reader).map(From::from)) | ||||||
|  |             } | ||||||
|  |             RTCP_PACKET_TYPE_SDES => { | ||||||
|  |                 track_err!(RtcpSourceDescription::read_from(reader).map(From::from)) | ||||||
|  |             } | ||||||
|  |             RTCP_PACKET_TYPE_BYE => track_err!(RtcpGoodbye::read_from(reader).map(From::from)), | ||||||
|  |             RTCP_PACKET_TYPE_APP => { | ||||||
|  |                 track_err!(RtcpApplicationDefined::read_from(reader).map(From::from)) | ||||||
|  |             } | ||||||
|  |             _ => { | ||||||
|  |                 track_panic!(ErrorKind::Unsupported, | ||||||
|  |                              "Unknown packet type: {}", | ||||||
|  |                              packet_type) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl WriteTo for RtcpPacket { | ||||||
|  |     fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> { | ||||||
|  |         match *self { | ||||||
|  |             RtcpPacket::Sr(ref p) => track_err!(p.write_to(writer)), | ||||||
|  |             RtcpPacket::Rr(ref p) => track_err!(p.write_to(writer)), | ||||||
|  |             RtcpPacket::Sdes(ref p) => track_err!(p.write_to(writer)), | ||||||
|  |             RtcpPacket::Bye(ref p) => track_err!(p.write_to(writer)), | ||||||
|  |             RtcpPacket::App(ref p) => track_err!(p.write_to(writer)), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl From<RtcpSenderReport> for RtcpPacket { | ||||||
|  |     fn from(f: RtcpSenderReport) -> Self { | ||||||
|  |         RtcpPacket::Sr(f) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl From<RtcpReceiverReport> for RtcpPacket { | ||||||
|  |     fn from(f: RtcpReceiverReport) -> Self { | ||||||
|  |         RtcpPacket::Rr(f) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl From<RtcpSourceDescription> for RtcpPacket { | ||||||
|  |     fn from(f: RtcpSourceDescription) -> Self { | ||||||
|  |         RtcpPacket::Sdes(f) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl From<RtcpGoodbye> for RtcpPacket { | ||||||
|  |     fn from(f: RtcpGoodbye) -> Self { | ||||||
|  |         RtcpPacket::Bye(f) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl From<RtcpApplicationDefined> for RtcpPacket { | ||||||
|  |     fn from(f: RtcpApplicationDefined) -> Self { | ||||||
|  |         RtcpPacket::App(f) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn read_sctp<R: Read>(reader: &mut R, expected_type: u8) -> Result<(U5, Vec<u8>)> { | ||||||
|  |     let b = track_try!(reader.read_u8()); | ||||||
|  |     track_assert_eq!(b >> 6, | ||||||
|  |                      RTP_VERSION, | ||||||
|  |                      ErrorKind::Unsupported, | ||||||
|  |                      "Unsupported RTP version: {}", | ||||||
|  |                      b >> 6); | ||||||
|  |     let padding = (b & 0b0010_0000) != 0; | ||||||
|  |     let packet_specific = b & 0b0001_1111; | ||||||
|  | 
 | ||||||
|  |     let packet_type = track_try!(reader.read_u8()); | ||||||
|  |     track_assert_eq!(packet_type, | ||||||
|  |                      expected_type, | ||||||
|  |                      ErrorKind::Invalid, | ||||||
|  |                      "Unexpected SCTP packet type: actual={}, expected={}", | ||||||
|  |                      packet_type, | ||||||
|  |                      expected_type); | ||||||
|  | 
 | ||||||
|  |     let word_count = track_try!(reader.read_u16be()) as usize; | ||||||
|  |     let mut payload = track_try!(reader.read_bytes(word_count * 4)); | ||||||
|  |     if padding { | ||||||
|  |         let payload_len = payload.len(); | ||||||
|  |         track_assert_ne!(payload_len, 0, ErrorKind::Invalid); | ||||||
|  | 
 | ||||||
|  |         let padding_len = payload[payload_len - 1] as usize; | ||||||
|  |         track_assert!(padding_len <= payload.len(), ErrorKind::Invalid); | ||||||
|  | 
 | ||||||
|  |         payload.truncate(payload_len - padding_len); | ||||||
|  |     } | ||||||
|  |     track_assert_eq!(payload.len() % 4, 0, ErrorKind::Invalid); | ||||||
|  | 
 | ||||||
|  |     Ok((packet_specific, payload)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn write_sctp<W: Write>(writer: &mut W, | ||||||
|  |                         packet_type: u8, | ||||||
|  |                         packet_specific: U5, | ||||||
|  |                         payload: &[u8]) | ||||||
|  |                         -> Result<()> { | ||||||
|  |     track_assert_eq!(payload.len() % 4, 0, ErrorKind::Invalid); | ||||||
|  | 
 | ||||||
|  |     track_try!(writer.write_u8(RTP_VERSION << 6 | packet_specific)); | ||||||
|  |     track_try!(writer.write_u8(packet_type)); | ||||||
|  | 
 | ||||||
|  |     let word_count = payload.len() / 4; | ||||||
|  |     track_assert!(word_count < 0x10000, ErrorKind::Invalid); | ||||||
|  | 
 | ||||||
|  |     track_try!(writer.write_u16be(word_count as u16)); | ||||||
|  |     track_try!(writer.write_all(payload)); | ||||||
|  | 
 | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, PartialEq, Eq)] | ||||||
|  | pub struct RtcpSenderReport { | ||||||
|  |     pub ssrc: Ssrc, | ||||||
|  |     pub ntp_timestamp: NtpTimestamp, | ||||||
|  |     pub rtp_timestamp: RtpTimestamp, | ||||||
|  |     pub sent_packets: u32, | ||||||
|  |     pub sent_octets: u32, | ||||||
|  |     pub reception_reports: Vec<ReceptionReport>, | ||||||
|  |     pub extensions: Vec<u8>, | ||||||
|  | } | ||||||
|  | impl RtcpSenderReport { | ||||||
|  |     pub fn new(ssrc: Ssrc) -> Self { | ||||||
|  |         RtcpSenderReport { | ||||||
|  |             ssrc: ssrc, | ||||||
|  |             ntp_timestamp: 0, | ||||||
|  |             rtp_timestamp: 0, | ||||||
|  |             sent_packets: 0, | ||||||
|  |             sent_octets: 0, | ||||||
|  |             reception_reports: Vec::new(), | ||||||
|  |             extensions: Vec::new(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl ReadFrom for RtcpSenderReport { | ||||||
|  |     fn read_from<R: Read>(reader: &mut R) -> Result<Self> { | ||||||
|  |         let (reception_report_count, payload) = track_try!(read_sctp(reader, RTCP_PACKET_TYPE_SR)); | ||||||
|  |         let reader = &mut &payload[..]; | ||||||
|  | 
 | ||||||
|  |         let ssrc = track_try!(reader.read_u32be()); | ||||||
|  | 
 | ||||||
|  |         let ntp_timestamp = track_try!(reader.read_u64be()); | ||||||
|  |         let rtp_timestamp = track_try!(reader.read_u32be()); | ||||||
|  |         let sent_packets = track_try!(reader.read_u32be()); | ||||||
|  |         let sent_octets = track_try!(reader.read_u32be()); | ||||||
|  | 
 | ||||||
|  |         let mut reception_reports = Vec::new(); | ||||||
|  |         for _ in 0..reception_report_count { | ||||||
|  |             let report = track_try!(ReceptionReport::read_from(reader)); | ||||||
|  |             reception_reports.push(report); | ||||||
|  |         } | ||||||
|  |         let extensions = track_try!(reader.read_all_bytes()); | ||||||
|  | 
 | ||||||
|  |         Ok(RtcpSenderReport { | ||||||
|  |                ssrc: ssrc, | ||||||
|  |                ntp_timestamp: ntp_timestamp, | ||||||
|  |                rtp_timestamp: rtp_timestamp, | ||||||
|  |                sent_packets: sent_packets, | ||||||
|  |                sent_octets: sent_octets, | ||||||
|  |                reception_reports: reception_reports, | ||||||
|  |                extensions: extensions, | ||||||
|  |            }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl WriteTo for RtcpSenderReport { | ||||||
|  |     fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> { | ||||||
|  |         let mut payload = Vec::new(); | ||||||
|  |         track_try!((&mut payload).write_u32be(self.ssrc)); | ||||||
|  |         track_try!((&mut payload).write_u64be(self.ntp_timestamp)); | ||||||
|  |         track_try!((&mut payload).write_u32be(self.rtp_timestamp)); | ||||||
|  |         track_try!((&mut payload).write_u32be(self.sent_packets)); | ||||||
|  |         track_try!((&mut payload).write_u32be(self.sent_octets)); | ||||||
|  |         for report in self.reception_reports.iter() { | ||||||
|  |             track_try!(report.write_to(&mut payload)); | ||||||
|  |         } | ||||||
|  |         payload.extend(&self.extensions); | ||||||
|  | 
 | ||||||
|  |         track_assert!(self.reception_reports.len() <= 0x0001_1111, | ||||||
|  |                       ErrorKind::Invalid); | ||||||
|  |         track_try!(write_sctp(writer, | ||||||
|  |                               RTCP_PACKET_TYPE_SR, | ||||||
|  |                               self.reception_reports.len() as u8, | ||||||
|  |                               &payload)); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, PartialEq, Eq)] | ||||||
|  | pub struct ReceptionReport { | ||||||
|  |     pub ssrc: Ssrc, | ||||||
|  |     pub fraction_lost: u8, | ||||||
|  |     pub packets_lost: U24, | ||||||
|  |     pub seq_num_ext: u32, | ||||||
|  |     pub jitter: u32, | ||||||
|  |     pub last_sr_timestamp: NtpMiddleTimetamp, | ||||||
|  |     pub delay_since_last_sr: u32, | ||||||
|  | } | ||||||
|  | impl ReceptionReport { | ||||||
|  |     pub fn new(ssrc: Ssrc) -> Self { | ||||||
|  |         ReceptionReport { | ||||||
|  |             ssrc: ssrc, | ||||||
|  |             fraction_lost: 0, | ||||||
|  |             packets_lost: 0, | ||||||
|  |             seq_num_ext: 0, | ||||||
|  |             jitter: 0, | ||||||
|  |             last_sr_timestamp: 0, | ||||||
|  |             delay_since_last_sr: 0, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl ReadFrom for ReceptionReport { | ||||||
|  |     fn read_from<R: Read>(reader: &mut R) -> Result<Self> { | ||||||
|  |         let ssrc = track_try!(reader.read_u32be()); | ||||||
|  |         let fraction_lost = track_try!(reader.read_u8()); | ||||||
|  |         let packets_lost = track_try!(reader.read_u24be()); | ||||||
|  |         let seq_num_ext = track_try!(reader.read_u32be()); | ||||||
|  |         let jitter = track_try!(reader.read_u32be()); | ||||||
|  |         let last_sr_timestamp = track_try!(reader.read_u32be()); | ||||||
|  |         let delay_since_last_sr = track_try!(reader.read_u32be()); | ||||||
|  | 
 | ||||||
|  |         Ok(ReceptionReport { | ||||||
|  |                ssrc: ssrc, | ||||||
|  |                fraction_lost: fraction_lost, | ||||||
|  |                packets_lost: packets_lost, | ||||||
|  |                seq_num_ext: seq_num_ext, | ||||||
|  |                jitter: jitter, | ||||||
|  |                last_sr_timestamp: last_sr_timestamp, | ||||||
|  |                delay_since_last_sr: delay_since_last_sr, | ||||||
|  |            }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl WriteTo for ReceptionReport { | ||||||
|  |     fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> { | ||||||
|  |         track_assert!(self.packets_lost <= 0x00FF_FFFF, ErrorKind::Invalid); | ||||||
|  | 
 | ||||||
|  |         track_try!(writer.write_u32be(self.ssrc)); | ||||||
|  |         track_try!(writer.write_u8(self.fraction_lost)); | ||||||
|  |         track_try!(writer.write_u24be(self.packets_lost)); | ||||||
|  |         track_try!(writer.write_u32be(self.seq_num_ext)); | ||||||
|  |         track_try!(writer.write_u32be(self.jitter)); | ||||||
|  |         track_try!(writer.write_u32be(self.last_sr_timestamp)); | ||||||
|  |         track_try!(writer.write_u32be(self.delay_since_last_sr)); | ||||||
|  | 
 | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, PartialEq, Eq)] | ||||||
|  | pub struct RtcpReceiverReport { | ||||||
|  |     pub ssrc: Ssrc, | ||||||
|  |     pub reception_reports: Vec<ReceptionReport>, | ||||||
|  |     pub extensions: Vec<u8>, | ||||||
|  | } | ||||||
|  | impl RtcpReceiverReport { | ||||||
|  |     pub fn new(ssrc: Ssrc) -> Self { | ||||||
|  |         RtcpReceiverReport { | ||||||
|  |             ssrc: ssrc, | ||||||
|  |             reception_reports: Vec::new(), | ||||||
|  |             extensions: Vec::new(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl ReadFrom for RtcpReceiverReport { | ||||||
|  |     fn read_from<R: Read>(reader: &mut R) -> Result<Self> { | ||||||
|  |         let (reception_report_count, payload) = track_try!(read_sctp(reader, RTCP_PACKET_TYPE_RR)); | ||||||
|  |         let reader = &mut &payload[..]; | ||||||
|  | 
 | ||||||
|  |         let ssrc = track_try!(reader.read_u32be()); | ||||||
|  | 
 | ||||||
|  |         let mut reception_reports = Vec::new(); | ||||||
|  |         for _ in 0..reception_report_count { | ||||||
|  |             let report = track_try!(ReceptionReport::read_from(reader)); | ||||||
|  |             reception_reports.push(report); | ||||||
|  |         } | ||||||
|  |         let extensions = track_try!(reader.read_all_bytes()); | ||||||
|  | 
 | ||||||
|  |         Ok(RtcpReceiverReport { | ||||||
|  |                ssrc: ssrc, | ||||||
|  |                reception_reports: reception_reports, | ||||||
|  |                extensions: extensions, | ||||||
|  |            }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl WriteTo for RtcpReceiverReport { | ||||||
|  |     fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> { | ||||||
|  |         let mut payload = Vec::new(); | ||||||
|  |         track_try!((&mut payload).write_u32be(self.ssrc)); | ||||||
|  |         for report in self.reception_reports.iter() { | ||||||
|  |             track_try!(report.write_to(&mut payload)); | ||||||
|  |         } | ||||||
|  |         payload.extend(&self.extensions); | ||||||
|  | 
 | ||||||
|  |         track_assert!(self.reception_reports.len() <= 0b0001_1111, | ||||||
|  |                       ErrorKind::Invalid); | ||||||
|  |         track_try!(write_sctp(writer, | ||||||
|  |                               RTCP_PACKET_TYPE_RR, | ||||||
|  |                               self.reception_reports.len() as u8, | ||||||
|  |                               &payload)); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, PartialEq, Eq)] | ||||||
|  | pub struct RtcpSourceDescription { | ||||||
|  |     pub chunks: Vec<SdesChunk>, | ||||||
|  | } | ||||||
|  | impl RtcpSourceDescription { | ||||||
|  |     pub fn new() -> Self { | ||||||
|  |         RtcpSourceDescription { chunks: Vec::new() } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl ReadFrom for RtcpSourceDescription { | ||||||
|  |     fn read_from<R: Read>(reader: &mut R) -> Result<Self> { | ||||||
|  |         let (source_count, payload) = track_try!(read_sctp(reader, RTCP_PACKET_TYPE_SDES)); | ||||||
|  |         let reader = &mut &payload[..]; | ||||||
|  | 
 | ||||||
|  |         let chunks = track_try!((0..source_count).map(|_| SdesChunk::read_from(reader)).collect()); | ||||||
|  |         Ok(RtcpSourceDescription { chunks: chunks }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl WriteTo for RtcpSourceDescription { | ||||||
|  |     fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> { | ||||||
|  |         let mut payload = Vec::new(); | ||||||
|  |         for chunk in self.chunks.iter() { | ||||||
|  |             track_try!(chunk.write_to(&mut payload)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         track_assert!(self.chunks.len() <= 0b0001_1111, ErrorKind::Invalid); | ||||||
|  |         track_try!(write_sctp(writer, | ||||||
|  |                               RTCP_PACKET_TYPE_SDES, | ||||||
|  |                               self.chunks.len() as u8, | ||||||
|  |                               &payload)); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, PartialEq, Eq)] | ||||||
|  | pub struct SdesChunk { | ||||||
|  |     pub ssrc_or_csrc: SsrcOrCsrc, | ||||||
|  |     pub items: Vec<SdesItem>, | ||||||
|  | } | ||||||
|  | impl ReadFrom for SdesChunk { | ||||||
|  |     fn read_from<R: Read>(reader: &mut R) -> Result<Self> { | ||||||
|  |         let mut read_bytes = 0; | ||||||
|  | 
 | ||||||
|  |         let ssrc_or_csrc = track_try!(reader.read_u32be()); | ||||||
|  |         read_bytes += 4; | ||||||
|  | 
 | ||||||
|  |         let mut items = Vec::new(); | ||||||
|  |         loop { | ||||||
|  |             let ty = track_try!(reader.read_u8()); | ||||||
|  |             read_bytes += 1; | ||||||
|  | 
 | ||||||
|  |             if ty == SDES_ITEM_TYPE_END { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             let len = track_try!(reader.read_u8()) as usize; | ||||||
|  |             let text = track_try!(reader.read_string(len)); | ||||||
|  |             read_bytes += 1 + len; | ||||||
|  |             let item = match ty { | ||||||
|  |                 SDES_ITEM_TYPE_CNAME => SdesItem::Cname(text), | ||||||
|  |                 SDES_ITEM_TYPE_NAME => SdesItem::Name(text), | ||||||
|  |                 SDES_ITEM_TYPE_EMAIL => SdesItem::Email(text), | ||||||
|  |                 SDES_ITEM_TYPE_PHONE => SdesItem::Phone(text), | ||||||
|  |                 SDES_ITEM_TYPE_LOC => SdesItem::Loc(text), | ||||||
|  |                 SDES_ITEM_TYPE_TOOL => SdesItem::Tool(text), | ||||||
|  |                 SDES_ITEM_TYPE_NOTE => SdesItem::Note(text), | ||||||
|  |                 SDES_ITEM_TYPE_PRIV => SdesItem::Priv(text), | ||||||
|  |                 _ => track_panic!(ErrorKind::Unsupported, "Unknown SDES item type: {}", ty), | ||||||
|  |             }; | ||||||
|  |             items.push(item); | ||||||
|  |         } | ||||||
|  |         let padding_len = (4 - read_bytes % 4) % 4; | ||||||
|  |         track_try!(reader.read_bytes(padding_len as usize)); // discard
 | ||||||
|  | 
 | ||||||
|  |         Ok(SdesChunk { | ||||||
|  |                ssrc_or_csrc: ssrc_or_csrc, | ||||||
|  |                items: items, | ||||||
|  |            }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl WriteTo for SdesChunk { | ||||||
|  |     fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> { | ||||||
|  |         let mut write_bytes = 0; | ||||||
|  | 
 | ||||||
|  |         track_try!(writer.write_u32be(self.ssrc_or_csrc)); | ||||||
|  |         write_bytes += 4; | ||||||
|  | 
 | ||||||
|  |         for item in self.items.iter() { | ||||||
|  |             track_try!(writer.write_u8(item.item_type())); | ||||||
|  |             write_bytes += 1; | ||||||
|  | 
 | ||||||
|  |             let text = item.text(); | ||||||
|  |             track_assert!(text.len() <= 0xFFFF, ErrorKind::Invalid); | ||||||
|  |             track_try!(writer.write_u16be(text.len() as u16)); | ||||||
|  |             track_try!(writer.write_all(text.as_bytes())); | ||||||
|  |             write_bytes += 2 + text.len(); | ||||||
|  |         } | ||||||
|  |         track_try!(writer.write_u8(SDES_ITEM_TYPE_END)); | ||||||
|  |         write_bytes += 1; | ||||||
|  | 
 | ||||||
|  |         let padding_len = (4 - write_bytes % 4) % 4; | ||||||
|  |         for _ in 0..padding_len { | ||||||
|  |             track_try!(writer.write_u8(0)); | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, PartialEq, Eq)] | ||||||
|  | pub enum SdesItem { | ||||||
|  |     Cname(String), | ||||||
|  |     Name(String), | ||||||
|  |     Email(String), | ||||||
|  |     Phone(String), | ||||||
|  |     Loc(String), | ||||||
|  |     Tool(String), | ||||||
|  |     Note(String), | ||||||
|  |     Priv(String), | ||||||
|  | } | ||||||
|  | impl SdesItem { | ||||||
|  |     pub fn item_type(&self) -> u8 { | ||||||
|  |         match *self { | ||||||
|  |             SdesItem::Cname(_) => SDES_ITEM_TYPE_CNAME, | ||||||
|  |             SdesItem::Name(_) => SDES_ITEM_TYPE_NAME, | ||||||
|  |             SdesItem::Email(_) => SDES_ITEM_TYPE_EMAIL, | ||||||
|  |             SdesItem::Phone(_) => SDES_ITEM_TYPE_PHONE, | ||||||
|  |             SdesItem::Loc(_) => SDES_ITEM_TYPE_LOC, | ||||||
|  |             SdesItem::Tool(_) => SDES_ITEM_TYPE_TOOL, | ||||||
|  |             SdesItem::Note(_) => SDES_ITEM_TYPE_NOTE, | ||||||
|  |             SdesItem::Priv(_) => SDES_ITEM_TYPE_PRIV, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     pub fn text(&self) -> &str { | ||||||
|  |         match *self { | ||||||
|  |             SdesItem::Cname(ref t) => t, | ||||||
|  |             SdesItem::Name(ref t) => t, | ||||||
|  |             SdesItem::Email(ref t) => t, | ||||||
|  |             SdesItem::Phone(ref t) => t, | ||||||
|  |             SdesItem::Loc(ref t) => t, | ||||||
|  |             SdesItem::Tool(ref t) => t, | ||||||
|  |             SdesItem::Note(ref t) => t, | ||||||
|  |             SdesItem::Priv(ref t) => t, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, PartialEq, Eq)] | ||||||
|  | pub struct RtcpGoodbye { | ||||||
|  |     pub ssrc_csrc_list: Vec<SsrcOrCsrc>, | ||||||
|  |     pub reason: Option<String>, | ||||||
|  | } | ||||||
|  | impl RtcpGoodbye { | ||||||
|  |     pub fn new() -> Self { | ||||||
|  |         RtcpGoodbye { | ||||||
|  |             ssrc_csrc_list: Vec::new(), | ||||||
|  |             reason: None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl ReadFrom for RtcpGoodbye { | ||||||
|  |     fn read_from<R: Read>(reader: &mut R) -> Result<Self> { | ||||||
|  |         let (source_count, payload) = track_try!(read_sctp(reader, RTCP_PACKET_TYPE_BYE)); | ||||||
|  |         let reader = &mut &payload[..]; | ||||||
|  | 
 | ||||||
|  |         let list = track_try!((0..source_count).map(|_| reader.read_u32be()).collect()); | ||||||
|  |         let mut reason = None; | ||||||
|  |         if let Ok(len) = reader.read_u8() { | ||||||
|  |             reason = Some(track_try!(reader.read_string(len as usize))); | ||||||
|  |         } | ||||||
|  |         Ok(RtcpGoodbye { | ||||||
|  |                ssrc_csrc_list: list, | ||||||
|  |                reason: reason, | ||||||
|  |            }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl WriteTo for RtcpGoodbye { | ||||||
|  |     fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> { | ||||||
|  |         let mut payload = Vec::new(); | ||||||
|  |         for x in self.ssrc_csrc_list.iter() { | ||||||
|  |             track_try!((&mut payload).write_u32be(*x)); | ||||||
|  |         } | ||||||
|  |         if let Some(ref reason) = self.reason { | ||||||
|  |             track_assert!(reason.len() <= 0xFF, ErrorKind::Invalid); | ||||||
|  |             track_try!((&mut payload).write_u8(reason.len() as u8)); | ||||||
|  |             track_try!((&mut payload).write_all(reason.as_bytes())); | ||||||
|  | 
 | ||||||
|  |             let padding_len = (4 - (reason.len() + 1) % 4) % 4; | ||||||
|  |             for _ in 0..padding_len { | ||||||
|  |                 track_try!((&mut payload).write_u8(0)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         track_assert!(self.ssrc_csrc_list.len() <= 0b0001_1111, ErrorKind::Invalid); | ||||||
|  |         track_try!(write_sctp(writer, | ||||||
|  |                               RTCP_PACKET_TYPE_BYE, | ||||||
|  |                               self.ssrc_csrc_list.len() as u8, | ||||||
|  |                               &payload)); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, PartialEq, Eq)] | ||||||
|  | pub struct RtcpApplicationDefined { | ||||||
|  |     pub subtype: U5, | ||||||
|  |     pub ssrc_or_csrc: SsrcOrCsrc, | ||||||
|  |     pub name: [u8; 4], | ||||||
|  |     pub data: Vec<u8>, | ||||||
|  | } | ||||||
|  | impl ReadFrom for RtcpApplicationDefined { | ||||||
|  |     fn read_from<R: Read>(reader: &mut R) -> Result<Self> { | ||||||
|  |         let (subtype, payload) = track_try!(read_sctp(reader, RTCP_PACKET_TYPE_APP)); | ||||||
|  |         let reader = &mut &payload[..]; | ||||||
|  | 
 | ||||||
|  |         let ssrc_or_csrc = track_try!(reader.read_u32be()); | ||||||
|  |         let mut name = [0; 4]; | ||||||
|  |         track_try!(reader.read_exact(&mut name)); | ||||||
|  |         let data = track_try!(reader.read_all_bytes()); | ||||||
|  |         Ok(RtcpApplicationDefined { | ||||||
|  |                subtype: subtype, | ||||||
|  |                ssrc_or_csrc: ssrc_or_csrc, | ||||||
|  |                name: name, | ||||||
|  |                data: data, | ||||||
|  |            }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | impl WriteTo for RtcpApplicationDefined { | ||||||
|  |     fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> { | ||||||
|  |         let mut payload = Vec::new(); | ||||||
|  |         track_try!((&mut payload).write_u32be(self.ssrc_or_csrc)); | ||||||
|  |         payload.extend(&self.name); | ||||||
|  |         payload.extend(&self.data); | ||||||
|  | 
 | ||||||
|  |         track_assert!(self.subtype <= 0b0001_1111, ErrorKind::Invalid); | ||||||
|  |         track_try!(write_sctp(writer, RTCP_PACKET_TYPE_APP, self.subtype, &payload)); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -61,7 +61,7 @@ pub struct RtpFixedHeader { | ||||||
|     pub seq_num: u16, |     pub seq_num: u16, | ||||||
|     pub timestamp: RtpTimestamp, |     pub timestamp: RtpTimestamp, | ||||||
|     pub ssrc: Ssrc, |     pub ssrc: Ssrc, | ||||||
|     pub csrcs: Vec<Csrc>, |     pub csrc_list: Vec<Csrc>, | ||||||
|     pub extension: Option<RtpHeaderExtension>, |     pub extension: Option<RtpHeaderExtension>, | ||||||
| } | } | ||||||
| impl ReadFrom for RtpFixedHeader { | impl ReadFrom for RtpFixedHeader { | ||||||
|  | @ -83,7 +83,7 @@ impl ReadFrom for RtpFixedHeader { | ||||||
|         let seq_num = track_try!(reader.read_u16be()); |         let seq_num = track_try!(reader.read_u16be()); | ||||||
|         let timestamp = track_try!(reader.read_u32be()); |         let timestamp = track_try!(reader.read_u32be()); | ||||||
|         let ssrc = track_try!(reader.read_u32be()); |         let ssrc = track_try!(reader.read_u32be()); | ||||||
|         let csrcs = track_try!((0..csrc_count).map(|_| reader.read_u32be()).collect()); |         let csrc_list = track_try!((0..csrc_count).map(|_| reader.read_u32be()).collect()); | ||||||
|         let extension = if extension { |         let extension = if extension { | ||||||
|             let e = track_try!(RtpHeaderExtension::read_from(reader)); |             let e = track_try!(RtpHeaderExtension::read_from(reader)); | ||||||
|             Some(e) |             Some(e) | ||||||
|  | @ -98,7 +98,7 @@ impl ReadFrom for RtpFixedHeader { | ||||||
|                seq_num: seq_num, |                seq_num: seq_num, | ||||||
|                timestamp: timestamp, |                timestamp: timestamp, | ||||||
|                ssrc: ssrc, |                ssrc: ssrc, | ||||||
|                csrcs: csrcs, |                csrc_list: csrc_list, | ||||||
|            }) |            }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -111,8 +111,8 @@ impl WriteTo for RtpFixedHeader { | ||||||
|         if self.extension.is_some() { |         if self.extension.is_some() { | ||||||
|             b |= 0b0001_0000; |             b |= 0b0001_0000; | ||||||
|         } |         } | ||||||
|         track_assert!(self.csrcs.len() <= 0b0000_1111, ErrorKind::Invalid); |         track_assert!(self.csrc_list.len() <= 0b0000_1111, ErrorKind::Invalid); | ||||||
|         b |= self.csrcs.len() as u8; |         b |= self.csrc_list.len() as u8; | ||||||
|         track_try!(writer.write_u8(b)); |         track_try!(writer.write_u8(b)); | ||||||
| 
 | 
 | ||||||
|         let mut b = 0; |         let mut b = 0; | ||||||
|  | @ -125,7 +125,7 @@ impl WriteTo for RtpFixedHeader { | ||||||
|         track_try!(writer.write_u16be(self.seq_num)); |         track_try!(writer.write_u16be(self.seq_num)); | ||||||
|         track_try!(writer.write_u32be(self.timestamp)); |         track_try!(writer.write_u32be(self.timestamp)); | ||||||
|         track_try!(writer.write_u32be(self.ssrc)); |         track_try!(writer.write_u32be(self.ssrc)); | ||||||
|         for csrc in self.csrcs.iter() { |         for csrc in self.csrc_list.iter() { | ||||||
|             track_try!(writer.write_u32be(*csrc)); |             track_try!(writer.write_u32be(*csrc)); | ||||||
|         } |         } | ||||||
|         if let Some(ref extension) = self.extension { |         if let Some(ref extension) = self.extension { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue