use bytes::Bytes;
use moq_vaapi::encode::{Config as VaapiConfig, Encoder};
use super::super::encoder::Config;
use super::Backend;
use crate::Error;
use crate::frame::{Frame, I420};
pub(crate) const NAME: &str = "vaapi";
pub(crate) struct Vaapi {
encoder: Encoder,
}
unsafe impl Send for Vaapi {}
impl Vaapi {
pub(crate) fn open(config: &Config) -> Result<Box<dyn Backend>, Error> {
let bitrate = config.resolved_bitrate().min(u32::MAX as u64) as u32;
let vaapi = VaapiConfig::new(config.width, config.height, config.framerate, bitrate, config.gop);
let encoder = Encoder::new(vaapi).map_err(|e| Error::Codec(anyhow::anyhow!("VAAPI encoder init: {e:?}")))?;
tracing::info!(
encoder = NAME,
width = config.width,
height = config.height,
"opened H.264 encoder"
);
Ok(Box::new(Self { encoder }))
}
}
impl Backend for Vaapi {
fn encode(&mut self, frame: &Frame, keyframe: bool) -> Result<Vec<Bytes>, Error> {
let i420 = frame.to_i420()?;
let nv12 = i420_to_nv12(&i420);
let annexb = self
.encoder
.encode_nv12(&nv12, keyframe)
.map_err(|e| Error::Codec(anyhow::anyhow!("VAAPI encode: {e:?}")))?;
Ok(if annexb.is_empty() {
Vec::new()
} else {
vec![Bytes::from(annexb)]
})
}
fn finish(&mut self) -> Result<Vec<Bytes>, Error> {
Ok(Vec::new())
}
fn set_bitrate(&mut self, _bitrate: u64) -> Result<(), Error> {
Err(Error::BitrateUnsupported(NAME))
}
fn name(&self) -> &str {
NAME
}
}
fn i420_to_nv12(i420: &I420) -> Vec<u8> {
let (w, h) = (i420.width as usize, i420.height as usize);
let (cw, ch) = (w / 2, h / 2);
let mut out = vec![0u8; w * h + 2 * cw * ch];
out[..w * h].copy_from_slice(i420.y());
let (u, v) = (i420.u(), i420.v());
let uv = &mut out[w * h..];
for i in 0..cw * ch {
uv[i * 2] = u[i];
uv[i * 2 + 1] = v[i];
}
out
}