use bytes::Bytes;
use moq_net::Timestamp;
use openh264::OpenH264API;
use openh264::decoder::{Decoder, DecoderConfig};
use openh264::formats::YUVSource;
use super::{Backend, Codec, Config, Decoded};
use crate::Error;
use crate::frame::{Frame, I420};
pub(crate) const NAME: &str = "openh264";
pub(crate) struct Openh264 {
decoder: Decoder,
}
impl Openh264 {
pub(crate) fn open(codec: Codec, _config: &Config) -> Result<Box<dyn Backend>, Error> {
if codec != Codec::H264 {
return Err(Error::Codec(anyhow::anyhow!(
"openh264 cannot decode {}",
codec.label()
)));
}
let decoder = Decoder::with_api_config(OpenH264API::from_source(), DecoderConfig::new())
.map_err(|e| Error::Codec(anyhow::anyhow!("openh264 decoder init: {e}")))?;
tracing::info!(decoder = NAME, "opened H.264 decoder");
Ok(Box::new(Self { decoder }))
}
}
impl Backend for Openh264 {
fn decode(&mut self, access_unit: Bytes, timestamp: Timestamp, _keyframe: bool) -> Result<Vec<Decoded>, Error> {
let decoded = self
.decoder
.decode(&access_unit)
.map_err(|e| Error::Codec(anyhow::anyhow!("openh264 decode: {e}")))?;
let Some(yuv) = decoded else {
return Ok(Vec::new());
};
let (width, height) = yuv.dimensions();
if width % 2 != 0 || height % 2 != 0 {
return Err(Error::Codec(anyhow::anyhow!(
"decoded frame has odd dimensions {width}x{height}, expected 4:2:0"
)));
}
let (y_stride, uv_stride, _) = yuv.strides();
let frame = I420::from_planes(
yuv.y(),
yuv.u(),
yuv.v(),
y_stride,
uv_stride,
width as u32,
height as u32,
);
Ok(vec![Decoded {
timestamp,
frame: Frame::I420(frame),
}])
}
fn name(&self) -> &str {
NAME
}
}