I am a newbie when it comes to Rust and have been playing around with ffmpeg. I am trying to use the ffmpeg-sys
crate to ingest a stream via a tcp socket. The whole point of the program is to consume the stream (both audio and video) and return the metadata (codec, framerate, bitrate, etc.).
The only api that I could find in ffmpeg to do this is creating my own AVIOContext
via avio_alloc_context
. Some blogs/tutorials I found on the web:
- https://ffmpeg.org/doxygen/4.0/avio_reading_8c-example.html
- https://www.codeproject.com/Tips/489450/Creating-Custom-FFmpeg-IO-Context
My project's dependencies:
[dependencies]
ffmpeg-sys = "3.4"
libc = "0.2"
The code:
extern crate ffmpeg_sys;
extern crate libc;
use ffmpeg_sys::*;
use std::ffi::CString;
use std::ptr;
use std::sync::mpsc;
#[repr(C)]
#[derive(Debug)]
pub struct Context {
ctx: *mut AVIOContext,
tx: mpsc::Sender<Vec<u8>>,
rx: mpsc::Receiver<Vec<u8>>,
}
impl Context {
pub fn new(size: usize) -> (Self, mpsc::Sender<Vec<u8>>) {
unsafe {
let buf: *mut u8 = av_mallocz(size) as *mut u8;
if buf.is_null() {
panic!("Cannot create buf");
}
let (tx, rx) = mpsc::channel();
let mut ret = Self {
ctx: ptr::null_mut(),
tx: tx.clone(),
rx: rx,
};
println!("context pointer = {:p}", &mut ret);
let ctx = avio_alloc_context(
buf,
size as i32,
0,
&mut ret as *mut _ as *mut libc::c_void,
Some(read_packet),
None,
None,
);
if ctx.is_null() {
panic!("Cannot create AVIO context");
}
ret.ctx = ctx;
(ret, tx)
}
}
}
impl Drop for Context {
fn drop(&mut self) {
if self.ctx.is_null() {
return;
}
unsafe {
av_free((*self.ctx).buffer as *mut _);
avio_context_free(self.ctx as *mut _);
}
}
}
unsafe extern "C" fn read_packet(
opaque: *mut libc::c_void,
buf: *mut u8,
size: libc::c_int,
) -> libc::c_int
{
println!("read_packet opaque = {:p}", opaque);
let ctx: &mut Context = &mut *(opaque as *mut _);
// here be the boom
let data = ctx.rx.recv();
// TODO write data to the internal ffmpeg buffer at ctx.ctx.buffer
0
}
fn main() {
let (ctx, tx) = Context::new(4096);
tx.send(b"Hello World".to_vec());
unsafe {
let mut fmt_ctx = avformat_alloc_context();
(*fmt_ctx).pb = ctx.ctx;
let blank_str = CString::new("").unwrap();
let h264 = CString::new("h264").unwrap();
avformat_open_input(
&mut fmt_ctx as *mut _,
blank_str.as_ptr(),
av_find_input_format(h264.as_ptr()),
ptr::null_mut(),
);
avformat_free_context(fmt_ctx);
}
}
Note that I am 100% a nublet when it comes to rust and especially unsafe rust. If you run the above code (couldn't create a playground example because of the dependency on ffmpeg) it produces output like:
context pointer = 0x7ffee0156c50
read_packet opaque = 0x7ffee0156c50
Illegal instruction: 4
the api that ffmpeg exposes is to use an opaque pointer which i'm using to contain the AVIOContext
pointer and the mpsc::Receiver<Vec<u8>>
that I want to use to pass in the data.
I've used rust-lldb
to debug the process and the opaque pointer appears to be pointing to the Context
struct. When I cast the opaque pointer to a Context object, it is completely mangled in memory.
Any pointers (pun-intended) as to what is going on?
Thanks!