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
92dvb_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";
334
335fn parse_cstr<'a>(data: &'a [u8], field: &'static str) -> Result<(&'a str, &'a [u8])> {
338 let term = data
339 .iter()
340 .position(|&b| b == STRING_TERMINATOR)
341 .ok_or(Error::InvalidString {
342 field,
343 reason: "missing null terminator",
344 })?;
345 let s = core::str::from_utf8(&data[..term]).map_err(|_| Error::InvalidString {
346 field,
347 reason: "invalid UTF-8",
348 })?;
349 Ok((s, &data[term + 1..]))
350}
351
352fn write_cstr(out: &mut [u8], off: usize, s: &str) -> usize {
354 let bytes = s.as_bytes();
355 out[off..off + bytes.len()].copy_from_slice(bytes);
356 let term = off + bytes.len();
357 out[term] = STRING_TERMINATOR;
358 term + 1
359}
360
361fn read_u32(b: &[u8]) -> u32 {
362 u32::from_be_bytes([b[0], b[1], b[2], b[3]])
363}
364
365fn read_u64(b: &[u8]) -> u64 {
366 u64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]])
367}
368
369fn write_u32(out: &mut [u8], off: usize, v: u32) -> usize {
370 out[off..off + U32_LEN].copy_from_slice(&v.to_be_bytes());
371 off + U32_LEN
372}
373
374fn write_u64(out: &mut [u8], off: usize, v: u64) -> usize {
375 out[off..off + U64_LEN].copy_from_slice(&v.to_be_bytes());
376 off + U64_LEN
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382 use alloc::vec;
383
384 #[test]
386 fn v0_exact_wire_bytes_round_trip() {
387 let msg = [0xDEu8, 0xAD, 0xBE, 0xEF];
388 let b = EmsgBox {
389 scheme_id_uri: "urn:example:scheme",
390 value: "",
391 timescale: 90_000,
392 presentation_time: PresentationTime::Delta(0x0001_0203),
393 event_duration: 0xFFFF_FFFF,
394 id: 0x0000_002A,
395 message_data: &msg,
396 };
397 assert_eq!(b.version(), EmsgVersion::SegmentRelative);
398
399 let out = b.to_vec().unwrap();
400
401 assert_eq!(&out[0..4], &(out.len() as u32).to_be_bytes());
403 assert_eq!(&out[4..8], b"emsg");
404 assert_eq!(out[8], 0); assert_eq!(&out[9..12], &[0, 0, 0]); let scheme = b"urn:example:scheme\x00";
409 assert_eq!(&out[12..12 + scheme.len()], scheme);
410 let mut off = 12 + scheme.len();
411 assert_eq!(out[off], 0x00); off += 1;
413 assert_eq!(&out[off..off + 4], &90_000u32.to_be_bytes());
414 off += 4;
415 assert_eq!(&out[off..off + 4], &0x0001_0203u32.to_be_bytes()); off += 4;
417 assert_eq!(&out[off..off + 4], &0xFFFF_FFFFu32.to_be_bytes());
418 off += 4;
419 assert_eq!(&out[off..off + 4], &0x0000_002Au32.to_be_bytes());
420 off += 4;
421 assert_eq!(&out[off..], &msg);
422
423 assert_eq!(EmsgBox::parse(&out).unwrap(), b);
424 }
425
426 #[test]
428 fn v1_exact_wire_bytes_round_trip() {
429 let msg = [0x01u8, 0x02];
430 let b = EmsgBox {
431 scheme_id_uri: "urn:scte:scte35:2013:bin",
432 value: "1001",
433 timescale: 1000,
434 presentation_time: PresentationTime::Absolute(0x0102_0304_0506_0708),
435 event_duration: 250,
436 id: 7,
437 message_data: &msg,
438 };
439 assert_eq!(b.version(), EmsgVersion::RepresentationRelative);
440 assert!(b.is_scte35());
441
442 let out = b.to_vec().unwrap();
443
444 assert_eq!(out[8], 1); let mut off = 12;
447 assert_eq!(&out[off..off + 4], &1000u32.to_be_bytes());
448 off += 4;
449 assert_eq!(&out[off..off + 8], &0x0102_0304_0506_0708u64.to_be_bytes());
450 off += 8;
451 assert_eq!(&out[off..off + 4], &250u32.to_be_bytes());
452 off += 4;
453 assert_eq!(&out[off..off + 4], &7u32.to_be_bytes());
454 off += 4;
455 let scheme = b"urn:scte:scte35:2013:bin\x00";
457 assert_eq!(&out[off..off + scheme.len()], scheme);
458 off += scheme.len();
459 let value = b"1001\x00";
460 assert_eq!(&out[off..off + value.len()], value);
461 off += value.len();
462 assert_eq!(&out[off..], &msg);
463
464 assert_eq!(EmsgBox::parse(&out).unwrap(), b);
465 }
466
467 #[test]
470 fn v0_v1_field_order_differs_but_both_round_trip() {
471 let msg = [0xAAu8, 0xBB];
472 let v0 = EmsgBox {
473 scheme_id_uri: "s",
474 value: "v",
475 timescale: 0x1111_1111,
476 presentation_time: PresentationTime::Delta(0x2222_2222),
477 event_duration: 0x3333_3333,
478 id: 0x4444_4444,
479 message_data: &msg,
480 };
481 let v1 = EmsgBox {
482 scheme_id_uri: "s",
483 value: "v",
484 timescale: 0x1111_1111,
485 presentation_time: PresentationTime::Absolute(0x2222_2222),
486 event_duration: 0x3333_3333,
487 id: 0x4444_4444,
488 message_data: &msg,
489 };
490
491 let o0 = v0.to_vec().unwrap();
492 let o1 = v1.to_vec().unwrap();
493
494 assert_eq!(&o0[12..14], b"s\x00");
496 assert_eq!(&o1[12..16], &0x1111_1111u32.to_be_bytes());
497 assert_ne!(o0, o1);
499
500 assert_eq!(EmsgBox::parse(&o0).unwrap(), v0);
502 assert_eq!(EmsgBox::parse(&o1).unwrap(), v1);
503 }
504
505 #[test]
507 fn mutating_a_field_changes_wire_bytes() {
508 let mk = |id: u32| EmsgBox {
509 scheme_id_uri: "urn:x",
510 value: "",
511 timescale: 48_000,
512 presentation_time: PresentationTime::Delta(10),
513 event_duration: 20,
514 id,
515 message_data: &[],
516 };
517 let a = mk(1).to_vec().unwrap();
518 let b = mk(2).to_vec().unwrap();
519 assert_ne!(a, b);
520 assert_eq!(a.len(), b.len());
522 }
523
524 #[test]
525 fn rejects_non_emsg_type() {
526 let mut data = vec![0u8; FULLBOX_HEADER_LEN];
527 data[0..4].copy_from_slice(&(FULLBOX_HEADER_LEN as u32).to_be_bytes());
528 data[4..8].copy_from_slice(b"moof");
529 assert!(matches!(EmsgBox::parse(&data), Err(Error::NotEmsg { .. })));
530 }
531
532 #[test]
533 fn rejects_unsupported_version() {
534 let b = EmsgBox {
535 scheme_id_uri: "x",
536 value: "",
537 timescale: 1,
538 presentation_time: PresentationTime::Delta(0),
539 event_duration: 0,
540 id: 0,
541 message_data: &[],
542 };
543 let mut out = b.to_vec().unwrap();
544 out[8] = 9; assert!(matches!(
546 EmsgBox::parse(&out),
547 Err(Error::UnsupportedVersion { version: 9 })
548 ));
549 }
550
551 #[test]
552 fn rejects_size_overrun() {
553 let mut data = vec![0u8; FULLBOX_HEADER_LEN];
554 data[0..4].copy_from_slice(&0xFFFF_FFFFu32.to_be_bytes()); data[4..8].copy_from_slice(b"emsg");
556 assert!(matches!(
557 EmsgBox::parse(&data),
558 Err(Error::InvalidSize { .. })
559 ));
560 }
561
562 #[test]
563 fn rejects_missing_terminator() {
564 let mut data = vec![0u8; 16];
566 let size = data.len() as u32;
567 data[0..4].copy_from_slice(&size.to_be_bytes());
568 data[4..8].copy_from_slice(b"emsg");
569 data[8] = 0; data[12..16].copy_from_slice(b"abcd"); assert!(matches!(
572 EmsgBox::parse(&data),
573 Err(Error::InvalidString { .. })
574 ));
575 }
576
577 #[test]
578 fn is_scte35_recognises_prefix() {
579 let b = EmsgBox {
580 scheme_id_uri: "urn:scte:scte35:2013:bin",
581 value: "",
582 timescale: 1,
583 presentation_time: PresentationTime::Delta(0),
584 event_duration: 0,
585 id: 0,
586 message_data: &[],
587 };
588 assert!(b.is_scte35());
589 let nb = EmsgBox {
590 scheme_id_uri: "urn:mpeg:dash:event:2012",
591 ..b.clone()
592 };
593 assert!(!nb.is_scte35());
594 }
595}