use std::borrow::Cow;
use std::time::Duration;
use crate::{
Path,
coding::{Decode, DecodeError, Encode, EncodeError},
};
use super::{Message, Version};
#[derive(Clone, Debug)]
pub struct Track<'a> {
pub broadcast: Path<'a>,
pub track: Cow<'a, str>,
}
impl Message for Track<'_> {
fn decode_msg<R: bytes::Buf>(r: &mut R, version: Version) -> Result<Self, DecodeError> {
if !version.has_track_stream() {
return Err(DecodeError::Version);
}
Ok(Self {
broadcast: Path::decode(r, version)?,
track: Cow::<str>::decode(r, version)?,
})
}
fn encode_msg<W: bytes::BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError> {
if !version.has_track_stream() {
return Err(EncodeError::Version);
}
self.broadcast.encode(w, version)?;
self.track.encode(w, version)?;
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TrackInfo {
pub priority: u8,
pub ordered: bool,
pub max_latency: Duration,
pub timescale: u64,
}
impl Message for TrackInfo {
fn decode_msg<R: bytes::Buf>(r: &mut R, version: Version) -> Result<Self, DecodeError> {
if !version.has_track_stream() {
return Err(DecodeError::Version);
}
let priority = u8::decode(r, version)?;
let ordered = u8::decode(r, version)? != 0;
let max_latency = Duration::decode(r, version)?;
let timescale = u64::decode(r, version)?;
if timescale == 0 {
return Err(DecodeError::InvalidValue);
}
Ok(Self {
priority,
ordered,
max_latency,
timescale,
})
}
fn encode_msg<W: bytes::BufMut>(&self, w: &mut W, version: Version) -> Result<(), EncodeError> {
if !version.has_track_stream() {
return Err(EncodeError::Version);
}
if self.timescale == 0 {
return Err(EncodeError::InvalidState);
}
self.priority.encode(w, version)?;
(self.ordered as u8).encode(w, version)?;
self.max_latency.encode(w, version)?;
self.timescale.encode(w, version)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::coding::{Decode, Encode};
use bytes::BytesMut;
fn roundtrip(info: &TrackInfo) -> TrackInfo {
let mut buf = BytesMut::new();
info.encode(&mut buf, Version::Lite05Wip).unwrap();
let mut slice = &buf[..];
let decoded = TrackInfo::decode(&mut slice, Version::Lite05Wip).unwrap();
assert!(bytes::Buf::remaining(&slice) == 0, "trailing bytes after decode");
decoded
}
#[test]
fn track_info_roundtrip() {
let info = TrackInfo {
priority: 7,
ordered: true,
max_latency: Duration::from_millis(2500),
timescale: 1000,
};
assert_eq!(roundtrip(&info), info);
}
#[test]
fn rejects_zero_timescale() {
let mut buf = BytesMut::new();
assert!(matches!(
TrackInfo {
priority: 0,
ordered: false,
max_latency: Duration::ZERO,
timescale: 0,
}
.encode(&mut buf, Version::Lite05Wip),
Err(EncodeError::InvalidState)
));
}
#[test]
fn track_roundtrip() {
let track = Track {
broadcast: Path::new("room/1"),
track: Cow::Borrowed("video"),
};
let mut buf = BytesMut::new();
track.encode(&mut buf, Version::Lite05Wip).unwrap();
let mut slice = &buf[..];
let decoded = Track::decode(&mut slice, Version::Lite05Wip).unwrap();
assert!(bytes::Buf::remaining(&slice) == 0, "trailing bytes after decode");
assert_eq!(decoded.broadcast, track.broadcast);
assert_eq!(decoded.track, track.track);
}
#[test]
fn rejects_before_lite05() {
let mut buf = BytesMut::new();
assert!(matches!(
TrackInfo {
priority: 0,
ordered: false,
max_latency: Duration::ZERO,
timescale: 1000,
}
.encode(&mut buf, Version::Lite04),
Err(EncodeError::Version)
));
}
}