1use crate::error::Result;
3use alloc::{string::String, vec::Vec};
4use scte35_splice::{commands::AnyCommand, SpliceInfoSection};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct MediaTime(pub u64);
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct MediaDuration(pub u64);
15
16impl MediaDuration {
17 pub fn as_seconds_f64(self) -> f64 {
19 self.0 as f64 / crate::PTS_HZ as f64
20 }
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26#[non_exhaustive]
27pub enum EventKind {
28 BreakStart,
30 BreakEnd,
32 Chapter,
34 Unspecified,
36}
37
38impl EventKind {
39 pub fn name(&self) -> &'static str {
41 match self {
42 EventKind::BreakStart => "break_start",
43 EventKind::BreakEnd => "break_end",
44 EventKind::Chapter => "chapter",
45 EventKind::Unspecified => "unspecified",
46 }
47 }
48}
49dvb_common::impl_spec_display!(EventKind);
50
51#[derive(Debug, Clone, PartialEq, Eq)]
53#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
54#[non_exhaustive]
55pub enum SourcePayload {
56 Scte35 { raw: Vec<u8> },
58 Emsg {
60 scheme_id_uri: String,
61 value: String,
62 raw: Vec<u8>,
63 },
64}
65
66#[derive(Debug, Clone, PartialEq, Eq)]
68#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
69pub struct TimedEvent {
70 pub id: Option<u32>,
72 pub kind: EventKind,
74 pub at: Option<MediaTime>,
76 pub duration: Option<MediaDuration>,
78 pub source: SourcePayload,
80}
81
82impl TimedEvent {
83 pub fn from_scte35(section: &SpliceInfoSection, raw: &[u8]) -> Result<Self> {
85 let mut id = None;
86 let mut kind = EventKind::Unspecified;
87 let mut at = None;
88 let mut duration = None;
89
90 if let Some(clear) = §ion.clear {
91 if let AnyCommand::SpliceInsert(si) = &clear.command {
92 id = Some(si.splice_event_id);
93 kind = if si.out_of_network_indicator {
94 EventKind::BreakStart
95 } else {
96 EventKind::BreakEnd
97 };
98 if let Some(st) = &si.splice_time {
99 at = st.pts_time.map(MediaTime);
100 }
101 if let Some(bd) = &si.break_duration {
102 duration = Some(MediaDuration(bd.duration));
103 }
104 }
105 }
106
107 Ok(TimedEvent {
108 id,
109 kind,
110 at,
111 duration,
112 source: SourcePayload::Scte35 { raw: raw.to_vec() },
113 })
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120 use alloc::vec::Vec;
121 use dvb_common::traits::Parse;
122 use scte35_splice::SpliceInfoSection;
123
124 fn splice_2002() -> Vec<u8> {
126 let hex = "FC302100000000000000FFF01005000007D27FEF7F7E0020F580C0000000000088B9661D";
127 (0..hex.len())
128 .step_by(2)
129 .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
130 .collect()
131 }
132
133 #[test]
134 fn from_scte35_extracts_break_start_and_duration() {
135 let raw = splice_2002();
136 let section = SpliceInfoSection::parse(&raw).unwrap();
137 let ev = TimedEvent::from_scte35(§ion, &raw).unwrap();
138 assert_eq!(ev.id, Some(2002));
139 assert_eq!(ev.kind, EventKind::BreakStart); assert_eq!(ev.at, None); assert_eq!(ev.duration, Some(MediaDuration(2_160_000)));
142 assert!((ev.duration.unwrap().as_seconds_f64() - 24.0).abs() < 1e-9);
143 match &ev.source {
144 SourcePayload::Scte35 { raw: r } => assert_eq!(r, &raw), _ => panic!("expected Scte35 payload"),
146 }
147 }
148
149 #[test]
150 fn event_kind_labels() {
151 assert_eq!(EventKind::BreakStart.name(), "break_start");
152 assert_eq!(alloc::format!("{}", EventKind::BreakEnd), "break_end");
153 }
154}