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 u32,
52        tb_den: *mut u32,
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    pub fn input_format(mut self, format: Option<InputFormat>) -> DemuxerBuilder {
149        self.input_format = format;
150        self
151    }
152
153    /// Build the demuxer.
154    ///
155    /// # Arguments
156    /// * `io` - an AVIO reader
157    pub fn build<T>(mut self, mut io: IO<T>) -> Result<Demuxer<T>, Error>
158    where
159        T: Read,
160    {
161        let io_context_ptr = io.io_context_mut().as_mut_ptr();
162
163        let format_ptr = self
164            .input_format
165            .take()
166            .map(|f| f.ptr)
167            .unwrap_or(ptr::null_mut());
168
169        let ret = unsafe { ffw_demuxer_init(self.ptr, io_context_ptr, format_ptr) };
170
171        if ret < 0 {
172            return Err(Error::from_raw_error_code(ret));
173        }
174
175        let ptr = self.ptr;
176
177        self.ptr = ptr::null_mut();
178
179        let res = Demuxer { ptr, io };
180
181        Ok(res)
182    }
183}
184
185impl Drop for DemuxerBuilder {
186    fn drop(&mut self) {
187        unsafe { ffw_demuxer_free(self.ptr) }
188    }
189}
190
191unsafe impl Send for DemuxerBuilder {}
192unsafe impl Sync for DemuxerBuilder {}
193
194/// Demuxer.
195pub struct Demuxer<T> {
196    ptr: *mut c_void,
197    io: IO<T>,
198}
199
200impl Demuxer<()> {
201    /// Get a demuxer builder.
202    pub fn builder() -> DemuxerBuilder {
203        DemuxerBuilder::new()
204    }
205}
206
207impl<T> Demuxer<T> {
208    /// Set an option.
209    pub fn set_option<V>(&mut self, name: &str, value: V) -> Result<(), Error>
210    where
211        V: ToString,
212    {
213        let name = CString::new(name).expect("invalid option name");
214        let value = CString::new(value.to_string()).expect("invalid option value");
215
216        let ret =
217            unsafe { ffw_demuxer_set_option(self.ptr, name.as_ptr() as _, value.as_ptr() as _) };
218
219        if ret < 0 {
220            Err(Error::from_raw_error_code(ret))
221        } else {
222            Ok(())
223        }
224    }
225
226    /// Take the next packet from the demuxer or `None` on EOF.
227    pub fn take(&mut self) -> Result<Option<Packet>, Error> {
228        let mut pptr = ptr::null_mut();
229
230        let mut tb_num = 0;
231        let mut tb_den = 0;
232
233        let ret = unsafe { ffw_demuxer_read_frame(self.ptr, &mut pptr, &mut tb_num, &mut tb_den) };
234
235        if ret < 0 {
236            Err(Error::from_raw_error_code(ret))
237        } else if pptr.is_null() {
238            Ok(None)
239        } else {
240            let packet = unsafe { Packet::from_raw_ptr(pptr, TimeBase::new(tb_num, tb_den)) };
241
242            Ok(Some(packet))
243        }
244    }
245
246    /// Seek to a specific timestamp in the stream.
247    pub fn seek_to_timestamp(
248        &self,
249        timestamp: Timestamp,
250        seek_target: SeekTarget,
251    ) -> Result<(), Error> {
252        let micros = timestamp
253            .as_micros()
254            .ok_or_else(|| Error::new("null timestamp"))?;
255
256        self.seek(micros, SeekType::Time, seek_target)
257    }
258
259    /// Seek to a specific frame in the stream.
260    pub fn seek_to_frame(&self, frame: u64, seek_target: SeekTarget) -> Result<(), Error> {
261        self.seek(frame as _, SeekType::Frame, seek_target)
262    }
263
264    /// Seek to a specific byte offset in the stream.
265    pub fn seek_to_byte(&self, offset: u64) -> Result<(), Error> {
266        // use SeekTarget::Precise here since this flag seems to be ignored by FFmpeg
267        self.seek(offset as _, SeekType::Byte, SeekTarget::Precise)
268    }
269
270    /// Seek to a given position.
271    fn seek(
272        &self,
273        target_position: i64,
274        seek_by: SeekType,
275        seek_target: SeekTarget,
276    ) -> Result<(), Error> {
277        let res = unsafe {
278            ffw_demuxer_seek(
279                self.ptr,
280                target_position,
281                seek_by.into_raw(),
282                seek_target.into_raw(),
283            )
284        };
285
286        if res >= 0 {
287            Ok(())
288        } else {
289            Err(Error::from_raw_error_code(res))
290        }
291    }
292
293    /// Try to find stream info. Optionally, you can pass `max_analyze_duration` which tells FFmpeg
294    /// how far it should look for stream info.
295    pub fn find_stream_info(
296        self,
297        max_analyze_duration: Option<Duration>,
298    ) -> Result<DemuxerWithStreamInfo<T>, (Self, Error)> {
299        let max_analyze_duration = max_analyze_duration
300            .unwrap_or_else(|| Duration::from_secs(0))
301            .as_micros()
302            .try_into()
303            .unwrap();
304
305        let ret = unsafe { ffw_demuxer_find_stream_info(self.ptr, max_analyze_duration) };
306
307        if ret < 0 {
308            return Err((self, Error::from_raw_error_code(ret)));
309        }
310
311        let stream_count = unsafe { ffw_demuxer_get_nb_streams(self.ptr) };
312
313        let mut streams = Vec::with_capacity(stream_count as usize);
314
315        for i in 0..stream_count {
316            let stream = unsafe {
317                let ptr = ffw_demuxer_get_stream(self.ptr, i as _);
318
319                if ptr.is_null() {
320                    panic!("unable to get stream info");
321                }
322
323                Stream::from_raw_ptr(ptr)
324            };
325
326            streams.push(stream);
327        }
328
329        let res = DemuxerWithStreamInfo {
330            inner: self,
331            streams,
332        };
333
334        Ok(res)
335    }
336
337    pub fn input_format(&self) -> InputFormat {
338        // XXX: This is potentially very ugly as we rely on the fact that the
339        // input formats are statically allocated by FFmpeg and not owned by
340        // demuxers (and the underlying format contexts). In future versions,
341        // we should capture lifetime of the demuxer in the returned type.
342        unsafe {
343            InputFormat {
344                ptr: ffw_demuxer_get_input_format(self.ptr) as _,
345            }
346        }
347    }
348    /// Get reference to the underlying IO.
349    pub fn io(&self) -> &IO<T> {
350        &self.io
351    }
352
353    /// Get mutable reference to the underlying IO.
354    pub fn io_mut(&mut self) -> &mut IO<T> {
355        &mut self.io
356    }
357}
358
359impl<T> Drop for Demuxer<T> {
360    fn drop(&mut self) {
361        unsafe { ffw_demuxer_free(self.ptr) }
362    }
363}
364
365unsafe impl<T> Send for Demuxer<T> where T: Send {}
366unsafe impl<T> Sync for Demuxer<T> where T: Sync {}
367
368/// Demuxer with information about individual streams.
369pub struct DemuxerWithStreamInfo<T> {
370    inner: Demuxer<T>,
371    streams: Vec<Stream>,
372}
373
374impl<T> DemuxerWithStreamInfo<T> {
375    /// Get streams.
376    pub fn streams(&self) -> &[Stream] {
377        &self.streams
378    }
379
380    /// Get the underlying demuxer.
381    pub fn into_demuxer(self) -> Demuxer<T> {
382        self.inner
383    }
384}
385
386impl<T> AsRef<Demuxer<T>> for DemuxerWithStreamInfo<T> {
387    fn as_ref(&self) -> &Demuxer<T> {
388        &self.inner
389    }
390}
391
392impl<T> AsMut<Demuxer<T>> for DemuxerWithStreamInfo<T> {
393    fn as_mut(&mut self) -> &mut Demuxer<T> {
394        &mut self.inner
395    }
396}
397
398impl<T> Borrow<Demuxer<T>> for DemuxerWithStreamInfo<T> {
399    fn borrow(&self) -> &Demuxer<T> {
400        &self.inner
401    }
402}
403
404impl<T> BorrowMut<Demuxer<T>> for DemuxerWithStreamInfo<T> {
405    fn borrow_mut(&mut self) -> &mut Demuxer<T> {
406        &mut self.inner
407    }
408}
409
410impl<T> Deref for DemuxerWithStreamInfo<T> {
411    type Target = Demuxer<T>;
412
413    fn deref(&self) -> &Self::Target {
414        &self.inner
415    }
416}
417
418impl<T> DerefMut for DemuxerWithStreamInfo<T> {
419    fn deref_mut(&mut self) -> &mut Self::Target {
420        &mut self.inner
421    }
422}
423
424/// FFmpeg input format.
425pub struct InputFormat {
426    ptr: *mut c_void,
427}
428
429impl InputFormat {
430    /// Try to find an input format by its name.
431    pub fn find_by_name(name: &str) -> Option<InputFormat> {
432        let name = CString::new(name).expect("invalid format name");
433
434        let ptr = unsafe { ffw_guess_input_format(name.as_ptr(), ptr::null(), ptr::null()) };
435
436        if ptr.is_null() {
437            return None;
438        }
439
440        let res = InputFormat { ptr };
441
442        Some(res)
443    }
444
445    /// Try to find an input format by a given MIME type.
446    pub fn find_by_mime_type(mime_type: &str) -> Option<InputFormat> {
447        let mime_type = CString::new(mime_type).expect("invalid MIME type");
448
449        let ptr = unsafe { ffw_guess_input_format(ptr::null(), ptr::null(), mime_type.as_ptr()) };
450
451        if ptr.is_null() {
452            return None;
453        }
454
455        let res = InputFormat { ptr };
456
457        Some(res)
458    }
459
460    /// Try to guess an input format based on a given file name.
461    pub fn guess_from_file_name(file_name: &str) -> Option<InputFormat> {
462        let file_name = CString::new(file_name).expect("invalid file name");
463
464        let ptr = unsafe { ffw_guess_input_format(ptr::null(), file_name.as_ptr(), ptr::null()) };
465
466        if ptr.is_null() {
467            return None;
468        }
469
470        let res = InputFormat { ptr };
471
472        Some(res)
473    }
474
475    pub fn name(&self) -> &str {
476        unsafe {
477            CStr::from_ptr(ffw_input_format_name(self.ptr))
478                .to_str()
479                .expect("invalid format name")
480        }
481    }
482}
483
484unsafe impl Send for InputFormat {}
485unsafe impl Sync for InputFormat {}