use thiserror::Error;
use super::model::{
AdaptationSet, BaseInfo, CommonAttributes, MultiInfo, Period, Representation, 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")]
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 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> {
if child.duration.is_some() && child.timeline.is_some() {
return Err(InheritError::DurationAndTimeline);
}
let (duration, timeline) = if child.timeline.is_some() {
(None, child.timeline.clone())
} else if child.duration.is_some() {
(child.duration, None)
} else {
(parent.duration, 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 layers = [&period.segment, &set.segment, &rep.segment];
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::mpd::model::SegmentTemplate::default();
for t in layers.iter().filter_map(|l| l.template.as_ref()) {
merged = crate::mpd::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),
}
}