libav-sys 0.2.0

Rust bindings for system libav libraries
Documentation

use libav_sys::ffi::{
    avformat_open_input,
    AVFormatContext, avformat_network_init, av_strerror, avformat_find_stream_info, av_dump_format
};

use std::{os::raw::c_char, error::Error};


fn main() -> Result<(), Box<dyn Error>> {
    println!("Hello, world!");

    // initialize and deinitialize libav
    let _guard = InitGuard::new();

    let url = r#"file:C:/Users/lucac/downloads/BigBuckBunny.mp4"#;
    //let url = r#"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"#;

    let c_url = std::ffi::CString::new(url)?;

    let mut format_context_ptr: *mut AVFormatContext = std::ptr::null_mut();

    handle_result(unsafe {
        avformat_open_input(
            &mut format_context_ptr,
            c_url.as_ptr(),
            std::ptr::null_mut(),
            std::ptr::null_mut()
        )
    }, "avformat_open_input failed")?;

    println!("Input opened!");
    println!("format_context_ptr: {:?}", format_context_ptr);

    handle_result(unsafe {
        avformat_find_stream_info(
            format_context_ptr,
            std::ptr::null_mut()
        )
    }, "failed to find stream info")?;

    unsafe {
        av_dump_format(
            format_context_ptr,
            0,
            c_url.as_ptr() as *const c_char,
            0
        );
    }

    // Find the first video stream
    let video_stream_index = {
        let mut video_stream_index = -1;
        for i in 0..unsafe { (*format_context_ptr).nb_streams } {
            let stream = unsafe { *(*format_context_ptr).streams.offset(i as isize) };
            if unsafe { (*(*stream).codecpar).codec_type } == libav_sys::ffi::AVMediaType_AVMEDIA_TYPE_VIDEO {
                video_stream_index = i as i32;
                break;
            }
        }
        video_stream_index
    };

    if video_stream_index < 0 {
        return Err("Could not find a video stream".into());
    }

    Ok(())
}

struct InitGuard;

impl InitGuard {
    fn new() -> Self {
        init();
        InitGuard
    }
}

impl Drop for InitGuard {
    fn drop(&mut self) {
        deinit();
    }
}

fn init() {
    // TODO ??? libav_sys::ffi::av_register_all();

    let result =  unsafe { avformat_network_init() };
    if result != 0 {
        panic!("avformat_network_init failed");
    }
}

fn deinit() {
    let result =  unsafe { libav_sys::ffi::avformat_network_deinit() };
    if result != 0 {
        panic!("avformat_network_deinit failed");
    }
}

fn av_error_to_string(error_code: std::os::raw::c_int) -> String {
    // create a buffer for the error message
    let mut error_buffer: Vec<std::os::raw::c_char> = vec![0; 1024];

    // get the error message
    let result = unsafe {
        av_strerror(
            error_code,
            error_buffer.as_mut_ptr(),
            error_buffer.len()
        )
    };
    if result != 0 {
        panic!("av_strerror failed");
    }

    // convert to rust string
    error_buffer.iter().take_while(|c| **c != 0).map(|c| *c as u8 as char).collect()
}

fn handle_result(result: std::os::raw::c_int, error_message: &str) -> Result<(), Box<dyn Error>> {
    if result < 0 {
        return Err(format!("{}: {}", error_message, av_error_to_string(result)).into());
    }

    Ok(())
}

fn tmp() {
    Drop
}