use crate::io::BufReader;
use crate::units::{Duration, Timestamp};
#[derive(Clone)]
#[non_exhaustive]
pub struct Packet {
pub track_id: u32,
pub pts: Timestamp,
pub dts: Timestamp,
pub dur: Duration,
pub trim_start: Duration,
pub trim_end: Duration,
pub data: Box<[u8]>,
}
impl Packet {
pub fn new(track_id: u32, pts: Timestamp, dur: Duration, data: impl Into<Box<[u8]>>) -> Self {
Packet {
track_id,
pts,
dts: pts,
dur,
trim_start: Duration::ZERO,
trim_end: Duration::ZERO,
data: data.into(),
}
}
#[inline]
pub const fn block_dur(&self) -> Duration {
self.dur.saturating_add(self.trim_start).saturating_add(self.trim_end)
}
#[inline]
pub fn as_buf_reader(&self) -> BufReader<'_> {
BufReader::new(&self.data)
}
#[inline]
pub fn as_packet_ref(&self) -> PacketRef<'_> {
PacketRef {
track_id: self.track_id,
pts: self.pts,
dts: self.dts,
dur: self.dur,
trim_start: self.trim_start,
trim_end: self.trim_end,
data: &self.data,
}
}
}
impl std::fmt::Debug for Packet {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Packet")
.field("track_id", &self.track_id)
.field("pts", &self.pts)
.field("dts", &self.dts)
.field("dur", &self.dur)
.field("trim_start", &self.trim_start)
.field("trim_end", &self.trim_end)
.field("data", &format_args!("<{} bytes>", self.data.len()))
.finish()
}
}
#[derive(Clone, Copy)]
#[non_exhaustive]
pub struct PacketRef<'a> {
pub track_id: u32,
pub pts: Timestamp,
pub dts: Timestamp,
pub dur: Duration,
pub trim_start: Duration,
pub trim_end: Duration,
pub data: &'a [u8],
}
impl<'a> PacketRef<'a> {
pub fn new(track_id: u32, pts: Timestamp, dur: Duration, data: &'a [u8]) -> Self {
PacketRef {
track_id,
pts,
dts: pts,
dur,
trim_start: Duration::ZERO,
trim_end: Duration::ZERO,
data,
}
}
#[inline]
pub const fn block_dur(&self) -> Duration {
self.dur.saturating_add(self.trim_start).saturating_add(self.trim_end)
}
#[inline]
pub fn as_buf_reader(&self) -> BufReader<'_> {
BufReader::new(self.data)
}
}
impl std::fmt::Debug for PacketRef<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PacketRef")
.field("track_id", &self.track_id)
.field("pts", &self.pts)
.field("dts", &self.dts)
.field("dur", &self.dur)
.field("trim_start", &self.trim_start)
.field("trim_end", &self.trim_end)
.field("data", &format_args!("<{} bytes>", self.data.len()))
.finish()
}
}
impl<'a> From<&'a Packet> for PacketRef<'a> {
fn from(packet: &'a Packet) -> Self {
packet.as_packet_ref()
}
}
mod builder {
use crate::packet::{Packet, PacketRef};
use crate::units::{Duration, Timestamp};
pub struct HasTrackId(u32);
pub struct NoTrackId;
pub struct HasPts(Timestamp);
pub struct NoPts;
pub struct HasDur(Duration);
pub struct NoDur;
pub struct HasBuf(Box<[u8]>);
pub struct HasBufRef<'a>(&'a [u8]);
pub struct NoBuf;
pub struct PacketBuilder<T, P, D, B> {
track_id: T,
pts: P,
dur: D,
buf: B,
dts: Option<Timestamp>,
trim_start: Duration,
trim_end: Duration,
}
impl Default for PacketBuilder<NoTrackId, NoPts, NoDur, NoBuf> {
fn default() -> Self {
Self::new()
}
}
impl PacketBuilder<NoTrackId, NoPts, NoDur, NoBuf> {
pub fn new() -> Self {
Self {
track_id: NoTrackId,
pts: NoPts,
dur: NoDur,
buf: NoBuf,
dts: None,
trim_start: Duration::ZERO,
trim_end: Duration::ZERO,
}
}
}
impl PacketBuilder<HasTrackId, HasPts, HasDur, HasBuf> {
pub fn build(self) -> Packet {
Packet {
track_id: self.track_id.0,
pts: self.pts.0,
dts: self.dts.unwrap_or(self.pts.0),
dur: self.dur.0,
trim_start: self.trim_start,
trim_end: self.trim_end,
data: self.buf.0,
}
}
}
impl<'a> PacketBuilder<HasTrackId, HasPts, HasDur, HasBufRef<'a>> {
pub fn build_packet_ref(self) -> PacketRef<'a> {
PacketRef {
track_id: self.track_id.0,
pts: self.pts.0,
dts: self.dts.unwrap_or(self.pts.0),
dur: self.dur.0,
trim_start: self.trim_start,
trim_end: self.trim_end,
data: self.buf.0,
}
}
}
impl<T, B> PacketBuilder<T, HasPts, NoDur, B> {
pub fn trimmed_dur(
self,
block_dur: Duration,
end_pts: Option<Timestamp>,
) -> PacketBuilder<T, HasPts, HasDur, B> {
let Self { track_id, pts, buf, dts, .. } = self;
let negative = pts.0.duration_to(Timestamp::ZERO).unwrap_or(Duration::ZERO);
let trim_start = negative.min(block_dur);
let mut trim_end = Duration::ZERO;
if let Some(end_pts) = end_pts {
if let Some(pkt_end_pts) = pts.0.checked_add(block_dur) {
trim_end = pkt_end_pts.duration_from(end_pts).unwrap_or(Duration::ZERO);
}
}
let dur = block_dur.saturating_sub(self.trim_start).saturating_sub(self.trim_end);
PacketBuilder { track_id, pts, dur: HasDur(dur), buf, dts, trim_start, trim_end }
}
}
impl<T, P, B> PacketBuilder<T, P, NoDur, B> {
pub fn dur(self, dur: Duration) -> PacketBuilder<T, P, HasDur, B> {
let Self { track_id, pts, buf, dts, trim_start, trim_end, .. } = self;
PacketBuilder { track_id, pts, dur: HasDur(dur), buf, dts, trim_start, trim_end }
}
}
impl<T, P, D, B> PacketBuilder<T, P, D, B> {
pub fn track_id(self, track_id: u32) -> PacketBuilder<HasTrackId, P, D, B> {
let Self { pts, dur, buf, dts, trim_start, trim_end, .. } = self;
PacketBuilder {
track_id: HasTrackId(track_id),
pts,
dur,
buf,
dts,
trim_start,
trim_end,
}
}
pub fn pts(self, pts: Timestamp) -> PacketBuilder<T, HasPts, D, B> {
let Self { track_id, dur, buf, dts, trim_start, trim_end, .. } = self;
PacketBuilder { track_id, pts: HasPts(pts), dur, buf, dts, trim_start, trim_end }
}
pub fn data(self, buf: impl Into<Box<[u8]>>) -> PacketBuilder<T, P, D, HasBuf> {
let Self { track_id, pts, dur, dts, trim_start, trim_end, .. } = self;
PacketBuilder { track_id, pts, dur, buf: HasBuf(buf.into()), dts, trim_start, trim_end }
}
pub fn data_by_ref<'a>(self, buf: &'a [u8]) -> PacketBuilder<T, P, D, HasBufRef<'a>> {
let Self { track_id, pts, dur, dts, trim_start, trim_end, .. } = self;
PacketBuilder { track_id, pts, dur, buf: HasBufRef(buf), dts, trim_start, trim_end }
}
pub fn dts(mut self, dts: Timestamp) -> Self {
self.dts = Some(dts);
self
}
pub fn trim_start(mut self, trim_start: Duration) -> Self {
self.trim_start = trim_start;
self
}
pub fn trim_end(mut self, trim_end: Duration) -> Self {
self.trim_end = trim_end;
self
}
}
}
pub use builder::PacketBuilder;
#[cfg(test)]
mod tests {
use super::PacketBuilder;
use crate::units::{Duration, Timestamp};
#[test]
fn verify_packet_ref_creation() {
let data: &[u8] = &[1, 2, 3, 4];
let pkt_ref = PacketBuilder::new()
.track_id(1)
.pts(Timestamp::new(100))
.dts(Timestamp::new(90))
.dur(Duration::new(50))
.data_by_ref(data)
.trim_start(Duration::new(10))
.trim_end(Duration::new(5))
.build_packet_ref();
assert_eq!(pkt_ref.track_id, 1);
assert_eq!(pkt_ref.pts, Timestamp::new(100));
assert_eq!(pkt_ref.dts, Timestamp::new(90));
assert_eq!(pkt_ref.dur, Duration::new(50));
assert_eq!(pkt_ref.trim_start, Duration::new(10));
assert_eq!(pkt_ref.trim_end, Duration::new(5));
assert_eq!(&pkt_ref.data, &[1, 2, 3, 4]);
assert_eq!(pkt_ref.block_dur(), Duration::new(65));
}
#[test]
fn verify_packet_to_packet_ref() {
let data = vec![5, 6, 7, 8];
let pkt = PacketBuilder::new()
.track_id(2)
.pts(Timestamp::new(200))
.dur(Duration::new(100))
.data(data)
.dts(Timestamp::new(190))
.trim_start(Duration::new(20))
.trim_end(Duration::new(10))
.build();
let pkt_ref = pkt.as_packet_ref();
assert_eq!(pkt_ref.track_id, 2);
assert_eq!(pkt_ref.pts, Timestamp::new(200));
assert_eq!(pkt_ref.dts, Timestamp::new(190));
assert_eq!(pkt_ref.dur, Duration::new(100));
assert_eq!(pkt_ref.trim_start, Duration::new(20));
assert_eq!(pkt_ref.trim_end, Duration::new(10));
assert_eq!(&pkt_ref.data, &[5, 6, 7, 8]);
}
}