Initial commit
commit
0b71263817
|
@ -0,0 +1,2 @@
|
|||
target
|
||||
Cargo.lock
|
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "rtp"
|
||||
version = "0.1.0"
|
||||
authors = ["Takeru Ohta <phjgt308@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
trackable = "0.1"
|
||||
handy_async = "0.2"
|
|
@ -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
|
|
@ -0,0 +1,18 @@
|
|||
use std::io;
|
||||
use trackable::error::{TrackableError, IntoTrackableError};
|
||||
use trackable::error::{ErrorKind as TrackableErrorKind, ErrorKindExt};
|
||||
|
||||
pub type Error = TrackableError<ErrorKind>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ErrorKind {
|
||||
Unsupported,
|
||||
Invalid,
|
||||
Other,
|
||||
}
|
||||
impl TrackableErrorKind for ErrorKind {}
|
||||
impl IntoTrackableError<io::Error> for ErrorKind {
|
||||
fn into_trackable_error(from: io::Error) -> Error {
|
||||
ErrorKind::Other.cause(from)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
use std::io::{Read, Write};
|
||||
|
||||
use Result;
|
||||
|
||||
pub trait ReadFrom: Sized {
|
||||
fn read_from<R: Read>(reader: &mut R) -> Result<Self>;
|
||||
}
|
||||
|
||||
pub trait WriteTo {
|
||||
fn write_to<W: Write>(&self, writer: &mut W) -> Result<()>;
|
||||
}
|
|
@ -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<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
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() {}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
use io::{ReadFrom, WriteTo};
|
||||
|
||||
pub trait Packet: ReadFrom + WriteTo {}
|
|
@ -0,0 +1,3 @@
|
|||
pub use self::rtp::{RtpPacket, RtpFixedHeader, RtpHeaderExtension};
|
||||
|
||||
mod rtp;
|
|
@ -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<u8>,
|
||||
pub padding: Vec<u8>,
|
||||
}
|
||||
impl Packet for RtpPacket {}
|
||||
impl ReadFrom for RtpPacket {
|
||||
fn read_from<R: Read>(reader: &mut R) -> Result<Self> {
|
||||
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<W: Write>(&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<Csrc>,
|
||||
pub extension: Option<RtpHeaderExtension>,
|
||||
}
|
||||
impl ReadFrom for RtpFixedHeader {
|
||||
fn read_from<R: Read>(reader: &mut R) -> Result<Self> {
|
||||
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<W: Write>(&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<u8>,
|
||||
}
|
||||
impl ReadFrom for RtpHeaderExtension {
|
||||
fn read_from<R: Read>(reader: &mut R) -> Result<Self> {
|
||||
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<W: Write>(&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(())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue