use std::borrow::Cow;
use std::collections::VecDeque;
use orfail::OrFail;
use crate::layout_decode_params::LayoutDecodeParams;
use crate::video::{VideoFormat, VideoFrame};
use crate::video_h264::{H264_NALU_TYPE_PPS, H264_NALU_TYPE_SPS};
use crate::video_h265::{
H265_NALU_TYPE_PPS, H265_NALU_TYPE_SPS, H265_NALU_TYPE_VPS, NALU_HEADER_LENGTH,
};
#[derive(Debug)]
pub struct NvcodecDecoder {
inner: shiguredo_nvcodec::Decoder,
input_queue: VecDeque<VideoFrame>,
output_queue: VecDeque<VideoFrame>,
parameter_sets: Option<Vec<u8>>, }
impl NvcodecDecoder {
pub fn new_h264(params: &LayoutDecodeParams) -> orfail::Result<Self> {
log::debug!("create nvcodec(H264) decoder");
let config = params.nvcodec_h264.clone();
Ok(Self {
inner: shiguredo_nvcodec::Decoder::new_h264(config).or_fail()?,
input_queue: VecDeque::new(),
output_queue: VecDeque::new(),
parameter_sets: None,
})
}
pub fn new_h265(params: &LayoutDecodeParams) -> orfail::Result<Self> {
log::debug!("create nvcodec(H265) decoder");
let config = params.nvcodec_h265.clone();
Ok(Self {
inner: shiguredo_nvcodec::Decoder::new_h265(config).or_fail()?,
input_queue: VecDeque::new(),
output_queue: VecDeque::new(),
parameter_sets: None,
})
}
pub fn new_av1(params: &LayoutDecodeParams) -> orfail::Result<Self> {
log::debug!("create nvcodec(AV1) decoder");
let config = params.nvcodec_av1.clone();
Ok(Self {
inner: shiguredo_nvcodec::Decoder::new_av1(config).or_fail()?,
input_queue: VecDeque::new(),
output_queue: VecDeque::new(),
parameter_sets: None,
})
}
pub fn new_vp8(params: &LayoutDecodeParams) -> orfail::Result<Self> {
log::debug!("create nvcodec(VP8) decoder");
let config = params.nvcodec_vp8.clone();
Ok(Self {
inner: shiguredo_nvcodec::Decoder::new_vp8(config).or_fail()?,
input_queue: VecDeque::new(),
output_queue: VecDeque::new(),
parameter_sets: None,
})
}
pub fn new_vp9(params: &LayoutDecodeParams) -> orfail::Result<Self> {
log::debug!("create nvcodec(VP9) decoder");
let config = params.nvcodec_vp9.clone();
Ok(Self {
inner: shiguredo_nvcodec::Decoder::new_vp9(config).or_fail()?,
input_queue: VecDeque::new(),
output_queue: VecDeque::new(),
parameter_sets: None,
})
}
pub fn decode(&mut self, frame: &VideoFrame) -> orfail::Result<()> {
matches!(
frame.format,
VideoFormat::H264
| VideoFormat::H264AnnexB
| VideoFormat::H265
| VideoFormat::Vp8
| VideoFormat::Vp9
| VideoFormat::Av1
)
.or_fail()?;
if self.parameter_sets.is_none()
&& let Some(sample_entry) = &frame.sample_entry
{
self.parameter_sets =
Some(extract_parameter_sets_annexb(sample_entry, frame.format).or_fail()?);
}
let data = if matches!(
frame.format,
VideoFormat::Vp8 | VideoFormat::Vp9 | VideoFormat::Av1
) {
Cow::Borrowed(&frame.data)
} else if frame.format == VideoFormat::H264AnnexB {
Cow::Borrowed(&frame.data)
} else {
let mut data = &frame.data[..];
let mut data_annexb = Vec::new();
if frame.keyframe
&& let Some(parameter_sets) = &self.parameter_sets
&& !contains_parameter_sets(data, frame.format)
{
data_annexb.extend_from_slice(parameter_sets);
}
while !data.is_empty() {
(data.len() >= NALU_HEADER_LENGTH).or_fail()?;
let n = u32::from_be_bytes([data[0], data[1], data[2], data[3]]) as usize;
data = &data[NALU_HEADER_LENGTH..];
(data.len() >= n).or_fail()?;
data_annexb.extend_from_slice(&[0, 0, 0, 1]);
data_annexb.extend_from_slice(&data[..n]);
data = &data[n..];
}
Cow::Owned(data_annexb)
};
self.inner.decode(&data).or_fail()?;
self.input_queue.push_back(frame.to_stripped());
self.handle_decoded_frames().or_fail()?;
Ok(())
}
pub fn finish(&mut self) -> orfail::Result<()> {
self.inner.finish().or_fail()?;
self.handle_decoded_frames().or_fail()?;
Ok(())
}
fn handle_decoded_frames(&mut self) -> orfail::Result<()> {
while let Some(nv12_frame) = self.inner.next_frame().or_fail()? {
let input_frame = self.input_queue.pop_front().or_fail()?;
let width = nv12_frame.width();
let height = nv12_frame.height();
let y_size = width * height;
let uv_width = width.div_ceil(2);
let uv_height = height.div_ceil(2);
let uv_size = uv_width * uv_height;
let total_size = y_size + uv_size * 2;
let mut i420_data = vec![0u8; total_size];
let (y_plane, rest) = i420_data.split_at_mut(y_size);
let (u_plane, v_plane) = rest.split_at_mut(uv_size);
let src = shiguredo_libyuv::Nv12Planes {
y: nv12_frame.y_plane(),
y_stride: nv12_frame.y_stride(),
uv: nv12_frame.uv_plane(),
uv_stride: nv12_frame.uv_stride(),
};
let mut dst = shiguredo_libyuv::I420PlanesMut {
y: y_plane,
y_stride: width,
u: u_plane,
u_stride: uv_width,
v: v_plane,
v_stride: uv_width,
};
let size = shiguredo_libyuv::ImageSize::new(width, height);
shiguredo_libyuv::nv12_to_i420(&src, &mut dst, size).or_fail()?;
self.output_queue.push_back(VideoFrame::new_i420(
input_frame,
width,
height,
y_plane,
u_plane,
v_plane,
width,
uv_width,
uv_width,
));
}
Ok(())
}
pub fn next_decoded_frame(&mut self) -> Option<VideoFrame> {
self.output_queue.pop_front()
}
}
fn extract_parameter_sets_annexb(
sample_entry: &shiguredo_mp4::boxes::SampleEntry,
format: VideoFormat,
) -> orfail::Result<Vec<u8>> {
use shiguredo_mp4::boxes::SampleEntry;
match (sample_entry, format) {
(SampleEntry::Hev1(entry), VideoFormat::H265) => {
let mut annexb_data = Vec::new();
for array in &entry.hvcc_box.nalu_arrays {
for nalu in &array.nalus {
annexb_data.extend_from_slice(&[0, 0, 0, 1]);
annexb_data.extend_from_slice(nalu);
}
}
Ok(annexb_data)
}
(SampleEntry::Avc1(entry), VideoFormat::H264) => {
let mut annexb_data = Vec::new();
for sps in &entry.avcc_box.sps_list {
annexb_data.extend_from_slice(&[0, 0, 0, 1]);
annexb_data.extend_from_slice(sps);
}
for pps in &entry.avcc_box.pps_list {
annexb_data.extend_from_slice(&[0, 0, 0, 1]);
annexb_data.extend_from_slice(pps);
}
Ok(annexb_data)
}
_ => {
Ok(Vec::new())
}
}
}
fn contains_parameter_sets(data: &[u8], format: VideoFormat) -> bool {
if data.len() < NALU_HEADER_LENGTH + 1 {
return false;
}
match format {
VideoFormat::H265 => {
let nal_unit_type = (data[NALU_HEADER_LENGTH] >> 1) & 0x3F;
matches!(
nal_unit_type,
H265_NALU_TYPE_PPS | H265_NALU_TYPE_SPS | H265_NALU_TYPE_VPS
)
}
VideoFormat::H264 => {
let nal_unit_type = data[NALU_HEADER_LENGTH] & 0x1F;
matches!(nal_unit_type, H264_NALU_TYPE_SPS | H264_NALU_TYPE_PPS)
}
VideoFormat::Av1 => {
false
}
_ => false,
}
}