use super::Muxer;
use crate::segment::{SegmentClock, SegmentDecision};
use crate::traits::StorageBackend;
use crate::{CodecId, FrameFlags, MediaFrame, Result};
use bytes::Bytes;
pub(crate) const BANDWIDTH_HINT: u32 = 2_000_000;
pub(crate) struct SegmentEngine<M: Muxer, S: StorageBackend> {
pub muxer: M,
pub storage: S,
pub prefix: String,
pub clock: SegmentClock,
pub seq: u64,
init_written: bool,
pending_configs: Vec<(CodecId, Bytes)>,
}
impl<M: Muxer, S: StorageBackend> SegmentEngine<M, S> {
pub fn new(muxer: M, storage: S, prefix: impl Into<String>, target_duration: u64) -> Self {
Self {
muxer,
storage,
prefix: prefix.into(),
clock: SegmentClock::new(target_duration),
seq: 0,
init_written: false,
pending_configs: Vec::new(),
}
}
pub fn segment_uri(&self, seq: u64) -> String {
format!("seg{}.{}", seq, self.muxer.extension())
}
pub fn part_uri(&self, seq: u64, part: u64) -> String {
format!("seg{}.{}.{}", seq, part, self.muxer.extension())
}
pub fn key(&self, uri: &str) -> String {
format!("{}/{}", self.prefix, uri)
}
pub fn observe(&mut self, frame: &MediaFrame) -> SegmentDecision {
self.clock.observe(frame)
}
pub fn flush(&mut self) -> Option<f64> {
self.clock.flush()
}
pub async fn ensure_init(&mut self, frame: &MediaFrame) -> Result<Option<String>> {
if self.init_written {
return Ok(None);
}
if frame.flags.contains(FrameFlags::CONFIG) {
self.pending_configs.push((frame.codec, frame.data.clone()));
return Ok(None);
}
self.init_written = true;
if self.pending_configs.is_empty() {
return Ok(None);
}
let configs = std::mem::take(&mut self.pending_configs);
let Some(init) = self.muxer.build_init_from(&configs)? else {
return Ok(None);
};
let uri = format!("init.{}", self.muxer.extension());
self.storage.put(&self.key(&uri), init).await?;
Ok(Some(uri))
}
}