1use 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
56pub struct MuxerBuilder {
58 ptr: *mut c_void,
59 streams: Vec<Stream>,
60 interleaved: bool,
61}
62
63impl MuxerBuilder {
64 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 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 #[inline]
102 pub fn streams(&self) -> &[Stream] {
103 &self.streams
104 }
105
106 #[inline]
108 pub fn streams_mut(&mut self) -> &mut [Stream] {
109 &mut self.streams
110 }
111
112 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 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 #[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 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 #[inline]
171 pub fn interleaved(mut self, interleaved: bool) -> MuxerBuilder {
172 self.interleaved = interleaved;
173 self
174 }
175
176 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
219pub struct Muxer<T> {
221 ptr: *mut c_void,
222 io: Option<IO<T>>,
223 interleaved: bool,
224}
225
226impl Muxer<()> {
227 pub fn builder() -> MuxerBuilder {
229 MuxerBuilder::new()
230 }
231}
232
233impl<T> Muxer<T> {
234 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 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 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 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 pub fn io(&self) -> &IO<T> {
312 self.io.as_ref().unwrap()
313 }
314
315 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
334pub struct OutputFormat {
336 ptr: *const c_void,
337}
338
339impl OutputFormat {
340 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 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 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 {}