From 0b712638170ae74de22f8f8d610e81483f27b1ab Mon Sep 17 00:00:00 2001 From: Takeru Ohta Date: Sat, 25 Mar 2017 23:26:29 +0900 Subject: [PATCH] Initial commit --- .gitignore | 2 + Cargo.toml | 8 +++ README.md | 13 ++++ src/error.rs | 18 +++++ src/io.rs | 11 +++ src/lib.rs | 34 ++++++++++ src/packet.rs | 3 + src/rfc3550/mod.rs | 3 + src/rfc3550/rtp.rs | 164 +++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 256 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/error.rs create mode 100644 src/io.rs create mode 100644 src/lib.rs create mode 100644 src/packet.rs create mode 100644 src/rfc3550/mod.rs create mode 100644 src/rfc3550/rtp.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9d37c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1b2fffc --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rtp" +version = "0.1.0" +authors = ["Takeru Ohta "] + +[dependencies] +trackable = "0.1" +handy_async = "0.2" diff --git a/README.md b/README.md new file mode 100644 index 0000000..ebeb643 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +rtp +=== + +A Rust implementation of RTP and profiles derived from it. + +RFC +--- + +- RTP: https://tools.ietf.org/html/rfc3550 +- SRTP: https://tools.ietf.org/html/rfc3711 +- AVP: https://tools.ietf.org/html/rfc3551 +- AVPF: https://tools.ietf.org/html/rfc4585 +- SAVPF: https://tools.ietf.org/html/rfc5124 diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..8749333 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,18 @@ +use std::io; +use trackable::error::{TrackableError, IntoTrackableError}; +use trackable::error::{ErrorKind as TrackableErrorKind, ErrorKindExt}; + +pub type Error = TrackableError; + +#[derive(Debug, Clone)] +pub enum ErrorKind { + Unsupported, + Invalid, + Other, +} +impl TrackableErrorKind for ErrorKind {} +impl IntoTrackableError for ErrorKind { + fn into_trackable_error(from: io::Error) -> Error { + ErrorKind::Other.cause(from) + } +} diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 0000000..ae9e37d --- /dev/null +++ b/src/io.rs @@ -0,0 +1,11 @@ +use std::io::{Read, Write}; + +use Result; + +pub trait ReadFrom: Sized { + fn read_from(reader: &mut R) -> Result; +} + +pub trait WriteTo { + fn write_to(&self, writer: &mut W) -> Result<()>; +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..7e5357a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,34 @@ +#[macro_use] +extern crate trackable; +extern crate handy_async; + +pub use error::{Error, ErrorKind}; + +pub mod io; +pub mod packet; +pub mod rfc3550; + +mod error; + +pub type Result = ::std::result::Result; + +pub mod types { + pub type U2 = u8; + pub type U4 = u8; + pub type U5 = u8; + pub type U7 = u8; + pub type U24 = u32; + pub type RtpTimestamp = u32; + pub type Ssrc = u32; + pub type Csrc = u32; +} + +pub mod constants { + pub const RTP_VERSION: u8 = 2; +} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() {} +} diff --git a/src/packet.rs b/src/packet.rs new file mode 100644 index 0000000..8108cd8 --- /dev/null +++ b/src/packet.rs @@ -0,0 +1,3 @@ +use io::{ReadFrom, WriteTo}; + +pub trait Packet: ReadFrom + WriteTo {} diff --git a/src/rfc3550/mod.rs b/src/rfc3550/mod.rs new file mode 100644 index 0000000..7ebe487 --- /dev/null +++ b/src/rfc3550/mod.rs @@ -0,0 +1,3 @@ +pub use self::rtp::{RtpPacket, RtpFixedHeader, RtpHeaderExtension}; + +mod rtp; diff --git a/src/rfc3550/rtp.rs b/src/rfc3550/rtp.rs new file mode 100644 index 0000000..016733e --- /dev/null +++ b/src/rfc3550/rtp.rs @@ -0,0 +1,164 @@ +use std::io::{Read, Write}; +use handy_async::sync_io::{ReadExt, WriteExt}; + +use {Result, ErrorKind}; +use io::{ReadFrom, WriteTo}; +use packet::Packet; +use types::{U7, RtpTimestamp, Ssrc, Csrc}; +use constants::RTP_VERSION; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RtpPacket { + pub header: RtpFixedHeader, + pub payload: Vec, + pub padding: Vec, +} +impl Packet for RtpPacket {} +impl ReadFrom for RtpPacket { + fn read_from(reader: &mut R) -> Result { + let header = track_try!(RtpFixedHeader::read_from(reader)); + let mut payload = track_try!(reader.read_all_bytes()); + let mut padding = Vec::new(); + if header.padding { + let payload_len = payload.len(); + track_assert_ne!(payload_len, 0, ErrorKind::Invalid); + + let padding_len = *payload.last().unwrap() as usize; + track_assert!(padding_len <= payload_len, ErrorKind::Invalid); + + padding = payload.drain(payload_len - padding_len..).collect(); + } + Ok(RtpPacket { + header: header, + payload: payload, + padding: padding, + }) + } +} +impl WriteTo for RtpPacket { + fn write_to(&self, writer: &mut W) -> Result<()> { + track_try!(self.header.write_to(writer)); + track_try!(writer.write_all(&self.payload)); + + track_assert_ne!(self.header.padding, + self.padding.is_empty(), + ErrorKind::Invalid); + if !self.padding.is_empty() { + track_assert_eq!(*self.padding.last().unwrap() as usize, + self.padding.len(), + ErrorKind::Invalid); + track_try!(writer.write_all(&self.padding)); + } + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RtpFixedHeader { + pub padding: bool, + pub marker: bool, + pub payload_type: U7, + pub seq_num: u16, + pub timestamp: RtpTimestamp, + pub ssrc: Ssrc, + pub csrcs: Vec, + pub extension: Option, +} +impl ReadFrom for RtpFixedHeader { + fn read_from(reader: &mut R) -> Result { + 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 extension = (b & 0b0001_0000) != 0; + let csrc_count = b & 0b0000_1111; + + let b = track_try!(reader.read_u8()); + let marker = (b & 0b1000_0000) != 0; + let payload_type = b & 0b0111_1111; + + let seq_num = track_try!(reader.read_u16be()); + let timestamp = 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 extension = if extension { + let e = track_try!(RtpHeaderExtension::read_from(reader)); + Some(e) + } else { + None + }; + Ok(RtpFixedHeader { + padding: padding, + extension: extension, + marker: marker, + payload_type: payload_type, + seq_num: seq_num, + timestamp: timestamp, + ssrc: ssrc, + csrcs: csrcs, + }) + } +} +impl WriteTo for RtpFixedHeader { + fn write_to(&self, writer: &mut W) -> Result<()> { + let mut b = RTP_VERSION << 6; + if self.padding { + b |= 0b0010_0000; + } + if self.extension.is_some() { + b |= 0b0001_0000; + } + track_assert!(self.csrcs.len() <= 0b0000_1111, ErrorKind::Invalid); + b |= self.csrcs.len() as u8; + track_try!(writer.write_u8(b)); + + let mut b = 0; + if self.marker { + b |= 0b1000_0000; + } + b |= self.payload_type; + track_try!(writer.write_u8(b)); + + track_try!(writer.write_u16be(self.seq_num)); + track_try!(writer.write_u32be(self.timestamp)); + track_try!(writer.write_u32be(self.ssrc)); + for csrc in self.csrcs.iter() { + track_try!(writer.write_u32be(*csrc)); + } + if let Some(ref extension) = self.extension { + track_try!(extension.write_to(writer)); + } + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RtpHeaderExtension { + pub profile_specific: u16, + pub extension: Vec, +} +impl ReadFrom for RtpHeaderExtension { + fn read_from(reader: &mut R) -> Result { + let profile_specific = track_try!(reader.read_u16be()); + let word_count = track_try!(reader.read_u16be()); + let extension = track_try!(reader.read_bytes(word_count as usize * 4)); + Ok(RtpHeaderExtension { + profile_specific: profile_specific, + extension: extension, + }) + } +} +impl WriteTo for RtpHeaderExtension { + fn write_to(&self, writer: &mut W) -> Result<()> { + track_assert_eq!(self.extension.len() % 4, 0, ErrorKind::Invalid); + track_assert!(self.extension.len() / 4 < 0x10000, ErrorKind::Invalid); + + track_try!(writer.write_u16be(self.profile_specific)); + track_try!(writer.write_u16be((self.extension.len() / 4) as u16)); + track_try!(writer.write_all(&self.extension)); + Ok(()) + } +}