|
1 | 1 | use crate::container::Timestamp; |
2 | 2 |
|
3 | | -/// Tracks the minimum duration between consecutive frames. |
| 3 | +/// Tracks the catalog `jitter` for a video/audio track: the maximum delay before a frame can |
| 4 | +/// be emitted, so a player sizes its buffer to at least this much. |
4 | 5 | /// |
5 | | -/// This is the value reported as `jitter` in the catalog: a player should |
6 | | -/// buffer at least this much before emitting frames. Despite the name "jitter", |
7 | | -/// what we actually record is the *minimum frame duration* observed so far. |
| 6 | +/// It reports whichever is larger of two contributions: |
| 7 | +/// - the minimum frame duration (the steady inter-frame spacing), and |
| 8 | +/// - the reorder delay (`max(PTS - DTS)`), which is non-zero only for reordered (B-frame) |
| 9 | +/// streams and which a transmuxer also reuses as the decode-clock reserve. |
| 10 | +/// |
| 11 | +/// A non-reordered stream reports the frame duration; a B-frame stream reports the deeper |
| 12 | +/// reorder delay (e.g. up to 3 consecutive B-frames is 3x the frame duration). |
8 | 13 | #[derive(Default)] |
9 | | -pub struct MinFrameDuration { |
| 14 | +pub struct Jitter { |
10 | 15 | last_timestamp: Option<Timestamp>, |
11 | 16 | min_duration: Option<Timestamp>, |
| 17 | + max_reorder: Timestamp, |
| 18 | + /// Last value handed back from [`observe`](Self::observe) / |
| 19 | + /// [`observe_reorder`](Self::observe_reorder), so they only report on a change. |
| 20 | + reported: Option<Timestamp>, |
12 | 21 | } |
13 | 22 |
|
14 | | -impl MinFrameDuration { |
| 23 | +impl Jitter { |
15 | 24 | pub fn new() -> Self { |
16 | 25 | Self::default() |
17 | 26 | } |
18 | 27 |
|
19 | | - /// Record a new frame timestamp. |
20 | | - /// |
21 | | - /// Returns the new minimum-frame-duration as a `moq_net::Time` if it |
22 | | - /// changed, so the caller can persist it on the catalog rendition. Returns |
23 | | - /// `None` when this is the first observation, the timestamps are |
24 | | - /// non-monotonic, or the new gap is no smaller than the recorded minimum. |
| 28 | + /// Record a frame's presentation timestamp (decode order), updating the minimum frame |
| 29 | + /// duration. Returns the new jitter as a [`moq_net::Time`] if it changed, else `None`. |
| 30 | + /// The first observation and non-monotonic timestamps (B-frames) only update state. |
25 | 31 | pub fn observe(&mut self, ts: Timestamp) -> Option<moq_net::Time> { |
26 | | - let last = self.last_timestamp.replace(ts)?; |
27 | | - let duration = ts.checked_sub(last).ok()?; |
| 32 | + if let Some(last) = self.last_timestamp.replace(ts) |
| 33 | + && let Ok(duration) = ts.checked_sub(last) |
| 34 | + && !duration.is_zero() |
| 35 | + && duration < self.min_duration.unwrap_or(Timestamp::MAX) |
| 36 | + { |
| 37 | + self.min_duration = Some(duration); |
| 38 | + } |
| 39 | + self.report() |
| 40 | + } |
| 41 | + |
| 42 | + /// Record a frame's reorder delay (`PTS - DTS`), updating the maximum. Returns the new |
| 43 | + /// jitter as a [`moq_net::Time`] if it changed, else `None`. |
| 44 | + pub fn observe_reorder(&mut self, reorder: Timestamp) -> Option<moq_net::Time> { |
| 45 | + self.max_reorder = self.max_reorder.max(reorder); |
| 46 | + self.report() |
| 47 | + } |
28 | 48 |
|
29 | | - if duration >= self.min_duration.unwrap_or(Timestamp::MAX) { |
| 49 | + /// The current jitter (the larger of the frame duration and the reorder delay), without |
| 50 | + /// the change-detection of [`observe`](Self::observe). Used to seed a freshly created |
| 51 | + /// catalog rendition with whatever has accumulated, since per-frame updates before the |
| 52 | + /// rendition exists would otherwise be lost. |
| 53 | + pub fn current(&self) -> Option<moq_net::Time> { |
| 54 | + let jitter = self.combined(); |
| 55 | + (!jitter.is_zero()).then(|| jitter.convert().ok()).flatten() |
| 56 | + } |
| 57 | + |
| 58 | + fn combined(&self) -> Timestamp { |
| 59 | + self.min_duration.unwrap_or(Timestamp::ZERO).max(self.max_reorder) |
| 60 | + } |
| 61 | + |
| 62 | + /// Report the current jitter only when it changes. |
| 63 | + fn report(&mut self) -> Option<moq_net::Time> { |
| 64 | + let jitter = self.combined(); |
| 65 | + if jitter.is_zero() || self.reported == Some(jitter) { |
30 | 66 | return None; |
31 | 67 | } |
32 | | - |
33 | | - self.min_duration = Some(duration); |
34 | | - duration.convert().ok() |
| 68 | + self.reported = Some(jitter); |
| 69 | + jitter.convert().ok() |
35 | 70 | } |
36 | 71 | } |
0 commit comments