#![allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimedMetaType {
Id3,
Emsg,
Scte35,
EventStream,
DateRange,
}
impl TimedMetaType {
#[must_use]
pub fn is_binary(&self) -> bool {
matches!(self, Self::Id3 | Self::Emsg | Self::Scte35)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TimedMetaEvent {
pub presentation_time_ms: u64,
pub duration_ms: Option<u64>,
pub meta_type: TimedMetaType,
pub data: Vec<u8>,
pub event_id: String,
}
impl TimedMetaEvent {
#[must_use]
pub fn new(
presentation_time_ms: u64,
duration_ms: Option<u64>,
meta_type: TimedMetaType,
data: Vec<u8>,
event_id: impl Into<String>,
) -> Self {
Self {
presentation_time_ms,
duration_ms,
meta_type,
data,
event_id: event_id.into(),
}
}
#[must_use]
pub fn is_instant(&self) -> bool {
self.duration_ms.is_none()
}
#[must_use]
pub fn data_size(&self) -> usize {
self.data.len()
}
}
#[derive(Debug, Clone, Default)]
pub struct TimedMetaStream {
pub events: Vec<TimedMetaEvent>,
pub scheme_id: String,
}
impl TimedMetaStream {
#[must_use]
pub fn new(scheme_id: impl Into<String>) -> Self {
Self {
events: Vec::new(),
scheme_id: scheme_id.into(),
}
}
pub fn add_event(&mut self, event: TimedMetaEvent) {
self.events.push(event);
}
#[must_use]
pub fn events_at(&self, time_ms: u64, window_ms: u64) -> Vec<&TimedMetaEvent> {
let end = time_ms.saturating_add(window_ms);
self.events
.iter()
.filter(|e| e.presentation_time_ms >= time_ms && e.presentation_time_ms < end)
.collect()
}
#[must_use]
pub fn event_count(&self) -> usize {
self.events.len()
}
#[must_use]
pub fn scte35_events(&self) -> Vec<&TimedMetaEvent> {
self.events
.iter()
.filter(|e| e.meta_type == TimedMetaType::Scte35)
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_id3_is_binary() {
assert!(TimedMetaType::Id3.is_binary());
}
#[test]
fn test_emsg_is_binary() {
assert!(TimedMetaType::Emsg.is_binary());
}
#[test]
fn test_scte35_is_binary() {
assert!(TimedMetaType::Scte35.is_binary());
}
#[test]
fn test_event_stream_not_binary() {
assert!(!TimedMetaType::EventStream.is_binary());
}
#[test]
fn test_date_range_not_binary() {
assert!(!TimedMetaType::DateRange.is_binary());
}
#[test]
fn test_event_is_instant_when_no_duration() {
let event = TimedMetaEvent::new(1000, None, TimedMetaType::Id3, vec![0x01, 0x02], "id3-1");
assert!(event.is_instant());
}
#[test]
fn test_event_not_instant_when_duration_present() {
let event = TimedMetaEvent::new(1000, Some(5000), TimedMetaType::Scte35, vec![], "scte-1");
assert!(!event.is_instant());
}
#[test]
fn test_event_data_size() {
let data = vec![0xDE, 0xAD, 0xBE, 0xEF];
let event = TimedMetaEvent::new(0, None, TimedMetaType::Emsg, data, "emsg-1");
assert_eq!(event.data_size(), 4);
}
#[test]
fn test_event_data_size_empty() {
let event = TimedMetaEvent::new(0, None, TimedMetaType::DateRange, vec![], "dr-1");
assert_eq!(event.data_size(), 0);
}
#[test]
fn test_stream_new_is_empty() {
let stream = TimedMetaStream::new("urn:example:id3");
assert_eq!(stream.event_count(), 0);
}
#[test]
fn test_stream_add_event() {
let mut stream = TimedMetaStream::new("urn:example:id3");
stream.add_event(TimedMetaEvent::new(
1000,
None,
TimedMetaType::Id3,
vec![],
"e1",
));
assert_eq!(stream.event_count(), 1);
}
#[test]
fn test_stream_events_at_window() {
let mut stream = TimedMetaStream::new("urn:scte:scte35:2013:bin");
stream.add_event(TimedMetaEvent::new(
0,
None,
TimedMetaType::Scte35,
vec![],
"e1",
));
stream.add_event(TimedMetaEvent::new(
500,
None,
TimedMetaType::Scte35,
vec![],
"e2",
));
stream.add_event(TimedMetaEvent::new(
1500,
None,
TimedMetaType::Scte35,
vec![],
"e3",
));
let found = stream.events_at(0, 1000);
assert_eq!(found.len(), 2);
}
#[test]
fn test_stream_events_at_empty_window() {
let mut stream = TimedMetaStream::new("urn:example");
stream.add_event(TimedMetaEvent::new(
5000,
None,
TimedMetaType::Id3,
vec![],
"e1",
));
let found = stream.events_at(0, 1000);
assert_eq!(found.len(), 0);
}
#[test]
fn test_stream_scte35_events_filter() {
let mut stream = TimedMetaStream::new("urn:mixed");
stream.add_event(TimedMetaEvent::new(
0,
None,
TimedMetaType::Id3,
vec![],
"i1",
));
stream.add_event(TimedMetaEvent::new(
1000,
None,
TimedMetaType::Scte35,
vec![],
"s1",
));
stream.add_event(TimedMetaEvent::new(
2000,
None,
TimedMetaType::Emsg,
vec![],
"em1",
));
stream.add_event(TimedMetaEvent::new(
3000,
None,
TimedMetaType::Scte35,
vec![],
"s2",
));
let scte35 = stream.scte35_events();
assert_eq!(scte35.len(), 2);
assert_eq!(scte35[0].event_id, "s1");
assert_eq!(scte35[1].event_id, "s2");
}
#[test]
fn test_stream_event_count_multiple() {
let mut stream = TimedMetaStream::new("urn:example");
for i in 0..5u64 {
stream.add_event(TimedMetaEvent::new(
i * 1000,
None,
TimedMetaType::EventStream,
vec![],
format!("e{i}"),
));
}
assert_eq!(stream.event_count(), 5);
}
#[test]
fn test_stream_scheme_id_stored() {
let stream = TimedMetaStream::new("urn:mpeg:dash:event:2012");
assert_eq!(stream.scheme_id, "urn:mpeg:dash:event:2012");
}
}