use anyhow::{anyhow, Result};
use std::mem::MaybeUninit;
use std::os::raw::c_void;
use std::ptr;
use vpx_sys::{
vpx_codec_ctx_t, vpx_codec_dec_init_ver, vpx_codec_decode, vpx_codec_destroy,
vpx_codec_err_to_string, vpx_codec_get_frame, vpx_codec_vp9_dx, vpx_image_t, VPX_CODEC_OK,
VPX_DECODER_ABI_VERSION,
};
#[derive(Debug, Clone)]
pub struct OracleFrame {
pub width: u32,
pub height: u32,
pub i420: Vec<u8>,
}
pub struct OracleDecoder {
ctx: vpx_codec_ctx_t,
}
impl OracleDecoder {
pub fn new() -> Result<Self> {
let mut ctx: vpx_codec_ctx_t = unsafe { MaybeUninit::zeroed().assume_init() };
let ret = unsafe {
vpx_codec_dec_init_ver(
&mut ctx,
vpx_codec_vp9_dx(),
ptr::null_mut(),
0,
VPX_DECODER_ABI_VERSION as i32,
)
};
if ret != VPX_CODEC_OK {
return Err(anyhow!(
"failed to init VP9 oracle decoder: {}",
err_str(ret)
));
}
Ok(Self { ctx })
}
pub fn decode(&mut self, data: &[u8]) -> Result<Vec<OracleFrame>> {
let ret = unsafe {
vpx_codec_decode(
&mut self.ctx,
data.as_ptr(),
data.len() as u32,
ptr::null_mut(),
0,
)
};
if ret != VPX_CODEC_OK {
return Err(anyhow!("VP9 oracle decode failed: {}", err_str(ret)));
}
let mut frames = Vec::new();
let mut iter = ptr::null_mut::<c_void>();
loop {
let img = unsafe {
vpx_codec_get_frame(&mut self.ctx, &mut iter as *mut _ as *mut *const c_void)
};
if img.is_null() {
break;
}
frames.push(unsafe { copy_image(img) });
}
Ok(frames)
}
}
impl Drop for OracleDecoder {
fn drop(&mut self) {
unsafe {
vpx_codec_destroy(&mut self.ctx);
}
}
}
unsafe fn copy_image(img: *const vpx_image_t) -> OracleFrame {
let width = (*img).d_w as usize;
let height = (*img).d_h as usize;
let uv_width = width.div_ceil(2);
let uv_height = height.div_ceil(2);
let mut i420 = Vec::with_capacity(width * height + 2 * uv_width * uv_height);
copy_plane((*img).planes[0], (*img).stride[0], width, height, &mut i420);
copy_plane(
(*img).planes[1],
(*img).stride[1],
uv_width,
uv_height,
&mut i420,
);
copy_plane(
(*img).planes[2],
(*img).stride[2],
uv_width,
uv_height,
&mut i420,
);
OracleFrame {
width: width as u32,
height: height as u32,
i420,
}
}
unsafe fn copy_plane(
plane: *const u8,
stride: i32,
width: usize,
height: usize,
buffer: &mut Vec<u8>,
) {
let mut row = plane;
for _ in 0..height {
buffer.extend_from_slice(std::slice::from_raw_parts(row, width));
row = row.offset(stride as isize);
}
}
fn err_str(code: vpx_sys::vpx_codec_err_t) -> String {
unsafe {
let ptr = vpx_codec_err_to_string(code);
if ptr.is_null() {
"unknown codec error".to_string()
} else {
std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned()
}
}
}