1use crate::error::{Error, Result};
21use mp4_emsg::{EmsgBox, PresentationTime};
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct SegmentTiming {
29 pub earliest_presentation_time: u64,
32 pub presentation_time_offset: u64,
37 pub timescale: u32,
39}
40
41pub fn emsg_to_v1<'a>(emsg: &EmsgBox<'a>, timing: &SegmentTiming) -> Result<EmsgBox<'a>> {
46 validate_timescale(emsg.timescale, timing.timescale)?;
47
48 let t = movie_timeline_t(emsg, timing);
49
50 Ok(EmsgBox {
51 scheme_id_uri: emsg.scheme_id_uri,
52 value: emsg.value,
53 timescale: emsg.timescale,
54 presentation_time: PresentationTime::Absolute(t),
55 event_duration: emsg.event_duration,
56 id: emsg.id,
57 message_data: emsg.message_data,
58 })
59}
60
61pub fn emsg_to_v0<'a>(emsg: &EmsgBox<'a>, timing: &SegmentTiming) -> Result<EmsgBox<'a>> {
66 validate_timescale(emsg.timescale, timing.timescale)?;
67
68 let t = movie_timeline_t(emsg, timing);
69
70 let delta = t
71 .checked_sub(timing.earliest_presentation_time)
72 .ok_or(Error::EmsgPresentationTimeBeforeEpt)?;
73
74 if delta > u64::from(u32::MAX) {
75 return Err(Error::EmsgDeltaOverflow(delta));
76 }
77
78 Ok(EmsgBox {
79 scheme_id_uri: emsg.scheme_id_uri,
80 value: emsg.value,
81 timescale: emsg.timescale,
82 presentation_time: PresentationTime::Delta(delta as u32),
83 event_duration: emsg.event_duration,
84 id: emsg.id,
85 message_data: emsg.message_data,
86 })
87}
88
89fn movie_timeline_t(emsg: &EmsgBox<'_>, timing: &SegmentTiming) -> u64 {
91 match emsg.presentation_time {
92 PresentationTime::Absolute(pt) => pt,
93 PresentationTime::Delta(d) => timing.earliest_presentation_time + u64::from(d),
94 #[allow(unreachable_patterns)]
95 _ => unreachable!("non_exhaustive PresentationTime"),
96 }
97}
98
99fn validate_timescale(emsg: u32, timing: u32) -> Result<()> {
101 if emsg != timing {
102 return Err(Error::EmsgTimescaleMismatch { emsg, timing });
103 }
104 Ok(())
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn v0_to_v1_to_v0_round_trips() {
113 let emsg = EmsgBox {
114 scheme_id_uri: "urn:scte:scte35:2013:bin",
115 value: "1",
116 timescale: 90_000,
117 presentation_time: PresentationTime::Delta(0),
118 event_duration: 2_160_000,
119 id: 1,
120 message_data: &[0xFC, 0x30, 0x21],
121 };
122 let timing = SegmentTiming {
123 earliest_presentation_time: 500_000,
124 presentation_time_offset: 0,
125 timescale: 90_000,
126 };
127 let v1 = emsg_to_v1(&emsg, &timing).unwrap();
128 assert_eq!(v1.presentation_time, PresentationTime::Absolute(500_000));
129 let v0_back = emsg_to_v0(&v1, &timing).unwrap();
130 assert_eq!(v0_back, emsg);
131 }
132
133 #[test]
134 fn v1_to_v0_to_v1_round_trips() {
135 let emsg = EmsgBox {
136 scheme_id_uri: "urn:scte:scte35:2013:bin",
137 value: "2",
138 timescale: 90_000,
139 presentation_time: PresentationTime::Absolute(1_000_000),
140 event_duration: 0,
141 id: 42,
142 message_data: &[],
143 };
144 let timing = SegmentTiming {
145 earliest_presentation_time: 900_000,
146 presentation_time_offset: 0,
147 timescale: 90_000,
148 };
149 let v0 = emsg_to_v0(&emsg, &timing).unwrap();
150 assert_eq!(v0.presentation_time, PresentationTime::Delta(100_000));
151 let v1_back = emsg_to_v1(&v0, &timing).unwrap();
152 assert_eq!(v1_back, emsg);
153 }
154
155 #[test]
156 fn timescale_mismatch_returns_error() {
157 let emsg = EmsgBox {
158 scheme_id_uri: "urn:scte:scte35:2013:bin",
159 value: "",
160 timescale: 90_000,
161 presentation_time: PresentationTime::Delta(0),
162 event_duration: 0,
163 id: 0,
164 message_data: &[],
165 };
166 let timing = SegmentTiming {
167 earliest_presentation_time: 0,
168 presentation_time_offset: 0,
169 timescale: 48_000,
170 };
171 assert!(matches!(
172 emsg_to_v1(&emsg, &timing),
173 Err(Error::EmsgTimescaleMismatch { .. })
174 ));
175 assert!(matches!(
176 emsg_to_v0(&emsg, &timing),
177 Err(Error::EmsgTimescaleMismatch { .. })
178 ));
179 }
180
181 #[test]
182 fn v0_event_before_ept_errors() {
183 let emsg = EmsgBox {
184 scheme_id_uri: "urn:scte:scte35:2013:bin",
185 value: "",
186 timescale: 90_000,
187 presentation_time: PresentationTime::Delta(10),
188 event_duration: 0,
189 id: 0,
190 message_data: &[],
191 };
192 let timing = SegmentTiming {
193 earliest_presentation_time: 1_000,
194 presentation_time_offset: 0,
195 timescale: 90_000,
196 };
197
198 let v1_before = EmsgBox {
200 presentation_time: PresentationTime::Absolute(500),
201 ..emsg
202 };
203 assert!(matches!(
204 emsg_to_v0(&v1_before, &timing),
205 Err(Error::EmsgPresentationTimeBeforeEpt)
206 ));
207 }
208}