Skip to main content

unbundle/
packet_iterator.rs

1//! Raw packet-level iteration.
2//!
3//! This module provides [`PacketIterator`] for iterating over the
4//! demuxed packets of a media file without decoding.  Each yielded
5//! [`PacketInfo`] carries the stream index, PTS, DTS, size and keyframe
6//! flag of a single packet.
7//!
8//! # Example
9//!
10//! ```no_run
11//! use unbundle::{MediaFile, UnbundleError};
12//!
13//! let mut unbundler = MediaFile::open("input.mp4")?;
14//! let iter = unbundler.packet_iter()?;
15//! for info in iter {
16//!     let pkt = info?;
17//!     if pkt.is_keyframe {
18//!         println!("Keyframe at PTS {:?} in stream {}", pkt.pts, pkt.stream_index);
19//!     }
20//! }
21//! # Ok::<(), UnbundleError>(())
22//! ```
23
24use std::time::Duration;
25
26use ffmpeg_next::{Error as FfmpegError, Packet, Rational};
27
28use crate::error::UnbundleError;
29use crate::unbundle::MediaFile;
30
31/// Metadata for a single demuxed packet.
32#[derive(Debug, Clone)]
33pub struct PacketInfo {
34    /// The stream index this packet belongs to.
35    pub stream_index: usize,
36    /// Presentation timestamp (if available).
37    pub pts: Option<i64>,
38    /// Decoding timestamp (if available).
39    pub dts: Option<i64>,
40    /// Presentation timestamp converted to [`Duration`] using the stream's
41    /// time base. `None` if no PTS is present.
42    pub pts_duration: Option<Duration>,
43    /// Packet payload size in bytes.
44    pub size: usize,
45    /// Whether this packet is a keyframe / sync point.
46    pub is_keyframe: bool,
47    /// The stream's time base numerator / denominator.
48    pub time_base: Rational,
49}
50
51/// A lazy iterator over demuxed packets.
52///
53/// Packets are read one at a time without decoding.  The iterator
54/// borrows the underlying [`MediaFile`] mutably.
55pub struct PacketIterator<'a> {
56    unbundler: &'a mut MediaFile,
57    /// Per-stream time bases, indexed by stream index.
58    time_bases: Vec<Rational>,
59    done: bool,
60}
61
62impl<'a> PacketIterator<'a> {
63    /// Create a new packet iterator over all streams.
64    pub(crate) fn new(unbundler: &'a mut MediaFile) -> Self {
65        log::debug!("Creating PacketIterator");
66        let time_bases: Vec<Rational> = unbundler
67            .input_context
68            .streams()
69            .map(|s| s.time_base())
70            .collect();
71
72        Self {
73            unbundler,
74            time_bases,
75            done: false,
76        }
77    }
78}
79
80impl<'a> Iterator for PacketIterator<'a> {
81    type Item = Result<PacketInfo, UnbundleError>;
82
83    fn next(&mut self) -> Option<Self::Item> {
84        if self.done {
85            return None;
86        }
87
88        let mut packet = Packet::empty();
89        match packet.read(&mut self.unbundler.input_context) {
90            Ok(()) => {
91                let stream_index = packet.stream() as usize;
92                let time_base = self
93                    .time_bases
94                    .get(stream_index)
95                    .copied()
96                    .unwrap_or(Rational::new(1, 90_000));
97
98                let pts = packet.pts();
99                let dts = packet.dts();
100                let pts_duration = pts.map(|p| {
101                    let seconds = p as f64 * time_base.numerator() as f64
102                        / time_base.denominator().max(1) as f64;
103                    Duration::from_secs_f64(seconds.max(0.0))
104                });
105
106                let is_keyframe = packet.is_key();
107                let size = packet.size();
108
109                Some(Ok(PacketInfo {
110                    stream_index,
111                    pts,
112                    dts,
113                    pts_duration,
114                    size,
115                    is_keyframe,
116                    time_base,
117                }))
118            }
119            Err(FfmpegError::Eof) => {
120                self.done = true;
121                None
122            }
123            Err(e) => {
124                self.done = true;
125                Some(Err(UnbundleError::from(e)))
126            }
127        }
128    }
129}