media-codec-vpx 0.3.0

Rust bindings to libvpx
Documentation
use std::{ffi::CStr, mem::MaybeUninit, ptr, slice, sync::Arc};

use ctor::ctor;
use media_codec::{
    codec::{Codec, CodecBuilder, CodecID, CodecParameters},
    decoder::{register_decoder, Decoder, DecoderBuilder},
    packet::Packet,
};
use media_codec_vpx_sys::vpx_codec_iter_t;
use media_core::{
    error::Error,
    frame::Frame,
    unsupported_error,
    variant::Variant,
    video::{ColorMatrix, ColorRange, PixelFormat, VideoFrameDescriptor},
    Result,
};

use crate::vpx_sys::{self, vpx_codec_ctx_t, vpx_codec_err_t::VPX_CODEC_OK, vpx_color_range, vpx_color_space, vpx_img_fmt, VPX_DECODER_ABI_VERSION};

fn vpx_img_fmt_to_pixel_format(fmt: vpx_img_fmt, depth: u32) -> Option<PixelFormat> {
    use vpx_img_fmt::*;

    match (fmt, depth) {
        (VPX_IMG_FMT_YV12, _) => Some(PixelFormat::YV12),
        (VPX_IMG_FMT_I420, _) => Some(PixelFormat::I420),
        (VPX_IMG_FMT_I422, _) => Some(PixelFormat::I422),
        (VPX_IMG_FMT_I444, _) => Some(PixelFormat::I444),
        (VPX_IMG_FMT_NV12, _) => Some(PixelFormat::NV12),
        (VPX_IMG_FMT_I42016, 10) => Some(PixelFormat::I010),
        (VPX_IMG_FMT_I42016, 12) => Some(PixelFormat::I012),
        (VPX_IMG_FMT_I42216, 10) => Some(PixelFormat::I210),
        (VPX_IMG_FMT_I42216, 12) => Some(PixelFormat::I212),
        (VPX_IMG_FMT_I44416, 10) => Some(PixelFormat::I410),
        (VPX_IMG_FMT_I44416, 12) => Some(PixelFormat::I412),
        _ => None,
    }
}

fn vpx_color_range_to_color_range(color_range: vpx_color_range) -> ColorRange {
    use vpx_color_range::*;

    match color_range {
        VPX_CR_STUDIO_RANGE => ColorRange::Video,
        VPX_CR_FULL_RANGE => ColorRange::Full,
    }
}

fn vpx_color_space_to_color_matrix(color_space: vpx_color_space) -> ColorMatrix {
    use vpx_color_space::*;

    match color_space {
        VPX_CS_UNKNOWN => ColorMatrix::Unspecified,
        VPX_CS_BT_601 => ColorMatrix::BT470BG,
        VPX_CS_BT_709 => ColorMatrix::BT709,
        VPX_CS_SMPTE_170 => ColorMatrix::SMPTE170M,
        VPX_CS_SMPTE_240 => ColorMatrix::SMPTE240M,
        VPX_CS_BT_2020 => ColorMatrix::BT2020NCL,
        VPX_CS_RESERVED => ColorMatrix::Reserved,
        VPX_CS_SRGB => ColorMatrix::Identity,
    }
}

pub struct VPXDecoder {
    ctx: vpx_codec_ctx_t,
    iter: vpx_codec_iter_t,
}

unsafe impl Send for VPXDecoder {}
unsafe impl Sync for VPXDecoder {}

impl Codec for VPXDecoder {
    fn configure(&mut self, _parameters: Option<&CodecParameters>, _options: Option<&Variant>) -> Result<()> {
        Ok(())
    }

    fn set_option(&mut self, _name: &str, _value: &Variant) -> Result<()> {
        Ok(())
    }
}

impl Decoder for VPXDecoder {
    fn send_packet(&mut self, _parameters: Option<&CodecParameters>, packet: &Packet) -> Result<()> {
        let ret = unsafe { vpx_sys::vpx_codec_decode(&mut self.ctx, packet.data.as_ptr(), packet.data.len() as u32, ptr::null_mut(), 0) };

        self.iter = ptr::null_mut();

        if ret != VPX_CODEC_OK {
            let c_str = unsafe { vpx_sys::vpx_codec_err_to_string(ret) };
            let msg = unsafe { CStr::from_ptr(c_str).to_string_lossy().into_owned() };
            return Err(Error::Invalid(msg));
        }

        Ok(())
    }

    fn receive_frame_borrowed(&mut self, _parameters: Option<&CodecParameters>) -> Result<Frame<'_>> {
        let img = unsafe { vpx_sys::vpx_codec_get_frame(&mut self.ctx, &mut self.iter) };
        if img.is_null() {
            return Err(Error::Again("no frame available".to_string()));
        }

        let img_ref = unsafe { &*img };
        let frame_width = img_ref.d_w;
        let frame_height = img_ref.d_h;
        let pix_fmt = img_ref.fmt;
        let depth = img_ref.bit_depth;

        let pixel_format = vpx_img_fmt_to_pixel_format(pix_fmt, depth).ok_or_else(|| unsupported_error!(pix_fmt))?;

        let planes = pixel_format.components() as usize;
        let mut buffers = Vec::with_capacity(planes);

        for plane in 0..planes {
            let height = pixel_format.calc_plane_height(plane, frame_height) as usize;
            let stride = img_ref.stride[plane] as usize;
            let buffer = unsafe { slice::from_raw_parts(img_ref.planes[plane], stride * height) };
            buffers.push((buffer, stride as u32));
        }

        let mut desc = VideoFrameDescriptor::try_new(pixel_format, frame_width, frame_height)?;
        desc.color_range = vpx_color_range_to_color_range(img_ref.range);
        desc.color_matrix = vpx_color_space_to_color_matrix(img_ref.cs);

        let frame = Frame::video_creator().create_from_buffers_with_descriptor(desc, &buffers)?;

        Ok(frame)
    }
}

impl Drop for VPXDecoder {
    fn drop(&mut self) {
        unsafe { vpx_sys::vpx_codec_destroy(&mut self.ctx) };
    }
}

impl VPXDecoder {
    pub fn new(codec_id: CodecID, _parameters: Option<CodecParameters>, _options: Option<Variant>) -> Result<Self> {
        let iface = match codec_id {
            CodecID::VP8 => unsafe { vpx_sys::vpx_codec_vp8_dx() },
            CodecID::VP9 => unsafe { vpx_sys::vpx_codec_vp9_dx() },
            _ => return Err(unsupported_error!(codec_id)),
        };

        let mut ctx = MaybeUninit::uninit();
        let cfg = MaybeUninit::zeroed();
        let ver = VPX_DECODER_ABI_VERSION as i32;
        let ret = unsafe { vpx_sys::vpx_codec_dec_init_ver(ctx.as_mut_ptr(), iface, cfg.as_ptr(), 0, ver) };

        if ret != VPX_CODEC_OK {
            return Err(Error::Invalid(unsafe { CStr::from_ptr(vpx_sys::vpx_codec_err_to_string(ret)).to_string_lossy().into_owned() }));
        }

        Ok(Self {
            ctx: unsafe { ctx.assume_init() },
            iter: ptr::null_mut(),
        })
    }
}

pub struct VPXDecoderBuilder {
    codec_id: CodecID,
    name: &'static str,
}

impl DecoderBuilder for VPXDecoderBuilder {
    fn new_decoder(&self, codec_id: CodecID, parameters: Option<CodecParameters>, options: Option<Variant>) -> Result<Box<dyn Decoder>> {
        Ok(Box::new(VPXDecoder::new(codec_id, parameters, options)?))
    }
}

impl CodecBuilder for VPXDecoderBuilder {
    fn id(&self) -> CodecID {
        self.codec_id
    }

    fn name(&self) -> &'static str {
        self.name
    }
}

const VP8_DECODER_NAME: &str = "vp8-dec";
const VP9_DECODER_NAME: &str = "vp9-dec";

const VP8_DECODER_BUILDER: VPXDecoderBuilder = VPXDecoderBuilder {
    codec_id: CodecID::VP8,
    name: VP8_DECODER_NAME,
};

const VP9_DECODER_BUILDER: VPXDecoderBuilder = VPXDecoderBuilder {
    codec_id: CodecID::VP9,
    name: VP9_DECODER_NAME,
};

#[ctor]
fn initialize() {
    register_decoder(Arc::new(VP8_DECODER_BUILDER), false);
    register_decoder(Arc::new(VP9_DECODER_BUILDER), false);
}