use super::display::{DisplayChangeSet, DisplayInfo};
use super::sample::Sample;
use super::track::TrackSpec;
use crate::TrackId;
use alloc::sync::Arc;
use mpeg_ts::pid::Pid;
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct EventProvenance {
pub pid: Option<Pid>,
pub packet_index: Option<u64>,
pub program_number: Option<u16>,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DiscontinuityKind {
Signalled,
TimelineReanchored,
BudgetExceeded {
bytes: u64,
},
}
impl DiscontinuityKind {
pub fn name(&self) -> &'static str {
match self {
DiscontinuityKind::Signalled => "signalled",
DiscontinuityKind::TimelineReanchored => "timeline reanchored",
DiscontinuityKind::BudgetExceeded { .. } => "budget exceeded",
}
}
}
broadcast_common::impl_spec_display!(DiscontinuityKind);
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AbandonReason {
ConfigUnrecoverable,
BudgetExceeded,
}
impl AbandonReason {
pub fn name(&self) -> &'static str {
match self {
AbandonReason::ConfigUnrecoverable => "config unrecoverable",
AbandonReason::BudgetExceeded => "budget exceeded",
}
}
}
broadcast_common::impl_spec_display!(AbandonReason);
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InputDegradation {
TransportError,
ContinuityGap {
expected: u8,
got: u8,
},
InvalidParameterSet,
MalformedUnit {
position: u64,
},
SyncLoss {
offset: u64,
dropped_bytes: u64,
},
TruncatedPacket {
offset: u64,
dropped_bytes: u64,
},
}
impl InputDegradation {
pub fn name(&self) -> &'static str {
match self {
InputDegradation::TransportError => "transport error",
InputDegradation::ContinuityGap { .. } => "continuity gap",
InputDegradation::InvalidParameterSet => "invalid parameter set",
InputDegradation::MalformedUnit { .. } => "malformed unit",
InputDegradation::SyncLoss { .. } => "sync loss",
InputDegradation::TruncatedPacket { .. } => "truncated packet",
}
}
}
broadcast_common::impl_spec_display!(InputDegradation);
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DegradationRecord {
pub kind: InputDegradation,
pub track: Option<TrackId>,
pub provenance: EventProvenance,
}
impl DegradationRecord {
pub fn new(
kind: InputDegradation,
track: Option<TrackId>,
provenance: EventProvenance,
) -> Self {
Self {
kind,
track,
provenance,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum DemuxEvent {
TrackAdded(TrackSpec),
TrackUpdated(TrackSpec),
#[non_exhaustive]
TrackRemoved {
track_id: TrackId,
provenance: EventProvenance,
},
#[non_exhaustive]
TrackAbandoned {
track_id: Option<TrackId>,
reason: AbandonReason,
provenance: EventProvenance,
},
#[non_exhaustive]
Sample {
track_id: TrackId,
sample: Sample,
},
#[non_exhaustive]
ClockReference {
ticks: u64,
clock_hz: u32,
discontinuous: bool,
provenance: EventProvenance,
},
#[non_exhaustive]
Discontinuity {
track: Option<TrackId>,
kind: DiscontinuityKind,
provenance: EventProvenance,
},
#[non_exhaustive]
InputDegraded {
track: Option<TrackId>,
kind: InputDegradation,
provenance: EventProvenance,
},
#[non_exhaustive]
TracksResolved {
generation: u32,
},
#[non_exhaustive]
DisplayChanged {
track_id: TrackId,
display: Arc<DisplayInfo>,
changed: DisplayChangeSet,
dts: Option<i64>,
pts: Option<i64>,
random_access: bool,
provenance: EventProvenance,
},
}
impl DemuxEvent {
pub fn clock_reference(
ticks: u64,
clock_hz: u32,
discontinuous: bool,
provenance: EventProvenance,
) -> Self {
DemuxEvent::ClockReference {
ticks,
clock_hz,
discontinuous,
provenance,
}
}
pub fn discontinuity(
track: Option<TrackId>,
kind: DiscontinuityKind,
provenance: EventProvenance,
) -> Self {
DemuxEvent::Discontinuity {
track,
kind,
provenance,
}
}
pub fn track_removed(track_id: TrackId, provenance: EventProvenance) -> Self {
DemuxEvent::TrackRemoved {
track_id,
provenance,
}
}
pub fn track_abandoned(
track_id: Option<TrackId>,
reason: AbandonReason,
provenance: EventProvenance,
) -> Self {
DemuxEvent::TrackAbandoned {
track_id,
reason,
provenance,
}
}
pub fn sample(track_id: TrackId, sample: Sample) -> Self {
DemuxEvent::Sample { track_id, sample }
}
pub fn input_degraded(
track: Option<TrackId>,
kind: InputDegradation,
provenance: EventProvenance,
) -> Self {
DemuxEvent::InputDegraded {
track,
kind,
provenance,
}
}
pub fn tracks_resolved(generation: u32) -> Self {
DemuxEvent::TracksResolved { generation }
}
pub fn display_changed(
track_id: TrackId,
display: Arc<DisplayInfo>,
changed: DisplayChangeSet,
dts: Option<i64>,
pts: Option<i64>,
random_access: bool,
provenance: EventProvenance,
) -> Self {
DemuxEvent::DisplayChanged {
track_id,
display,
changed,
dts,
pts,
random_access,
provenance,
}
}
}
#[cfg(test)]
mod constructor_tests {
use super::*;
#[test]
fn track_abandoned_builds_the_matching_variant() {
let provenance = EventProvenance::default();
let ev = DemuxEvent::track_abandoned(
Some(TrackId::new(7)),
AbandonReason::ConfigUnrecoverable,
provenance,
);
match ev {
DemuxEvent::TrackAbandoned {
track_id,
reason,
provenance: p,
} => {
assert_eq!(track_id, Some(TrackId::new(7)));
assert_eq!(reason, AbandonReason::ConfigUnrecoverable);
assert_eq!(p, provenance);
}
other => panic!("expected TrackAbandoned, got {other:?}"),
}
}
#[test]
fn display_changed_builds_the_matching_variant() {
let display = Arc::new(DisplayInfo::new(None));
let changed = DisplayChangeSet {
initial: true,
..DisplayChangeSet::default()
};
let ev = DemuxEvent::display_changed(
TrackId::new(3),
Arc::clone(&display),
changed,
Some(10),
Some(20),
true,
EventProvenance::default(),
);
match ev {
DemuxEvent::DisplayChanged {
track_id,
display: d,
changed: c,
dts,
pts,
random_access,
..
} => {
assert_eq!(track_id, TrackId::new(3));
assert_eq!(d, display);
assert_eq!(c, changed);
assert_eq!((dts, pts, random_access), (Some(10), Some(20), true));
}
other => panic!("expected DisplayChanged, got {other:?}"),
}
}
}