playa_ffmpeg/format/mod.rs
1//! Container format support for muxing and demuxing.
2//!
3//! This module provides functionality for reading (demuxing) and writing (muxing) multimedia
4//! container formats. It wraps FFmpeg's `libavformat` library with safe Rust interfaces.
5//!
6//! # Main Components
7//!
8//! - [`Context`] - Format context managing streams and container metadata
9//! - [`stream`] - Individual media streams within a container
10//! - [`chapter`] - Chapter/bookmark support for seekable formats
11//! - [`mod@format`] - Container format information and discovery
12//!
13//! # Common Operations
14//!
15//! ## Opening Files for Reading
16//!
17//! Use [`input()`] to open a media file for reading:
18//!
19//! ```ignore
20//! let mut input = ffmpeg::format::input(&"video.mp4")?;
21//!
22//! // Find best video stream
23//! let stream = input.streams().best(Type::Video).unwrap();
24//! let decoder = stream.codec().decoder().video()?;
25//! ```
26//!
27//! ## Opening Files for Writing
28//!
29//! Use [`output()`] to create a new media file:
30//!
31//! ```ignore
32//! let mut output = ffmpeg::format::output(&"output.mp4")?;
33//!
34//! // Add video stream
35//! let mut stream = output.add_stream(encoder)?;
36//! stream.set_parameters(&encoder);
37//!
38//! output.write_header()?;
39//! // ... write packets ...
40//! output.write_trailer()?;
41//! ```
42
43pub use crate::util::format::{Pixel, Sample, pixel, sample};
44use crate::util::interrupt;
45
46pub mod stream;
47
48pub mod chapter;
49
50pub mod context;
51pub use self::context::Context;
52
53pub mod format;
54#[cfg(not(feature = "ffmpeg_5_0"))]
55pub use self::format::list;
56pub use self::format::{Flags, Input, Output, flag};
57
58pub mod network;
59
60use std::{
61 ffi::{CStr, CString},
62 path::Path,
63 ptr,
64 str::from_utf8_unchecked,
65};
66
67use crate::{Dictionary, Error, Format, ffi::*};
68
69/// Registers all muxers and demuxers (FFmpeg < 5.0 only).
70///
71/// In FFmpeg 5.0+, formats are automatically registered and this is a no-op.
72/// Called automatically by [`crate::init()`].
73#[cfg(not(feature = "ffmpeg_5_0"))]
74pub fn register_all() {
75 unsafe {
76 av_register_all();
77 }
78}
79
80/// Registers a specific format (FFmpeg < 5.0 only).
81///
82/// In FFmpeg 5.0+, this is a no-op. Most users should rely on [`register_all()`]
83/// or automatic registration instead of manually registering formats.
84#[cfg(not(feature = "ffmpeg_5_0"))]
85pub fn register(format: &Format) {
86 match *format {
87 Format::Input(ref format) => unsafe {
88 av_register_input_format(format.as_ptr() as *mut _);
89 },
90
91 Format::Output(ref format) => unsafe {
92 av_register_output_format(format.as_ptr() as *mut _);
93 },
94 }
95}
96
97/// Returns the libavformat version number.
98///
99/// The version is encoded as `(major << 16) | (minor << 8) | micro`.
100pub fn version() -> u32 {
101 unsafe { avformat_version() }
102}
103
104/// Returns the libavformat build configuration string.
105///
106/// Shows compile-time options used when building FFmpeg's format library.
107pub fn configuration() -> &'static str {
108 unsafe { from_utf8_unchecked(CStr::from_ptr(avformat_configuration()).to_bytes()) }
109}
110
111/// Returns the libavformat license string.
112///
113/// Typically "LGPL version 2.1 or later" unless built with GPL components.
114pub fn license() -> &'static str {
115 unsafe { from_utf8_unchecked(CStr::from_ptr(avformat_license()).to_bytes()) }
116}
117
118/// Converts a path to a C string for FFmpeg API calls.
119///
120/// # Panics
121///
122/// Panics if the path contains invalid UTF-8 or null bytes.
123// XXX: use to_cstring when stable
124fn from_path<P: AsRef<Path> + ?Sized>(path: &P) -> CString {
125 CString::new(path.as_ref().as_os_str().to_str().unwrap()).unwrap()
126}
127
128/// Opens a file with a specific format (input or output).
129///
130/// Prefer [`input()`] or [`output()`] unless you need format override.
131///
132/// # Parameters
133///
134/// * `path` - File path to open
135/// * `format` - Format to use (Input for reading, Output for writing)
136///
137/// # Errors
138///
139/// Returns an error if the file cannot be opened or the format is unsupported.
140///
141/// # Note
142///
143/// For input contexts, this automatically probes stream information after opening.
144// NOTE: this will be better with specialization or anonymous return types
145pub fn open<P: AsRef<Path> + ?Sized>(path: &P, format: &Format) -> Result<Context, Error> {
146 unsafe {
147 let mut ps = ptr::null_mut();
148 let path = from_path(path);
149
150 match *format {
151 Format::Input(ref format) => match avformat_open_input(&mut ps, path.as_ptr(), format.as_ptr() as *mut _, ptr::null_mut()) {
152 0 => match avformat_find_stream_info(ps, ptr::null_mut()) {
153 r if r >= 0 => Ok(Context::Input(context::Input::wrap(ps))),
154 e => Err(Error::from(e)),
155 },
156
157 e => Err(Error::from(e)),
158 },
159
160 Format::Output(ref format) => match avformat_alloc_output_context2(&mut ps, format.as_ptr() as *mut _, ptr::null(), path.as_ptr()) {
161 0 => match avio_open(&mut (*ps).pb, path.as_ptr(), AVIO_FLAG_WRITE) {
162 0 => Ok(Context::Output(context::Output::wrap(ps))),
163 e => Err(Error::from(e)),
164 },
165
166 e => Err(Error::from(e)),
167 },
168 }
169 }
170}
171
172/// Opens a file with a specific format and options dictionary.
173///
174/// Like [`open()`] but allows passing codec/format options.
175///
176/// # Parameters
177///
178/// * `path` - File path to open
179/// * `format` - Format to use
180/// * `options` - Dictionary of format-specific options
181pub fn open_with<P: AsRef<Path> + ?Sized>(path: &P, format: &Format, options: Dictionary) -> Result<Context, Error> {
182 unsafe {
183 let mut ps = ptr::null_mut();
184 let path = from_path(path);
185 let mut opts = options.disown();
186
187 match *format {
188 Format::Input(ref format) => {
189 let res = avformat_open_input(&mut ps, path.as_ptr(), format.as_ptr() as *mut _, &mut opts);
190
191 Dictionary::own(opts);
192
193 match res {
194 0 => match avformat_find_stream_info(ps, ptr::null_mut()) {
195 r if r >= 0 => Ok(Context::Input(context::Input::wrap(ps))),
196 e => Err(Error::from(e)),
197 },
198
199 e => Err(Error::from(e)),
200 }
201 }
202
203 Format::Output(ref format) => match avformat_alloc_output_context2(&mut ps, format.as_ptr() as *mut _, ptr::null(), path.as_ptr()) {
204 0 => match avio_open(&mut (*ps).pb, path.as_ptr(), AVIO_FLAG_WRITE) {
205 0 => Ok(Context::Output(context::Output::wrap(ps))),
206 e => Err(Error::from(e)),
207 },
208
209 e => Err(Error::from(e)),
210 },
211 }
212 }
213}
214
215/// Opens a media file for reading (demuxing).
216///
217/// This is the primary function for opening input files. It automatically detects the
218/// container format and probes all streams to gather codec information.
219///
220/// # Parameters
221///
222/// * `path` - Path to the media file (supports various protocols: file://, http://, rtsp://, etc.)
223///
224/// # Returns
225///
226/// An [`context::Input`] that can be used to access streams and read packets.
227///
228/// # Errors
229///
230/// - File not found or inaccessible
231/// - Unsupported or corrupted format
232/// - Permission denied
233///
234/// # Example
235///
236/// ```ignore
237/// let mut input = ffmpeg::format::input(&"video.mp4")?;
238///
239/// // Find the best video stream
240/// let stream = input.streams().best(Type::Video).ok_or(Error::StreamNotFound)?;
241/// let stream_index = stream.index();
242///
243/// // Create decoder for this stream
244/// let decoder = stream.codec().decoder().video()?;
245/// ```
246pub fn input<P: AsRef<Path> + ?Sized>(path: &P) -> Result<context::Input, Error> {
247 unsafe {
248 let mut ps = ptr::null_mut();
249 let path = from_path(path);
250
251 match avformat_open_input(&mut ps, path.as_ptr(), ptr::null_mut(), ptr::null_mut()) {
252 0 => match avformat_find_stream_info(ps, ptr::null_mut()) {
253 r if r >= 0 => Ok(context::Input::wrap(ps)),
254 e => {
255 avformat_close_input(&mut ps);
256 Err(Error::from(e))
257 }
258 },
259
260 e => Err(Error::from(e)),
261 }
262 }
263}
264
265/// Opens a media file for reading with options dictionary.
266///
267/// Like [`input()`] but allows passing format-specific options (e.g., timeouts,
268/// buffer sizes, protocol options).
269///
270/// # Parameters
271///
272/// * `path` - Path to the media file
273/// * `options` - Dictionary of format/protocol options
274pub fn input_with_dictionary<P: AsRef<Path> + ?Sized>(path: &P, options: Dictionary) -> Result<context::Input, Error> {
275 unsafe {
276 let mut ps = ptr::null_mut();
277 let path = from_path(path);
278 let mut opts = options.disown();
279 let res = avformat_open_input(&mut ps, path.as_ptr(), ptr::null_mut(), &mut opts);
280
281 Dictionary::own(opts);
282
283 match res {
284 0 => match avformat_find_stream_info(ps, ptr::null_mut()) {
285 r if r >= 0 => Ok(context::Input::wrap(ps)),
286 e => {
287 avformat_close_input(&mut ps);
288 Err(Error::from(e))
289 }
290 },
291
292 e => Err(Error::from(e)),
293 }
294 }
295}
296
297/// Opens a media file for reading with interrupt callback.
298///
299/// Allows cancellation of long-running operations (network streams, slow I/O).
300/// The callback is called periodically; returning `true` aborts the operation.
301///
302/// # Parameters
303///
304/// * `path` - Path to the media file
305/// * `closure` - Callback invoked periodically, return `true` to abort
306///
307/// # Example
308///
309/// ```ignore
310/// use std::sync::atomic::{AtomicBool, Ordering};
311/// use std::sync::Arc;
312///
313/// let should_abort = Arc::new(AtomicBool::new(false));
314/// let abort_flag = should_abort.clone();
315///
316/// let input = ffmpeg::format::input_with_interrupt(&"http://stream.example.com/live", move || {
317/// abort_flag.load(Ordering::Relaxed)
318/// })?;
319/// ```
320pub fn input_with_interrupt<P: AsRef<Path> + ?Sized, F>(path: &P, closure: F) -> Result<context::Input, Error>
321where
322 F: FnMut() -> bool,
323{
324 unsafe {
325 let mut ps = avformat_alloc_context();
326 let path = from_path(path);
327 // Set interrupt callback for cancellation support
328 (*ps).interrupt_callback = interrupt::new(Box::new(closure)).interrupt;
329
330 match avformat_open_input(&mut ps, path.as_ptr(), ptr::null_mut(), ptr::null_mut()) {
331 0 => match avformat_find_stream_info(ps, ptr::null_mut()) {
332 r if r >= 0 => Ok(context::Input::wrap(ps)),
333 e => {
334 avformat_close_input(&mut ps);
335 Err(Error::from(e))
336 }
337 },
338
339 e => Err(Error::from(e)),
340 }
341 }
342}
343
344/// Opens a media file for writing (muxing).
345///
346/// Creates a new output file with format auto-detected from the file extension.
347/// The file is created/truncated and ready for writing after adding streams.
348///
349/// # Parameters
350///
351/// * `path` - Path to the output file
352///
353/// # Returns
354///
355/// An [`context::Output`] that can be used to add streams and write packets.
356///
357/// # Errors
358///
359/// - File cannot be created (permission denied, invalid path)
360/// - Format cannot be determined from extension
361/// - Unsupported format
362///
363/// # Example
364///
365/// ```ignore
366/// let mut output = ffmpeg::format::output(&"output.mp4")?;
367///
368/// // Add video stream
369/// let mut stream = output.add_stream(encoder)?;
370/// stream.set_parameters(&encoder);
371///
372/// // Write header
373/// output.write_header()?;
374///
375/// // Write packets...
376/// output.write_packet(&packet)?;
377///
378/// // Finalize
379/// output.write_trailer()?;
380/// ```
381pub fn output<P: AsRef<Path> + ?Sized>(path: &P) -> Result<context::Output, Error> {
382 unsafe {
383 let mut ps = ptr::null_mut();
384 let path = from_path(path);
385
386 match avformat_alloc_output_context2(&mut ps, ptr::null_mut(), ptr::null(), path.as_ptr()) {
387 0 => match avio_open(&mut (*ps).pb, path.as_ptr(), AVIO_FLAG_WRITE) {
388 0 => Ok(context::Output::wrap(ps)),
389 e => Err(Error::from(e)),
390 },
391
392 e => Err(Error::from(e)),
393 }
394 }
395}
396
397/// Opens a media file for writing with options dictionary.
398///
399/// Like [`output()`] but allows passing I/O and format options.
400///
401/// # Parameters
402///
403/// * `path` - Path to the output file
404/// * `options` - Dictionary of I/O and format options
405pub fn output_with<P: AsRef<Path> + ?Sized>(path: &P, options: Dictionary) -> Result<context::Output, Error> {
406 unsafe {
407 let mut ps = ptr::null_mut();
408 let path = from_path(path);
409 let mut opts = options.disown();
410
411 match avformat_alloc_output_context2(&mut ps, ptr::null_mut(), ptr::null(), path.as_ptr()) {
412 0 => {
413 let res = avio_open2(&mut (*ps).pb, path.as_ptr(), AVIO_FLAG_WRITE, ptr::null(), &mut opts);
414
415 Dictionary::own(opts);
416
417 match res {
418 0 => Ok(context::Output::wrap(ps)),
419 e => Err(Error::from(e)),
420 }
421 }
422
423 e => Err(Error::from(e)),
424 }
425 }
426}
427
428/// Opens a media file for writing with explicit format specification.
429///
430/// Use this when the file extension doesn't match the desired format
431/// or when writing to streams/pipes.
432///
433/// # Parameters
434///
435/// * `path` - Path to the output file
436/// * `format` - Format name (e.g., "mp4", "matroska", "mpeg")
437///
438/// # Example
439///
440/// ```ignore
441/// // Create MP4 file with .bin extension
442/// let output = ffmpeg::format::output_as(&"stream.bin", "mp4")?;
443/// ```
444pub fn output_as<P: AsRef<Path> + ?Sized>(path: &P, format: &str) -> Result<context::Output, Error> {
445 unsafe {
446 let mut ps = ptr::null_mut();
447 let path = from_path(path);
448 let format = CString::new(format).unwrap();
449
450 match avformat_alloc_output_context2(&mut ps, ptr::null_mut(), format.as_ptr(), path.as_ptr()) {
451 0 => match avio_open(&mut (*ps).pb, path.as_ptr(), AVIO_FLAG_WRITE) {
452 0 => Ok(context::Output::wrap(ps)),
453 e => Err(Error::from(e)),
454 },
455
456 e => Err(Error::from(e)),
457 }
458 }
459}
460
461/// Opens a media file for writing with explicit format and options.
462///
463/// Combines [`output_as()`] with options dictionary support.
464///
465/// # Parameters
466///
467/// * `path` - Path to the output file
468/// * `format` - Format name
469/// * `options` - Dictionary of I/O and format options
470pub fn output_as_with<P: AsRef<Path> + ?Sized>(path: &P, format: &str, options: Dictionary) -> Result<context::Output, Error> {
471 unsafe {
472 let mut ps = ptr::null_mut();
473 let path = from_path(path);
474 let format = CString::new(format).unwrap();
475 let mut opts = options.disown();
476
477 match avformat_alloc_output_context2(&mut ps, ptr::null_mut(), format.as_ptr(), path.as_ptr()) {
478 0 => {
479 let res = avio_open2(&mut (*ps).pb, path.as_ptr(), AVIO_FLAG_WRITE, ptr::null(), &mut opts);
480
481 Dictionary::own(opts);
482
483 match res {
484 0 => Ok(context::Output::wrap(ps)),
485 e => Err(Error::from(e)),
486 }
487 }
488
489 e => Err(Error::from(e)),
490 }
491 }
492}