use rff_codec::{Codec, CodecParams, CodecRegistry, Decoder};
use rff_core::{CodecId, Error, Frame, MediaType, Packet, PixelFormat, Result, VideoFrame};
use rusty_av2d::{PixelLayout, PlanarImageComponent, Rav1dError};
pub use rusty_av2d;
pub fn register(registry: &mut CodecRegistry) {
registry.register(Codec {
id: CodecId::Av2,
name: "av2",
long_name: "AOMedia Video 2",
media_type: MediaType::Video,
decoder: Some(|| Box::new(Av2Decoder::new())),
encoder: None,
});
}
fn map_err(e: Rav1dError) -> Error {
match e {
Rav1dError::TryAgain => Error::Again,
Rav1dError::InvalidArgument => Error::InvalidData("av2: invalid bitstream".into()),
Rav1dError::UnsupportedBitstream => {
Error::Unsupported("av2: unsupported bitstream feature".into())
}
other => Error::InvalidData(format!("av2: {other:?}")),
}
}
struct Av2Decoder {
inner: Option<rusty_av2d::Decoder>,
pending: bool,
draining: bool,
}
impl Av2Decoder {
fn new() -> Self {
Self {
inner: None,
pending: false,
draining: false,
}
}
fn engine(&mut self) -> Result<&mut rusty_av2d::Decoder> {
if self.inner.is_none() {
self.inner = Some(rusty_av2d::Decoder::new().map_err(map_err)?);
}
Ok(self.inner.as_mut().expect("just initialized"))
}
}
impl Decoder for Av2Decoder {
fn configure(&mut self, _params: &CodecParams) -> Result<()> {
Ok(())
}
fn send_packet(&mut self, packet: &Packet) -> Result<()> {
let pts = packet.pts;
let data = packet.data.clone().into_boxed_slice();
if self.pending {
let r = self.engine()?.send_pending_data();
match r {
Ok(()) => self.pending = false,
Err(Rav1dError::TryAgain) => return Err(Error::Again),
Err(e) => {
self.pending = false;
return Err(map_err(e));
}
}
}
let r = self.engine()?.send_data(data, None, pts, None);
match r {
Ok(()) => Ok(()),
Err(Rav1dError::TryAgain) => {
self.pending = true;
Err(Error::Again)
}
Err(e) => Err(map_err(e)),
}
}
fn receive_frame(&mut self) -> Result<Frame> {
let draining = self.draining;
let Some(engine) = self.inner.as_mut() else {
return Err(if draining { Error::Eof } else { Error::Again });
};
match engine.get_picture() {
Ok(pic) => picture_to_frame(&pic),
Err(Rav1dError::TryAgain) if draining => Err(Error::Eof),
Err(e) => Err(map_err(e)),
}
}
fn flush(&mut self) {
self.draining = true;
}
}
fn picture_to_frame(pic: &rusty_av2d::Picture) -> Result<Frame> {
let layout = pic.pixel_layout();
let format = match (layout, pic.bit_depth()) {
(PixelLayout::I420, 8) => PixelFormat::Yuv420p,
(PixelLayout::I422, 8) => PixelFormat::Yuv422p,
(PixelLayout::I444, 8) => PixelFormat::Yuv444p,
(PixelLayout::I420, 10) => PixelFormat::Yuv420p10,
(PixelLayout::I422, 10) => PixelFormat::Yuv422p10,
(PixelLayout::I444, 10) => PixelFormat::Yuv444p10,
(PixelLayout::I420, 12) => PixelFormat::Yuv420p12,
(PixelLayout::I422, 12) => PixelFormat::Yuv422p12,
(PixelLayout::I444, 12) => PixelFormat::Yuv444p12,
(PixelLayout::I400, _) => {
return Err(Error::Unsupported("av2: monochrome (4:0:0) output".into()))
}
(_, depth) => {
return Err(Error::Unsupported(format!(
"av2: {depth}-bit output is not mapped to a pixel format"
)))
}
};
let mut planes = Vec::with_capacity(3);
let mut strides = Vec::with_capacity(3);
for c in [
PlanarImageComponent::Y,
PlanarImageComponent::U,
PlanarImageComponent::V,
] {
planes.push(pic.plane(c).to_vec());
strides.push(pic.stride(c) as usize);
}
Ok(Frame::Video(VideoFrame {
width: pic.width(),
height: pic.height(),
format,
planes,
strides,
pts: pic.timestamp(),
}))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn registers_a_decode_only_av2_codec() {
let mut reg = CodecRegistry::new();
register(&mut reg);
let codec = reg.by_id(CodecId::Av2).expect("av2 registered");
assert_eq!(codec.name, "av2");
assert_eq!(codec.media_type, MediaType::Video);
assert!(codec.decoder.is_some());
assert!(
codec.encoder.is_none(),
"there is no AV2 encoder; the registry must not advertise one"
);
}
#[test]
fn try_again_maps_to_the_control_flow_error() {
assert!(matches!(map_err(Rav1dError::TryAgain), Error::Again));
}
#[test]
fn garbage_input_errors_rather_than_panicking() {
let mut dec = Av2Decoder::new();
let packet = Packet {
data: vec![0xde, 0xad, 0xbe, 0xef, 0x00, 0x11, 0x22, 0x33],
..Default::default()
};
let _ = dec.send_packet(&packet);
let _ = dec.receive_frame();
}
}
#[cfg(test)]
mod fuzz_smoke {
use super::*;
use rff_codec::Decoder as _;
struct Rng(u64);
impl Rng {
fn next(&mut self) -> u64 {
self.0 ^= self.0 << 13;
self.0 ^= self.0 >> 7;
self.0 ^= self.0 << 17;
self.0
}
fn byte(&mut self) -> u8 {
(self.next() >> 33) as u8
}
fn range(&mut self, n: usize) -> usize {
(self.next() >> 33) as usize % n.max(1)
}
}
#[test]
fn random_bytes_never_panic() {
let mut rng = Rng(0xFACE_B00C_1234_5678);
for i in 0..1500 {
let len = if i % 10 == 0 {
rng.range(65536)
} else {
rng.range(4096)
} + 1;
let data: Vec<u8> = (0..len).map(|_| rng.byte()).collect();
let mut dec = Av2Decoder::new();
let pkt = Packet {
data,
..Default::default()
};
let _ = dec.send_packet(&pkt);
for _ in 0..8 {
if dec.receive_frame().is_err() {
break;
}
}
dec.flush();
let _ = dec.receive_frame();
}
}
}