ffmpeg-the-third 5.0.0+ffmpeg-8.1

Safe FFmpeg wrapper (FFmpeg 5+ compatible fork of the ffmpeg crate)
Documentation
#[cfg(feature = "ffmpeg_8_1")]
pub use crate::util::format::AlphaMode;
pub use crate::util::format::{pixel, Pixel};
pub use crate::util::format::{sample, Sample};

use crate::util::interrupt;

pub mod stream;

pub mod chapter;

pub mod context;
pub use self::context::Context;

pub mod format;
pub use self::format::{flag, Flags};
pub use self::format::{Input, Output};

pub mod network;

use std::ffi::{CString, OsStr};
use std::ptr;

use crate::ffi::*;
use crate::utils;
use crate::{AsMutPtr, Error};

pub fn version() -> u32 {
    unsafe { avformat_version() }
}

pub fn configuration() -> &'static str {
    unsafe { utils::str_from_c_ptr(avformat_configuration()) }
}

pub fn license() -> &'static str {
    unsafe { utils::str_from_c_ptr(avformat_license()) }
}

pub fn input<P: AsRef<OsStr>>(path_or_url: P) -> Result<context::Input, Error> {
    unsafe {
        let mut ps = ptr::null_mut();
        let path = from_os_str(path_or_url);

        match avformat_open_input(&mut ps, path.as_ptr(), ptr::null_mut(), ptr::null_mut()) {
            0 => match avformat_find_stream_info(ps, ptr::null_mut()) {
                r if r >= 0 => Ok(context::Input::wrap(ps)),
                e => {
                    avformat_close_input(&mut ps);
                    Err(Error::from(e))
                }
            },

            e => Err(Error::from(e)),
        }
    }
}

pub fn input_with_dictionary<P, Dict>(
    path_or_url: P,
    mut options: Dict,
) -> Result<context::Input, Error>
where
    Dict: AsMutPtr<*mut AVDictionary>,
    P: AsRef<OsStr>,
{
    unsafe {
        let mut ps = ptr::null_mut();
        let path = from_os_str(path_or_url);
        let res = avformat_open_input(
            &mut ps,
            path.as_ptr(),
            ptr::null_mut(),
            options.as_mut_ptr(),
        );

        match res {
            0 => match avformat_find_stream_info(ps, ptr::null_mut()) {
                r if r >= 0 => Ok(context::Input::wrap(ps)),
                e => {
                    avformat_close_input(&mut ps);
                    Err(Error::from(e))
                }
            },

            e => Err(Error::from(e)),
        }
    }
}

pub fn input_with_interrupt<P, F>(path_or_url: P, closure: F) -> Result<context::Input, Error>
where
    P: AsRef<OsStr>,
    F: FnMut() -> bool,
{
    unsafe {
        let mut ps = avformat_alloc_context();
        let path = from_os_str(path_or_url);
        (*ps).interrupt_callback = interrupt::new(Box::new(closure)).interrupt;

        match avformat_open_input(&mut ps, path.as_ptr(), ptr::null_mut(), ptr::null_mut()) {
            0 => match avformat_find_stream_info(ps, ptr::null_mut()) {
                r if r >= 0 => Ok(context::Input::wrap(ps)),
                e => {
                    avformat_close_input(&mut ps);
                    Err(Error::from(e))
                }
            },

            e => Err(Error::from(e)),
        }
    }
}

fn from_os_str(path_or_url: impl AsRef<OsStr>) -> CString {
    CString::new(path_or_url.as_ref().as_encoded_bytes()).unwrap()
}

fn alloc_context(
    format_name: *const libc::c_char,
    filename: *const libc::c_char,
) -> Result<context::Output, Error> {
    let mut ps = ptr::null_mut();

    unsafe {
        let res = avformat_alloc_output_context2(&mut ps, ptr::null(), format_name, filename);
        if res >= 0 {
            Ok(context::Output::wrap(ps))
        } else {
            Err(Error::from(res))
        }
    }
}

fn open_context_write(
    ctx: &mut context::Output,
    filename: *const libc::c_char,
    opts: *mut *mut AVDictionary,
) -> Result<(), Error> {
    let res = unsafe {
        avio_open2(
            &mut (*ctx.as_mut_ptr()).pb,
            filename,
            AVIO_FLAG_WRITE,
            ptr::null(),
            opts,
        )
    };

    if res >= 0 {
        Ok(())
    } else {
        Err(Error::from(res))
    }
}

pub fn output<P: AsRef<OsStr>>(path_or_url: P) -> Result<context::Output, Error> {
    let filename = from_os_str(path_or_url);
    let mut ctx = alloc_context(ptr::null(), filename.as_ptr())?;

    if !ctx.format().flags().contains(Flags::NO_FILE) {
        open_context_write(&mut ctx, filename.as_ptr(), ptr::null_mut())?;
    }

    Ok(ctx)
}

pub fn output_with<P, Dict>(path_or_url: P, mut options: Dict) -> Result<context::Output, Error>
where
    P: AsRef<OsStr>,
    Dict: AsMutPtr<*mut AVDictionary>,
{
    let path = from_os_str(path_or_url);
    let mut ctx = alloc_context(ptr::null(), path.as_ptr())?;

    if !ctx.format().flags().contains(Flags::NO_FILE) {
        open_context_write(&mut ctx, path.as_ptr(), options.as_mut_ptr())?;
    }

    Ok(ctx)
}

pub fn output_as<P: AsRef<OsStr>>(path_or_url: P, format: &str) -> Result<context::Output, Error> {
    let path = from_os_str(path_or_url);
    let format = CString::new(format).unwrap();
    let mut ctx = alloc_context(format.as_ptr(), path.as_ptr())?;

    if !ctx.format().flags().contains(Flags::NO_FILE) {
        open_context_write(&mut ctx, path.as_ptr(), ptr::null_mut())?;
    }

    Ok(ctx)
}

pub fn output_as_with<P, Dict>(
    path_or_url: P,
    format: &str,
    mut options: Dict,
) -> Result<context::Output, Error>
where
    P: AsRef<OsStr>,
    Dict: AsMutPtr<*mut AVDictionary>,
{
    let path = from_os_str(path_or_url);
    let format = CString::new(format).unwrap();
    let mut ctx = alloc_context(format.as_ptr(), path.as_ptr())?;

    if !ctx.format().flags().contains(Flags::NO_FILE) {
        open_context_write(&mut ctx, path.as_ptr(), options.as_mut_ptr())?;
    }

    Ok(ctx)
}