libav-ng 0.1.6

New high level Rust bindings for libav
Documentation
use std::ffi::CString;

use libav_sys_ng::{
    self, AVFormatContext, AVInputFormat, AVOutputFormat, av_dump_format, av_read_frame, av_seek_frame, av_write_frame, av_write_trailer, avformat_alloc_output_context2, avformat_find_stream_info, avformat_free_context, avformat_open_input, avformat_write_header, avio_open
};

use crate::{
    avdictionary::Dictionary, avformat::streams_iter::FormatStreamsIter, avpacket::Packet,
};

pub mod streams_iter;
pub struct FormatContext {
    _format_ctx: *mut libav_sys_ng::AVFormatContext,
}

impl FormatContext {
    pub fn new(
        format_name: &str,
        filename: &str,
        output_format: Option<AVOutputFormat>,
    ) -> Option<FormatContext> {
        unsafe {
            let mut context = core::ptr::null_mut::<libav_sys_ng::AVFormatContext>();

            let ptr = match output_format {
                Some(fmt) => &fmt,
                None => core::ptr::null::<AVOutputFormat>(),
            };

            let fmt_name = CString::new(format_name).expect("CString::new(format_name) failed");
            let real_filename = CString::new(filename).expect("CString::new(filename) failed");

            avformat_alloc_output_context2(
                &mut context,
                ptr,
                fmt_name.as_ptr(),
                real_filename.as_ptr(),
            );

            if context.is_null() {
                None
            } else {
                Some(FormatContext {
                    _format_ctx: context,
                })
            }
        }
    }

    pub fn open_input(url: &str) -> Option<Self> {
        unsafe {
            let mut context = core::ptr::null_mut::<libav_sys_ng::AVFormatContext>();

            let url_c = CString::new(url).unwrap();

            // TODO: Manage InputFormat and Options arguments.
            let result = avformat_open_input(
                &mut context,
                url_c.as_ptr(),
                core::ptr::null(),
                core::ptr::null_mut(),
            );

            if result < 0 {
                None
            } else {
                Some(FormatContext {
                    _format_ctx: context,
                })
            }
        }
    }

    pub fn find_stream_info(&self) -> i32 {
        unsafe { avformat_find_stream_info(self._format_ctx, core::ptr::null_mut()) }
    }

    pub unsafe fn get_input_format(&self) -> *const AVInputFormat {
        (*self._format_ctx).iformat
    }

    pub fn get_output_format(&self) -> &AVOutputFormat {
        unsafe {
            &*(*self._format_ctx).oformat as &AVOutputFormat
        }
    }

    pub unsafe fn raw(&self) -> &AVFormatContext {
        &*self._format_ctx
    }

    pub unsafe fn raw_mut(&mut self) -> &mut AVFormatContext {
        &mut *self._format_ctx
    }

    pub fn dump(&self, index: i32, url: &str, is_output: bool) {
        unsafe {
            let raw_url = CString::new(url).expect("CString::new(url) failed");

            av_dump_format(self._format_ctx, index, raw_url.as_ptr(), is_output as i32)
        }
    }

    pub fn open(&mut self, url: &str, flags: i32) -> Result<(), i32> {
        unsafe {
            let raw_url = CString::new(url).expect("CString::new(url) failed");

            let x = avio_open(&mut (*self._format_ctx).pb, raw_url.as_ptr(), flags);

            if x < 0 {
                return Err(x);
            }
        }

        Ok(())
    }

    pub fn write_header(&mut self, options: Option<&mut Dictionary>) -> Result<(), i32> {
        unsafe {
            let raw_options = match options {
                Some(op) => &mut op.raw(),
                None => core::ptr::null_mut(),
            };

            let code = avformat_write_header(self._format_ctx, raw_options);

            if code < 0 {
                return Err(code);
            }
        }

        Ok(())
    }

    pub fn seek_ts(&mut self, stream_idx: i32, timestamp: i64) -> i32 {
        unsafe { av_seek_frame(self._format_ctx, stream_idx, timestamp, 0) }
    }

    pub fn seek_msec(&mut self, stream_idx: i32, msec: i64) -> i32 {
        let tm = match self.streams().nth(stream_idx as _) {
            Some(st) => st.time_base(),
            None => return -1,
        };

        self.seek_ts(
            stream_idx,
            (msec as f64 * (tm.den as f64 / tm.num as f64) / 1000.0).floor() as _,
        )
    }

    pub fn read_frame(&mut self, packet: &mut Packet) -> i32 {
        unsafe { av_read_frame(self._format_ctx, packet.raw_mut()) }
    }

    pub fn write_frame(&mut self, packet: &mut Packet) -> i32 {
        unsafe { av_write_frame(self._format_ctx, packet.raw_mut()) }
    }

    pub fn streams(&mut self) -> FormatStreamsIter<'_> {
        FormatStreamsIter::new(self)
    }

    pub fn write_trailer(&mut self) {
        unsafe { av_write_trailer(self._format_ctx) };
    }
}

impl Drop for FormatContext {
    fn drop(&mut self) {
        unsafe {
            avformat_free_context(self._format_ctx);
        }
    }
}