1use bytes::{Buf, Bytes, BytesMut};
29use moq_net::{BoundsExceeded, DecodeError, EncodeError, VarInt};
30
31const PROP_TIMESCALE: u64 = 0x08;
33const PROP_TIMESTAMP: u64 = 0x10;
34
35const PROP_TIMESTAMP_DRAFT03: u64 = 0x06;
43
44#[derive(Clone, Debug)]
46pub struct Frame {
47 pub timestamp: u64,
49
50 pub timescale: Option<u64>,
55
56 pub payload: Bytes,
58}
59
60#[derive(Debug, Clone, thiserror::Error)]
62#[non_exhaustive]
63pub enum Error {
64 #[error("loc frame missing required timestamp property")]
66 MissingTimestamp,
67
68 #[error("malformed loc properties")]
70 MalformedProperties,
71
72 #[error("short buffer")]
74 ShortBuffer,
75
76 #[error("value out of range: {0}")]
78 OutOfRange(#[from] BoundsExceeded),
79}
80
81impl From<DecodeError> for Error {
85 fn from(err: DecodeError) -> Self {
86 match err {
87 DecodeError::Short => Self::ShortBuffer,
88 _ => Self::MalformedProperties,
89 }
90 }
91}
92
93impl From<EncodeError> for Error {
94 fn from(err: EncodeError) -> Self {
95 match err {
96 EncodeError::Short => Self::ShortBuffer,
97 _ => Self::OutOfRange(BoundsExceeded),
98 }
99 }
100}
101
102pub fn decode(mut buf: Bytes) -> Result<Frame, Error> {
107 let properties_length: u64 = VarInt::decode_quic(&mut buf)?.into();
108 let properties_length: usize = properties_length.try_into().map_err(|_| Error::MalformedProperties)?;
109
110 if properties_length > buf.remaining() {
111 return Err(Error::MalformedProperties);
112 }
113
114 let mut props = buf.split_to(properties_length);
115
116 let mut timestamp: Option<u64> = None;
117 let mut timescale: Option<u64> = None;
118 let mut prev_type: u64 = 0;
119 let mut first = true;
120
121 while props.has_remaining() {
122 let delta: u64 = VarInt::decode_quic(&mut props)?.into();
123 let abs = if first {
124 first = false;
125 delta
126 } else {
127 prev_type.checked_add(delta).ok_or(Error::MalformedProperties)?
128 };
129 prev_type = abs;
130
131 if abs % 2 == 0 {
132 let value: u64 = VarInt::decode_quic(&mut props)?.into();
133 match abs {
134 PROP_TIMESTAMP | PROP_TIMESTAMP_DRAFT03 => timestamp = Some(value),
135 PROP_TIMESCALE => {
136 if value == 0 {
137 return Err(Error::MalformedProperties);
138 }
139 timescale = Some(value);
140 }
141 _ => {}
142 }
143 } else {
144 let len: u64 = VarInt::decode_quic(&mut props)?.into();
145 let len: usize = len.try_into().map_err(|_| Error::MalformedProperties)?;
146 if len > props.remaining() {
147 return Err(Error::MalformedProperties);
148 }
149 props.advance(len);
152 }
153 }
154
155 let timestamp = timestamp.ok_or(Error::MissingTimestamp)?;
156
157 Ok(Frame {
158 timestamp,
159 timescale,
160 payload: buf,
161 })
162}
163
164pub fn encode(timestamp: u64, payload: &[u8]) -> Result<Bytes, Error> {
169 let mut props = BytesMut::with_capacity(16);
170 VarInt::try_from(PROP_TIMESTAMP)?.encode_quic(&mut props)?;
171 VarInt::try_from(timestamp)?.encode_quic(&mut props)?;
172
173 let mut out = BytesMut::with_capacity(props.len() + payload.len() + 8);
174 VarInt::try_from(props.len() as u64)?.encode_quic(&mut out)?;
175 out.extend_from_slice(&props);
176 out.extend_from_slice(payload);
177
178 Ok(out.freeze())
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 fn write_varint(buf: &mut BytesMut, value: u64) {
187 VarInt::try_from(value).unwrap().encode_quic(buf).unwrap();
188 }
189
190 #[test]
191 fn roundtrip() {
192 let payload = Bytes::from_static(b"hello world");
193 let encoded = encode(12345, &payload).unwrap();
194
195 let frame = decode(encoded).unwrap();
196 assert_eq!(frame.timestamp, 12345);
197 assert_eq!(frame.timescale, None);
198 assert_eq!(frame.payload, payload);
199 }
200
201 #[test]
202 fn decode_per_frame_timescale() {
203 let mut props = BytesMut::new();
205 write_varint(&mut props, PROP_TIMESCALE);
206 write_varint(&mut props, 48_000);
207 write_varint(&mut props, PROP_TIMESTAMP - PROP_TIMESCALE); write_varint(&mut props, 96_000);
209
210 let mut frame = BytesMut::new();
211 write_varint(&mut frame, props.len() as u64);
212 frame.extend_from_slice(&props);
213 frame.extend_from_slice(b"payload");
214
215 let decoded = decode(frame.freeze()).unwrap();
216 assert_eq!(decoded.timestamp, 96_000);
217 assert_eq!(decoded.timescale, Some(48_000));
218 assert_eq!(decoded.payload, Bytes::from_static(b"payload"));
219 }
220
221 #[test]
222 fn decode_skips_video_config() {
223 let mut props = BytesMut::new();
225 write_varint(&mut props, 0x0d);
226 write_varint(&mut props, 3); props.extend_from_slice(&[0x01, 0x02, 0x03]);
228 write_varint(&mut props, PROP_TIMESTAMP - 0x0d); write_varint(&mut props, 10);
230
231 let mut frame = BytesMut::new();
232 write_varint(&mut frame, props.len() as u64);
233 frame.extend_from_slice(&props);
234 frame.extend_from_slice(b"data");
235
236 let decoded = decode(frame.freeze()).unwrap();
237 assert_eq!(decoded.timestamp, 10);
238 assert_eq!(decoded.timescale, None);
239 assert_eq!(decoded.payload, Bytes::from_static(b"data"));
240 }
241
242 #[test]
243 fn decode_missing_timestamp_errors() {
244 let mut props = BytesMut::new();
246 write_varint(&mut props, PROP_TIMESCALE);
247 write_varint(&mut props, 1000);
248
249 let mut frame = BytesMut::new();
250 write_varint(&mut frame, props.len() as u64);
251 frame.extend_from_slice(&props);
252 frame.extend_from_slice(b"x");
253
254 assert!(matches!(decode(frame.freeze()), Err(Error::MissingTimestamp)));
255 }
256
257 #[test]
258 fn decode_empty_properties_errors() {
259 let mut frame = BytesMut::new();
260 write_varint(&mut frame, 0);
261 frame.extend_from_slice(b"payload");
262
263 assert!(matches!(decode(frame.freeze()), Err(Error::MissingTimestamp)));
264 }
265
266 #[test]
267 fn decode_rejects_zero_timescale() {
268 let mut props = BytesMut::new();
270 write_varint(&mut props, PROP_TIMESCALE);
271 write_varint(&mut props, 0);
272 write_varint(&mut props, PROP_TIMESTAMP - PROP_TIMESCALE);
273 write_varint(&mut props, 10);
274
275 let mut frame = BytesMut::new();
276 write_varint(&mut frame, props.len() as u64);
277 frame.extend_from_slice(&props);
278 frame.extend_from_slice(b"x");
279
280 assert!(matches!(decode(frame.freeze()), Err(Error::MalformedProperties)));
281 }
282
283 #[test]
284 fn decode_overflowing_properties_length_errors() {
285 let mut frame = BytesMut::new();
286 write_varint(&mut frame, 100); frame.extend_from_slice(&[0x10]); assert!(matches!(decode(frame.freeze()), Err(Error::MalformedProperties)));
290 }
291
292 #[test]
293 fn decode_accepts_draft03_timestamp() {
294 let mut props = BytesMut::new();
296 write_varint(&mut props, PROP_TIMESTAMP_DRAFT03);
297 write_varint(&mut props, 4242);
298
299 let mut frame = BytesMut::new();
300 write_varint(&mut frame, props.len() as u64);
301 frame.extend_from_slice(&props);
302 frame.extend_from_slice(b"payload");
303
304 let decoded = decode(frame.freeze()).unwrap();
305 assert_eq!(decoded.timestamp, 4242);
306 assert_eq!(decoded.payload, Bytes::from_static(b"payload"));
307 }
308
309 #[test]
310 fn encode_uses_draft04_timestamp() {
311 let encoded = encode(7, b"x").unwrap();
313 let props_len = encoded[0] as usize;
314 assert_eq!(encoded[1..=props_len][0], PROP_TIMESTAMP as u8);
315 }
316}