use crate::error::{Error, Result};
use crate::mkv::cluster::RawFrame;
use crate::packet::Packet;
use crate::track::Track;
pub(crate) fn build_packets(
frames: &[RawFrame],
tracks: &mut [Track],
timestamp_scale: u64,
) -> Result<Vec<Packet>> {
let mut counts = vec![0u64; tracks.len()];
let mut out = Vec::with_capacity(frames.len());
for frame in frames {
let slot = tracks
.iter()
.position(|track| u64::from(track.id) == frame.track)
.ok_or_else(|| {
Error::mkv_inconsistent(
format!("Segment/Cluster[{}]", frame.cluster),
format!(
"a block names track {}, which Tracks does not declare; it declares {:?}",
frame.track,
tracks.iter().map(|track| track.id).collect::<Vec<_>>()
),
)
})?;
let default_duration = tracks
.get(slot)
.and_then(|track| track.matroska.as_ref())
.and_then(|mkv| mkv.default_duration);
let (shift, duration) = lace_timing(frame, default_duration, timestamp_scale);
let pts = frame.timestamp.checked_add(shift).ok_or_else(|| {
Error::mkv_inconsistent(
format!("Segment/Cluster[{}]", frame.cluster),
format!(
"the block timestamp {} plus the lace offset {shift} overflows 64 bits",
frame.timestamp
),
)
})?;
let index = counts.get_mut(slot).map_or(0, |count| {
let index = *count;
*count += 1;
index
});
out.push(Packet {
track_id: frame.track as u32,
index,
dts: None,
pts,
duration,
sync: frame.keyframe,
discardable: frame.discardable,
discard_padding: frame.discard_padding,
lace: frame.lace,
offset: frame.offset,
size: frame.size,
description_index: 1,
});
}
for (track, count) in tracks.iter_mut().zip(counts) {
track.sample_count = count;
}
Ok(out)
}
fn lace_timing(frame: &RawFrame, default_duration: Option<u64>, scale: u64) -> (i64, Option<u64>) {
let (index, count) = frame.lace.map_or((0u128, 1u128), |lace| {
(u128::from(lace.index), u128::from(lace.count))
});
let (numerator, denominator) = match (frame.block_duration, default_duration) {
(Some(ticks), _) => (u128::from(ticks), count),
(None, Some(nanoseconds)) => (u128::from(nanoseconds), u128::from(scale)),
(None, None) => return (0, None),
};
let start = index * numerator / denominator;
let end = (index + 1) * numerator / denominator;
let shift = i64::try_from(start).unwrap_or(i64::MAX);
(shift, u64::try_from(end - start).ok())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::packet::Lace;
fn frame(lace: Option<Lace>, block_duration: Option<u64>) -> RawFrame {
RawFrame {
track: 1,
timestamp: 100,
keyframe: true,
discardable: false,
block_duration,
discard_padding: None,
lace,
offset: 0,
size: 1,
cluster: 1,
}
}
#[test]
fn block_duration_splits_across_a_lace() {
let laced = |index| frame(Some(Lace { index, count: 3 }), Some(10));
let timing: Vec<_> = (0..3)
.map(|index| lace_timing(&laced(index), None, 1_000_000))
.collect();
assert_eq!(timing, vec![(0, Some(3)), (3, Some(3)), (6, Some(4))]);
}
#[test]
fn default_duration_counts_in_nanoseconds() {
let laced = frame(Some(Lace { index: 2, count: 4 }), None);
assert_eq!(
lace_timing(&laced, Some(21_333_333), 1_000_000),
(42, Some(21))
);
assert_eq!(lace_timing(&frame(None, None), None, 1_000_000), (0, None));
}
}