Skip to main content

symphonia_core/
packet.rs

1// Symphonia
2// Copyright (c) 2019-2026 The Project Symphonia Developers.
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at https://mozilla.org/MPL/2.0/.
7
8//! The `packet` module defines the packet structure.
9
10use crate::io::BufReader;
11use crate::units::{Duration, Timestamp};
12
13/// A `Packet` contains a discrete amount of encoded data for a single codec bitstream. The exact
14/// amount of data is bounded, but not defined, and is dependant on the container and/or the
15/// encapsulated codec.
16///
17/// # Timing
18///
19/// Packets carry various timing information to support the decoding process. Generally, this
20/// timing information is read directly from the media container but it may also be synthesized by
21/// a format reader if it is not explicitly signalled.
22///
23/// * **Presentation Timestamp (PTS):** The time relative to the start of the stream that the
24///   decoded packet should be presented.
25///
26/// * **Decode Timestamp (DTS):** The time relative to the start of the stream that the packet
27///   should be decoded. The DTS is usually the same as PTS for audio.
28///
29/// * **Duration:** The duration of all *valid* frames in the packet. Equal to the duration of all
30///   *decoded* frames if there is no trimming.
31///
32/// * **Trim Start/End:** The duration of frames that should be trimmed from the start/end of the
33///   decoded packet before presentation. The sum of the duration and trim start/end equals the
34///   duration of *decoded* frames.
35///
36/// Take note of the difference between *valid* and *decoded* frames. Valid frames are frames that
37/// should be presented (played back) to the user, while decoded frames include any encoder delay
38/// and/or padding frames. The latter are generally discarded by the decoder. The duration of all
39/// *decoded* frames is also called the block duration.
40///
41/// # For Implementers
42///
43/// When synthesizing PTS, negative PTS should be used for encoder delay frames. However, this is
44/// not strictly mandatory. Encoder delay and padding frames are ultimately signalled using the trim
45/// fields. Regardless, the `dur` field must only be populated with the duration of *valid* frames,
46/// while the sum of `dur`, `trim_start`, and `trim_end` must equal the amount of frames the decoder
47/// would produce if no trimming occurs.
48#[derive(Clone)]
49#[non_exhaustive]
50pub struct Packet {
51    /// The track ID of the track this packet belongs to.
52    pub track_id: u32,
53    /// The presentation timestamp (PTS) of the packet in `TimeBase` units.+
54    ///
55    /// This is the time relative to the start of the media that the decoded packet should be
56    /// presented to the user.
57    pub pts: Timestamp,
58    /// The decode timestamp (DTS) of the packet in `TimeBase` units.
59    ///
60    /// This is the time relative to the start of the media that the packet should be decoded.
61    pub dts: Timestamp,
62    /// The duration of all *valid* frames in the packet in `TimeBase` units.
63    ///
64    /// This duration excludes any delay or padding frames that may be produced by the decoder.
65    /// Generally, delay or padding frames should not be presented to the user.
66    pub dur: Duration,
67    /// The duration of *decoded* frames that should be trimmed from the start of the decoded
68    /// buffer to remove encoder delay.
69    pub trim_start: Duration,
70    /// The duration of *decoded* frames that should be trimmed from the end of the decoded
71    /// buffer to remove encoder padding.
72    pub trim_end: Duration,
73    /// The packet data buffer.
74    pub data: Box<[u8]>,
75}
76
77impl Packet {
78    /// Create a new untrimmed `Packet`.
79    pub fn new(track_id: u32, pts: Timestamp, dur: Duration, data: impl Into<Box<[u8]>>) -> Self {
80        Packet {
81            track_id,
82            pts,
83            dts: pts,
84            dur,
85            trim_start: Duration::ZERO,
86            trim_end: Duration::ZERO,
87            data: data.into(),
88        }
89    }
90
91    /// Get the duration of all *decoded* frames in the packet in `TimeBase` units.
92    ///
93    /// This duration includes any delay or padding frames that may be produced by the decoder. As
94    /// such, this is a sum of the duration, start trim, and end trim.
95    #[inline]
96    pub const fn block_dur(&self) -> Duration {
97        self.dur.saturating_add(self.trim_start).saturating_add(self.trim_end)
98    }
99
100    /// Get a `BufReader` to read the packet data buffer sequentially.
101    #[inline]
102    pub fn as_buf_reader(&self) -> BufReader<'_> {
103        BufReader::new(&self.data)
104    }
105
106    /// Get a `PacketRef` borrowing this packet's data buffer.
107    #[inline]
108    pub fn as_packet_ref(&self) -> PacketRef<'_> {
109        PacketRef {
110            track_id: self.track_id,
111            pts: self.pts,
112            dts: self.dts,
113            dur: self.dur,
114            trim_start: self.trim_start,
115            trim_end: self.trim_end,
116            data: &self.data,
117        }
118    }
119}
120
121impl std::fmt::Debug for Packet {
122    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123        f.debug_struct("Packet")
124            .field("track_id", &self.track_id)
125            .field("pts", &self.pts)
126            .field("dts", &self.dts)
127            .field("dur", &self.dur)
128            .field("trim_start", &self.trim_start)
129            .field("trim_end", &self.trim_end)
130            // Omit the data buffer contents.
131            .field("data", &format_args!("<{} bytes>", self.data.len()))
132            .finish()
133    }
134}
135
136/// A non-owning equivalent to `Packet`.
137///
138/// `PacketRef` is the zero-copy, non-owning, equivalent of `Packet`. It is particularly useful when
139/// packet data is already residing in an application-managed fixed buffer or when data is
140/// passed in from external environments like C/C++ via FFI. This allows decoders to process
141/// the data directly without forcing a heap allocation and deep copy.
142///
143/// See [`Packet`] for more details on the various timing and implementation details.
144#[derive(Clone, Copy)]
145#[non_exhaustive]
146pub struct PacketRef<'a> {
147    /// The track ID of the track this packet belongs to.
148    pub track_id: u32,
149    /// The presentation timestamp (PTS) of the packet in `TimeBase` units.+
150    ///
151    /// This is the time relative to the start of the media that the decoded packet should be
152    /// presented to the user.
153    pub pts: Timestamp,
154    /// The decode timestamp (DTS) of the packet in `TimeBase` units.
155    ///
156    /// This is the time relative to the start of the media that the packet should be decoded.
157    pub dts: Timestamp,
158    /// The duration of all *valid* frames in the packet in `TimeBase` units.
159    ///
160    /// This duration excludes any delay or padding frames that may be produced by the decoder.
161    /// Generally, delay or padding frames should not be presented to the user.
162    pub dur: Duration,
163    /// The duration of *decoded* frames that should be trimmed from the start of the decoded
164    /// buffer to remove encoder delay.
165    pub trim_start: Duration,
166    /// The duration of *decoded* frames that should be trimmed from the end of the decoded
167    /// buffer to remove encoder padding.
168    pub trim_end: Duration,
169    /// The packet data buffer.
170    pub data: &'a [u8],
171}
172
173impl<'a> PacketRef<'a> {
174    /// Create a new untrimmed `PacketRef`.
175    ///
176    /// The `data` slice can be constructed directly from a fixed-size buffer, or from
177    /// raw FFI pointers (e.g., a C++ `std::vector` payload) using `std::slice::from_raw_parts`.
178    pub fn new(track_id: u32, pts: Timestamp, dur: Duration, data: &'a [u8]) -> Self {
179        PacketRef {
180            track_id,
181            pts,
182            dts: pts,
183            dur,
184            trim_start: Duration::ZERO,
185            trim_end: Duration::ZERO,
186            data,
187        }
188    }
189
190    /// Get the duration of all *decoded* frames in the packet in `TimeBase` units.
191    #[inline]
192    pub const fn block_dur(&self) -> Duration {
193        self.dur.saturating_add(self.trim_start).saturating_add(self.trim_end)
194    }
195
196    /// Get a `BufReader` to read the packet data buffer sequentially.
197    #[inline]
198    pub fn as_buf_reader(&self) -> BufReader<'_> {
199        BufReader::new(self.data)
200    }
201}
202
203impl std::fmt::Debug for PacketRef<'_> {
204    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205        f.debug_struct("PacketRef")
206            .field("track_id", &self.track_id)
207            .field("pts", &self.pts)
208            .field("dts", &self.dts)
209            .field("dur", &self.dur)
210            .field("trim_start", &self.trim_start)
211            .field("trim_end", &self.trim_end)
212            // Omit the data buffer contents.
213            .field("data", &format_args!("<{} bytes>", self.data.len()))
214            .finish()
215    }
216}
217
218impl<'a> From<&'a Packet> for PacketRef<'a> {
219    fn from(packet: &'a Packet) -> Self {
220        packet.as_packet_ref()
221    }
222}
223
224mod builder {
225    use crate::packet::{Packet, PacketRef};
226    use crate::units::{Duration, Timestamp};
227
228    pub struct HasTrackId(u32);
229    pub struct NoTrackId;
230
231    pub struct HasPts(Timestamp);
232    pub struct NoPts;
233
234    pub struct HasDur(Duration);
235    pub struct NoDur;
236
237    pub struct HasBuf(Box<[u8]>);
238    pub struct HasBufRef<'a>(&'a [u8]);
239    pub struct NoBuf;
240
241    /// A builder for creating owning or non-owning packets.
242    ///
243    /// See [`Packet`] for a detailed description of all packet fields.
244    ///
245    /// The track ID, PTS, duration, and data fields are mandatory and must be provided before a
246    /// packet can be built.
247    pub struct PacketBuilder<T, P, D, B> {
248        track_id: T,
249        pts: P,
250        dur: D,
251        buf: B,
252        dts: Option<Timestamp>,
253        trim_start: Duration,
254        trim_end: Duration,
255    }
256
257    impl Default for PacketBuilder<NoTrackId, NoPts, NoDur, NoBuf> {
258        fn default() -> Self {
259            Self::new()
260        }
261    }
262
263    impl PacketBuilder<NoTrackId, NoPts, NoDur, NoBuf> {
264        /// Create the packet builder.
265        pub fn new() -> Self {
266            Self {
267                track_id: NoTrackId,
268                pts: NoPts,
269                dur: NoDur,
270                buf: NoBuf,
271                dts: None,
272                trim_start: Duration::ZERO,
273                trim_end: Duration::ZERO,
274            }
275        }
276    }
277
278    impl PacketBuilder<HasTrackId, HasPts, HasDur, HasBuf> {
279        /// Build the packet.
280        pub fn build(self) -> Packet {
281            Packet {
282                track_id: self.track_id.0,
283                pts: self.pts.0,
284                dts: self.dts.unwrap_or(self.pts.0),
285                dur: self.dur.0,
286                trim_start: self.trim_start,
287                trim_end: self.trim_end,
288                data: self.buf.0,
289            }
290        }
291    }
292
293    impl<'a> PacketBuilder<HasTrackId, HasPts, HasDur, HasBufRef<'a>> {
294        /// Build a non-owning packet.
295        pub fn build_packet_ref(self) -> PacketRef<'a> {
296            PacketRef {
297                track_id: self.track_id.0,
298                pts: self.pts.0,
299                dts: self.dts.unwrap_or(self.pts.0),
300                dur: self.dur.0,
301                trim_start: self.trim_start,
302                trim_end: self.trim_end,
303                data: self.buf.0,
304            }
305        }
306    }
307
308    impl<T, B> PacketBuilder<T, HasPts, NoDur, B> {
309        /// Provide the packet's duration and calculate the trim fields.
310        ///
311        /// Given the provided duration including delay and padding frames, the stream end timestamp
312        /// (optional), and the previously provided PTS, this function calculates the packet's
313        /// duration, trim start, and trim end.
314        ///
315        /// This helper assumes that all frames with a PTS < 0 are to be trimmed from the start.
316        /// Frames whose PTS exceeds the end timestamp of the stream are trimmed from the end. The
317        /// trim end will only be calculated if the stream's end timestamp is provided.
318        pub fn trimmed_dur(
319            self,
320            block_dur: Duration,
321            end_pts: Option<Timestamp>,
322        ) -> PacketBuilder<T, HasPts, HasDur, B> {
323            let Self { track_id, pts, buf, dts, .. } = self;
324
325            // All frames with a negative PTS must be trimmed first. This duration may exceed the
326            // number of decoded frames.
327            let negative = pts.0.duration_to(Timestamp::ZERO).unwrap_or(Duration::ZERO);
328
329            // Cap to the number of decoded frames.
330            let trim_start = negative.min(block_dur);
331            let mut trim_end = Duration::ZERO;
332
333            // It is only possible to trim the end of a packet if the end PTS is known.
334            if let Some(end_pts) = end_pts {
335                if let Some(pkt_end_pts) = pts.0.checked_add(block_dur) {
336                    trim_end = pkt_end_pts.duration_from(end_pts).unwrap_or(Duration::ZERO);
337                }
338            }
339
340            let dur = block_dur.saturating_sub(self.trim_start).saturating_sub(self.trim_end);
341
342            PacketBuilder { track_id, pts, dur: HasDur(dur), buf, dts, trim_start, trim_end }
343        }
344    }
345
346    impl<T, P, B> PacketBuilder<T, P, NoDur, B> {
347        /// Provide the packet's duration including delay and padding frames.
348        pub fn dur(self, dur: Duration) -> PacketBuilder<T, P, HasDur, B> {
349            let Self { track_id, pts, buf, dts, trim_start, trim_end, .. } = self;
350            PacketBuilder { track_id, pts, dur: HasDur(dur), buf, dts, trim_start, trim_end }
351        }
352    }
353
354    impl<T, P, D, B> PacketBuilder<T, P, D, B> {
355        /// Provide the track ID.
356        pub fn track_id(self, track_id: u32) -> PacketBuilder<HasTrackId, P, D, B> {
357            let Self { pts, dur, buf, dts, trim_start, trim_end, .. } = self;
358            PacketBuilder {
359                track_id: HasTrackId(track_id),
360                pts,
361                dur,
362                buf,
363                dts,
364                trim_start,
365                trim_end,
366            }
367        }
368
369        /// Provide the presentation timestamp (PTS).
370        pub fn pts(self, pts: Timestamp) -> PacketBuilder<T, HasPts, D, B> {
371            let Self { track_id, dur, buf, dts, trim_start, trim_end, .. } = self;
372            PacketBuilder { track_id, pts: HasPts(pts), dur, buf, dts, trim_start, trim_end }
373        }
374
375        /// Provide the packet's data buffer.
376        ///
377        /// When holding an owned data buffer, an owning `Packet` is built.
378        pub fn data(self, buf: impl Into<Box<[u8]>>) -> PacketBuilder<T, P, D, HasBuf> {
379            let Self { track_id, pts, dur, dts, trim_start, trim_end, .. } = self;
380            PacketBuilder { track_id, pts, dur, buf: HasBuf(buf.into()), dts, trim_start, trim_end }
381        }
382
383        /// Provide the packet's data buffer as a non-owning reference.
384        ///
385        /// When holding a non-owning data buffer reference, a non-owning `PacketRef` is built.
386        pub fn data_by_ref<'a>(self, buf: &'a [u8]) -> PacketBuilder<T, P, D, HasBufRef<'a>> {
387            let Self { track_id, pts, dur, dts, trim_start, trim_end, .. } = self;
388            PacketBuilder { track_id, pts, dur, buf: HasBufRef(buf), dts, trim_start, trim_end }
389        }
390
391        /// Provide the decode timestamp (DTS).
392        pub fn dts(mut self, dts: Timestamp) -> Self {
393            self.dts = Some(dts);
394            self
395        }
396
397        /// Provide the trim start duration.
398        pub fn trim_start(mut self, trim_start: Duration) -> Self {
399            self.trim_start = trim_start;
400            self
401        }
402
403        /// Provide the trim end duration.
404        pub fn trim_end(mut self, trim_end: Duration) -> Self {
405            self.trim_end = trim_end;
406            self
407        }
408    }
409}
410
411pub use builder::PacketBuilder;
412
413#[cfg(test)]
414mod tests {
415    use super::PacketBuilder;
416    use crate::units::{Duration, Timestamp};
417
418    #[test]
419    fn verify_packet_ref_creation() {
420        let data: &[u8] = &[1, 2, 3, 4];
421
422        let pkt_ref = PacketBuilder::new()
423            .track_id(1)
424            .pts(Timestamp::new(100))
425            .dts(Timestamp::new(90))
426            .dur(Duration::new(50))
427            .data_by_ref(data)
428            .trim_start(Duration::new(10))
429            .trim_end(Duration::new(5))
430            .build_packet_ref();
431
432        assert_eq!(pkt_ref.track_id, 1);
433        assert_eq!(pkt_ref.pts, Timestamp::new(100));
434        assert_eq!(pkt_ref.dts, Timestamp::new(90));
435        assert_eq!(pkt_ref.dur, Duration::new(50));
436        assert_eq!(pkt_ref.trim_start, Duration::new(10));
437        assert_eq!(pkt_ref.trim_end, Duration::new(5));
438        assert_eq!(&pkt_ref.data, &[1, 2, 3, 4]);
439
440        // block_dur = dur + trim_start + trim_end = 50 + 10 + 5 = 65
441        assert_eq!(pkt_ref.block_dur(), Duration::new(65));
442    }
443
444    #[test]
445    fn verify_packet_to_packet_ref() {
446        let data = vec![5, 6, 7, 8];
447
448        let pkt = PacketBuilder::new()
449            .track_id(2)
450            .pts(Timestamp::new(200))
451            .dur(Duration::new(100))
452            .data(data)
453            .dts(Timestamp::new(190))
454            .trim_start(Duration::new(20))
455            .trim_end(Duration::new(10))
456            .build();
457
458        let pkt_ref = pkt.as_packet_ref();
459
460        assert_eq!(pkt_ref.track_id, 2);
461        assert_eq!(pkt_ref.pts, Timestamp::new(200));
462        assert_eq!(pkt_ref.dts, Timestamp::new(190));
463        assert_eq!(pkt_ref.dur, Duration::new(100));
464        assert_eq!(pkt_ref.trim_start, Duration::new(20));
465        assert_eq!(pkt_ref.trim_end, Duration::new(10));
466        assert_eq!(&pkt_ref.data, &[5, 6, 7, 8]);
467    }
468}