ac_ffmpeg/format/
muxer.rs

1//! A/V muxer.
2
3use std::{
4    ffi::CString,
5    io::Write,
6    os::raw::{c_char, c_int, c_uint, c_void},
7    ptr,
8};
9
10use crate::{
11    codec::CodecParameters,
12    format::{io::IO, stream::Stream},
13    packet::Packet,
14    Error,
15};
16
17extern "C" {
18    fn ffw_guess_output_format(
19        short_name: *const c_char,
20        file_name: *const c_char,
21        mime_type: *const c_char,
22    ) -> *const c_void;
23
24    fn ffw_muxer_new() -> *mut c_void;
25    fn ffw_muxer_get_nb_streams(muxer: *const c_void) -> c_uint;
26    fn ffw_muxer_get_stream(muxer: *mut c_void, stream_index: c_uint) -> *mut c_void;
27    fn ffw_muxer_new_stream(muxer: *mut c_void, params: *const c_void) -> c_int;
28    fn ffw_muxer_init(muxer: *mut c_void, io_context: *mut c_void, format: *const c_void) -> c_int;
29    fn ffw_muxer_set_initial_option(
30        muxer: *mut c_void,
31        key: *const c_char,
32        value: *const c_char,
33    ) -> c_int;
34    fn ffw_muxer_set_option(muxer: *mut c_void, key: *const c_char, value: *const c_char) -> c_int;
35    fn ffw_muxer_set_url(muxer: *mut c_void, url: *const c_char) -> c_int;
36    fn ffw_muxer_set_metadata(
37        stream: *mut c_void,
38        key: *const c_char,
39        value: *const c_char,
40    ) -> c_int;
41    fn ffw_muxer_write_frame(
42        muxer: *mut c_void,
43        packet: *mut c_void,
44        tb_num: c_int,
45        tb_den: c_int,
46    ) -> c_int;
47    fn ffw_muxer_interleaved_write_frame(
48        muxer: *mut c_void,
49        packet: *mut c_void,
50        tb_num: c_int,
51        tb_den: c_int,
52    ) -> c_int;
53    fn ffw_muxer_free(muxer: *mut c_void) -> c_int;
54}
55
56/// Muxer builder.
57pub struct MuxerBuilder {
58    ptr: *mut c_void,
59    streams: Vec<Stream>,
60    interleaved: bool,
61}
62
63impl MuxerBuilder {
64    /// Create a new muxer builder.
65    fn new() -> MuxerBuilder {
66        let ptr = unsafe { ffw_muxer_new() };
67
68        if ptr.is_null() {
69            panic!("unable to allocate a muxer context");
70        }
71
72        MuxerBuilder {
73            ptr,
74            streams: Vec::new(),
75            interleaved: false,
76        }
77    }
78
79    /// Add a new stream with given parameters and return index of the new stream.
80    pub fn add_stream(&mut self, params: &CodecParameters) -> Result<usize, Error> {
81        let stream_index = unsafe { ffw_muxer_new_stream(self.ptr, params.as_ptr()) };
82
83        if stream_index < 0 {
84            return Err(Error::from_raw_error_code(stream_index));
85        }
86
87        let stream = unsafe { ffw_muxer_get_stream(self.ptr, stream_index as _) };
88
89        if stream.is_null() {
90            panic!("stream was not created");
91        }
92
93        let stream = unsafe { Stream::from_raw_ptr(stream) };
94
95        self.streams.push(stream);
96
97        Ok(stream_index as usize)
98    }
99
100    /// Get streams.
101    #[inline]
102    pub fn streams(&self) -> &[Stream] {
103        &self.streams
104    }
105
106    /// Get mutable streams.
107    #[inline]
108    pub fn streams_mut(&mut self) -> &mut [Stream] {
109        &mut self.streams
110    }
111
112    /// Set a muxer option.
113    pub fn set_option<V>(self, name: &str, value: V) -> MuxerBuilder
114    where
115        V: ToString,
116    {
117        let value = CString::new(value.to_string()).expect("invalid option value");
118
119        // NOTE: the "url" field cannot be set using the options interface
120        if name == "url" {
121            let ret = unsafe { ffw_muxer_set_url(self.ptr, value.as_ptr()) };
122
123            if ret < 0 {
124                panic!("unable to allocate URL")
125            }
126        } else {
127            let name = CString::new(name).expect("invalid option name");
128
129            let ret =
130                unsafe { ffw_muxer_set_initial_option(self.ptr, name.as_ptr(), value.as_ptr()) };
131
132            if ret < 0 {
133                panic!("unable to allocate an option");
134            }
135        }
136
137        self
138    }
139
140    /// Set the `url` field of FFmpeg format context to the specified value.
141    ///
142    /// __WARNING__: this is a hack to accommodate certain muxer types (e.g.
143    /// DASH) that bypass avio abstraction layer/produce multiple output files.
144    /// Setting this can cause FFmpeg open its own files or sockets.
145    #[doc(hidden)]
146    #[deprecated(since = "0.17.0", note = "Use `set_option(\"url\", ...)` instead.")]
147    pub fn set_url(self, url: &str) -> MuxerBuilder {
148        self.set_option("url", url)
149    }
150
151    /// Set container metadata.
152    pub fn set_metadata<V>(self, key: &str, value: V) -> Self
153    where
154        V: ToString,
155    {
156        let key = CString::new(key).expect("invalid metadata key");
157        let value = CString::new(value.to_string()).expect("invalid metadata value");
158
159        let ret = unsafe { ffw_muxer_set_metadata(self.ptr, key.as_ptr(), value.as_ptr()) };
160
161        if ret < 0 {
162            panic!("unable to allocate metadata");
163        }
164
165        self
166    }
167
168    /// Set the muxer to do the interleaving automatically. It is disabled by
169    /// default.
170    #[inline]
171    pub fn interleaved(mut self, interleaved: bool) -> MuxerBuilder {
172        self.interleaved = interleaved;
173        self
174    }
175
176    /// Build the muxer.
177    ///
178    /// # Arguments
179    /// * `io_context` - an AVIO writer
180    /// * `format` - an output format
181    pub fn build<T>(mut self, mut io: IO<T>, format: OutputFormat) -> Result<Muxer<T>, Error>
182    where
183        T: Write,
184    {
185        let io_context_ptr = io.io_context_mut().as_mut_ptr();
186        let format_ptr = format.ptr;
187
188        let ret = unsafe { ffw_muxer_init(self.ptr, io_context_ptr, format_ptr) };
189
190        if ret < 0 {
191            return Err(Error::from_raw_error_code(ret));
192        }
193
194        let muxer_ptr = self.ptr;
195
196        self.ptr = ptr::null_mut();
197
198        let res = Muxer {
199            ptr: muxer_ptr,
200            io: Some(io),
201            interleaved: self.interleaved,
202        };
203
204        Ok(res)
205    }
206}
207
208impl Drop for MuxerBuilder {
209    fn drop(&mut self) {
210        unsafe {
211            ffw_muxer_free(self.ptr);
212        }
213    }
214}
215
216unsafe impl Send for MuxerBuilder {}
217unsafe impl Sync for MuxerBuilder {}
218
219/// Muxer.
220pub struct Muxer<T> {
221    ptr: *mut c_void,
222    io: Option<IO<T>>,
223    interleaved: bool,
224}
225
226impl Muxer<()> {
227    /// Get a muxer builder.
228    pub fn builder() -> MuxerBuilder {
229        MuxerBuilder::new()
230    }
231}
232
233impl<T> Muxer<T> {
234    /// Set an option.
235    pub fn set_option<V>(&mut self, name: &str, value: V) -> Result<(), Error>
236    where
237        V: ToString,
238    {
239        let name = CString::new(name).expect("invalid option name");
240        let value = CString::new(value.to_string()).expect("invalid option value");
241
242        let ret =
243            unsafe { ffw_muxer_set_option(self.ptr, name.as_ptr() as _, value.as_ptr() as _) };
244
245        if ret < 0 {
246            Err(Error::from_raw_error_code(ret))
247        } else {
248            Ok(())
249        }
250    }
251
252    /// Mux a given packet. The packet pts and dts are expected to be in
253    /// microseconds. They will be automatically rescaled to match the time
254    /// base of the corresponding stream.
255    pub fn push(&mut self, mut packet: Packet) -> Result<(), Error> {
256        let nb_streams = unsafe { ffw_muxer_get_nb_streams(self.ptr) as usize };
257
258        assert!(packet.stream_index() < nb_streams);
259
260        let tb = packet.time_base();
261
262        let tb_num: c_int = tb.num() as _;
263        let tb_den: c_int = tb.den() as _;
264
265        let ret = unsafe {
266            if self.interleaved {
267                ffw_muxer_interleaved_write_frame(self.ptr, packet.as_mut_ptr(), tb_num, tb_den)
268            } else {
269                ffw_muxer_write_frame(self.ptr, packet.as_mut_ptr(), tb_num, tb_den)
270            }
271        };
272
273        if ret < 0 {
274            Err(Error::from_raw_error_code(ret))
275        } else {
276            Ok(())
277        }
278    }
279
280    /// Flush the muxer.
281    pub fn flush(&mut self) -> Result<(), Error> {
282        let ret = unsafe {
283            if self.interleaved {
284                ffw_muxer_interleaved_write_frame(self.ptr, ptr::null_mut(), 1, 1_000_000)
285            } else {
286                ffw_muxer_write_frame(self.ptr, ptr::null_mut(), 1, 1_000_000)
287            }
288        };
289
290        if ret < 0 {
291            Err(Error::from_raw_error_code(ret))
292        } else {
293            Ok(())
294        }
295    }
296
297    /// Close the muxer and take the underlying IO.
298    pub fn close(mut self) -> Result<IO<T>, Error> {
299        let ret = unsafe { ffw_muxer_free(self.ptr) };
300
301        self.ptr = ptr::null_mut();
302
303        if ret != 0 {
304            Err(Error::from_raw_error_code(ret))
305        } else {
306            Ok(self.io.take().unwrap())
307        }
308    }
309
310    /// Get reference to the underlying IO.
311    pub fn io(&self) -> &IO<T> {
312        self.io.as_ref().unwrap()
313    }
314
315    /// Get mutable reference to the underlying IO.
316    pub fn io_mut(&mut self) -> &mut IO<T> {
317        self.io.as_mut().unwrap()
318    }
319}
320
321impl<T> Drop for Muxer<T> {
322    fn drop(&mut self) {
323        if !self.ptr.is_null() {
324            unsafe {
325                ffw_muxer_free(self.ptr);
326            }
327        }
328    }
329}
330
331unsafe impl<T> Send for Muxer<T> where T: Send {}
332unsafe impl<T> Sync for Muxer<T> where T: Sync {}
333
334/// FFmpeg output format.
335pub struct OutputFormat {
336    ptr: *const c_void,
337}
338
339impl OutputFormat {
340    /// Try to find an output format by its name.
341    pub fn find_by_name(name: &str) -> Option<OutputFormat> {
342        let name = CString::new(name).expect("invalid format name");
343
344        let ptr =
345            unsafe { ffw_guess_output_format(name.as_ptr() as *const _, ptr::null(), ptr::null()) };
346
347        if ptr.is_null() {
348            return None;
349        }
350
351        let res = OutputFormat { ptr };
352
353        Some(res)
354    }
355
356    /// Try to find an output format by the MIME type.
357    pub fn find_by_mime_type(mime_type: &str) -> Option<OutputFormat> {
358        let mime_type = CString::new(mime_type).expect("invalid MIME type");
359
360        let ptr = unsafe {
361            ffw_guess_output_format(ptr::null(), ptr::null(), mime_type.as_ptr() as *const _)
362        };
363
364        if ptr.is_null() {
365            return None;
366        }
367
368        let res = OutputFormat { ptr };
369
370        Some(res)
371    }
372
373    /// Try to guess an output format from a file name.
374    pub fn guess_from_file_name(file_name: &str) -> Option<OutputFormat> {
375        let file_name = CString::new(file_name).expect("invalid file name");
376
377        let ptr = unsafe {
378            ffw_guess_output_format(ptr::null(), file_name.as_ptr() as *const _, ptr::null())
379        };
380
381        if ptr.is_null() {
382            return None;
383        }
384
385        let res = OutputFormat { ptr };
386
387        Some(res)
388    }
389}
390
391unsafe impl Send for OutputFormat {}
392unsafe impl Sync for OutputFormat {}