blob: de75baffd38c44df32453727f8adb9c0964beb8a [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {bitfield::bitfield, thiserror::Error};
/// The error types for rtp header parsing.
#[derive(Error, Debug, PartialEq)]
pub enum RtpError {
/// The buffer used to create the header was too short
#[error("The buffer is too short.")]
BufferTooShort,
/// The RTP version of this packet is not supported
#[error("Unsupported RTP Version.")]
UnsupportedVersion,
/// The value that was provided is invalid.
#[error("Unsupported flags or fields were found in the header.")]
UnsupportedFeature,
#[doc(hidden)]
#[error("__Nonexhaustive error should never be created.")]
__Nonexhaustive,
}
bitfield! {
pub struct RtpHeaderInner(MSB0 [u8]);
impl Debug;
pub u8, version, _: 1, 0;
pub bool, padding, _: 2;
pub bool, extension, _: 3;
pub u8, csrc_count, _: 7, 4;
pub bool, marker, _: 8;
pub u8, payload_type, _: 15, 9;
pub u16, sequence_number, _: 31, 16;
pub u32, timestamp, _: 63, 32;
pub u32, ssrc, _: 95, 64;
}
/// RTP Packet header as described in https://tools.ietf.org/html/rfc1889 Section 5.1
///
/// RTP, the real-time transport protocol, provides end-to-end network transport functions
/// suitable for applications transmitting real-time data, such as audio, video or
/// simulation data, over multicast or unicast network services.
#[cfg_attr(test, derive(Debug))]
pub struct RtpHeader(RtpHeaderInner<[u8; Self::LENGTH]>);
impl RtpHeader {
/// The minimum length of an RTP Header in bytes. This is the length of the header,
/// if there are no extension fields or csrc fields.
pub const LENGTH: usize = 12;
/// Parse and validate an RTP Packet from a buffer of bytes. Only packets that do
/// not contain extension fields or csrc fields will be parsed. This is done because
/// we do not currently use the aformentioned fields and a fixed size header is guaranteed
/// when headers to not contain them.
pub fn new(buf: &[u8]) -> Result<Self, RtpError> {
if buf.len() < Self::LENGTH {
return Err(RtpError::BufferTooShort);
}
let mut b = [0; Self::LENGTH];
b.copy_from_slice(&buf[..Self::LENGTH]);
let r = RtpHeaderInner(b);
if r.version() != 2 {
return Err(RtpError::UnsupportedVersion);
}
if r.extension() {
return Err(RtpError::UnsupportedFeature);
}
if r.csrc_count() > 0 {
return Err(RtpError::UnsupportedFeature);
}
Ok(Self(r))
}
/// The sequence number increments by one for each RTP data packet
/// sent, and may be used by the receiver to detect packet loss and
/// to restore packet sequence. The initial value of the sequence
/// number is random (unpredictable) to make known-plaintext attacks
/// on encryption more difficult, even if the source itself does not
/// encrypt, because the packets may flow through a translator that
/// does.
pub fn sequence_number(&self) -> u16 {
self.0.sequence_number()
}
/// The timestamp reflects the sampling instant of the first octet
/// in the RTP data packet. The sampling instant must be derived
/// from a clock that increments monotonically and linearly in time
/// to allow synchronization and jitter calculations (see Section
/// 6.3.1). The resolution of the clock must be sufficient for the
/// desired synchronization accuracy and for measuring packet
/// arrival jitter (one tick per video frame is typically not
/// sufficient). The clock frequency is dependent on the format of
/// data carried as payload and is specified statically in the
/// profile or payload format specification that defines the format,
/// or may be specified dynamically for payload formats defined
/// through non-RTP means. If RTP packets are generated
/// periodically, the nominal sampling instant as determined from
/// the sampling clock is to be used, not a reading of the system
/// clock. As an example, for fixed-rate audio the timestamp clock
/// would likely increment by one for each sampling period. If an
/// audio application reads blocks covering 160 sampling periods
/// from the input device, the timestamp would be increased by 160
/// for each such block, regardless of whether the block is
/// transmitted in a packet or dropped as silent.
///
/// The initial value of the timestamp is random, as for the sequence
/// number. Several consecutive RTP packets may have equal timestamps if
/// they are (logically) generated at once, e.g., belong to the same
/// video frame. Consecutive RTP packets may contain timestamps that are
/// not monotonic if the data is not transmitted in the order it was
/// sampled, as in the case of MPEG interpolated video frames. (The
/// sequence numbers of the packets as transmitted will still be
/// monotonic.)
pub fn timestamp(&self) -> u32 {
self.0.timestamp()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_rtp_headers() {
let raw = [128, 96, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 5];
let header = RtpHeader::new(&raw).expect("valid header");
assert_eq!(header.sequence_number(), 1);
assert_eq!(header.timestamp(), 0);
let raw_with_payload = [128, 96, 0, 2, 0, 0, 2, 128, 0, 0, 0, 0, 5, 0, 1, 2, 3, 4];
let header = RtpHeader::new(&raw_with_payload).expect("valid header");
assert_eq!(header.sequence_number(), 2);
assert_eq!(header.timestamp(), 640);
}
#[test]
fn test_invalid_rtp_headers() {
let raw_short = [128, 96];
let err = RtpHeader::new(&raw_short).expect_err("invalid header");
assert_eq!(err, RtpError::BufferTooShort);
let raw_unsupported_version =
[0b0100_0000, 96, 0, 2, 0, 0, 2, 128, 0, 0, 0, 0, 5, 0, 1, 2, 3, 4];
let err = RtpHeader::new(&raw_unsupported_version).expect_err("invalid header");
assert_eq!(err, RtpError::UnsupportedVersion);
let raw_unsupported_extension =
[0b1001_0000, 96, 0, 2, 0, 0, 2, 128, 0, 0, 0, 0, 5, 0, 1, 2, 3, 4];
let err = RtpHeader::new(&raw_unsupported_extension).expect_err("invalid header");
assert_eq!(err, RtpError::UnsupportedFeature);
let raw_includes_csrc = [0b1000_1000, 96, 0, 2, 0, 0, 2, 128, 0, 0, 0, 0, 5, 0, 1, 2, 3, 4];
let err = RtpHeader::new(&raw_includes_csrc).expect_err("invalid header");
assert_eq!(err, RtpError::UnsupportedFeature);
}
}