1pub use crate::util::format::{Pixel, pixel};
2pub use crate::util::format::{Sample, sample};
3use crate::util::interrupt;
4
5pub mod stream;
6
7pub mod chapter;
8
9pub mod context;
10pub use self::context::Context;
11
12pub mod format;
13#[cfg(not(feature = "ffmpeg_5_0"))]
14pub use self::format::list;
15pub use self::format::{Flags, flag};
16pub use self::format::{Input, Output};
17
18pub mod network;
19
20use std::ffi::{CStr, CString};
21use std::path::Path;
22use std::ptr;
23use std::str::from_utf8_unchecked;
24
25use crate::ffi::*;
26use crate::{Dictionary, Error, Format};
27
28#[cfg(not(feature = "ffmpeg_5_0"))]
29pub fn register_all() {
30 unsafe {
31 av_register_all();
32 }
33}
34
35#[cfg(not(feature = "ffmpeg_5_0"))]
36pub fn register(format: &Format) {
37 match *format {
38 Format::Input(ref format) => unsafe {
39 av_register_input_format(format.as_ptr() as *mut _);
40 },
41
42 Format::Output(ref format) => unsafe {
43 av_register_output_format(format.as_ptr() as *mut _);
44 },
45 }
46}
47
48pub fn version() -> u32 {
49 unsafe { avformat_version() }
50}
51
52pub fn configuration() -> &'static str {
53 unsafe { from_utf8_unchecked(CStr::from_ptr(avformat_configuration()).to_bytes()) }
54}
55
56pub fn license() -> &'static str {
57 unsafe { from_utf8_unchecked(CStr::from_ptr(avformat_license()).to_bytes()) }
58}
59
60fn from_path<P: AsRef<Path> + ?Sized>(path: &P) -> CString {
62 CString::new(path.as_ref().as_os_str().to_str().unwrap()).unwrap()
63}
64
65fn opt_cstring(s: Option<&str>) -> Result<Option<CString>, Error> {
66 s.map(CString::new)
67 .transpose()
68 .map_err(|_| Error::Other { errno: EINVAL })
69}
70
71pub fn open<P: AsRef<Path> + ?Sized>(path: &P, format: &Format) -> Result<Context, Error> {
73 unsafe {
74 let mut ps = ptr::null_mut();
75 let path = from_path(path);
76
77 match *format {
78 Format::Input(ref format) => match avformat_open_input(
79 &mut ps,
80 path.as_ptr(),
81 format.as_ptr() as *mut _,
82 ptr::null_mut(),
83 ) {
84 0 => match avformat_find_stream_info(ps, ptr::null_mut()) {
85 r if r >= 0 => Ok(Context::Input(context::Input::wrap(ps))),
86 e => Err(Error::from(e)),
87 },
88
89 e => Err(Error::from(e)),
90 },
91
92 Format::Output(ref format) => match avformat_alloc_output_context2(
93 &mut ps,
94 format.as_ptr() as *mut _,
95 ptr::null(),
96 path.as_ptr(),
97 ) {
98 0 => match avio_open(&mut (*ps).pb, path.as_ptr(), AVIO_FLAG_WRITE) {
99 0 => Ok(Context::Output(context::Output::wrap(ps))),
100 e => Err(Error::from(e)),
101 },
102
103 e => Err(Error::from(e)),
104 },
105 }
106 }
107}
108
109pub fn open_with<P: AsRef<Path> + ?Sized>(
110 path: &P,
111 format: &Format,
112 options: Dictionary,
113) -> Result<Context, Error> {
114 unsafe {
115 let mut ps = ptr::null_mut();
116 let path = from_path(path);
117 let mut opts = options.disown();
118
119 match *format {
120 Format::Input(ref format) => {
121 let res = avformat_open_input(
122 &mut ps,
123 path.as_ptr(),
124 format.as_ptr() as *mut _,
125 &mut opts,
126 );
127
128 Dictionary::own(opts);
129
130 match res {
131 0 => match avformat_find_stream_info(ps, ptr::null_mut()) {
132 r if r >= 0 => Ok(Context::Input(context::Input::wrap(ps))),
133 e => Err(Error::from(e)),
134 },
135
136 e => Err(Error::from(e)),
137 }
138 }
139
140 Format::Output(ref format) => match avformat_alloc_output_context2(
141 &mut ps,
142 format.as_ptr() as *mut _,
143 ptr::null(),
144 path.as_ptr(),
145 ) {
146 0 => match avio_open(&mut (*ps).pb, path.as_ptr(), AVIO_FLAG_WRITE) {
147 0 => Ok(Context::Output(context::Output::wrap(ps))),
148 e => Err(Error::from(e)),
149 },
150
151 e => Err(Error::from(e)),
152 },
153 }
154 }
155}
156
157pub fn input<P: AsRef<Path> + ?Sized>(path: &P) -> Result<context::Input, Error> {
158 unsafe {
159 let mut ps = ptr::null_mut();
160 let path = from_path(path);
161
162 match avformat_open_input(&mut ps, path.as_ptr(), ptr::null_mut(), ptr::null_mut()) {
163 0 => match avformat_find_stream_info(ps, ptr::null_mut()) {
164 r if r >= 0 => Ok(context::Input::wrap(ps)),
165 e => {
166 avformat_close_input(&mut ps);
167 Err(Error::from(e))
168 }
169 },
170
171 e => Err(Error::from(e)),
172 }
173 }
174}
175
176pub fn input_with_dictionary<P: AsRef<Path> + ?Sized>(
177 path: &P,
178 options: Dictionary,
179) -> Result<context::Input, Error> {
180 unsafe {
181 let mut ps = ptr::null_mut();
182 let path = from_path(path);
183 let mut opts = options.disown();
184 let res = avformat_open_input(&mut ps, path.as_ptr(), ptr::null_mut(), &mut opts);
185
186 Dictionary::own(opts);
187
188 match res {
189 0 => match avformat_find_stream_info(ps, ptr::null_mut()) {
190 r if r >= 0 => Ok(context::Input::wrap(ps)),
191 e => {
192 avformat_close_input(&mut ps);
193 Err(Error::from(e))
194 }
195 },
196
197 e => Err(Error::from(e)),
198 }
199 }
200}
201
202pub fn input_with_interrupt<P: AsRef<Path> + ?Sized, F>(
203 path: &P,
204 closure: F,
205) -> Result<context::Input, Error>
206where
207 F: FnMut() -> bool + Send + 'static,
208{
209 unsafe {
210 let mut ps = avformat_alloc_context();
211 if ps.is_null() {
212 return Err(Error::Other { errno: ENOMEM });
213 }
214 let path = from_path(path);
215 let interrupt = interrupt::new(Box::new(closure));
216 (*ps).interrupt_callback = interrupt.interrupt;
217
218 match avformat_open_input(&mut ps, path.as_ptr(), ptr::null_mut(), ptr::null_mut()) {
219 0 => match avformat_find_stream_info(ps, ptr::null_mut()) {
220 r if r >= 0 => Ok(context::Input::wrap_with_interrupt(ps, interrupt.guard)),
221 e => {
222 avformat_close_input(&mut ps);
223 Err(Error::from(e))
224 }
225 },
226
227 e => Err(Error::from(e)),
228 }
229 }
230}
231
232pub fn input_with_interrupt_and_dictionary<P: AsRef<Path> + ?Sized, F>(
233 path: &P,
234 closure: F,
235 options: Dictionary,
236) -> Result<context::Input, Error>
237where
238 F: FnMut() -> bool + Send + 'static,
239{
240 unsafe {
241 let mut ps = avformat_alloc_context();
242 if ps.is_null() {
243 return Err(Error::Other { errno: ENOMEM });
244 }
245 let interrupt = interrupt::new(Box::new(closure));
246 (*ps).interrupt_callback = interrupt.interrupt;
247 let path = from_path(path);
248
249 let mut opts = options.disown();
250 let res = avformat_open_input(&raw mut ps, path.as_ptr(), ptr::null_mut(), &raw mut opts);
251 Dictionary::own(opts);
252
253 match res {
254 0 => match avformat_find_stream_info(ps, ptr::null_mut()) {
255 r if r >= 0 => Ok(context::Input::wrap_with_interrupt(ps, interrupt.guard)),
256 e => {
257 avformat_close_input(&raw mut ps);
258 Err(Error::from(e))
259 }
260 },
261
262 e => Err(Error::from(e)),
263 }
264 }
265}
266pub fn input_from_stream(
273 custom_io: context::StreamIo,
274 filename: Option<&str>,
275 options: Option<Dictionary>,
276) -> Result<context::Input, Error> {
277 input_from_stream_impl(custom_io, filename, options, None)
278}
279
280pub fn input_from_stream_with_interrupt<F>(
289 custom_io: context::StreamIo,
290 filename: Option<&str>,
291 options: Option<Dictionary>,
292 closure: F,
293) -> Result<context::Input, Error>
294where
295 F: FnMut() -> bool + Send + 'static,
296{
297 input_from_stream_impl(
298 custom_io,
299 filename,
300 options,
301 Some(interrupt::new(Box::new(closure))),
302 )
303}
304
305fn input_from_stream_impl(
317 mut custom_io: context::StreamIo,
318 filename: Option<&str>,
319 options: Option<Dictionary>,
320 interrupt: Option<interrupt::Interrupt>,
321) -> Result<context::Input, Error> {
322 if custom_io.is_writable() {
323 return Err(Error::Other { errno: EINVAL });
324 }
325
326 let filename = opt_cstring(filename)?;
327 let filename_ptr = filename.as_ref().map_or(ptr::null(), |f| f.as_ptr());
328
329 unsafe {
330 let mut ps = avformat_alloc_context();
331 if ps.is_null() {
332 return Err(Error::Other { errno: ENOMEM });
333 }
334 if let Some(ref it) = interrupt {
335 (*ps).interrupt_callback = it.interrupt;
336 custom_io.set_interrupt(it.interrupt);
337 }
338 (*ps).pb = custom_io.as_mut_ptr();
339 (*ps).flags |= AVFMT_FLAG_CUSTOM_IO;
340
341 let result = if let Some(opts) = options {
342 let mut opts = opts.disown();
343 let res = avformat_open_input(&mut ps, filename_ptr, ptr::null_mut(), &mut opts);
344 Dictionary::own(opts);
345 res
346 } else {
347 avformat_open_input(&mut ps, filename_ptr, ptr::null_mut(), ptr::null_mut())
348 };
349
350 match result {
351 0 => match avformat_find_stream_info(ps, ptr::null_mut()) {
352 r if r >= 0 => Ok(match interrupt {
353 Some(it) => {
354 context::Input::wrap_with_custom_io_and_interrupt(ps, custom_io, it.guard)
355 }
356 None => context::Input::wrap_with_custom_io(ps, custom_io),
357 }),
358 e => {
359 avformat_close_input(&mut ps);
360 Err(Error::from(e))
361 }
362 },
363
364 e => Err(Error::from(e)),
365 }
366 }
367}
368
369pub fn output<P: AsRef<Path> + ?Sized>(path: &P) -> Result<context::Output, Error> {
370 unsafe {
371 let mut ps = ptr::null_mut();
372 let path = from_path(path);
373
374 match avformat_alloc_output_context2(&mut ps, ptr::null_mut(), ptr::null(), path.as_ptr()) {
375 0 => match avio_open(&mut (*ps).pb, path.as_ptr(), AVIO_FLAG_WRITE) {
376 0 => Ok(context::Output::wrap(ps)),
377 e => Err(Error::from(e)),
378 },
379
380 e => Err(Error::from(e)),
381 }
382 }
383}
384
385pub fn output_with<P: AsRef<Path> + ?Sized>(
386 path: &P,
387 options: Dictionary,
388) -> Result<context::Output, Error> {
389 unsafe {
390 let mut ps = ptr::null_mut();
391 let path = from_path(path);
392 let mut opts = options.disown();
393
394 match avformat_alloc_output_context2(&mut ps, ptr::null_mut(), ptr::null(), path.as_ptr()) {
395 0 => {
396 let res = avio_open2(
397 &mut (*ps).pb,
398 path.as_ptr(),
399 AVIO_FLAG_WRITE,
400 ptr::null(),
401 &mut opts,
402 );
403
404 Dictionary::own(opts);
405
406 match res {
407 0 => Ok(context::Output::wrap(ps)),
408 e => Err(Error::from(e)),
409 }
410 }
411
412 e => Err(Error::from(e)),
413 }
414 }
415}
416
417pub fn output_as<P: AsRef<Path> + ?Sized>(
418 path: &P,
419 format: &str,
420) -> Result<context::Output, Error> {
421 unsafe {
422 let mut ps = ptr::null_mut();
423 let path = from_path(path);
424 let format = CString::new(format).unwrap();
425
426 match avformat_alloc_output_context2(
427 &mut ps,
428 ptr::null_mut(),
429 format.as_ptr(),
430 path.as_ptr(),
431 ) {
432 0 => match avio_open(&mut (*ps).pb, path.as_ptr(), AVIO_FLAG_WRITE) {
433 0 => Ok(context::Output::wrap(ps)),
434 e => Err(Error::from(e)),
435 },
436
437 e => Err(Error::from(e)),
438 }
439 }
440}
441
442pub fn output_as_with<P: AsRef<Path> + ?Sized>(
443 path: &P,
444 format: &str,
445 options: Dictionary,
446) -> Result<context::Output, Error> {
447 unsafe {
448 let mut ps = ptr::null_mut();
449 let path = from_path(path);
450 let format = CString::new(format).unwrap();
451 let mut opts = options.disown();
452
453 match avformat_alloc_output_context2(
454 &mut ps,
455 ptr::null_mut(),
456 format.as_ptr(),
457 path.as_ptr(),
458 ) {
459 0 => {
460 let res = avio_open2(
461 &mut (*ps).pb,
462 path.as_ptr(),
463 AVIO_FLAG_WRITE,
464 ptr::null(),
465 &mut opts,
466 );
467
468 Dictionary::own(opts);
469
470 match res {
471 0 => Ok(context::Output::wrap(ps)),
472 e => Err(Error::from(e)),
473 }
474 }
475
476 e => Err(Error::from(e)),
477 }
478 }
479}
480
481pub fn output_to_stream(
493 mut custom_io: context::StreamIo,
494 filename: Option<&str>,
495 format: Option<&str>,
496) -> Result<context::Output, Error> {
497 if !custom_io.is_writable() {
498 return Err(Error::Other { errno: EINVAL });
499 }
500
501 let filename = opt_cstring(filename)?;
502 let filename_ptr = filename.as_ref().map_or(ptr::null(), |f| f.as_ptr());
503
504 let format = opt_cstring(format)?;
505 let format_ptr = format.as_ref().map_or(ptr::null(), |f| f.as_ptr());
506
507 unsafe {
508 let mut ps = ptr::null_mut();
509
510 match avformat_alloc_output_context2(&mut ps, ptr::null_mut(), format_ptr, filename_ptr) {
511 0 => {
512 if (*(*ps).oformat).flags & AVFMT_NOFILE != 0 {
517 avformat_free_context(ps);
518 return Err(Error::Other { errno: EINVAL });
519 }
520
521 (*ps).pb = custom_io.as_mut_ptr();
522 (*ps).flags |= AVFMT_FLAG_CUSTOM_IO;
523
524 Ok(context::Output::wrap_with_custom_io(ps, custom_io))
525 }
526
527 e => Err(Error::from(e)),
528 }
529 }
530}
531
532#[cfg(test)]
533mod tests {
534 use super::*;
535
536 #[test]
537 fn input_with_interrupt_and_dictionary_accepts_str_path() {
538 let result = input_with_interrupt_and_dictionary(
539 "/ffmpeg-next-input-does-not-exist",
540 || false,
541 Dictionary::new(),
542 );
543
544 assert!(result.is_err());
545 }
546}