use super::{
AVCodecID, AVColorPrimaries, AVColorRange, AVColorSpace, AVPixelFormat, AvFrameGuard,
ColorPrimaries, ColorRange, ColorSpace, DecodeError, PixelFormat, VideoCodec,
VideoDecoderInner, VideoFrame,
};
impl VideoDecoderInner {
pub(super) fn convert_pixel_format(fmt: AVPixelFormat) -> PixelFormat {
if fmt == ff_sys::AVPixelFormat_AV_PIX_FMT_YUV420P {
PixelFormat::Yuv420p
} else if fmt == ff_sys::AVPixelFormat_AV_PIX_FMT_YUV422P {
PixelFormat::Yuv422p
} else if fmt == ff_sys::AVPixelFormat_AV_PIX_FMT_YUV444P {
PixelFormat::Yuv444p
} else if fmt == ff_sys::AVPixelFormat_AV_PIX_FMT_RGB24 {
PixelFormat::Rgb24
} else if fmt == ff_sys::AVPixelFormat_AV_PIX_FMT_BGR24 {
PixelFormat::Bgr24
} else if fmt == ff_sys::AVPixelFormat_AV_PIX_FMT_RGBA {
PixelFormat::Rgba
} else if fmt == ff_sys::AVPixelFormat_AV_PIX_FMT_BGRA {
PixelFormat::Bgra
} else if fmt == ff_sys::AVPixelFormat_AV_PIX_FMT_GRAY8 {
PixelFormat::Gray8
} else if fmt == ff_sys::AVPixelFormat_AV_PIX_FMT_NV12 {
PixelFormat::Nv12
} else if fmt == ff_sys::AVPixelFormat_AV_PIX_FMT_NV21 {
PixelFormat::Nv21
} else if fmt == ff_sys::AVPixelFormat_AV_PIX_FMT_YUV420P10LE {
PixelFormat::Yuv420p10le
} else if fmt == ff_sys::AVPixelFormat_AV_PIX_FMT_YUV422P10LE {
PixelFormat::Yuv422p10le
} else if fmt == ff_sys::AVPixelFormat_AV_PIX_FMT_YUV444P10LE {
PixelFormat::Yuv444p10le
} else if fmt == ff_sys::AVPixelFormat_AV_PIX_FMT_P010LE {
PixelFormat::P010le
} else if fmt == ff_sys::AVPixelFormat_AV_PIX_FMT_GBRPF32LE {
PixelFormat::Gbrpf32le
} else {
log::warn!(
"pixel_format unsupported, falling back to Yuv420p requested={fmt} fallback=Yuv420p"
);
PixelFormat::Yuv420p
}
}
pub(super) fn convert_color_space(space: AVColorSpace) -> ColorSpace {
if space == ff_sys::AVColorSpace_AVCOL_SPC_BT709 {
ColorSpace::Bt709
} else if space == ff_sys::AVColorSpace_AVCOL_SPC_BT470BG
|| space == ff_sys::AVColorSpace_AVCOL_SPC_SMPTE170M
{
ColorSpace::Bt601
} else if space == ff_sys::AVColorSpace_AVCOL_SPC_BT2020_NCL {
ColorSpace::Bt2020
} else {
log::warn!(
"color_space unsupported, falling back to Bt709 requested={space} fallback=Bt709"
);
ColorSpace::Bt709
}
}
pub(super) fn convert_color_range(range: AVColorRange) -> ColorRange {
if range == ff_sys::AVColorRange_AVCOL_RANGE_JPEG {
ColorRange::Full
} else if range == ff_sys::AVColorRange_AVCOL_RANGE_MPEG {
ColorRange::Limited
} else {
log::warn!(
"color_range unsupported, falling back to Limited requested={range} fallback=Limited"
);
ColorRange::Limited
}
}
pub(super) fn convert_color_primaries(primaries: AVColorPrimaries) -> ColorPrimaries {
if primaries == ff_sys::AVColorPrimaries_AVCOL_PRI_BT709 {
ColorPrimaries::Bt709
} else if primaries == ff_sys::AVColorPrimaries_AVCOL_PRI_BT470BG
|| primaries == ff_sys::AVColorPrimaries_AVCOL_PRI_SMPTE170M
{
ColorPrimaries::Bt601
} else if primaries == ff_sys::AVColorPrimaries_AVCOL_PRI_BT2020 {
ColorPrimaries::Bt2020
} else {
log::warn!(
"color_primaries unsupported, falling back to Bt709 requested={primaries} fallback=Bt709"
);
ColorPrimaries::Bt709
}
}
pub(super) fn convert_codec(codec_id: AVCodecID) -> VideoCodec {
if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_H264 {
VideoCodec::H264
} else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_HEVC {
VideoCodec::H265
} else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_VP8 {
VideoCodec::Vp8
} else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_VP9 {
VideoCodec::Vp9
} else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_AV1 {
VideoCodec::Av1
} else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_MPEG4 {
VideoCodec::Mpeg4
} else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_PRORES {
VideoCodec::ProRes
} else {
log::warn!(
"video codec unsupported, falling back to H264 codec_id={codec_id} fallback=H264"
);
VideoCodec::H264
}
}
pub(super) fn pixel_format_to_av(format: PixelFormat) -> AVPixelFormat {
match format {
PixelFormat::Yuv420p => ff_sys::AVPixelFormat_AV_PIX_FMT_YUV420P,
PixelFormat::Yuv422p => ff_sys::AVPixelFormat_AV_PIX_FMT_YUV422P,
PixelFormat::Yuv444p => ff_sys::AVPixelFormat_AV_PIX_FMT_YUV444P,
PixelFormat::Rgb24 => ff_sys::AVPixelFormat_AV_PIX_FMT_RGB24,
PixelFormat::Bgr24 => ff_sys::AVPixelFormat_AV_PIX_FMT_BGR24,
PixelFormat::Rgba => ff_sys::AVPixelFormat_AV_PIX_FMT_RGBA,
PixelFormat::Bgra => ff_sys::AVPixelFormat_AV_PIX_FMT_BGRA,
PixelFormat::Gray8 => ff_sys::AVPixelFormat_AV_PIX_FMT_GRAY8,
PixelFormat::Nv12 => ff_sys::AVPixelFormat_AV_PIX_FMT_NV12,
PixelFormat::Nv21 => ff_sys::AVPixelFormat_AV_PIX_FMT_NV21,
PixelFormat::Yuv420p10le => ff_sys::AVPixelFormat_AV_PIX_FMT_YUV420P10LE,
PixelFormat::Yuv422p10le => ff_sys::AVPixelFormat_AV_PIX_FMT_YUV422P10LE,
PixelFormat::Yuv444p10le => ff_sys::AVPixelFormat_AV_PIX_FMT_YUV444P10LE,
PixelFormat::Yuva444p10le => ff_sys::AVPixelFormat_AV_PIX_FMT_YUVA444P10LE,
PixelFormat::P010le => ff_sys::AVPixelFormat_AV_PIX_FMT_P010LE,
PixelFormat::Gbrpf32le => ff_sys::AVPixelFormat_AV_PIX_FMT_GBRPF32LE,
_ => {
log::warn!(
"pixel_format has no AV mapping, falling back to Yuv420p format={format:?} fallback=AV_PIX_FMT_YUV420P"
);
ff_sys::AVPixelFormat_AV_PIX_FMT_YUV420P
}
}
}
pub(super) unsafe fn convert_with_sws(
&mut self,
src_width: u32,
src_height: u32,
src_format: i32,
dst_width: u32,
dst_height: u32,
dst_format: i32,
) -> Result<VideoFrame, DecodeError> {
unsafe {
let cache_key = (
src_width, src_height, src_format, dst_width, dst_height, dst_format,
);
if self.sws_cache_key != Some(cache_key) {
if let Some(old_ctx) = self.sws_ctx.take() {
ff_sys::swscale::free_context(old_ctx);
}
let ctx = ff_sys::swscale::get_context(
src_width as i32,
src_height as i32,
src_format,
dst_width as i32,
dst_height as i32,
dst_format,
ff_sys::swscale::scale_flags::BILINEAR,
)
.map_err(|e| DecodeError::Ffmpeg {
code: 0,
message: format!("Failed to create sws context: {e}"),
})?;
self.sws_ctx = Some(ctx);
self.sws_cache_key = Some(cache_key);
}
let Some(sws_ctx) = self.sws_ctx else {
return Err(DecodeError::Ffmpeg {
code: 0,
message: "SwsContext not initialized".to_string(),
});
};
let dst_frame_guard = AvFrameGuard::new()?;
let dst_frame = dst_frame_guard.as_ptr();
(*dst_frame).width = dst_width as i32;
(*dst_frame).height = dst_height as i32;
(*dst_frame).format = dst_format;
let buffer_ret = ff_sys::av_frame_get_buffer(dst_frame, 0);
if buffer_ret < 0 {
return Err(DecodeError::Ffmpeg {
code: buffer_ret,
message: format!(
"Failed to allocate frame buffer: {}",
ff_sys::av_error_string(buffer_ret)
),
});
}
ff_sys::swscale::scale(
sws_ctx,
(*self.frame).data.as_ptr() as *const *const u8,
(*self.frame).linesize.as_ptr(),
0,
src_height as i32,
(*dst_frame).data.as_ptr() as *const *mut u8,
(*dst_frame).linesize.as_ptr(),
)
.map_err(|e| DecodeError::Ffmpeg {
code: 0,
message: format!("Failed to scale frame: {e}"),
})?;
(*dst_frame).pts = (*self.frame).pts;
let video_frame = self.av_frame_to_video_frame(dst_frame)?;
Ok(video_frame)
}
}
}