Skip to content

Commit 08189da

Browse files
authored
Added PlayoutDelayExtension to control receiver jitter buffer size (webrtc-rs#573)
1 parent 937b1f5 commit 08189da

File tree

4 files changed

+128
-0
lines changed

4 files changed

+128
-0
lines changed

rtp/src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ pub enum Error {
5454
HeaderExtensionPayloadNot32BitWords,
5555
#[error("audio level overflow")]
5656
AudioLevelOverflow,
57+
#[error("playout delay overflow")]
58+
PlayoutDelayOverflow,
5759
#[error("payload is not large enough")]
5860
PayloadIsNotLargeEnough,
5961
#[error("STAP-A declared size({0}) is larger than buffer({1})")]

rtp/src/extension/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ use util::{Marshal, MarshalSize};
55

66
pub mod abs_send_time_extension;
77
pub mod audio_level_extension;
8+
pub mod playout_delay_extension;
89
pub mod transport_cc_extension;
910
pub mod video_orientation_extension;
1011

1112
/// A generic RTP header extension.
1213
pub enum HeaderExtension {
1314
AbsSendTime(abs_send_time_extension::AbsSendTimeExtension),
1415
AudioLevel(audio_level_extension::AudioLevelExtension),
16+
PlayoutDelay(playout_delay_extension::PlayoutDelayExtension),
1517
TransportCc(transport_cc_extension::TransportCcExtension),
1618
VideoOrientation(video_orientation_extension::VideoOrientationExtension),
1719

@@ -29,6 +31,7 @@ impl HeaderExtension {
2931
match self {
3032
AbsSendTime(_) => "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time".into(),
3133
AudioLevel(_) => "urn:ietf:params:rtp-hdrext:ssrc-audio-level".into(),
34+
PlayoutDelay(_) => "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay".into(),
3235
TransportCc(_) => {
3336
"http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01".into()
3437
}
@@ -56,6 +59,7 @@ impl MarshalSize for HeaderExtension {
5659
match self {
5760
AbsSendTime(ext) => ext.marshal_size(),
5861
AudioLevel(ext) => ext.marshal_size(),
62+
PlayoutDelay(ext) => ext.marshal_size(),
5963
TransportCc(ext) => ext.marshal_size(),
6064
VideoOrientation(ext) => ext.marshal_size(),
6165
Custom { extension: ext, .. } => ext.marshal_size(),
@@ -69,6 +73,7 @@ impl Marshal for HeaderExtension {
6973
match self {
7074
AbsSendTime(ext) => ext.marshal_to(buf),
7175
AudioLevel(ext) => ext.marshal_to(buf),
76+
PlayoutDelay(ext) => ext.marshal_to(buf),
7277
TransportCc(ext) => ext.marshal_to(buf),
7378
VideoOrientation(ext) => ext.marshal_to(buf),
7479
Custom { extension: ext, .. } => ext.marshal_to(buf),
@@ -83,6 +88,7 @@ impl fmt::Debug for HeaderExtension {
8388
match self {
8489
AbsSendTime(ext) => f.debug_tuple("AbsSendTime").field(ext).finish(),
8590
AudioLevel(ext) => f.debug_tuple("AudioLevel").field(ext).finish(),
91+
PlayoutDelay(ext) => f.debug_tuple("PlayoutDelay").field(ext).finish(),
8692
TransportCc(ext) => f.debug_tuple("TransportCc").field(ext).finish(),
8793
VideoOrientation(ext) => f.debug_tuple("VideoOrientation").field(ext).finish(),
8894
Custom { uri, extension: _ } => f.debug_struct("Custom").field("uri", uri).finish(),
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#[cfg(test)]
2+
mod playout_delay_extension_test;
3+
4+
use bytes::BufMut;
5+
use util::marshal::{Marshal, MarshalSize, Unmarshal};
6+
7+
use crate::error::Error;
8+
9+
pub const PLAYOUT_DELAY_EXTENSION_SIZE: usize = 3;
10+
pub const PLAYOUT_DELAY_MAX_VALUE: u16 = (1 << 12) - 1;
11+
12+
/// PlayoutDelayExtension is an extension payload format described in
13+
/// http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
14+
/// 0 1 2 3
15+
/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
16+
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17+
/// | ID | len=2 | MIN delay | MAX delay |
18+
/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19+
#[derive(PartialEq, Eq, Debug, Default, Copy, Clone)]
20+
pub struct PlayoutDelayExtension {
21+
pub min_delay: u16,
22+
pub max_delay: u16,
23+
}
24+
25+
impl Unmarshal for PlayoutDelayExtension {
26+
/// Unmarshal parses the passed byte slice and stores the result in the members.
27+
fn unmarshal<B>(buf: &mut B) -> util::Result<Self>
28+
where
29+
Self: Sized,
30+
B: bytes::Buf,
31+
{
32+
if buf.remaining() < PLAYOUT_DELAY_EXTENSION_SIZE {
33+
return Err(Error::ErrBufferTooSmall.into());
34+
}
35+
36+
let b0 = buf.get_u8();
37+
let b1 = buf.get_u8();
38+
let b2 = buf.get_u8();
39+
40+
let min_delay = u16::from_be_bytes([b0, b1]) >> 4;
41+
let max_delay = u16::from_be_bytes([b1, b2]) & 0x0FFF;
42+
43+
Ok(PlayoutDelayExtension {
44+
min_delay,
45+
max_delay,
46+
})
47+
}
48+
}
49+
50+
impl MarshalSize for PlayoutDelayExtension {
51+
/// MarshalSize returns the size of the PlayoutDelayExtension once marshaled.
52+
fn marshal_size(&self) -> usize {
53+
PLAYOUT_DELAY_EXTENSION_SIZE
54+
}
55+
}
56+
57+
impl Marshal for PlayoutDelayExtension {
58+
/// MarshalTo serializes the members to buffer
59+
fn marshal_to(&self, mut buf: &mut [u8]) -> util::Result<usize> {
60+
if buf.remaining_mut() < PLAYOUT_DELAY_EXTENSION_SIZE {
61+
return Err(Error::ErrBufferTooSmall.into());
62+
}
63+
if self.min_delay > PLAYOUT_DELAY_MAX_VALUE || self.max_delay > PLAYOUT_DELAY_MAX_VALUE {
64+
return Err(Error::PlayoutDelayOverflow.into());
65+
}
66+
67+
buf.put_u8((self.min_delay >> 4) as u8);
68+
buf.put_u8(((self.min_delay << 4) as u8) | (self.max_delay >> 8) as u8);
69+
buf.put_u8(self.max_delay as u8);
70+
71+
Ok(PLAYOUT_DELAY_EXTENSION_SIZE)
72+
}
73+
}
74+
75+
impl PlayoutDelayExtension {
76+
pub fn new(min_delay: u16, max_delay: u16) -> Self {
77+
PlayoutDelayExtension {
78+
min_delay,
79+
max_delay,
80+
}
81+
}
82+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use bytes::BytesMut;
2+
3+
use crate::error::Result;
4+
5+
use super::*;
6+
7+
#[test]
8+
fn test_playout_delay_extension_roundtrip() -> Result<()> {
9+
let test = PlayoutDelayExtension {
10+
max_delay: 2345,
11+
min_delay: 1234,
12+
};
13+
14+
let mut raw = BytesMut::with_capacity(test.marshal_size());
15+
raw.resize(test.marshal_size(), 0);
16+
test.marshal_to(&mut raw)?;
17+
let raw = raw.freeze();
18+
let buf = &mut raw.clone();
19+
let out = PlayoutDelayExtension::unmarshal(buf)?;
20+
assert_eq!(test, out);
21+
22+
Ok(())
23+
}
24+
25+
#[test]
26+
fn test_playout_delay_value_overflow() -> Result<()> {
27+
let test = PlayoutDelayExtension {
28+
max_delay: u16::MAX,
29+
min_delay: u16::MAX,
30+
};
31+
32+
let mut dst = BytesMut::with_capacity(test.marshal_size());
33+
dst.resize(test.marshal_size(), 0);
34+
let result = test.marshal_to(&mut dst);
35+
assert!(result.is_err());
36+
37+
Ok(())
38+
}

0 commit comments

Comments
 (0)