use thiserror::Error;
use super::model::{
AdaptationSet, BaseInfo, CommonAttributes, MultiInfo, Period, Representation, SegmentLayer,
SegmentTemplate, SegmentTimeline, SegmentUrl,
};
pub const DEFAULT_TIMESCALE: u64 = 1;
pub const DEFAULT_START_NUMBER: u64 = 1;
pub const DEFAULT_PRESENTATION_TIME_OFFSET: u64 = 0;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
#[non_exhaustive]
pub enum InheritError {
#[error(
"SegmentTemplate and SegmentList both appear along one Period/AdaptationSet/Representation chain"
)]
TemplateAndList,
#[error("both @duration and SegmentTimeline are present (on one level or across inheritance)")]
DurationAndTimeline,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct ResolvedMulti {
pub base: BaseInfo,
pub timescale: u64,
pub presentation_time_offset: u64,
pub duration: Option<u64>,
pub start_number: u64,
pub end_number: Option<u64>,
pub timeline: Option<SegmentTimeline>,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct TemplateInfo {
pub multi: ResolvedMulti,
pub media: Option<String>,
pub index: Option<String>,
pub initialization: Option<String>,
pub bitstream_switching: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct ListInfo {
pub multi: ResolvedMulti,
pub urls: Vec<SegmentUrl>,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum SegmentInfo {
Template(TemplateInfo),
List(ListInfo),
Base(BaseInfo),
}
fn with_inherited_timeline<'a>(
enclosing: impl Iterator<Item = &'a SegmentLayer>,
level: &'a SegmentLayer,
) -> Result<SegmentLayer, InheritError> {
let Some(template) = &level.template else {
return Ok(level.clone());
};
let inherited = enclosing
.filter_map(|l| l.template.as_ref())
.map(|t| &t.multi)
.try_fold(MultiInfo::default(), |merged, multi| {
merge_multi(&merged, multi)
})?;
let mut layer = level.clone();
layer.template = Some(SegmentTemplate {
multi: merge_multi(&inherited, &template.multi)?,
media: template.media.clone(),
index: template.index.clone(),
initialization: template.initialization.clone(),
bitstream_switching: template.bitstream_switching.clone(),
});
Ok(layer)
}
fn merge_base(parent: &BaseInfo, child: &BaseInfo) -> BaseInfo {
BaseInfo {
timescale: child.timescale.or(parent.timescale),
presentation_time_offset: child
.presentation_time_offset
.or(parent.presentation_time_offset),
ept_delta: child.ept_delta.or(parent.ept_delta),
pd_delta: child.pd_delta.or(parent.pd_delta),
presentation_duration: child.presentation_duration.or(parent.presentation_duration),
time_shift_buffer_depth: child
.time_shift_buffer_depth
.or(parent.time_shift_buffer_depth),
index_range: child.index_range.or(parent.index_range),
index_range_exact: child.index_range_exact.or(parent.index_range_exact),
availability_time_offset: child
.availability_time_offset
.or(parent.availability_time_offset),
availability_time_complete: child
.availability_time_complete
.or(parent.availability_time_complete),
initialization: child
.initialization
.clone()
.or_else(|| parent.initialization.clone()),
representation_index: child
.representation_index
.clone()
.or_else(|| parent.representation_index.clone()),
}
}
fn merge_multi(parent: &MultiInfo, child: &MultiInfo) -> Result<MultiInfo, InheritError> {
let (duration, timeline) = (
child.duration.or(parent.duration),
child.timeline.clone().or_else(|| parent.timeline.clone()),
);
Ok(MultiInfo {
base: merge_base(&parent.base, &child.base),
duration,
start_number: child.start_number.or(parent.start_number),
end_number: child.end_number.or(parent.end_number),
timeline,
bitstream_switching: child
.bitstream_switching
.clone()
.or_else(|| parent.bitstream_switching.clone()),
})
}
fn finish_multi(m: MultiInfo) -> Result<ResolvedMulti, InheritError> {
if m.duration.is_some() && m.timeline.is_some() {
return Err(InheritError::DurationAndTimeline);
}
Ok(ResolvedMulti {
timescale: m.base.timescale.unwrap_or(DEFAULT_TIMESCALE),
presentation_time_offset: m
.base
.presentation_time_offset
.unwrap_or(DEFAULT_PRESENTATION_TIME_OFFSET),
base: m.base,
duration: m.duration,
start_number: m.start_number.unwrap_or(DEFAULT_START_NUMBER),
end_number: m.end_number,
timeline: m.timeline,
})
}
pub fn resolve_segment_info(
period: &Period,
set: &AdaptationSet,
rep: &Representation,
) -> Result<SegmentInfo, InheritError> {
let segments = [&period.segment, &set.segment, &rep.segment];
let resolved: Vec<SegmentLayer> = segments
.iter()
.copied()
.enumerate()
.map(|(i, l)| with_inherited_timeline(segments[..i].iter().copied(), l))
.collect::<Result<_, _>>()?;
let layers = [&resolved[0], &resolved[1], &resolved[2]];
let has_template = layers.iter().any(|l| l.template.is_some());
let has_list = layers.iter().any(|l| l.list.is_some());
if has_template && has_list {
return Err(InheritError::TemplateAndList);
}
if has_template {
let mut merged = crate::model::SegmentTemplate::default();
for t in layers.iter().filter_map(|l| l.template.as_ref()) {
merged = crate::model::SegmentTemplate {
multi: merge_multi(&merged.multi, &t.multi)?,
media: t.media.clone().or(merged.media),
index: t.index.clone().or(merged.index),
initialization: t.initialization.clone().or(merged.initialization),
bitstream_switching: t.bitstream_switching.clone().or(merged.bitstream_switching),
};
}
return Ok(SegmentInfo::Template(TemplateInfo {
multi: finish_multi(merged.multi)?,
media: merged.media,
index: merged.index,
initialization: merged.initialization,
bitstream_switching: merged.bitstream_switching,
}));
}
if has_list {
let mut multi = MultiInfo::default();
let mut urls: Vec<SegmentUrl> = Vec::new();
for l in layers.iter().filter_map(|l| l.list.as_ref()) {
multi = merge_multi(&multi, &l.multi)?;
if !l.urls.is_empty() {
urls.clone_from(&l.urls);
}
}
return Ok(SegmentInfo::List(ListInfo {
multi: finish_multi(multi)?,
urls,
}));
}
let mut base = BaseInfo::default();
for b in layers.iter().filter_map(|l| l.base.as_ref()) {
base = merge_base(&base, &b.info);
}
Ok(SegmentInfo::Base(base))
}
#[must_use]
pub fn effective_common(set: &AdaptationSet, rep: &Representation) -> CommonAttributes {
let (s, r) = (&set.common, &rep.common);
CommonAttributes {
profiles: r.profiles.clone().or_else(|| s.profiles.clone()),
width: r.width.or(s.width),
height: r.height.or(s.height),
sar: r.sar.clone().or_else(|| s.sar.clone()),
frame_rate: r.frame_rate.clone().or_else(|| s.frame_rate.clone()),
audio_sampling_rate: r
.audio_sampling_rate
.clone()
.or_else(|| s.audio_sampling_rate.clone()),
mime_type: r.mime_type.clone().or_else(|| s.mime_type.clone()),
codecs: r.codecs.clone().or_else(|| s.codecs.clone()),
max_playout_rate: r.max_playout_rate.or(s.max_playout_rate),
coding_dependency: r.coding_dependency.or(s.coding_dependency),
}
}