ez_ffmpeg/core/
packet_scanner.rs1use ffmpeg_sys_next::{
2 av_packet_alloc, av_packet_free, av_packet_unref, av_read_frame,
3 avformat_seek_file, AVPacket, AVERROR, EAGAIN,
4 AV_PKT_FLAG_CORRUPT, AV_PKT_FLAG_KEY,
5};
6
7use std::iter::FusedIterator;
8
9use crate::core::context::AVFormatContextBox;
10use crate::error::{DemuxingError, OpenInputError, PacketScannerError, Result};
11
12#[derive(Debug, Clone)]
17pub struct PacketInfo {
18 stream_index: usize,
19 pts: Option<i64>,
20 dts: Option<i64>,
21 duration: i64,
22 size: usize,
23 pos: i64,
24 is_keyframe: bool,
25 is_corrupt: bool,
26}
27
28impl PacketInfo {
29 pub fn stream_index(&self) -> usize {
31 self.stream_index
32 }
33
34 pub fn pts(&self) -> Option<i64> {
36 self.pts
37 }
38
39 pub fn dts(&self) -> Option<i64> {
41 self.dts
42 }
43
44 pub fn duration(&self) -> i64 {
46 self.duration
47 }
48
49 pub fn size(&self) -> usize {
51 self.size
52 }
53
54 pub fn pos(&self) -> i64 {
56 self.pos
57 }
58
59 pub fn is_keyframe(&self) -> bool {
61 self.is_keyframe
62 }
63
64 pub fn is_corrupt(&self) -> bool {
66 self.is_corrupt
67 }
68}
69
70pub struct PacketScanner {
94 fmt_ctx_box: AVFormatContextBox,
95 pkt: *mut AVPacket,
96}
97
98unsafe impl Send for PacketScanner {}
102
103impl PacketScanner {
104 pub fn open(url: impl Into<String>) -> Result<Self> {
106 let fmt_ctx_box = crate::core::stream_info::init_format_context(url)?;
107
108 unsafe {
109 let pkt = av_packet_alloc();
110 if pkt.is_null() {
111 return Err(OpenInputError::OutOfMemory.into());
112 }
113
114 Ok(Self { fmt_ctx_box, pkt })
115 }
116 }
117
118 pub fn seek(&mut self, timestamp_us: i64) -> Result<()> {
126 unsafe {
127 let ret = avformat_seek_file(
128 self.fmt_ctx_box.fmt_ctx,
129 -1,
130 i64::MIN,
131 timestamp_us,
132 timestamp_us,
133 0,
134 );
135 if ret < 0 {
136 return Err(
137 PacketScannerError::SeekError(DemuxingError::from(ret)).into()
138 );
139 }
140 }
141 Ok(())
142 }
143
144 pub fn next_packet(&mut self) -> Result<Option<PacketInfo>> {
150 const MAX_EAGAIN_RETRIES: u32 = 500;
151
152 unsafe {
153 av_packet_unref(self.pkt);
154
155 let mut eagain_retries: u32 = 0;
156 loop {
157 let ret = av_read_frame(self.fmt_ctx_box.fmt_ctx, self.pkt);
158 if ret == AVERROR(EAGAIN) {
159 eagain_retries += 1;
160 if eagain_retries > MAX_EAGAIN_RETRIES {
161 return Err(
162 PacketScannerError::ReadError(DemuxingError::from(ret)).into()
163 );
164 }
165 std::thread::sleep(std::time::Duration::from_millis(10));
166 continue;
167 }
168 if ret < 0 {
169 if ret == ffmpeg_sys_next::AVERROR_EOF {
170 return Ok(None);
171 }
172 return Err(
173 PacketScannerError::ReadError(DemuxingError::from(ret)).into()
174 );
175 }
176 break;
177 }
178
179 let pkt = &*self.pkt;
180 let pts = if pkt.pts == ffmpeg_sys_next::AV_NOPTS_VALUE {
181 None
182 } else {
183 Some(pkt.pts)
184 };
185 let dts = if pkt.dts == ffmpeg_sys_next::AV_NOPTS_VALUE {
186 None
187 } else {
188 Some(pkt.dts)
189 };
190
191 Ok(Some(PacketInfo {
192 stream_index: pkt.stream_index.max(0) as usize,
193 pts,
194 dts,
195 duration: pkt.duration,
196 size: pkt.size.max(0) as usize,
197 pos: pkt.pos,
198 is_keyframe: (pkt.flags & AV_PKT_FLAG_KEY) != 0,
199 is_corrupt: (pkt.flags & AV_PKT_FLAG_CORRUPT) != 0,
200 }))
201 }
202 }
203
204 pub fn packets(&mut self) -> PacketIter<'_> {
212 PacketIter { scanner: self, done: false }
213 }
214}
215
216impl Drop for PacketScanner {
217 fn drop(&mut self) {
218 unsafe {
219 if !self.pkt.is_null() {
220 av_packet_free(&mut self.pkt);
221 }
222 }
223 }
225}
226
227pub struct PacketIter<'a> {
232 scanner: &'a mut PacketScanner,
233 done: bool,
234}
235
236impl<'a> Iterator for PacketIter<'a> {
237 type Item = Result<PacketInfo>;
238
239 fn next(&mut self) -> Option<Self::Item> {
240 if self.done {
241 return None;
242 }
243 match self.scanner.next_packet() {
244 Ok(Some(info)) => Some(Ok(info)),
245 Ok(None) => {
246 self.done = true;
247 None
248 }
249 Err(e) => {
250 self.done = true;
251 Some(Err(e))
252 }
253 }
254 }
255}
256
257impl<'a> FusedIterator for PacketIter<'a> {}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn test_open_not_found() {
265 let result = PacketScanner::open("not_found.mp4");
266 assert!(result.is_err());
267 }
268
269 #[test]
270 fn test_scan_packets() {
271 let mut scanner = PacketScanner::open("test.mp4").unwrap();
272 let mut count = 0;
273 let mut keyframes = 0;
274 for packet in scanner.packets() {
275 let info = packet.unwrap();
276 count += 1;
277 if info.is_keyframe() {
278 keyframes += 1;
279 }
280 }
281 assert!(count > 0, "expected at least one packet");
282 assert!(keyframes > 0, "expected at least one keyframe");
283 println!("total packets: {}, keyframes: {}", count, keyframes);
284 }
285
286 #[test]
287 fn test_seek_and_read() {
288 let mut scanner = PacketScanner::open("test.mp4").unwrap();
289 scanner.seek(1_000_000).unwrap();
291 let packet = scanner.next_packet().unwrap();
292 assert!(packet.is_some(), "expected a packet after seeking");
293 }
294}