use bytes::Bytes;
use std::os::raw::c_int;
use crate::frame::{PixelFormat, VideoFrame};
use super::NvdecError;
use super::ffi::{
CUVID_AV1, CUVID_CHROMA_420, CUVID_H264, CUVID_HEVC, CUVID_MPEG2, CUVID_MPEG4, CUVID_VP8,
CUVID_VP9,
};
use super::state::DecodedFrame;
pub fn codec_to_cuvid(codec: &str) -> Option<c_int> {
match codec {
"h264" | "avc1" | "avc" => Some(CUVID_H264),
"h265" | "hevc" | "hvc1" | "hev1" => Some(CUVID_HEVC),
"vp8" => Some(CUVID_VP8),
"vp9" | "vp09" => Some(CUVID_VP9),
"av1" | "av01" => Some(CUVID_AV1),
"mpeg2" | "mpeg2video" => Some(CUVID_MPEG2),
"mpeg4" | "mp4v" => Some(CUVID_MPEG4),
_ => None,
}
}
pub fn validate_format(
chroma_format: c_int,
bit_depth_luma_minus8: u8,
coded_width: u32,
coded_height: u32,
) -> Option<NvdecError> {
if chroma_format != CUVID_CHROMA_420 {
let label: &'static str = match chroma_format {
0 => "Monochrome",
2 => "4:2:2",
3 => "4:4:4",
_ => "unknown",
};
return Some(NvdecError::UnsupportedChroma {
chroma_format,
label,
width: coded_width,
height: coded_height,
});
}
let bit_depth = bit_depth_luma_minus8 + 8;
if bit_depth > 12 {
return Some(NvdecError::UnsupportedPixelFormat { bit_depth });
}
None
}
pub fn deinterleave_p016_to_yuv420p10le(p016_bytes: &[u8], w: usize, h: usize) -> Vec<u8> {
let cw = w.div_ceil(2);
let ch = h.div_ceil(2);
let uv_pairs = cw * ch;
let y_bytes = w * h * 2;
let mut out = Vec::with_capacity(y_bytes + uv_pairs * 4);
let y_src = &p016_bytes[..y_bytes.min(p016_bytes.len())];
for chunk in y_src.chunks_exact(2) {
let sample = u16::from_le_bytes([chunk[0], chunk[1]]);
out.extend_from_slice(&(sample >> 6).to_le_bytes());
}
if out.len() < y_bytes {
out.resize(y_bytes, 0);
}
if p016_bytes.len() > y_bytes {
let uv = &p016_bytes[y_bytes..];
let mut u = Vec::with_capacity(uv_pairs * 2);
let mut v = Vec::with_capacity(uv_pairs * 2);
for i in 0..uv_pairs {
let base = i * 4;
if base + 3 < uv.len() {
let us = u16::from_le_bytes([uv[base], uv[base + 1]]) >> 6;
let vs = u16::from_le_bytes([uv[base + 2], uv[base + 3]]) >> 6;
u.extend_from_slice(&us.to_le_bytes());
v.extend_from_slice(&vs.to_le_bytes());
}
}
out.extend_from_slice(&u);
out.extend_from_slice(&v);
}
out
}
pub fn decoded_frame_to_video_frame(frame: &DecodedFrame) -> VideoFrame {
let w = frame.width as usize;
let h = frame.height as usize;
let cw = w.div_ceil(2);
let ch = h.div_ceil(2);
let uv_pairs = cw * ch;
let (yuv, pixel_format) = if frame.bit_depth_minus8 > 0 {
let _ = uv_pairs; let out = deinterleave_p016_to_yuv420p10le(&frame.nv12, w, h);
(out, PixelFormat::Yuv420p10le)
} else {
let y_size = w * h;
let mut out = Vec::with_capacity(y_size + uv_pairs * 2);
out.extend_from_slice(&frame.nv12[..y_size.min(frame.nv12.len())]);
if frame.nv12.len() > y_size {
let uv = &frame.nv12[y_size..];
let mut u = Vec::with_capacity(uv_pairs);
let mut v = Vec::with_capacity(uv_pairs);
for i in 0..uv_pairs {
if i * 2 + 1 < uv.len() {
u.push(uv[i * 2]);
v.push(uv[i * 2 + 1]);
}
}
out.extend_from_slice(&u);
out.extend_from_slice(&v);
}
(out, PixelFormat::Yuv420p)
};
VideoFrame::new(
Bytes::from(yuv),
frame.width,
frame.height,
pixel_format,
frame.color_space,
frame.timestamp,
)
}