Skip to main content

ffmpeg_next/format/context/
input.rs

1use std::ffi::CString;
2use std::mem;
3use std::ops::{Deref, DerefMut};
4
5use super::common::Context;
6use super::destructor;
7#[cfg(not(feature = "ffmpeg_5_0"))]
8use crate::Codec;
9use crate::ffi::*;
10use crate::util::range::Range;
11use crate::{Error, Packet, Stream, format};
12
13pub struct Input {
14    ptr: *mut AVFormatContext,
15    ctx: Context,
16}
17
18unsafe impl Send for Input {}
19
20impl Input {
21    pub unsafe fn wrap(ptr: *mut AVFormatContext) -> Self {
22        unsafe {
23            Input {
24                ptr,
25                ctx: Context::wrap(ptr, destructor::Mode::Input),
26            }
27        }
28    }
29    pub unsafe fn wrap_with_custom_io(
30        ptr: *mut AVFormatContext,
31        custom_io: format::context::StreamIo,
32    ) -> Self {
33        unsafe {
34            Input {
35                ptr,
36                ctx: Context::wrap(ptr, destructor::Mode::InputCustomIo(custom_io)),
37            }
38        }
39    }
40
41    pub unsafe fn wrap_with_interrupt(
42        ptr: *mut AVFormatContext,
43        guard: crate::util::interrupt::InterruptGuard,
44    ) -> Self {
45        unsafe {
46            Input {
47                ptr,
48                ctx: Context::wrap_with_interrupt(ptr, destructor::Mode::Input, guard),
49            }
50        }
51    }
52
53    pub unsafe fn wrap_with_custom_io_and_interrupt(
54        ptr: *mut AVFormatContext,
55        custom_io: format::context::StreamIo,
56        guard: crate::util::interrupt::InterruptGuard,
57    ) -> Self {
58        unsafe {
59            Input {
60                ptr,
61                ctx: Context::wrap_with_interrupt(
62                    ptr,
63                    destructor::Mode::InputCustomIo(custom_io),
64                    guard,
65                ),
66            }
67        }
68    }
69
70    pub unsafe fn as_ptr(&self) -> *const AVFormatContext {
71        self.ptr as *const _
72    }
73
74    pub unsafe fn as_mut_ptr(&mut self) -> *mut AVFormatContext {
75        self.ptr
76    }
77}
78
79impl Input {
80    pub fn format(&self) -> format::Input {
81        // We get a clippy warning in 4.4 but not in 5.0 and newer, so we allow that cast to not complicate the code
82        #[allow(clippy::unnecessary_cast)]
83        unsafe {
84            format::Input::wrap((*self.as_ptr()).iformat as *mut AVInputFormat)
85        }
86    }
87
88    #[cfg(not(feature = "ffmpeg_5_0"))]
89    pub fn video_codec(&self) -> Option<Codec> {
90        unsafe {
91            let ptr = (*self.as_ptr()).video_codec;
92
93            if ptr.is_null() {
94                None
95            } else {
96                Some(Codec::wrap(ptr))
97            }
98        }
99    }
100
101    #[cfg(not(feature = "ffmpeg_5_0"))]
102    pub fn audio_codec(&self) -> Option<Codec> {
103        unsafe {
104            let ptr = (*self.as_ptr()).audio_codec;
105
106            if ptr.is_null() {
107                None
108            } else {
109                Some(Codec::wrap(ptr))
110            }
111        }
112    }
113
114    #[cfg(not(feature = "ffmpeg_5_0"))]
115    pub fn subtitle_codec(&self) -> Option<Codec> {
116        unsafe {
117            let ptr = (*self.as_ptr()).subtitle_codec;
118
119            if ptr.is_null() {
120                None
121            } else {
122                Some(Codec::wrap(ptr))
123            }
124        }
125    }
126
127    #[cfg(not(feature = "ffmpeg_5_0"))]
128    pub fn data_codec(&self) -> Option<Codec> {
129        unsafe {
130            let ptr = (*self.as_ptr()).data_codec;
131
132            if ptr.is_null() {
133                None
134            } else {
135                Some(Codec::wrap(ptr))
136            }
137        }
138    }
139
140    pub fn probe_score(&self) -> i32 {
141        unsafe { (*self.as_ptr()).probe_score }
142    }
143
144    pub fn packets(&mut self) -> PacketIter<'_> {
145        PacketIter::new(self)
146    }
147
148    pub fn pause(&mut self) -> Result<(), Error> {
149        unsafe {
150            match av_read_pause(self.as_mut_ptr()) {
151                0 => Ok(()),
152                e => Err(Error::from(e)),
153            }
154        }
155    }
156
157    pub fn play(&mut self) -> Result<(), Error> {
158        unsafe {
159            match av_read_play(self.as_mut_ptr()) {
160                0 => Ok(()),
161                e => Err(Error::from(e)),
162            }
163        }
164    }
165
166    pub fn seek<R: Range<i64>>(&mut self, ts: i64, range: R) -> Result<(), Error> {
167        unsafe {
168            let pb = (*self.ptr).pb;
169            // Clear the latch BEFORE seeking: the seek machinery itself gates
170            // on `eof_reached`/`error`, so a "clear only on success" ordering
171            // cannot work.
172            let relatch = unlatch_exit(pb);
173            let ret = avformat_seek_file(
174                self.as_mut_ptr(),
175                -1,
176                range.start().cloned().unwrap_or(i64::MIN),
177                ts,
178                range.end().cloned().unwrap_or(i64::MAX),
179                0,
180            );
181            if ret < 0 && relatch {
182                // The seek failed after we cleared the latch — and after
183                // `avformat_seek_file` already flushed/reset demuxer state
184                // (e.g. EPIPE on a non-seekable `from_read` stream). Re-poison
185                // the session so the next read fails loudly with `Error::Exit`
186                // rather than resyncing into silent data skips: `eof_reached=1`
187                // makes the next read short-circuit to EOF, which
188                // `read_frame_internal` rewrites back into the sticky EXIT.
189                (*pb).error = AVERROR_EXIT;
190                (*pb).eof_reached = 1;
191            }
192            match ret {
193                s if s >= 0 => Ok(()),
194                e => Err(Error::from(e)),
195            }
196        }
197    }
198
199    pub fn io_size(&self) -> Option<i64> {
200        unsafe {
201            let pb = (*self.as_ptr()).pb;
202            if pb.is_null() {
203                return None;
204            }
205            let sz = avio_size(pb);
206            if sz < 0 { None } else { Some(sz) }
207        }
208    }
209
210    pub fn clear_eof(&mut self) -> bool {
211        unsafe {
212            let pb = (*self.as_ptr()).pb;
213            if pb.is_null() {
214                return false;
215            }
216            (*pb).eof_reached = 0;
217            (*pb).error = 0;
218            true
219        }
220    }
221
222    /// Clears a pending interrupt-callback abort (`AVERROR_EXIT`) latched into
223    /// the `AVIOContext` by a cancelled blocking read, returning `true` if one
224    /// was cleared. Unlike [`clear_eof`](Self::clear_eof), a genuine sticky I/O
225    /// error is preserved.
226    ///
227    /// This is the post-cancel resume point for a **non-seekable** stream (a
228    /// `from_read` stream whose [`seek`](Self::seek) would fail with EPIPE):
229    /// re-arm the interrupt token, call this, then keep reading forward.
230    pub fn clear_interrupt(&mut self) -> bool {
231        unsafe { unlatch_exit((*self.ptr).pb) }
232    }
233}
234
235/// Un-latch a prior interrupt-callback abort (`AVERROR_EXIT`) from an
236/// `AVIOContext`, returning whether one was latched. A genuine sticky I/O error
237/// (any other `error` value) is preserved. Callers that must keep the abort
238/// poisoned on a later failure re-latch it (see [`Input::seek`]).
239///
240/// # Safety
241/// `pb` must be null or a valid `AVIOContext` owned by the format context.
242unsafe fn unlatch_exit(pb: *mut AVIOContext) -> bool {
243    unsafe {
244        if pb.is_null() || (*pb).error != AVERROR_EXIT {
245            return false;
246        }
247        (*pb).error = 0;
248        (*pb).eof_reached = 0;
249        true
250    }
251}
252
253impl Deref for Input {
254    type Target = Context;
255
256    fn deref(&self) -> &Self::Target {
257        &self.ctx
258    }
259}
260
261impl DerefMut for Input {
262    fn deref_mut(&mut self) -> &mut Self::Target {
263        &mut self.ctx
264    }
265}
266
267pub struct PacketIter<'a> {
268    context: &'a mut Input,
269}
270
271impl<'a> PacketIter<'a> {
272    pub fn new(context: &mut Input) -> PacketIter<'_> {
273        PacketIter { context }
274    }
275}
276
277impl<'a> Iterator for PacketIter<'a> {
278    type Item = (Stream<'a>, Packet);
279
280    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
281        let mut packet = Packet::empty();
282
283        loop {
284            match packet.read(self.context) {
285                Ok(..) => unsafe {
286                    return Some((
287                        Stream::wrap(mem::transmute_copy(&self.context), packet.stream()),
288                        packet,
289                    ));
290                },
291
292                Err(Error::Eof) => return None,
293
294                // Skip a single corrupt packet and keep demuxing: a demuxer can
295                // resync past `AVERROR_INVALIDDATA`, and it is not latched into
296                // the `AVIOContext` (`pb->error`), so retrying makes progress.
297                Err(Error::InvalidData) => (),
298
299                // Every other error is terminal. A cancelled read's
300                // `AVERROR_EXIT`, or any I/O error, is latched into `pb->error`
301                // (aviobuf.c `fill_buffer`) and `av_read_frame` then returns it
302                // on EVERY subsequent call (demux.c rewrites even a later clean
303                // EOF back into the sticky error), so retrying would spin
304                // forever at 100% CPU. End the iteration instead. Callers that
305                // must observe these errors drive `Packet::read` directly.
306                Err(..) => return None,
307            }
308        }
309    }
310}
311
312pub fn dump(ctx: &Input, index: i32, url: Option<&str>) {
313    let url = url.map(|u| CString::new(u).unwrap());
314
315    unsafe {
316        av_dump_format(
317            ctx.as_ptr() as *mut _,
318            index,
319            url.unwrap_or_else(|| CString::new("").unwrap()).as_ptr(),
320            0,
321        );
322    }
323}