ac_ffmpeg/format/
demuxer.rs

1//! A/V demuxer.
2
3use std::{
4    borrow::{Borrow, BorrowMut},
5    convert::TryInto,
6    ffi::{CStr, CString},
7    io::Read,
8    ops::{Deref, DerefMut},
9    os::raw::{c_char, c_int, c_uint, c_void},
10    ptr,
11    time::Duration,
12};
13
14use crate::{
15    format::{io::IO, stream::Stream},
16    packet::Packet,
17    time::{TimeBase, Timestamp},
18    Error,
19};
20
21extern "C" {
22    fn ffw_guess_input_format(
23        short_name: *const c_char,
24        file_name: *const c_char,
25        mime_type: *const c_char,
26    ) -> *mut c_void;
27
28    fn ffw_demuxer_new() -> *mut c_void;
29    fn ffw_demuxer_init(
30        demuxer: *mut c_void,
31        io_context: *mut c_void,
32        format: *mut c_void,
33    ) -> c_int;
34    fn ffw_demuxer_set_initial_option(
35        demuxer: *mut c_void,
36        key: *const c_char,
37        value: *const c_char,
38    ) -> c_int;
39    fn ffw_demuxer_set_option(
40        demuxer: *mut c_void,
41        key: *const c_char,
42        value: *const c_char,
43    ) -> c_int;
44    fn ffw_demuxer_find_stream_info(demuxer: *mut c_void, max_analyze_duration: i64) -> c_int;
45    fn ffw_demuxer_get_nb_streams(demuxer: *const c_void) -> c_uint;
46    fn ffw_demuxer_get_stream(demuxer: *mut c_void, index: c_uint) -> *mut c_void;
47    fn ffw_demuxer_get_input_format(demuxer: *const c_void) -> *const c_void;
48    fn ffw_demuxer_read_frame(
49        demuxer: *mut c_void,
50        packet: *mut *mut c_void,
51        tb_num: *mut c_int,
52        tb_den: *mut c_int,
53    ) -> c_int;
54    fn ffw_demuxer_seek(
55        demuxer: *mut c_void,
56        timestamp: i64,
57        seek_by: c_int,
58        seek_target: c_int,
59    ) -> c_int;
60    fn ffw_demuxer_free(demuxer: *mut c_void);
61    fn ffw_input_format_name(input_format: *const c_void) -> *const c_char;
62}
63
64/// Seek type/mode.
65enum SeekType {
66    Time,
67    Byte,
68    Frame,
69}
70
71impl SeekType {
72    /// Get the internal raw representation.
73    fn into_raw(self) -> i32 {
74        match self {
75            SeekType::Time => 0,
76            SeekType::Byte => 1,
77            SeekType::Frame => 2,
78        }
79    }
80}
81
82/// Used to specify a search direction when a stream cannot seek exactly to the requested target
83/// point; timestamp, frame or byte.
84pub enum SeekTarget {
85    /// Seek, at least, to the requested target point in the stream. If the target cannot be met
86    /// then move forward in the stream until a possible seek target can be hit.
87    From,
88    /// Seek, at most, to the requested target point in the stream. If the target cannot be met
89    /// then move backward in the stream until a possible seek target can be hit.
90    UpTo,
91    /// Force seeking to the requested target point in the stream, even if the Demuxer for this
92    /// type of stream, does not support it.
93    Precise,
94}
95
96impl SeekTarget {
97    /// Get the internal raw representation.
98    fn into_raw(self) -> i32 {
99        match self {
100            SeekTarget::From => 0,
101            SeekTarget::UpTo => 1,
102            SeekTarget::Precise => 2,
103        }
104    }
105}
106
107/// Demuxer builder.
108pub struct DemuxerBuilder {
109    ptr: *mut c_void,
110    input_format: Option<InputFormat>,
111}
112
113impl DemuxerBuilder {
114    /// Create a new demuxer builder.
115    fn new() -> DemuxerBuilder {
116        let ptr = unsafe { ffw_demuxer_new() };
117
118        if ptr.is_null() {
119            panic!("unable to allocate a demuxer context");
120        }
121
122        DemuxerBuilder {
123            ptr,
124            input_format: None,
125        }
126    }
127
128    /// Set a demuxer option.
129    pub fn set_option<V>(self, name: &str, value: V) -> DemuxerBuilder
130    where
131        V: ToString,
132    {
133        let name = CString::new(name).expect("invalid option name");
134        let value = CString::new(value.to_string()).expect("invalid option value");
135
136        let ret = unsafe {
137            ffw_demuxer_set_initial_option(self.ptr, name.as_ptr() as _, value.as_ptr() as _)
138        };
139
140        if ret < 0 {
141            panic!("unable to allocate an option");
142        }
143
144        self
145    }
146
147    /// Set input format. If the input format is not set, it will be guessed from the input.
148    #[inline]
149    pub fn input_format(mut self, format: Option<InputFormat>) -> DemuxerBuilder {
150        self.input_format = format;
151        self
152    }
153
154    /// Build the demuxer.
155    ///
156    /// # Arguments
157    /// * `io` - an AVIO reader
158    pub fn build<T>(mut self, mut io: IO<T>) -> Result<Demuxer<T>, Error>
159    where
160        T: Read,
161    {
162        let io_context_ptr = io.io_context_mut().as_mut_ptr();
163
164        let format_ptr = self
165            .input_format
166            .take()
167            .map(|f| f.ptr)
168            .unwrap_or(ptr::null_mut());
169
170        let ret = unsafe { ffw_demuxer_init(self.ptr, io_context_ptr, format_ptr) };
171
172        if ret < 0 {
173            return Err(Error::from_raw_error_code(ret));
174        }
175
176        let ptr = self.ptr;
177
178        self.ptr = ptr::null_mut();
179
180        let res = Demuxer { ptr, io };
181
182        Ok(res)
183    }
184}
185
186impl Drop for DemuxerBuilder {
187    fn drop(&mut self) {
188        unsafe { ffw_demuxer_free(self.ptr) }
189    }
190}
191
192unsafe impl Send for DemuxerBuilder {}
193unsafe impl Sync for DemuxerBuilder {}
194
195/// Demuxer.
196pub struct Demuxer<T> {
197    ptr: *mut c_void,
198    io: IO<T>,
199}
200
201impl Demuxer<()> {
202    /// Get a demuxer builder.
203    pub fn builder() -> DemuxerBuilder {
204        DemuxerBuilder::new()
205    }
206}
207
208impl<T> Demuxer<T> {
209    /// Set an option.
210    pub fn set_option<V>(&mut self, name: &str, value: V) -> Result<(), Error>
211    where
212        V: ToString,
213    {
214        let name = CString::new(name).expect("invalid option name");
215        let value = CString::new(value.to_string()).expect("invalid option value");
216
217        let ret =
218            unsafe { ffw_demuxer_set_option(self.ptr, name.as_ptr() as _, value.as_ptr() as _) };
219
220        if ret < 0 {
221            Err(Error::from_raw_error_code(ret))
222        } else {
223            Ok(())
224        }
225    }
226
227    /// Take the next packet from the demuxer or `None` on EOF.
228    pub fn take(&mut self) -> Result<Option<Packet>, Error> {
229        let mut pptr = ptr::null_mut();
230
231        let mut tb_num: c_int = 0;
232        let mut tb_den: c_int = 0;
233
234        let ret = unsafe { ffw_demuxer_read_frame(self.ptr, &mut pptr, &mut tb_num, &mut tb_den) };
235
236        if ret < 0 {
237            Err(Error::from_raw_error_code(ret))
238        } else if pptr.is_null() {
239            Ok(None)
240        } else {
241            let tb = TimeBase::new(tb_num as i32, tb_den as i32);
242
243            let packet = unsafe { Packet::from_raw_ptr(pptr, tb) };
244
245            Ok(Some(packet))
246        }
247    }
248
249    /// Seek to a specific timestamp in the stream.
250    pub fn seek_to_timestamp(
251        &self,
252        timestamp: Timestamp,
253        seek_target: SeekTarget,
254    ) -> Result<(), Error> {
255        let micros = timestamp
256            .as_micros()
257            .ok_or_else(|| Error::new("null timestamp"))?;
258
259        self.seek(micros, SeekType::Time, seek_target)
260    }
261
262    /// Seek to a specific frame in the stream.
263    pub fn seek_to_frame(&self, frame: u64, seek_target: SeekTarget) -> Result<(), Error> {
264        self.seek(frame as _, SeekType::Frame, seek_target)
265    }
266
267    /// Seek to a specific byte offset in the stream.
268    pub fn seek_to_byte(&self, offset: u64) -> Result<(), Error> {
269        // use SeekTarget::Precise here since this flag seems to be ignored by FFmpeg
270        self.seek(offset as _, SeekType::Byte, SeekTarget::Precise)
271    }
272
273    /// Seek to a given position.
274    fn seek(
275        &self,
276        target_position: i64,
277        seek_by: SeekType,
278        seek_target: SeekTarget,
279    ) -> Result<(), Error> {
280        let res = unsafe {
281            ffw_demuxer_seek(
282                self.ptr,
283                target_position,
284                seek_by.into_raw(),
285                seek_target.into_raw(),
286            )
287        };
288
289        if res >= 0 {
290            Ok(())
291        } else {
292            Err(Error::from_raw_error_code(res))
293        }
294    }
295
296    /// Try to find stream info. Optionally, you can pass `max_analyze_duration` which tells FFmpeg
297    /// how far it should look for stream info.
298    pub fn find_stream_info(
299        self,
300        max_analyze_duration: Option<Duration>,
301    ) -> Result<DemuxerWithStreamInfo<T>, (Self, Error)> {
302        let max_analyze_duration = max_analyze_duration
303            .unwrap_or_else(|| Duration::from_secs(0))
304            .as_micros()
305            .try_into()
306            .unwrap();
307
308        let ret = unsafe { ffw_demuxer_find_stream_info(self.ptr, max_analyze_duration) };
309
310        if ret < 0 {
311            return Err((self, Error::from_raw_error_code(ret)));
312        }
313
314        let stream_count = unsafe { ffw_demuxer_get_nb_streams(self.ptr) };
315
316        let mut streams = Vec::with_capacity(stream_count as usize);
317
318        for i in 0..stream_count {
319            let stream = unsafe {
320                let ptr = ffw_demuxer_get_stream(self.ptr, i as _);
321
322                if ptr.is_null() {
323                    panic!("unable to get stream info");
324                }
325
326                Stream::from_raw_ptr(ptr)
327            };
328
329            streams.push(stream);
330        }
331
332        let res = DemuxerWithStreamInfo {
333            inner: self,
334            streams,
335        };
336
337        Ok(res)
338    }
339
340    pub fn input_format(&self) -> InputFormat {
341        // XXX: This is potentially very ugly as we rely on the fact that the
342        // input formats are statically allocated by FFmpeg and not owned by
343        // demuxers (and the underlying format contexts). In future versions,
344        // we should capture lifetime of the demuxer in the returned type.
345        unsafe {
346            InputFormat {
347                ptr: ffw_demuxer_get_input_format(self.ptr) as _,
348            }
349        }
350    }
351    /// Get reference to the underlying IO.
352    pub fn io(&self) -> &IO<T> {
353        &self.io
354    }
355
356    /// Get mutable reference to the underlying IO.
357    pub fn io_mut(&mut self) -> &mut IO<T> {
358        &mut self.io
359    }
360}
361
362impl<T> Drop for Demuxer<T> {
363    fn drop(&mut self) {
364        unsafe { ffw_demuxer_free(self.ptr) }
365    }
366}
367
368unsafe impl<T> Send for Demuxer<T> where T: Send {}
369unsafe impl<T> Sync for Demuxer<T> where T: Sync {}
370
371/// Demuxer with information about individual streams.
372pub struct DemuxerWithStreamInfo<T> {
373    inner: Demuxer<T>,
374    streams: Vec<Stream>,
375}
376
377impl<T> DemuxerWithStreamInfo<T> {
378    /// Get streams.
379    pub fn streams(&self) -> &[Stream] {
380        &self.streams
381    }
382
383    /// Get the underlying demuxer.
384    pub fn into_demuxer(self) -> Demuxer<T> {
385        self.inner
386    }
387}
388
389impl<T> AsRef<Demuxer<T>> for DemuxerWithStreamInfo<T> {
390    fn as_ref(&self) -> &Demuxer<T> {
391        &self.inner
392    }
393}
394
395impl<T> AsMut<Demuxer<T>> for DemuxerWithStreamInfo<T> {
396    fn as_mut(&mut self) -> &mut Demuxer<T> {
397        &mut self.inner
398    }
399}
400
401impl<T> Borrow<Demuxer<T>> for DemuxerWithStreamInfo<T> {
402    fn borrow(&self) -> &Demuxer<T> {
403        &self.inner
404    }
405}
406
407impl<T> BorrowMut<Demuxer<T>> for DemuxerWithStreamInfo<T> {
408    fn borrow_mut(&mut self) -> &mut Demuxer<T> {
409        &mut self.inner
410    }
411}
412
413impl<T> Deref for DemuxerWithStreamInfo<T> {
414    type Target = Demuxer<T>;
415
416    fn deref(&self) -> &Self::Target {
417        &self.inner
418    }
419}
420
421impl<T> DerefMut for DemuxerWithStreamInfo<T> {
422    fn deref_mut(&mut self) -> &mut Self::Target {
423        &mut self.inner
424    }
425}
426
427/// FFmpeg input format.
428pub struct InputFormat {
429    ptr: *mut c_void,
430}
431
432impl InputFormat {
433    /// Try to find an input format by its name.
434    pub fn find_by_name(name: &str) -> Option<InputFormat> {
435        let name = CString::new(name).expect("invalid format name");
436
437        let ptr = unsafe { ffw_guess_input_format(name.as_ptr(), ptr::null(), ptr::null()) };
438
439        if ptr.is_null() {
440            return None;
441        }
442
443        let res = InputFormat { ptr };
444
445        Some(res)
446    }
447
448    /// Try to find an input format by a given MIME type.
449    pub fn find_by_mime_type(mime_type: &str) -> Option<InputFormat> {
450        let mime_type = CString::new(mime_type).expect("invalid MIME type");
451
452        let ptr = unsafe { ffw_guess_input_format(ptr::null(), ptr::null(), mime_type.as_ptr()) };
453
454        if ptr.is_null() {
455            return None;
456        }
457
458        let res = InputFormat { ptr };
459
460        Some(res)
461    }
462
463    /// Try to guess an input format based on a given file name.
464    pub fn guess_from_file_name(file_name: &str) -> Option<InputFormat> {
465        let file_name = CString::new(file_name).expect("invalid file name");
466
467        let ptr = unsafe { ffw_guess_input_format(ptr::null(), file_name.as_ptr(), ptr::null()) };
468
469        if ptr.is_null() {
470            return None;
471        }
472
473        let res = InputFormat { ptr };
474
475        Some(res)
476    }
477
478    pub fn name(&self) -> &str {
479        unsafe {
480            CStr::from_ptr(ffw_input_format_name(self.ptr))
481                .to_str()
482                .expect("invalid format name")
483        }
484    }
485}
486
487unsafe impl Send for InputFormat {}
488unsafe impl Sync for InputFormat {}