1use bytes::{Buf, Bytes, BytesMut};
28use moq_net::{BoundsExceeded, DecodeError, EncodeError, VarInt};
29
30const PROP_TIMESTAMP: u64 = 0x06;
32const PROP_TIMESCALE: u64 = 0x08;
33
34#[derive(Clone, Debug)]
36pub struct Frame {
37 pub timestamp: u64,
39
40 pub timescale: Option<u64>,
45
46 pub payload: Bytes,
48}
49
50#[derive(Debug, Clone, thiserror::Error)]
52#[non_exhaustive]
53pub enum Error {
54 #[error("loc frame missing required timestamp property")]
56 MissingTimestamp,
57
58 #[error("malformed loc properties")]
60 MalformedProperties,
61
62 #[error("short buffer")]
64 ShortBuffer,
65
66 #[error("value out of range: {0}")]
68 OutOfRange(#[from] BoundsExceeded),
69}
70
71impl From<DecodeError> for Error {
75 fn from(err: DecodeError) -> Self {
76 match err {
77 DecodeError::Short => Self::ShortBuffer,
78 _ => Self::MalformedProperties,
79 }
80 }
81}
82
83impl From<EncodeError> for Error {
84 fn from(err: EncodeError) -> Self {
85 match err {
86 EncodeError::Short => Self::ShortBuffer,
87 _ => Self::OutOfRange(BoundsExceeded),
88 }
89 }
90}
91
92pub fn decode(mut buf: Bytes) -> Result<Frame, Error> {
97 let properties_length: u64 = VarInt::decode_quic(&mut buf)?.into();
98 let properties_length: usize = properties_length.try_into().map_err(|_| Error::MalformedProperties)?;
99
100 if properties_length > buf.remaining() {
101 return Err(Error::MalformedProperties);
102 }
103
104 let mut props = buf.split_to(properties_length);
105
106 let mut timestamp: Option<u64> = None;
107 let mut timescale: Option<u64> = None;
108 let mut prev_type: u64 = 0;
109 let mut first = true;
110
111 while props.has_remaining() {
112 let delta: u64 = VarInt::decode_quic(&mut props)?.into();
113 let abs = if first {
114 first = false;
115 delta
116 } else {
117 prev_type.checked_add(delta).ok_or(Error::MalformedProperties)?
118 };
119 prev_type = abs;
120
121 if abs % 2 == 0 {
122 let value: u64 = VarInt::decode_quic(&mut props)?.into();
123 match abs {
124 PROP_TIMESTAMP => timestamp = Some(value),
125 PROP_TIMESCALE => {
126 if value == 0 {
127 return Err(Error::MalformedProperties);
128 }
129 timescale = Some(value);
130 }
131 _ => {}
132 }
133 } else {
134 let len: u64 = VarInt::decode_quic(&mut props)?.into();
135 let len: usize = len.try_into().map_err(|_| Error::MalformedProperties)?;
136 if len > props.remaining() {
137 return Err(Error::MalformedProperties);
138 }
139 props.advance(len);
142 }
143 }
144
145 let timestamp = timestamp.ok_or(Error::MissingTimestamp)?;
146
147 Ok(Frame {
148 timestamp,
149 timescale,
150 payload: buf,
151 })
152}
153
154pub fn encode(timestamp: u64, payload: &[u8]) -> Result<Bytes, Error> {
159 let mut props = BytesMut::with_capacity(16);
160 VarInt::try_from(PROP_TIMESTAMP)?.encode_quic(&mut props)?;
161 VarInt::try_from(timestamp)?.encode_quic(&mut props)?;
162
163 let mut out = BytesMut::with_capacity(props.len() + payload.len() + 8);
164 VarInt::try_from(props.len() as u64)?.encode_quic(&mut out)?;
165 out.extend_from_slice(&props);
166 out.extend_from_slice(payload);
167
168 Ok(out.freeze())
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 fn write_varint(buf: &mut BytesMut, value: u64) {
177 VarInt::try_from(value).unwrap().encode_quic(buf).unwrap();
178 }
179
180 #[test]
181 fn roundtrip() {
182 let payload = Bytes::from_static(b"hello world");
183 let encoded = encode(12345, &payload).unwrap();
184
185 let frame = decode(encoded).unwrap();
186 assert_eq!(frame.timestamp, 12345);
187 assert_eq!(frame.timescale, None);
188 assert_eq!(frame.payload, payload);
189 }
190
191 #[test]
192 fn decode_per_frame_timescale() {
193 let mut props = BytesMut::new();
195 write_varint(&mut props, PROP_TIMESTAMP);
196 write_varint(&mut props, 96_000);
197 write_varint(&mut props, PROP_TIMESCALE - PROP_TIMESTAMP); write_varint(&mut props, 48_000);
199
200 let mut frame = BytesMut::new();
201 write_varint(&mut frame, props.len() as u64);
202 frame.extend_from_slice(&props);
203 frame.extend_from_slice(b"payload");
204
205 let decoded = decode(frame.freeze()).unwrap();
206 assert_eq!(decoded.timestamp, 96_000);
207 assert_eq!(decoded.timescale, Some(48_000));
208 assert_eq!(decoded.payload, Bytes::from_static(b"payload"));
209 }
210
211 #[test]
212 fn decode_skips_video_config() {
213 let mut props = BytesMut::new();
215 write_varint(&mut props, PROP_TIMESTAMP);
216 write_varint(&mut props, 10);
217 write_varint(&mut props, 0x0d - PROP_TIMESTAMP); write_varint(&mut props, 3); props.extend_from_slice(&[0x01, 0x02, 0x03]);
220
221 let mut frame = BytesMut::new();
222 write_varint(&mut frame, props.len() as u64);
223 frame.extend_from_slice(&props);
224 frame.extend_from_slice(b"data");
225
226 let decoded = decode(frame.freeze()).unwrap();
227 assert_eq!(decoded.timestamp, 10);
228 assert_eq!(decoded.timescale, None);
229 assert_eq!(decoded.payload, Bytes::from_static(b"data"));
230 }
231
232 #[test]
233 fn decode_missing_timestamp_errors() {
234 let mut props = BytesMut::new();
236 write_varint(&mut props, PROP_TIMESCALE);
237 write_varint(&mut props, 1000);
238
239 let mut frame = BytesMut::new();
240 write_varint(&mut frame, props.len() as u64);
241 frame.extend_from_slice(&props);
242 frame.extend_from_slice(b"x");
243
244 assert!(matches!(decode(frame.freeze()), Err(Error::MissingTimestamp)));
245 }
246
247 #[test]
248 fn decode_empty_properties_errors() {
249 let mut frame = BytesMut::new();
250 write_varint(&mut frame, 0);
251 frame.extend_from_slice(b"payload");
252
253 assert!(matches!(decode(frame.freeze()), Err(Error::MissingTimestamp)));
254 }
255
256 #[test]
257 fn decode_rejects_zero_timescale() {
258 let mut props = BytesMut::new();
260 write_varint(&mut props, PROP_TIMESTAMP);
261 write_varint(&mut props, 10);
262 write_varint(&mut props, PROP_TIMESCALE - PROP_TIMESTAMP);
263 write_varint(&mut props, 0);
264
265 let mut frame = BytesMut::new();
266 write_varint(&mut frame, props.len() as u64);
267 frame.extend_from_slice(&props);
268 frame.extend_from_slice(b"x");
269
270 assert!(matches!(decode(frame.freeze()), Err(Error::MalformedProperties)));
271 }
272
273 #[test]
274 fn decode_overflowing_properties_length_errors() {
275 let mut frame = BytesMut::new();
276 write_varint(&mut frame, 100); frame.extend_from_slice(&[0x06]); assert!(matches!(decode(frame.freeze()), Err(Error::MalformedProperties)));
280 }
281}