1use alloc::vec::Vec;
39
40use crate::error::{Error, Result};
41use crate::version::EmsgVersion;
42
43pub const EMSG_BOX_TYPE: [u8; 4] = *b"emsg";
45pub const FULLBOX_HEADER_LEN: usize = 12;
48pub const EMSG_FLAGS: u32 = 0;
50pub const STRING_TERMINATOR: u8 = 0x00;
52
53const U32_LEN: usize = 4;
54const U64_LEN: usize = 8;
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
63#[cfg_attr(feature = "serde", derive(serde::Serialize))]
64#[non_exhaustive]
65pub enum PresentationTime {
66 Delta(u32),
69 Absolute(u64),
72}
73
74impl PresentationTime {
75 pub fn version(self) -> EmsgVersion {
77 match self {
78 PresentationTime::Delta(_) => EmsgVersion::SegmentRelative,
79 PresentationTime::Absolute(_) => EmsgVersion::RepresentationRelative,
80 }
81 }
82
83 pub fn name(&self) -> &'static str {
85 match self {
86 PresentationTime::Delta(_) => "presentation_time_delta",
87 PresentationTime::Absolute(_) => "presentation_time",
88 }
89 }
90}
91
92broadcast_common::impl_spec_display!(PresentationTime, Delta, Absolute);
93
94#[derive(Debug, Clone, PartialEq, Eq)]
102#[cfg_attr(feature = "serde", derive(serde::Serialize))]
103pub struct EmsgBox<'a> {
104 pub scheme_id_uri: &'a str,
107 pub value: &'a str,
110 pub timescale: u32,
113 pub presentation_time: PresentationTime,
116 pub event_duration: u32,
118 pub id: u32,
121 #[cfg_attr(feature = "serde", serde(skip))]
124 pub message_data: &'a [u8],
125}
126
127impl<'a> EmsgBox<'a> {
128 pub fn version(&self) -> EmsgVersion {
130 self.presentation_time.version()
131 }
132
133 pub fn is_scte35(&self) -> bool {
137 self.scheme_id_uri.starts_with(SCTE35_SCHEME_PREFIX)
138 }
139
140 pub fn serialized_len(&self) -> usize {
142 FULLBOX_HEADER_LEN + self.body_len()
143 }
144
145 fn body_len(&self) -> usize {
148 let strings = self.scheme_id_uri.len()
149 + 1 + self.value.len()
151 + 1; let ints = match self.presentation_time {
153 PresentationTime::Delta(_) => U32_LEN * 4,
155 PresentationTime::Absolute(_) => U32_LEN * 3 + U64_LEN,
157 };
158 strings + ints + self.message_data.len()
159 }
160
161 pub fn parse(data: &'a [u8]) -> Result<Self> {
164 if data.len() < FULLBOX_HEADER_LEN {
165 return Err(Error::BufferTooShort {
166 need: FULLBOX_HEADER_LEN,
167 have: data.len(),
168 what: "emsg FullBox header",
169 });
170 }
171
172 let size = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
173 let box_type = [data[4], data[5], data[6], data[7]];
174 if box_type != EMSG_BOX_TYPE {
175 return Err(Error::NotEmsg { found: box_type });
176 }
177 let version = data[8];
178 if (size as usize) < FULLBOX_HEADER_LEN {
182 return Err(Error::InvalidSize {
183 size,
184 reason: "size smaller than the FullBox header (0/1 large-size forms unsupported)",
185 });
186 }
187 if size as usize > data.len() {
188 return Err(Error::InvalidSize {
189 size,
190 reason: "size exceeds available bytes",
191 });
192 }
193
194 let version = EmsgVersion::from_u8(version).ok_or(Error::UnsupportedVersion { version })?;
195
196 let body = &data[FULLBOX_HEADER_LEN..size as usize];
198
199 match version {
200 EmsgVersion::SegmentRelative => Self::parse_v0(body),
201 EmsgVersion::RepresentationRelative => Self::parse_v1(body),
202 }
203 }
204
205 fn parse_v0(body: &'a [u8]) -> Result<Self> {
208 let (scheme_id_uri, rest) = parse_cstr(body, "scheme_id_uri")?;
209 let (value, rest) = parse_cstr(rest, "value")?;
210
211 let need = U32_LEN * 4;
212 if rest.len() < need {
213 return Err(Error::BufferTooShort {
214 need,
215 have: rest.len(),
216 what: "emsg v0 integer fields",
217 });
218 }
219 let timescale = read_u32(&rest[0..U32_LEN]);
220 let delta = read_u32(&rest[U32_LEN..U32_LEN * 2]);
221 let event_duration = read_u32(&rest[U32_LEN * 2..U32_LEN * 3]);
222 let id = read_u32(&rest[U32_LEN * 3..U32_LEN * 4]);
223 let message_data = &rest[need..];
224
225 Ok(EmsgBox {
226 scheme_id_uri,
227 value,
228 timescale,
229 presentation_time: PresentationTime::Delta(delta),
230 event_duration,
231 id,
232 message_data,
233 })
234 }
235
236 fn parse_v1(body: &'a [u8]) -> Result<Self> {
239 let need = U32_LEN + U64_LEN + U32_LEN + U32_LEN;
240 if body.len() < need {
241 return Err(Error::BufferTooShort {
242 need,
243 have: body.len(),
244 what: "emsg v1 integer fields",
245 });
246 }
247 let timescale = read_u32(&body[0..U32_LEN]);
248 let presentation_time = read_u64(&body[U32_LEN..U32_LEN + U64_LEN]);
249 let mut off = U32_LEN + U64_LEN;
250 let event_duration = read_u32(&body[off..off + U32_LEN]);
251 off += U32_LEN;
252 let id = read_u32(&body[off..off + U32_LEN]);
253 off += U32_LEN;
254
255 let (scheme_id_uri, rest) = parse_cstr(&body[off..], "scheme_id_uri")?;
256 let (value, message_data) = parse_cstr(rest, "value")?;
257
258 Ok(EmsgBox {
259 scheme_id_uri,
260 value,
261 timescale,
262 presentation_time: PresentationTime::Absolute(presentation_time),
263 event_duration,
264 id,
265 message_data,
266 })
267 }
268
269 pub fn serialize_into(&self, out: &mut [u8]) -> Result<usize> {
272 let total = self.serialized_len();
273 if out.len() < total {
274 return Err(Error::OutputBufferTooSmall {
275 need: total,
276 have: out.len(),
277 });
278 }
279 if total > u32::MAX as usize {
280 return Err(Error::FieldTooWide {
281 what: "size",
282 value: total as u64,
283 bits: 32,
284 });
285 }
286
287 out[0..U32_LEN].copy_from_slice(&(total as u32).to_be_bytes());
289 out[4..8].copy_from_slice(&EMSG_BOX_TYPE);
290 out[8] = self.version().to_u8();
291 out[9] = 0;
293 out[10] = 0;
294 out[11] = 0;
295
296 let mut off = FULLBOX_HEADER_LEN;
297 match self.presentation_time {
298 PresentationTime::Delta(delta) => {
299 off = write_cstr(out, off, self.scheme_id_uri);
301 off = write_cstr(out, off, self.value);
302 off = write_u32(out, off, self.timescale);
303 off = write_u32(out, off, delta);
304 off = write_u32(out, off, self.event_duration);
305 off = write_u32(out, off, self.id);
306 }
307 PresentationTime::Absolute(pt) => {
308 off = write_u32(out, off, self.timescale);
310 off = write_u64(out, off, pt);
311 off = write_u32(out, off, self.event_duration);
312 off = write_u32(out, off, self.id);
313 off = write_cstr(out, off, self.scheme_id_uri);
314 off = write_cstr(out, off, self.value);
315 }
316 }
317 out[off..off + self.message_data.len()].copy_from_slice(self.message_data);
318 off += self.message_data.len();
319 debug_assert_eq!(off, total);
320 Ok(off)
321 }
322
323 pub fn to_vec(&self) -> Result<Vec<u8>> {
325 let mut out = alloc::vec![0u8; self.serialized_len()];
326 self.serialize_into(&mut out)?;
327 Ok(out)
328 }
329}
330
331pub const SCTE35_SCHEME_PREFIX: &str = "urn:scte:scte35";
337
338fn parse_cstr<'a>(data: &'a [u8], field: &'static str) -> Result<(&'a str, &'a [u8])> {
341 let term = data
342 .iter()
343 .position(|&b| b == STRING_TERMINATOR)
344 .ok_or(Error::InvalidString {
345 field,
346 reason: "missing null terminator",
347 })?;
348 let s = core::str::from_utf8(&data[..term]).map_err(|_| Error::InvalidString {
349 field,
350 reason: "invalid UTF-8",
351 })?;
352 Ok((s, &data[term + 1..]))
353}
354
355fn write_cstr(out: &mut [u8], off: usize, s: &str) -> usize {
357 let bytes = s.as_bytes();
358 out[off..off + bytes.len()].copy_from_slice(bytes);
359 let term = off + bytes.len();
360 out[term] = STRING_TERMINATOR;
361 term + 1
362}
363
364fn read_u32(b: &[u8]) -> u32 {
365 u32::from_be_bytes([b[0], b[1], b[2], b[3]])
366}
367
368fn read_u64(b: &[u8]) -> u64 {
369 u64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]])
370}
371
372fn write_u32(out: &mut [u8], off: usize, v: u32) -> usize {
373 out[off..off + U32_LEN].copy_from_slice(&v.to_be_bytes());
374 off + U32_LEN
375}
376
377fn write_u64(out: &mut [u8], off: usize, v: u64) -> usize {
378 out[off..off + U64_LEN].copy_from_slice(&v.to_be_bytes());
379 off + U64_LEN
380}
381
382#[cfg(test)]
383mod tests {
384 use super::*;
385 use alloc::vec;
386
387 #[test]
389 fn v0_exact_wire_bytes_round_trip() {
390 let msg = [0xDEu8, 0xAD, 0xBE, 0xEF];
391 let b = EmsgBox {
392 scheme_id_uri: "urn:example:scheme",
393 value: "",
394 timescale: 90_000,
395 presentation_time: PresentationTime::Delta(0x0001_0203),
396 event_duration: 0xFFFF_FFFF,
397 id: 0x0000_002A,
398 message_data: &msg,
399 };
400 assert_eq!(b.version(), EmsgVersion::SegmentRelative);
401
402 let out = b.to_vec().unwrap();
403
404 assert_eq!(&out[0..4], &(out.len() as u32).to_be_bytes());
406 assert_eq!(&out[4..8], b"emsg");
407 assert_eq!(out[8], 0); assert_eq!(&out[9..12], &[0, 0, 0]); let scheme = b"urn:example:scheme\x00";
412 assert_eq!(&out[12..12 + scheme.len()], scheme);
413 let mut off = 12 + scheme.len();
414 assert_eq!(out[off], 0x00); off += 1;
416 assert_eq!(&out[off..off + 4], &90_000u32.to_be_bytes());
417 off += 4;
418 assert_eq!(&out[off..off + 4], &0x0001_0203u32.to_be_bytes()); off += 4;
420 assert_eq!(&out[off..off + 4], &0xFFFF_FFFFu32.to_be_bytes());
421 off += 4;
422 assert_eq!(&out[off..off + 4], &0x0000_002Au32.to_be_bytes());
423 off += 4;
424 assert_eq!(&out[off..], &msg);
425
426 assert_eq!(EmsgBox::parse(&out).unwrap(), b);
427 }
428
429 #[test]
431 fn v1_exact_wire_bytes_round_trip() {
432 let msg = [0x01u8, 0x02];
433 let b = EmsgBox {
434 scheme_id_uri: "urn:scte:scte35:2013:bin",
435 value: "1001",
436 timescale: 1000,
437 presentation_time: PresentationTime::Absolute(0x0102_0304_0506_0708),
438 event_duration: 250,
439 id: 7,
440 message_data: &msg,
441 };
442 assert_eq!(b.version(), EmsgVersion::RepresentationRelative);
443 assert!(b.is_scte35());
444
445 let out = b.to_vec().unwrap();
446
447 assert_eq!(out[8], 1); let mut off = 12;
450 assert_eq!(&out[off..off + 4], &1000u32.to_be_bytes());
451 off += 4;
452 assert_eq!(&out[off..off + 8], &0x0102_0304_0506_0708u64.to_be_bytes());
453 off += 8;
454 assert_eq!(&out[off..off + 4], &250u32.to_be_bytes());
455 off += 4;
456 assert_eq!(&out[off..off + 4], &7u32.to_be_bytes());
457 off += 4;
458 let scheme = b"urn:scte:scte35:2013:bin\x00";
460 assert_eq!(&out[off..off + scheme.len()], scheme);
461 off += scheme.len();
462 let value = b"1001\x00";
463 assert_eq!(&out[off..off + value.len()], value);
464 off += value.len();
465 assert_eq!(&out[off..], &msg);
466
467 assert_eq!(EmsgBox::parse(&out).unwrap(), b);
468 }
469
470 #[test]
473 fn v0_v1_field_order_differs_but_both_round_trip() {
474 let msg = [0xAAu8, 0xBB];
475 let v0 = EmsgBox {
476 scheme_id_uri: "s",
477 value: "v",
478 timescale: 0x1111_1111,
479 presentation_time: PresentationTime::Delta(0x2222_2222),
480 event_duration: 0x3333_3333,
481 id: 0x4444_4444,
482 message_data: &msg,
483 };
484 let v1 = EmsgBox {
485 scheme_id_uri: "s",
486 value: "v",
487 timescale: 0x1111_1111,
488 presentation_time: PresentationTime::Absolute(0x2222_2222),
489 event_duration: 0x3333_3333,
490 id: 0x4444_4444,
491 message_data: &msg,
492 };
493
494 let o0 = v0.to_vec().unwrap();
495 let o1 = v1.to_vec().unwrap();
496
497 assert_eq!(&o0[12..14], b"s\x00");
499 assert_eq!(&o1[12..16], &0x1111_1111u32.to_be_bytes());
500 assert_ne!(o0, o1);
502
503 assert_eq!(EmsgBox::parse(&o0).unwrap(), v0);
505 assert_eq!(EmsgBox::parse(&o1).unwrap(), v1);
506 }
507
508 #[test]
510 fn mutating_a_field_changes_wire_bytes() {
511 let mk = |id: u32| EmsgBox {
512 scheme_id_uri: "urn:x",
513 value: "",
514 timescale: 48_000,
515 presentation_time: PresentationTime::Delta(10),
516 event_duration: 20,
517 id,
518 message_data: &[],
519 };
520 let a = mk(1).to_vec().unwrap();
521 let b = mk(2).to_vec().unwrap();
522 assert_ne!(a, b);
523 assert_eq!(a.len(), b.len());
525 }
526
527 #[test]
528 fn rejects_non_emsg_type() {
529 let mut data = vec![0u8; FULLBOX_HEADER_LEN];
530 data[0..4].copy_from_slice(&(FULLBOX_HEADER_LEN as u32).to_be_bytes());
531 data[4..8].copy_from_slice(b"moof");
532 assert!(matches!(EmsgBox::parse(&data), Err(Error::NotEmsg { .. })));
533 }
534
535 #[test]
536 fn rejects_unsupported_version() {
537 let b = EmsgBox {
538 scheme_id_uri: "x",
539 value: "",
540 timescale: 1,
541 presentation_time: PresentationTime::Delta(0),
542 event_duration: 0,
543 id: 0,
544 message_data: &[],
545 };
546 let mut out = b.to_vec().unwrap();
547 out[8] = 9; assert!(matches!(
549 EmsgBox::parse(&out),
550 Err(Error::UnsupportedVersion { version: 9 })
551 ));
552 }
553
554 #[test]
555 fn rejects_size_overrun() {
556 let mut data = vec![0u8; FULLBOX_HEADER_LEN];
557 data[0..4].copy_from_slice(&0xFFFF_FFFFu32.to_be_bytes()); data[4..8].copy_from_slice(b"emsg");
559 assert!(matches!(
560 EmsgBox::parse(&data),
561 Err(Error::InvalidSize { .. })
562 ));
563 }
564
565 #[test]
566 fn rejects_missing_terminator() {
567 let mut data = vec![0u8; 16];
569 let size = data.len() as u32;
570 data[0..4].copy_from_slice(&size.to_be_bytes());
571 data[4..8].copy_from_slice(b"emsg");
572 data[8] = 0; data[12..16].copy_from_slice(b"abcd"); assert!(matches!(
575 EmsgBox::parse(&data),
576 Err(Error::InvalidString { .. })
577 ));
578 }
579
580 #[test]
581 fn is_scte35_recognises_prefix() {
582 let b = EmsgBox {
583 scheme_id_uri: "urn:scte:scte35:2013:bin",
584 value: "",
585 timescale: 1,
586 presentation_time: PresentationTime::Delta(0),
587 event_duration: 0,
588 id: 0,
589 message_data: &[],
590 };
591 assert!(b.is_scte35());
592 let nb = EmsgBox {
593 scheme_id_uri: "urn:mpeg:dash:event:2012",
594 ..b.clone()
595 };
596 assert!(!nb.is_scte35());
597 }
598}