use core::{
ffi::c_int,
ptr::{addr_of, read_unaligned},
};
use derive_more::{IsVariant, TryUnwrap, Unwrap};
use ffmpeg_next::{Packet, ffi::AVPixelFormat};
use mediadecode::{
PixelFormat, Timestamp,
demuxer::{AttachmentPacket, DataPacket},
frame::{AudioFrame, Dimensions, Plane, SubtitleFrame, VideoFrame},
packet::{AudioPacket, PacketFlags as MdPacketFlags, SubtitlePacket, VideoPacket},
subtitle::{SubtitlePayload, Text as SubtitleText},
};
use mediaframe::audio::ChannelLayoutDescription;
use crate::{
FfmpegBuffer,
buffer::{
PacketBufferError, Refcount, SideDataAlloc as BufferSideDataAlloc, SideDataArray,
SideDataBytes, SideDataEntries, SideDataPayload, UnrepresentableFlags,
},
convert::{SIDE_DATA_MAX_ENTRIES, SIDE_DATA_MAX_TOTAL_BYTES},
extras::{
AttachmentPacketExtra, AudioFrameExtra, AudioPacketExtra, DataPacketExtra, SideDataEntry,
SubtitleFrameExtra, SubtitlePacketExtra, VideoFrameExtra, VideoPacketExtra,
},
sample_format::SampleFormat,
};
pub const fn from_av_pixel_format(raw: i32) -> PixelFormat {
match raw {
x if x == AVPixelFormat::AV_PIX_FMT_YUV420P as i32 => PixelFormat::Yuv420p,
x if x == AVPixelFormat::AV_PIX_FMT_YUV422P as i32 => PixelFormat::Yuv422p,
x if x == AVPixelFormat::AV_PIX_FMT_YUV440P as i32 => PixelFormat::Yuv440p,
x if x == AVPixelFormat::AV_PIX_FMT_YUV444P as i32 => PixelFormat::Yuv444p,
x if x == AVPixelFormat::AV_PIX_FMT_YUV411P as i32 => PixelFormat::Yuv411p,
x if x == AVPixelFormat::AV_PIX_FMT_YUV410P as i32 => PixelFormat::Yuv410p,
x if x == AVPixelFormat::AV_PIX_FMT_YUVJ411P as i32 => PixelFormat::Yuvj411p,
x if x == AVPixelFormat::AV_PIX_FMT_YUVJ420P as i32 => PixelFormat::Yuvj420p,
x if x == AVPixelFormat::AV_PIX_FMT_YUVJ422P as i32 => PixelFormat::Yuvj422p,
x if x == AVPixelFormat::AV_PIX_FMT_YUVJ440P as i32 => PixelFormat::Yuvj440p,
x if x == AVPixelFormat::AV_PIX_FMT_YUVJ444P as i32 => PixelFormat::Yuvj444p,
x if x == AVPixelFormat::AV_PIX_FMT_YUV420P9LE as i32 => PixelFormat::Yuv420p9Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUV420P9BE as i32 => PixelFormat::Yuv420p9Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUV420P10LE as i32 => PixelFormat::Yuv420p10Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUV420P10BE as i32 => PixelFormat::Yuv420p10Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUV420P12LE as i32 => PixelFormat::Yuv420p12Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUV420P12BE as i32 => PixelFormat::Yuv420p12Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUV420P14LE as i32 => PixelFormat::Yuv420p14Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUV420P14BE as i32 => PixelFormat::Yuv420p14Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUV420P16LE as i32 => PixelFormat::Yuv420p16Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUV420P16BE as i32 => PixelFormat::Yuv420p16Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUV422P9LE as i32 => PixelFormat::Yuv422p9Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUV422P9BE as i32 => PixelFormat::Yuv422p9Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUV422P10LE as i32 => PixelFormat::Yuv422p10Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUV422P10BE as i32 => PixelFormat::Yuv422p10Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUV422P12LE as i32 => PixelFormat::Yuv422p12Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUV422P12BE as i32 => PixelFormat::Yuv422p12Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUV422P14LE as i32 => PixelFormat::Yuv422p14Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUV422P14BE as i32 => PixelFormat::Yuv422p14Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUV422P16LE as i32 => PixelFormat::Yuv422p16Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUV422P16BE as i32 => PixelFormat::Yuv422p16Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUV440P10LE as i32 => PixelFormat::Yuv440p10Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUV440P10BE as i32 => PixelFormat::Yuv440p10Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUV440P12LE as i32 => PixelFormat::Yuv440p12Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUV440P12BE as i32 => PixelFormat::Yuv440p12Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUV444P9LE as i32 => PixelFormat::Yuv444p9Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUV444P9BE as i32 => PixelFormat::Yuv444p9Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUV444P10LE as i32 => PixelFormat::Yuv444p10Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUV444P10BE as i32 => PixelFormat::Yuv444p10Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUV444P12LE as i32 => PixelFormat::Yuv444p12Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUV444P12BE as i32 => PixelFormat::Yuv444p12Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUV444P14LE as i32 => PixelFormat::Yuv444p14Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUV444P14BE as i32 => PixelFormat::Yuv444p14Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUV444P16LE as i32 => PixelFormat::Yuv444p16Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUV444P16BE as i32 => PixelFormat::Yuv444p16Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUV444P10MSBLE as i32 => PixelFormat::Yuv444p10MsbLe,
x if x == AVPixelFormat::AV_PIX_FMT_YUV444P10MSBBE as i32 => PixelFormat::Yuv444p10MsbBe,
x if x == AVPixelFormat::AV_PIX_FMT_YUV444P12MSBLE as i32 => PixelFormat::Yuv444p12MsbLe,
x if x == AVPixelFormat::AV_PIX_FMT_YUV444P12MSBBE as i32 => PixelFormat::Yuv444p12MsbBe,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA420P as i32 => PixelFormat::Yuva420p,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA422P as i32 => PixelFormat::Yuva422p,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA444P as i32 => PixelFormat::Yuva444p,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA420P9LE as i32 => PixelFormat::Yuva420p9Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA420P9BE as i32 => PixelFormat::Yuva420p9Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA422P9LE as i32 => PixelFormat::Yuva422p9Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA422P9BE as i32 => PixelFormat::Yuva422p9Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA444P9LE as i32 => PixelFormat::Yuva444p9Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA444P9BE as i32 => PixelFormat::Yuva444p9Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA420P10LE as i32 => PixelFormat::Yuva420p10Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA420P10BE as i32 => PixelFormat::Yuva420p10Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA422P10LE as i32 => PixelFormat::Yuva422p10Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA422P10BE as i32 => PixelFormat::Yuva422p10Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA444P10LE as i32 => PixelFormat::Yuva444p10Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA444P10BE as i32 => PixelFormat::Yuva444p10Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA422P12LE as i32 => PixelFormat::Yuva422p12Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA422P12BE as i32 => PixelFormat::Yuva422p12Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA444P12LE as i32 => PixelFormat::Yuva444p12Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA444P12BE as i32 => PixelFormat::Yuva444p12Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA420P16LE as i32 => PixelFormat::Yuva420p16Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA420P16BE as i32 => PixelFormat::Yuva420p16Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA422P16LE as i32 => PixelFormat::Yuva422p16Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA422P16BE as i32 => PixelFormat::Yuva422p16Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA444P16LE as i32 => PixelFormat::Yuva444p16Le,
x if x == AVPixelFormat::AV_PIX_FMT_YUVA444P16BE as i32 => PixelFormat::Yuva444p16Be,
x if x == AVPixelFormat::AV_PIX_FMT_NV12 as i32 => PixelFormat::Nv12,
x if x == AVPixelFormat::AV_PIX_FMT_NV21 as i32 => PixelFormat::Nv21,
x if x == AVPixelFormat::AV_PIX_FMT_NV16 as i32 => PixelFormat::Nv16,
x if x == AVPixelFormat::AV_PIX_FMT_NV24 as i32 => PixelFormat::Nv24,
x if x == AVPixelFormat::AV_PIX_FMT_NV42 as i32 => PixelFormat::Nv42,
x if x == AVPixelFormat::AV_PIX_FMT_NV20LE as i32 => PixelFormat::Nv20Le,
x if x == AVPixelFormat::AV_PIX_FMT_NV20BE as i32 => PixelFormat::Nv20Be,
x if x == AVPixelFormat::AV_PIX_FMT_P010LE as i32 => PixelFormat::P010Le,
x if x == AVPixelFormat::AV_PIX_FMT_P010BE as i32 => PixelFormat::P010Be,
x if x == AVPixelFormat::AV_PIX_FMT_P012LE as i32 => PixelFormat::P012Le,
x if x == AVPixelFormat::AV_PIX_FMT_P012BE as i32 => PixelFormat::P012Be,
x if x == AVPixelFormat::AV_PIX_FMT_P016LE as i32 => PixelFormat::P016Le,
x if x == AVPixelFormat::AV_PIX_FMT_P016BE as i32 => PixelFormat::P016Be,
x if x == AVPixelFormat::AV_PIX_FMT_P210LE as i32 => PixelFormat::P210Le,
x if x == AVPixelFormat::AV_PIX_FMT_P210BE as i32 => PixelFormat::P210Be,
x if x == AVPixelFormat::AV_PIX_FMT_P212LE as i32 => PixelFormat::P212Le,
x if x == AVPixelFormat::AV_PIX_FMT_P212BE as i32 => PixelFormat::P212Be,
x if x == AVPixelFormat::AV_PIX_FMT_P216LE as i32 => PixelFormat::P216Le,
x if x == AVPixelFormat::AV_PIX_FMT_P216BE as i32 => PixelFormat::P216Be,
x if x == AVPixelFormat::AV_PIX_FMT_P410LE as i32 => PixelFormat::P410Le,
x if x == AVPixelFormat::AV_PIX_FMT_P410BE as i32 => PixelFormat::P410Be,
x if x == AVPixelFormat::AV_PIX_FMT_P412LE as i32 => PixelFormat::P412Le,
x if x == AVPixelFormat::AV_PIX_FMT_P412BE as i32 => PixelFormat::P412Be,
x if x == AVPixelFormat::AV_PIX_FMT_P416LE as i32 => PixelFormat::P416Le,
x if x == AVPixelFormat::AV_PIX_FMT_P416BE as i32 => PixelFormat::P416Be,
x if x == AVPixelFormat::AV_PIX_FMT_YUYV422 as i32 => PixelFormat::Yuyv422,
x if x == AVPixelFormat::AV_PIX_FMT_UYVY422 as i32 => PixelFormat::Uyvy422,
x if x == AVPixelFormat::AV_PIX_FMT_YVYU422 as i32 => PixelFormat::Yvyu422,
x if x == AVPixelFormat::AV_PIX_FMT_UYYVYY411 as i32 => PixelFormat::Uyyvyy411,
x if x == AVPixelFormat::AV_PIX_FMT_Y210LE as i32 => PixelFormat::Y210Le,
x if x == AVPixelFormat::AV_PIX_FMT_Y210BE as i32 => PixelFormat::Y210Be,
x if x == AVPixelFormat::AV_PIX_FMT_Y212LE as i32 => PixelFormat::Y212Le,
x if x == AVPixelFormat::AV_PIX_FMT_Y212BE as i32 => PixelFormat::Y212Be,
x if x == AVPixelFormat::AV_PIX_FMT_Y216LE as i32 => PixelFormat::Y216Le,
x if x == AVPixelFormat::AV_PIX_FMT_Y216BE as i32 => PixelFormat::Y216Be,
x if x == AVPixelFormat::AV_PIX_FMT_XV30LE as i32 => PixelFormat::Xv30Le,
x if x == AVPixelFormat::AV_PIX_FMT_XV30BE as i32 => PixelFormat::Xv30Be,
x if x == AVPixelFormat::AV_PIX_FMT_V30XLE as i32 => PixelFormat::V30xLe,
x if x == AVPixelFormat::AV_PIX_FMT_V30XBE as i32 => PixelFormat::V30xBe,
x if x == AVPixelFormat::AV_PIX_FMT_XV36LE as i32 => PixelFormat::Xv36Le,
x if x == AVPixelFormat::AV_PIX_FMT_XV36BE as i32 => PixelFormat::Xv36Be,
x if x == AVPixelFormat::AV_PIX_FMT_XV48LE as i32 => PixelFormat::Xv48Le,
x if x == AVPixelFormat::AV_PIX_FMT_XV48BE as i32 => PixelFormat::Xv48Be,
x if x == AVPixelFormat::AV_PIX_FMT_VUYA as i32 => PixelFormat::Vuya,
x if x == AVPixelFormat::AV_PIX_FMT_VUYX as i32 => PixelFormat::Vuyx,
x if x == AVPixelFormat::AV_PIX_FMT_AYUV as i32 => PixelFormat::Ayuv,
x if x == AVPixelFormat::AV_PIX_FMT_AYUV64LE as i32 => PixelFormat::Ayuv64Le,
x if x == AVPixelFormat::AV_PIX_FMT_AYUV64BE as i32 => PixelFormat::Ayuv64Be,
x if x == AVPixelFormat::AV_PIX_FMT_UYVA as i32 => PixelFormat::Uyva,
x if x == AVPixelFormat::AV_PIX_FMT_VYU444 as i32 => PixelFormat::Vyu444,
x if x == AVPixelFormat::AV_PIX_FMT_XYZ12LE as i32 => PixelFormat::Xyz12Le,
x if x == AVPixelFormat::AV_PIX_FMT_XYZ12BE as i32 => PixelFormat::Xyz12Be,
x if x == AVPixelFormat::AV_PIX_FMT_RGB24 as i32 => PixelFormat::Rgb24,
x if x == AVPixelFormat::AV_PIX_FMT_BGR24 as i32 => PixelFormat::Bgr24,
x if x == AVPixelFormat::AV_PIX_FMT_RGBA as i32 => PixelFormat::Rgba,
x if x == AVPixelFormat::AV_PIX_FMT_BGRA as i32 => PixelFormat::Bgra,
x if x == AVPixelFormat::AV_PIX_FMT_ARGB as i32 => PixelFormat::Argb,
x if x == AVPixelFormat::AV_PIX_FMT_ABGR as i32 => PixelFormat::Abgr,
x if x == AVPixelFormat::AV_PIX_FMT_RGB0 as i32 => PixelFormat::Rgbx,
x if x == AVPixelFormat::AV_PIX_FMT_BGR0 as i32 => PixelFormat::Bgrx,
x if x == AVPixelFormat::AV_PIX_FMT_0RGB as i32 => PixelFormat::Xrgb,
x if x == AVPixelFormat::AV_PIX_FMT_0BGR as i32 => PixelFormat::Xbgr,
x if x == AVPixelFormat::AV_PIX_FMT_X2RGB10LE as i32 => PixelFormat::X2Rgb10Le,
x if x == AVPixelFormat::AV_PIX_FMT_X2RGB10BE as i32 => PixelFormat::X2Rgb10Be,
x if x == AVPixelFormat::AV_PIX_FMT_X2BGR10LE as i32 => PixelFormat::X2Bgr10Le,
x if x == AVPixelFormat::AV_PIX_FMT_X2BGR10BE as i32 => PixelFormat::X2Bgr10Be,
x if x == AVPixelFormat::AV_PIX_FMT_RGB48LE as i32 => PixelFormat::Rgb48Le,
x if x == AVPixelFormat::AV_PIX_FMT_RGB48BE as i32 => PixelFormat::Rgb48Be,
x if x == AVPixelFormat::AV_PIX_FMT_BGR48LE as i32 => PixelFormat::Bgr48Le,
x if x == AVPixelFormat::AV_PIX_FMT_BGR48BE as i32 => PixelFormat::Bgr48Be,
x if x == AVPixelFormat::AV_PIX_FMT_RGBA64LE as i32 => PixelFormat::Rgba64Le,
x if x == AVPixelFormat::AV_PIX_FMT_RGBA64BE as i32 => PixelFormat::Rgba64Be,
x if x == AVPixelFormat::AV_PIX_FMT_BGRA64LE as i32 => PixelFormat::Bgra64Le,
x if x == AVPixelFormat::AV_PIX_FMT_BGRA64BE as i32 => PixelFormat::Bgra64Be,
x if x == AVPixelFormat::AV_PIX_FMT_RGB96LE as i32 => PixelFormat::Rgb96Le,
x if x == AVPixelFormat::AV_PIX_FMT_RGB96BE as i32 => PixelFormat::Rgb96Be,
x if x == AVPixelFormat::AV_PIX_FMT_RGBA128LE as i32 => PixelFormat::Rgba128Le,
x if x == AVPixelFormat::AV_PIX_FMT_RGBA128BE as i32 => PixelFormat::Rgba128Be,
x if x == AVPixelFormat::AV_PIX_FMT_RGBF16LE as i32 => PixelFormat::Rgbf16Le,
x if x == AVPixelFormat::AV_PIX_FMT_RGBF16BE as i32 => PixelFormat::Rgbf16Be,
x if x == AVPixelFormat::AV_PIX_FMT_RGBF32LE as i32 => PixelFormat::Rgbf32Le,
x if x == AVPixelFormat::AV_PIX_FMT_RGBF32BE as i32 => PixelFormat::Rgbf32Be,
x if x == AVPixelFormat::AV_PIX_FMT_RGBAF16LE as i32 => PixelFormat::Rgbaf16Le,
x if x == AVPixelFormat::AV_PIX_FMT_RGBAF16BE as i32 => PixelFormat::Rgbaf16Be,
x if x == AVPixelFormat::AV_PIX_FMT_RGBAF32LE as i32 => PixelFormat::Rgbaf32Le,
x if x == AVPixelFormat::AV_PIX_FMT_RGBAF32BE as i32 => PixelFormat::Rgbaf32Be,
x if x == AVPixelFormat::AV_PIX_FMT_GBRP as i32 => PixelFormat::Gbrp,
x if x == AVPixelFormat::AV_PIX_FMT_GBRP9LE as i32 => PixelFormat::Gbrp9Le,
x if x == AVPixelFormat::AV_PIX_FMT_GBRP9BE as i32 => PixelFormat::Gbrp9Be,
x if x == AVPixelFormat::AV_PIX_FMT_GBRP10LE as i32 => PixelFormat::Gbrp10Le,
x if x == AVPixelFormat::AV_PIX_FMT_GBRP10BE as i32 => PixelFormat::Gbrp10Be,
x if x == AVPixelFormat::AV_PIX_FMT_GBRP10MSBLE as i32 => PixelFormat::Gbrp10MsbLe,
x if x == AVPixelFormat::AV_PIX_FMT_GBRP10MSBBE as i32 => PixelFormat::Gbrp10MsbBe,
x if x == AVPixelFormat::AV_PIX_FMT_GBRP12LE as i32 => PixelFormat::Gbrp12Le,
x if x == AVPixelFormat::AV_PIX_FMT_GBRP12BE as i32 => PixelFormat::Gbrp12Be,
x if x == AVPixelFormat::AV_PIX_FMT_GBRP12MSBLE as i32 => PixelFormat::Gbrp12MsbLe,
x if x == AVPixelFormat::AV_PIX_FMT_GBRP12MSBBE as i32 => PixelFormat::Gbrp12MsbBe,
x if x == AVPixelFormat::AV_PIX_FMT_GBRP14LE as i32 => PixelFormat::Gbrp14Le,
x if x == AVPixelFormat::AV_PIX_FMT_GBRP14BE as i32 => PixelFormat::Gbrp14Be,
x if x == AVPixelFormat::AV_PIX_FMT_GBRP16LE as i32 => PixelFormat::Gbrp16Le,
x if x == AVPixelFormat::AV_PIX_FMT_GBRP16BE as i32 => PixelFormat::Gbrp16Be,
x if x == AVPixelFormat::AV_PIX_FMT_GBRPF16LE as i32 => PixelFormat::Gbrpf16Le,
x if x == AVPixelFormat::AV_PIX_FMT_GBRPF16BE as i32 => PixelFormat::Gbrpf16Be,
x if x == AVPixelFormat::AV_PIX_FMT_GBRPF32LE as i32 => PixelFormat::Gbrpf32Le,
x if x == AVPixelFormat::AV_PIX_FMT_GBRPF32BE as i32 => PixelFormat::Gbrpf32Be,
x if x == AVPixelFormat::AV_PIX_FMT_GBRAP as i32 => PixelFormat::Gbrap,
x if x == AVPixelFormat::AV_PIX_FMT_GBRAP10LE as i32 => PixelFormat::Gbrap10Le,
x if x == AVPixelFormat::AV_PIX_FMT_GBRAP10BE as i32 => PixelFormat::Gbrap10Be,
x if x == AVPixelFormat::AV_PIX_FMT_GBRAP12LE as i32 => PixelFormat::Gbrap12Le,
x if x == AVPixelFormat::AV_PIX_FMT_GBRAP12BE as i32 => PixelFormat::Gbrap12Be,
x if x == AVPixelFormat::AV_PIX_FMT_GBRAP14LE as i32 => PixelFormat::Gbrap14Le,
x if x == AVPixelFormat::AV_PIX_FMT_GBRAP14BE as i32 => PixelFormat::Gbrap14Be,
x if x == AVPixelFormat::AV_PIX_FMT_GBRAP16LE as i32 => PixelFormat::Gbrap16Le,
x if x == AVPixelFormat::AV_PIX_FMT_GBRAP16BE as i32 => PixelFormat::Gbrap16Be,
x if x == AVPixelFormat::AV_PIX_FMT_GBRAP32LE as i32 => PixelFormat::Gbrap32Le,
x if x == AVPixelFormat::AV_PIX_FMT_GBRAP32BE as i32 => PixelFormat::Gbrap32Be,
x if x == AVPixelFormat::AV_PIX_FMT_GBRAPF16LE as i32 => PixelFormat::Gbrapf16Le,
x if x == AVPixelFormat::AV_PIX_FMT_GBRAPF16BE as i32 => PixelFormat::Gbrapf16Be,
x if x == AVPixelFormat::AV_PIX_FMT_GBRAPF32LE as i32 => PixelFormat::Gbrapf32Le,
x if x == AVPixelFormat::AV_PIX_FMT_GBRAPF32BE as i32 => PixelFormat::Gbrapf32Be,
x if x == AVPixelFormat::AV_PIX_FMT_GRAY8 as i32 => PixelFormat::Gray8,
x if x == AVPixelFormat::AV_PIX_FMT_GRAY9LE as i32 => PixelFormat::Gray9Le,
x if x == AVPixelFormat::AV_PIX_FMT_GRAY9BE as i32 => PixelFormat::Gray9Be,
x if x == AVPixelFormat::AV_PIX_FMT_GRAY10LE as i32 => PixelFormat::Gray10Le,
x if x == AVPixelFormat::AV_PIX_FMT_GRAY10BE as i32 => PixelFormat::Gray10Be,
x if x == AVPixelFormat::AV_PIX_FMT_GRAY12LE as i32 => PixelFormat::Gray12Le,
x if x == AVPixelFormat::AV_PIX_FMT_GRAY12BE as i32 => PixelFormat::Gray12Be,
x if x == AVPixelFormat::AV_PIX_FMT_GRAY14LE as i32 => PixelFormat::Gray14Le,
x if x == AVPixelFormat::AV_PIX_FMT_GRAY14BE as i32 => PixelFormat::Gray14Be,
x if x == AVPixelFormat::AV_PIX_FMT_GRAY16LE as i32 => PixelFormat::Gray16Le,
x if x == AVPixelFormat::AV_PIX_FMT_GRAY16BE as i32 => PixelFormat::Gray16Be,
x if x == AVPixelFormat::AV_PIX_FMT_GRAY32LE as i32 => PixelFormat::Gray32Le,
x if x == AVPixelFormat::AV_PIX_FMT_GRAY32BE as i32 => PixelFormat::Gray32Be,
x if x == AVPixelFormat::AV_PIX_FMT_GRAYF16LE as i32 => PixelFormat::Grayf16Le,
x if x == AVPixelFormat::AV_PIX_FMT_GRAYF16BE as i32 => PixelFormat::Grayf16Be,
x if x == AVPixelFormat::AV_PIX_FMT_GRAYF32LE as i32 => PixelFormat::Grayf32Le,
x if x == AVPixelFormat::AV_PIX_FMT_GRAYF32BE as i32 => PixelFormat::Grayf32Be,
x if x == AVPixelFormat::AV_PIX_FMT_YA8 as i32 => PixelFormat::Ya8,
x if x == AVPixelFormat::AV_PIX_FMT_YA16LE as i32 => PixelFormat::Ya16Le,
x if x == AVPixelFormat::AV_PIX_FMT_YA16BE as i32 => PixelFormat::Ya16Be,
x if x == AVPixelFormat::AV_PIX_FMT_YAF16LE as i32 => PixelFormat::Yaf16Le,
x if x == AVPixelFormat::AV_PIX_FMT_YAF16BE as i32 => PixelFormat::Yaf16Be,
x if x == AVPixelFormat::AV_PIX_FMT_YAF32LE as i32 => PixelFormat::Yaf32Le,
x if x == AVPixelFormat::AV_PIX_FMT_YAF32BE as i32 => PixelFormat::Yaf32Be,
x if x == AVPixelFormat::AV_PIX_FMT_MONOWHITE as i32 => PixelFormat::Monowhite,
x if x == AVPixelFormat::AV_PIX_FMT_MONOBLACK as i32 => PixelFormat::Monoblack,
x if x == AVPixelFormat::AV_PIX_FMT_PAL8 as i32 => PixelFormat::Pal8,
x if x == AVPixelFormat::AV_PIX_FMT_RGB4 as i32 => PixelFormat::Rgb4,
x if x == AVPixelFormat::AV_PIX_FMT_RGB4_BYTE as i32 => PixelFormat::Rgb4Byte,
x if x == AVPixelFormat::AV_PIX_FMT_RGB8 as i32 => PixelFormat::Rgb8,
x if x == AVPixelFormat::AV_PIX_FMT_BGR4 as i32 => PixelFormat::Bgr4,
x if x == AVPixelFormat::AV_PIX_FMT_BGR4_BYTE as i32 => PixelFormat::Bgr4Byte,
x if x == AVPixelFormat::AV_PIX_FMT_BGR8 as i32 => PixelFormat::Bgr8,
x if x == AVPixelFormat::AV_PIX_FMT_RGB444LE as i32 => PixelFormat::Rgb444Le,
x if x == AVPixelFormat::AV_PIX_FMT_RGB444BE as i32 => PixelFormat::Rgb444Be,
x if x == AVPixelFormat::AV_PIX_FMT_BGR444LE as i32 => PixelFormat::Bgr444Le,
x if x == AVPixelFormat::AV_PIX_FMT_BGR444BE as i32 => PixelFormat::Bgr444Be,
x if x == AVPixelFormat::AV_PIX_FMT_RGB555LE as i32 => PixelFormat::Rgb555Le,
x if x == AVPixelFormat::AV_PIX_FMT_RGB555BE as i32 => PixelFormat::Rgb555Be,
x if x == AVPixelFormat::AV_PIX_FMT_BGR555LE as i32 => PixelFormat::Bgr555Le,
x if x == AVPixelFormat::AV_PIX_FMT_BGR555BE as i32 => PixelFormat::Bgr555Be,
x if x == AVPixelFormat::AV_PIX_FMT_RGB565LE as i32 => PixelFormat::Rgb565Le,
x if x == AVPixelFormat::AV_PIX_FMT_RGB565BE as i32 => PixelFormat::Rgb565Be,
x if x == AVPixelFormat::AV_PIX_FMT_BGR565LE as i32 => PixelFormat::Bgr565Le,
x if x == AVPixelFormat::AV_PIX_FMT_BGR565BE as i32 => PixelFormat::Bgr565Be,
x if x == AVPixelFormat::AV_PIX_FMT_BAYER_BGGR8 as i32 => PixelFormat::BayerBggr8,
x if x == AVPixelFormat::AV_PIX_FMT_BAYER_RGGB8 as i32 => PixelFormat::BayerRggb8,
x if x == AVPixelFormat::AV_PIX_FMT_BAYER_GBRG8 as i32 => PixelFormat::BayerGbrg8,
x if x == AVPixelFormat::AV_PIX_FMT_BAYER_GRBG8 as i32 => PixelFormat::BayerGrbg8,
x if x == AVPixelFormat::AV_PIX_FMT_BAYER_BGGR16LE as i32 => PixelFormat::BayerBggr16Le,
x if x == AVPixelFormat::AV_PIX_FMT_BAYER_BGGR16BE as i32 => PixelFormat::BayerBggr16Be,
x if x == AVPixelFormat::AV_PIX_FMT_BAYER_RGGB16LE as i32 => PixelFormat::BayerRggb16Le,
x if x == AVPixelFormat::AV_PIX_FMT_BAYER_RGGB16BE as i32 => PixelFormat::BayerRggb16Be,
x if x == AVPixelFormat::AV_PIX_FMT_BAYER_GBRG16LE as i32 => PixelFormat::BayerGbrg16Le,
x if x == AVPixelFormat::AV_PIX_FMT_BAYER_GBRG16BE as i32 => PixelFormat::BayerGbrg16Be,
x if x == AVPixelFormat::AV_PIX_FMT_BAYER_GRBG16LE as i32 => PixelFormat::BayerGrbg16Le,
x if x == AVPixelFormat::AV_PIX_FMT_BAYER_GRBG16BE as i32 => PixelFormat::BayerGrbg16Be,
_ => PixelFormat::None,
}
}
pub const fn is_hardware_pix_fmt(raw: i32) -> bool {
matches!(
raw,
x if x == AVPixelFormat::AV_PIX_FMT_VIDEOTOOLBOX as i32
|| x == AVPixelFormat::AV_PIX_FMT_VAAPI as i32
|| x == AVPixelFormat::AV_PIX_FMT_CUDA as i32
|| x == AVPixelFormat::AV_PIX_FMT_D3D11 as i32
|| x == AVPixelFormat::AV_PIX_FMT_DRM_PRIME as i32
|| x == AVPixelFormat::AV_PIX_FMT_MEDIACODEC as i32
|| x == AVPixelFormat::AV_PIX_FMT_VULKAN as i32
)
}
fn try_packet_copy(data: &[u8]) -> std::result::Result<Packet, ffmpeg_next::Error> {
if data.len() > c_int::MAX as usize {
return Err(ffmpeg_next::Error::Other {
errno: libc::EINVAL,
});
}
let mut pkt = Packet::new(data.len());
match pkt.data_mut() {
Some(slot) if slot.len() == data.len() => {
if !data.is_empty() {
unsafe {
core::ptr::copy_nonoverlapping(data.as_ptr(), slot.as_mut_ptr(), data.len());
}
}
Ok(pkt)
}
_ => Err(ffmpeg_next::Error::Other {
errno: libc::ENOMEM,
}),
}
}
unsafe fn write_md_flags(packet: &mut Packet, flags: MdPacketFlags) {
use ffmpeg_next::packet::Mut;
unsafe {
(*packet.as_mut_ptr()).flags = c_int::from(flags.bits());
}
}
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
#[error("side-data type {kind} is not one this FFmpeg build names (0..{limit})")]
pub struct UnknownSideData {
kind: i32,
limit: i32,
}
impl UnknownSideData {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(kind: i32, limit: i32) -> Self {
Self { kind, limit }
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn kind(&self) -> i32 {
self.kind
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn limit(&self) -> i32 {
self.limit
}
}
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
#[error("out of memory attaching {size} bytes of side data of type {kind}")]
pub struct SideDataAlloc {
kind: i32,
size: usize,
}
impl SideDataAlloc {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(kind: i32, size: usize) -> Self {
Self { kind, size }
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn kind(&self) -> i32 {
self.kind
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn size(&self) -> usize {
self.size
}
}
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error, IsVariant, Unwrap, TryUnwrap)]
#[unwrap(ref, ref_mut)]
#[try_unwrap(ref, ref_mut)]
pub enum PacketBuildError {
#[error(transparent)]
Ffmpeg(#[from] ffmpeg_next::Error),
#[error(transparent)]
UnknownSideData(#[from] UnknownSideData),
#[error(transparent)]
SideDataAlloc(#[from] SideDataAlloc),
}
fn attach_side_data(out: &mut Packet, entries: &[SideDataEntry]) -> Result<(), PacketBuildError> {
use ffmpeg_next::packet::Mut;
for entry in entries {
let kind = entry.kind();
let size = entry.data().len();
let slot = unsafe { crate::ffi::packet_new_side_data(out.as_mut_ptr(), kind, size) };
let Some(slot) = slot else {
let limit = crate::ffi::side_data_type_count();
return Err(if kind < 0 || kind >= limit {
PacketBuildError::UnknownSideData(UnknownSideData::new(kind, limit))
} else {
PacketBuildError::SideDataAlloc(SideDataAlloc::new(kind, size))
});
};
if size > 0 {
unsafe { core::ptr::copy_nonoverlapping(entry.data().as_ptr(), slot, size) };
}
}
Ok(())
}
pub fn ffmpeg_packet_from_video_packet(
packet: &mediadecode::packet::VideoPacket<VideoPacketExtra, FfmpegBuffer>,
) -> std::result::Result<Packet, PacketBuildError> {
let mut out = try_packet_copy(packet.data().as_ref())?;
attach_side_data(&mut out, packet.extra().side_data())?;
if let Some(ts) = packet.pts() {
out.set_pts(Some(ts.pts()));
}
if let Some(ts) = packet.dts() {
out.set_dts(Some(ts.pts()));
}
if let Some(d) = packet.duration() {
out.set_duration(d.pts());
}
unsafe { write_md_flags(&mut out, packet.flags()) };
out.set_stream(packet.extra().stream_index() as usize);
Ok(out)
}
pub fn ffmpeg_packet_from_audio_packet(
packet: &mediadecode::packet::AudioPacket<AudioPacketExtra, FfmpegBuffer>,
) -> std::result::Result<Packet, PacketBuildError> {
let mut out = try_packet_copy(packet.data().as_ref())?;
attach_side_data(&mut out, packet.extra().side_data())?;
if let Some(ts) = packet.pts() {
out.set_pts(Some(ts.pts()));
}
if let Some(ts) = packet.dts() {
out.set_dts(Some(ts.pts()));
}
if let Some(d) = packet.duration() {
out.set_duration(d.pts());
}
unsafe { write_md_flags(&mut out, packet.flags()) };
out.set_stream(packet.extra().stream_index() as usize);
Ok(out)
}
pub fn ffmpeg_packet_from_subtitle_packet(
packet: &mediadecode::packet::SubtitlePacket<SubtitlePacketExtra, FfmpegBuffer>,
) -> std::result::Result<Packet, PacketBuildError> {
let mut out = try_packet_copy(packet.data().as_ref())?;
attach_side_data(&mut out, packet.extra().side_data())?;
if let Some(ts) = packet.pts() {
out.set_pts(Some(ts.pts()));
}
if let Some(d) = packet.duration() {
out.set_duration(d.pts());
}
unsafe { write_md_flags(&mut out, packet.flags()) };
out.set_stream(packet.extra().stream_index() as usize);
Ok(out)
}
pub fn video_packet_from_ffmpeg(
packet: &Packet,
) -> Result<Option<VideoPacket<VideoPacketExtra, FfmpegBuffer>>, PacketBufferError> {
video_packet_from_ffmpeg_in(packet, mediadecode::Timebase::default())
}
pub fn audio_packet_from_ffmpeg(
packet: &Packet,
) -> Result<Option<AudioPacket<AudioPacketExtra, FfmpegBuffer>>, PacketBufferError> {
audio_packet_from_ffmpeg_in(packet, mediadecode::Timebase::default())
}
pub fn subtitle_packet_from_ffmpeg(
packet: &Packet,
) -> Result<Option<SubtitlePacket<SubtitlePacketExtra, FfmpegBuffer>>, PacketBufferError> {
subtitle_packet_from_ffmpeg_in(packet, mediadecode::Timebase::default())
}
fn side_data_entry_cap() -> usize {
SIDE_DATA_MAX_ENTRIES.max(crate::ffi::side_data_type_count().max(0) as usize)
}
fn packet_side_data(packet: &Packet) -> Result<Vec<SideDataEntry>, PacketBufferError> {
use ffmpeg_next::packet::Ref;
let count_raw = unsafe { (*packet.as_ptr()).side_data_elems };
let entries = unsafe { (*packet.as_ptr()).side_data };
if count_raw == 0 {
return Ok(Vec::new());
}
let cap = side_data_entry_cap();
if count_raw < 0 || count_raw as usize > cap {
return Err(PacketBufferError::SideDataEntries(SideDataEntries::new(
count_raw, cap,
)));
}
if entries.is_null() {
return Err(PacketBufferError::SideDataArray(SideDataArray::new(
count_raw,
)));
}
let count = count_raw as usize;
let mut out: Vec<SideDataEntry> = Vec::new();
if out.try_reserve_exact(count).is_err() {
return Err(PacketBufferError::SideDataAlloc(BufferSideDataAlloc::new(
count * core::mem::size_of::<SideDataEntry>(),
)));
}
let mut total_bytes: usize = 0;
for index in 0..count {
let entry = unsafe { entries.add(index) };
let kind = unsafe { read_unaligned(addr_of!((*entry).type_).cast::<i32>()) };
let size = unsafe { (*entry).size };
let data_ptr = unsafe { (*entry).data };
let data = if size == 0 {
Vec::new()
} else if data_ptr.is_null() {
return Err(PacketBufferError::SideDataPayload(SideDataPayload::new(
index, size,
)));
} else {
total_bytes = total_bytes.saturating_add(size);
if total_bytes > SIDE_DATA_MAX_TOTAL_BYTES {
return Err(PacketBufferError::SideDataBytes(SideDataBytes::new(
total_bytes,
SIDE_DATA_MAX_TOTAL_BYTES,
)));
}
let mut buf: Vec<u8> = Vec::new();
if buf.try_reserve_exact(size).is_err() {
return Err(PacketBufferError::SideDataAlloc(BufferSideDataAlloc::new(
size,
)));
}
buf.extend_from_slice(unsafe { core::slice::from_raw_parts(data_ptr, size) });
buf
};
out.push(SideDataEntry::new(kind, data));
}
Ok(out)
}
fn delivered_payload(
packet: &Packet,
side_data: &[SideDataEntry],
) -> Result<Option<FfmpegBuffer>, PacketBufferError> {
if let Some(buf) = FfmpegBuffer::from_packet(packet)? {
return Ok(Some(buf));
}
if side_data.is_empty() {
return Ok(None);
}
FfmpegBuffer::copy_from_slice(&[])
.map(Some)
.ok_or(PacketBufferError::Refcount(Refcount::new(0)))
}
pub fn video_packet_from_ffmpeg_in(
packet: &Packet,
time_base: mediadecode::Timebase,
) -> Result<Option<VideoPacket<VideoPacketExtra, FfmpegBuffer>>, PacketBufferError> {
let side_data = packet_side_data(packet)?;
let Some(buf) = delivered_payload(packet, &side_data)? else {
return Ok(None);
};
let extra = VideoPacketExtra::new(packet.stream() as i32).with_side_data(side_data);
let mut out = VideoPacket::new(buf, extra)
.with_flags(md_flags_from_packet(packet)?)
.with_pts(packet.pts().map(|p| Timestamp::new(p, time_base)))
.with_dts(packet.dts().map(|d| Timestamp::new(d, time_base)));
let dur = packet.duration();
if dur > 0 {
out = out.with_duration(Some(Timestamp::new(dur, time_base)));
}
Ok(Some(out))
}
pub fn audio_packet_from_ffmpeg_in(
packet: &Packet,
time_base: mediadecode::Timebase,
) -> Result<Option<AudioPacket<AudioPacketExtra, FfmpegBuffer>>, PacketBufferError> {
let side_data = packet_side_data(packet)?;
let Some(buf) = delivered_payload(packet, &side_data)? else {
return Ok(None);
};
let extra = AudioPacketExtra::new(packet.stream() as i32).with_side_data(side_data);
let mut out = AudioPacket::new(buf, extra)
.with_flags(md_flags_from_packet(packet)?)
.with_pts(packet.pts().map(|p| Timestamp::new(p, time_base)))
.with_dts(packet.dts().map(|d| Timestamp::new(d, time_base)));
let dur = packet.duration();
if dur > 0 {
out = out.with_duration(Some(Timestamp::new(dur, time_base)));
}
Ok(Some(out))
}
pub fn subtitle_packet_from_ffmpeg_in(
packet: &Packet,
time_base: mediadecode::Timebase,
) -> Result<Option<SubtitlePacket<SubtitlePacketExtra, FfmpegBuffer>>, PacketBufferError> {
let side_data = packet_side_data(packet)?;
let Some(buf) = delivered_payload(packet, &side_data)? else {
return Ok(None);
};
let extra = SubtitlePacketExtra::new(packet.stream() as i32).with_side_data(side_data);
let mut out = SubtitlePacket::new(buf, extra)
.with_flags(md_flags_from_packet(packet)?)
.with_pts(packet.pts().map(|p| Timestamp::new(p, time_base)));
let dur = packet.duration();
if dur > 0 {
out = out.with_duration(Some(Timestamp::new(dur, time_base)));
}
Ok(Some(out))
}
pub fn data_packet_from_ffmpeg_in(
packet: &Packet,
time_base: mediadecode::Timebase,
) -> Result<Option<DataPacket<DataPacketExtra, FfmpegBuffer>>, PacketBufferError> {
let side_data = packet_side_data(packet)?;
let Some(buf) = delivered_payload(packet, &side_data)? else {
return Ok(None);
};
let pos = packet.position();
let extra = DataPacketExtra::new(packet.stream() as i32)
.with_byte_pos((pos >= 0).then_some(pos as i64))
.with_side_data(side_data);
let mut out = DataPacket::new(buf, extra)
.with_flags(md_flags_from_packet(packet)?)
.with_pts(packet.pts().map(|p| Timestamp::new(p, time_base)));
let dur = packet.duration();
if dur > 0 {
out = out.with_duration(Some(Timestamp::new(dur, time_base)));
}
Ok(Some(out))
}
pub fn attachment_packet_from_ffmpeg(
packet: &Packet,
) -> Result<Option<AttachmentPacket<AttachmentPacketExtra, FfmpegBuffer>>, PacketBufferError> {
let Some(buf) = FfmpegBuffer::from_packet(packet)? else {
return Ok(None);
};
Ok(Some(
AttachmentPacket::new(buf, AttachmentPacketExtra::new(packet.stream() as i32))
.with_flags(md_flags_from_packet(packet)?),
))
}
fn md_flags_from_packet(packet: &Packet) -> Result<MdPacketFlags, PacketBufferError> {
use ffmpeg_next::packet::Ref;
unsafe { md_flags_from_av_packet(packet.as_ptr()) }
}
pub(crate) unsafe fn md_flags_from_av_packet(
pkt: *const ffmpeg_next::ffi::AVPacket,
) -> Result<MdPacketFlags, PacketBufferError> {
let raw = unsafe { (*pkt).flags };
let carried = i32::from(u8::MAX);
if raw & !carried != 0 {
return Err(PacketBufferError::UnrepresentableFlags(
UnrepresentableFlags::new(raw),
));
}
Ok(MdPacketFlags::from_bits_retain(raw as u8))
}
const _: () = {
assert!(
(ffmpeg_next::ffi::AV_PKT_FLAG_KEY
| ffmpeg_next::ffi::AV_PKT_FLAG_CORRUPT
| ffmpeg_next::ffi::AV_PKT_FLAG_DISCARD
| ffmpeg_next::ffi::AV_PKT_FLAG_TRUSTED
| ffmpeg_next::ffi::AV_PKT_FLAG_DISPOSABLE)
<= u8::MAX as c_int,
"FFmpeg names a packet flag outside the byte `PacketFlags` carries",
);
};
pub fn empty_video_frame() -> VideoFrame<PixelFormat, VideoFrameExtra, FfmpegBuffer> {
try_empty_video_frame().expect("empty_video_frame: av_buffer_alloc returned null (OOM)")
}
pub fn try_empty_video_frame() -> Option<VideoFrame<PixelFormat, VideoFrameExtra, FfmpegBuffer>> {
let planes = [
Plane::new(FfmpegBuffer::try_empty()?, 0),
Plane::new(FfmpegBuffer::try_empty()?, 0),
Plane::new(FfmpegBuffer::try_empty()?, 0),
Plane::new(FfmpegBuffer::try_empty()?, 0),
];
Some(VideoFrame::new(
Dimensions::new(0, 0),
PixelFormat::None,
planes,
0,
VideoFrameExtra::default(),
))
}
pub fn empty_audio_frame()
-> AudioFrame<SampleFormat, ChannelLayoutDescription, AudioFrameExtra, FfmpegBuffer> {
try_empty_audio_frame().expect("empty_audio_frame: av_buffer_alloc returned null (OOM)")
}
pub fn try_empty_audio_frame()
-> Option<AudioFrame<SampleFormat, ChannelLayoutDescription, AudioFrameExtra, FfmpegBuffer>> {
let planes = [
Plane::new(FfmpegBuffer::try_empty()?, 0),
Plane::new(FfmpegBuffer::try_empty()?, 0),
Plane::new(FfmpegBuffer::try_empty()?, 0),
Plane::new(FfmpegBuffer::try_empty()?, 0),
Plane::new(FfmpegBuffer::try_empty()?, 0),
Plane::new(FfmpegBuffer::try_empty()?, 0),
Plane::new(FfmpegBuffer::try_empty()?, 0),
Plane::new(FfmpegBuffer::try_empty()?, 0),
];
Some(AudioFrame::new(
0,
0,
0,
SampleFormat::NONE,
ChannelLayoutDescription::default(),
planes,
0,
AudioFrameExtra::default(),
))
}
pub fn empty_subtitle_frame() -> SubtitleFrame<SubtitleFrameExtra, FfmpegBuffer> {
try_empty_subtitle_frame().expect("empty_subtitle_frame: av_buffer_alloc returned null (OOM)")
}
pub fn try_empty_subtitle_frame() -> Option<SubtitleFrame<SubtitleFrameExtra, FfmpegBuffer>> {
let buf = FfmpegBuffer::copy_from_slice(&[]).or_else(FfmpegBuffer::try_empty)?;
Some(SubtitleFrame::new(
SubtitlePayload::Text(SubtitleText::new(buf, None)),
SubtitleFrameExtra::default(),
))
}
#[cfg(test)]
mod tests {
use super::*;
fn out_of_bounds_packet() -> Packet {
use ffmpeg_next::packet::Mut;
let mut packet = Packet::copy(&[1u8, 2, 3, 4]);
unsafe {
(*packet.as_mut_ptr()).size = 1 << 20;
}
packet
}
#[test]
fn a_payload_that_cannot_be_wrapped_is_an_error_on_every_arm() {
let forged = out_of_bounds_packet();
let tb = mediadecode::Timebase::default();
assert!(matches!(
video_packet_from_ffmpeg_in(&forged, tb),
Err(PacketBufferError::Bounds(_)),
));
assert!(matches!(
audio_packet_from_ffmpeg_in(&forged, tb),
Err(PacketBufferError::Bounds(_)),
));
assert!(matches!(
subtitle_packet_from_ffmpeg_in(&forged, tb),
Err(PacketBufferError::Bounds(_)),
));
assert!(matches!(
data_packet_from_ffmpeg_in(&forged, tb),
Err(PacketBufferError::Bounds(_)),
));
assert!(matches!(
attachment_packet_from_ffmpeg(&forged),
Err(PacketBufferError::Bounds(_)),
));
}
fn side_data_only_packet() -> Packet {
use ffmpeg_next::{
ffi::{AVPacketSideDataType, av_packet_new_side_data},
packet::Mut,
};
let mut packet = Packet::empty();
unsafe {
let ptr = av_packet_new_side_data(
packet.as_mut_ptr(),
AVPacketSideDataType::AV_PKT_DATA_NEW_EXTRADATA,
4,
);
assert!(!ptr.is_null(), "av_packet_new_side_data");
core::ptr::copy_nonoverlapping([1u8, 2, 3, 4].as_ptr(), ptr, 4);
}
packet
}
const NEW_EXTRADATA: i32 =
ffmpeg_next::ffi::AVPacketSideDataType::AV_PKT_DATA_NEW_EXTRADATA as i32;
use ffmpeg_next::ffi::{AVPacketSideData, AVPacketSideDataType, av_malloc};
fn packet_with_side_data(size: usize, body: bool) -> Packet {
use ffmpeg_next::{
ffi::{AVPacketSideDataType, av_packet_new_side_data},
packet::Mut,
};
let mut packet = if body {
Packet::copy(&[1u8, 2, 3])
} else {
Packet::empty()
};
let ptr = unsafe {
av_packet_new_side_data(
packet.as_mut_ptr(),
AVPacketSideDataType::AV_PKT_DATA_NEW_EXTRADATA,
size,
)
};
assert!(!ptr.is_null(), "av_packet_new_side_data({size})");
packet
}
struct Forged {
packet: Packet,
freeable: i32,
}
impl Drop for Forged {
fn drop(&mut self) {
use ffmpeg_next::packet::Mut;
unsafe { (*self.packet.as_mut_ptr()).side_data_elems = self.freeable };
}
}
impl Forged {
fn entries(count: usize, body: bool) -> Self {
let mut packet = Self::carrier(body);
unsafe {
let array = Self::array(count);
for index in 0..count {
let data = av_malloc(4) as *mut u8;
assert!(!data.is_null(), "av_malloc");
core::ptr::write_bytes(data, 7, 4);
(*array.add(index)).data = data;
(*array.add(index)).size = 4;
(*array.add(index)).type_ = AVPacketSideDataType::AV_PKT_DATA_NEW_EXTRADATA;
}
Self::attach(&mut packet, array, count as i32);
}
Self {
packet,
freeable: count as i32,
}
}
fn null_array(count: i32, body: bool) -> Self {
let mut packet = Self::carrier(body);
use ffmpeg_next::packet::Mut;
unsafe { (*packet.as_mut_ptr()).side_data_elems = count };
Self {
packet,
freeable: 0,
}
}
fn null_entry_data(size: usize, body: bool) -> Self {
let mut packet = Self::carrier(body);
unsafe {
let array = Self::array(1);
(*array).data = core::ptr::null_mut();
(*array).size = size;
(*array).type_ = AVPacketSideDataType::AV_PKT_DATA_NEW_EXTRADATA;
Self::attach(&mut packet, array, 1);
}
Self {
packet,
freeable: 1,
}
}
fn with_declared_count(mut self, count: i32) -> Self {
use ffmpeg_next::packet::Mut;
unsafe { (*self.packet.as_mut_ptr()).side_data_elems = count };
self
}
fn carrier(body: bool) -> Packet {
if body {
Packet::copy(&[1u8, 2, 3])
} else {
Packet::empty()
}
}
unsafe fn array(count: usize) -> *mut AVPacketSideData {
let array = unsafe { av_malloc(count * core::mem::size_of::<AVPacketSideData>()) }
as *mut AVPacketSideData;
assert!(!array.is_null(), "av_malloc");
array
}
unsafe fn attach(packet: &mut Packet, array: *mut AVPacketSideData, count: i32) {
use ffmpeg_next::packet::Mut;
unsafe {
(*packet.as_mut_ptr()).side_data = array;
(*packet.as_mut_ptr()).side_data_elems = count;
}
}
}
fn every_timed_arm(packet: &Packet) -> [(&'static str, Result<bool, PacketBufferError>); 4] {
let tb = mediadecode::Timebase::default();
[
(
"video",
video_packet_from_ffmpeg_in(packet, tb).map(|p| p.is_some()),
),
(
"audio",
audio_packet_from_ffmpeg_in(packet, tb).map(|p| p.is_some()),
),
(
"subtitle",
subtitle_packet_from_ffmpeg_in(packet, tb).map(|p| p.is_some()),
),
(
"data",
data_packet_from_ffmpeg_in(packet, tb).map(|p| p.is_some()),
),
]
}
#[test]
fn side_data_a_packet_declares_and_does_not_carry_refuses_it() {
for body in [true, false] {
let forged = Forged::null_array(3, body);
for (arm, result) in every_timed_arm(&forged.packet) {
match result {
Err(PacketBufferError::SideDataArray(p)) => assert_eq!(p.count(), 3, "{arm}"),
other => panic!("{arm} (body={body}): expected SideDataArray, got {other:?}"),
}
}
let forged = Forged::null_entry_data(64, body);
for (arm, result) in every_timed_arm(&forged.packet) {
match result {
Err(PacketBufferError::SideDataPayload(p)) => {
assert_eq!((p.index(), p.size()), (0, 64), "{arm}");
}
other => panic!("{arm} (body={body}): expected SideDataPayload, got {other:?}"),
}
}
let forged = Forged::null_array(-3, body);
for (arm, result) in every_timed_arm(&forged.packet) {
assert!(
matches!(
result,
Err(PacketBufferError::SideDataEntries(p)) if p.count() == -3
),
"{arm} (body={body}): {result:?}",
);
}
let forged = Forged::null_array(i32::MAX, body);
for (arm, result) in every_timed_arm(&forged.packet) {
assert!(
matches!(result, Err(PacketBufferError::SideDataEntries(_))),
"{arm} (body={body}): {result:?}",
);
}
}
let marker = Forged::null_entry_data(0, true);
let packet = video_packet_from_ffmpeg_in(&marker.packet, mediadecode::Timebase::default())
.expect("a marker entry is carriable")
.expect("present");
assert_eq!(packet.extra().side_data().len(), 1);
assert!(packet.extra().side_data()[0].data().is_empty());
}
#[test]
fn side_data_that_cannot_be_carried_whole_refuses_the_packet() {
let tb = mediadecode::Timebase::default();
let oversized = packet_with_side_data(SIDE_DATA_MAX_TOTAL_BYTES + 1, true);
for (arm, result) in [
(
"video",
video_packet_from_ffmpeg_in(&oversized, tb).map(|p| p.is_some()),
),
(
"audio",
audio_packet_from_ffmpeg_in(&oversized, tb).map(|p| p.is_some()),
),
(
"subtitle",
subtitle_packet_from_ffmpeg_in(&oversized, tb).map(|p| p.is_some()),
),
(
"data",
data_packet_from_ffmpeg_in(&oversized, tb).map(|p| p.is_some()),
),
] {
match result {
Err(PacketBufferError::SideDataBytes(p)) => {
assert_eq!(p.cap(), SIDE_DATA_MAX_TOTAL_BYTES);
assert!(p.bytes() > p.cap(), "{arm}");
}
other => panic!("{arm}: expected SideDataBytes, got {other:?}"),
}
}
let oversized = packet_with_side_data(SIDE_DATA_MAX_TOTAL_BYTES + 1, false);
assert!(matches!(
video_packet_from_ffmpeg_in(&oversized, tb).map(|p| p.is_some()),
Err(PacketBufferError::SideDataBytes(_)),
));
}
#[test]
fn the_side_data_caps_are_refusals_at_the_boundary_not_before_it() {
let tb = mediadecode::Timebase::default();
let at_cap = packet_with_side_data(SIDE_DATA_MAX_TOTAL_BYTES, true);
let packet = video_packet_from_ffmpeg_in(&at_cap, tb)
.expect("exactly at the cap is not over it")
.expect("present");
assert_eq!(packet.extra().side_data().len(), 1);
assert_eq!(
packet.extra().side_data()[0].data().len(),
SIDE_DATA_MAX_TOTAL_BYTES,
);
let past = packet_with_side_data(SIDE_DATA_MAX_TOTAL_BYTES + 1, true);
assert!(matches!(
video_packet_from_ffmpeg_in(&past, tb).map(|p| p.is_some()),
Err(PacketBufferError::SideDataBytes(_)),
));
let cap = side_data_entry_cap();
assert_eq!(cap, SIDE_DATA_MAX_ENTRIES, "the cap this lane straddles");
let at_cap = Forged::entries(cap, true);
let packet = video_packet_from_ffmpeg_in(&at_cap.packet, tb)
.expect("exactly at the cap is not over it")
.expect("present");
assert_eq!(packet.extra().side_data().len(), cap);
let past = Forged::entries(cap + 1, true);
match video_packet_from_ffmpeg_in(&past.packet, tb).map(|p| p.is_some()) {
Err(PacketBufferError::SideDataEntries(p)) => {
assert_eq!(p.count() as usize, cap + 1);
assert_eq!(p.cap(), cap);
}
other => panic!("expected SideDataEntries, got {other:?}"),
}
let corrupt = Forged::entries(1, true).with_declared_count(-3);
assert!(matches!(
video_packet_from_ffmpeg_in(&corrupt.packet, tb).map(|p| p.is_some()),
Err(PacketBufferError::SideDataEntries(p)) if p.count() == -3,
));
}
#[test]
fn every_flag_bit_survives_both_directions() {
use ffmpeg_next::packet::{Mut, Ref};
const DISCARD: i32 = ffmpeg_next::ffi::AV_PKT_FLAG_DISCARD;
const TRUSTED: i32 = ffmpeg_next::ffi::AV_PKT_FLAG_TRUSTED;
const DISPOSABLE: i32 = ffmpeg_next::ffi::AV_PKT_FLAG_DISPOSABLE;
const UNNAMED: i32 = 0b0010_0000; let raw = ffmpeg_next::ffi::AV_PKT_FLAG_KEY | DISCARD | TRUSTED | DISPOSABLE | UNNAMED;
let mut packet = Packet::copy(&[1u8, 2, 3]);
unsafe { (*packet.as_mut_ptr()).flags = raw };
assert_ne!(
packet.flags().bits(),
raw,
"the wrapper's own accessor is what loses them",
);
let tb = mediadecode::Timebase::default();
let expected = MdPacketFlags::from_bits_retain(raw as u8);
let video = video_packet_from_ffmpeg_in(&packet, tb)
.expect("wrappable")
.expect("present");
assert_eq!(video.flags(), expected);
assert!(video.flags().contains(MdPacketFlags::DISCARD));
let audio = audio_packet_from_ffmpeg_in(&packet, tb)
.expect("wrappable")
.expect("present");
assert_eq!(audio.flags(), expected);
let subtitle = subtitle_packet_from_ffmpeg_in(&packet, tb)
.expect("wrappable")
.expect("present");
assert_eq!(subtitle.flags(), expected);
let data = data_packet_from_ffmpeg_in(&packet, tb)
.expect("wrappable")
.expect("present");
assert_eq!(data.flags(), expected);
let attachment = attachment_packet_from_ffmpeg(&packet)
.expect("wrappable")
.expect("present");
assert_eq!(attachment.flags(), expected);
for (arm, rebuilt) in [
(
"video",
ffmpeg_packet_from_video_packet(&video).expect("rebuilt"),
),
(
"audio",
ffmpeg_packet_from_audio_packet(&audio).expect("rebuilt"),
),
(
"subtitle",
ffmpeg_packet_from_subtitle_packet(&subtitle).expect("rebuilt"),
),
] {
let carried = unsafe { (*rebuilt.as_ptr()).flags };
assert_eq!(carried, raw, "{arm} rebuilt {carried:#x} from {raw:#x}");
}
}
#[test]
fn a_flag_bit_the_vocabulary_cannot_hold_refuses_the_packet() {
use ffmpeg_next::packet::Mut;
let mut packet = Packet::copy(&[1u8]);
unsafe { (*packet.as_mut_ptr()).flags = 0x1_00 };
let tb = mediadecode::Timebase::default();
for (arm, result) in every_timed_arm(&packet) {
assert!(
matches!(
result,
Err(PacketBufferError::UnrepresentableFlags(p)) if p.raw() == 0x1_00
),
"{arm}: {result:?}",
);
}
assert!(matches!(
attachment_packet_from_ffmpeg(&packet).map(|p| p.is_some()),
Err(PacketBufferError::UnrepresentableFlags(_)),
));
let _ = tb;
}
#[test]
fn side_data_survives_the_round_trip_on_every_decoder_bound_arm() {
let tb = mediadecode::Timebase::default();
let mut with_body = Packet::copy(&[4u8, 5, 6]);
{
use ffmpeg_next::{
ffi::{AVPacketSideDataType, av_packet_new_side_data},
packet::Mut,
};
unsafe {
let ptr = av_packet_new_side_data(
with_body.as_mut_ptr(),
AVPacketSideDataType::AV_PKT_DATA_SKIP_SAMPLES,
3,
);
assert!(!ptr.is_null());
core::ptr::copy_nonoverlapping([1u8, 2, 3].as_ptr(), ptr, 3);
}
}
const SKIP_SAMPLES: i32 =
ffmpeg_next::ffi::AVPacketSideDataType::AV_PKT_DATA_SKIP_SAMPLES as i32;
for (name, source) in [
("body plus side data", &with_body),
("side data only", &side_data_only_packet()),
] {
let expected_kind = if name == "side data only" {
NEW_EXTRADATA
} else {
SKIP_SAMPLES
};
let video = video_packet_from_ffmpeg_in(source, tb)
.expect("wrappable")
.expect("present");
let rebuilt = ffmpeg_packet_from_video_packet(&video).expect("rebuilt");
let carried = packet_side_data(&rebuilt).expect("readable");
assert_eq!(carried.len(), 1, "{name}: video lost its side data");
assert_eq!(carried[0].kind(), expected_kind, "{name}: video");
assert_eq!(carried[0].data(), video.extra().side_data()[0].data());
let audio = audio_packet_from_ffmpeg_in(source, tb)
.expect("wrappable")
.expect("present");
let rebuilt = ffmpeg_packet_from_audio_packet(&audio).expect("rebuilt");
let carried = packet_side_data(&rebuilt).expect("readable");
assert_eq!(carried.len(), 1, "{name}: audio lost its side data");
assert_eq!(carried[0].data(), audio.extra().side_data()[0].data());
let subtitle = subtitle_packet_from_ffmpeg_in(source, tb)
.expect("wrappable")
.expect("present");
let rebuilt = ffmpeg_packet_from_subtitle_packet(&subtitle).expect("rebuilt");
let carried = packet_side_data(&rebuilt).expect("readable");
assert_eq!(carried.len(), 1, "{name}: subtitle lost its side data");
assert_eq!(carried[0].data(), subtitle.extra().side_data()[0].data());
}
}
#[test]
fn a_side_data_type_this_build_cannot_name_is_refused_not_dropped() {
let limit = crate::ffi::side_data_type_count();
let packet = mediadecode::packet::VideoPacket::new(
FfmpegBuffer::copy_from_slice(&[1u8]).expect("body"),
VideoPacketExtra::new(0).with_side_data(vec![SideDataEntry::new(limit, vec![9u8])]),
);
match ffmpeg_packet_from_video_packet(&packet).map(|_| ()) {
Err(PacketBuildError::UnknownSideData(p)) => {
assert_eq!(p.kind(), limit);
assert_eq!(p.limit(), limit);
}
other => panic!("expected UnknownSideData, got {other:?}"),
}
assert!(matches!(
ffmpeg_packet_from_video_packet(&mediadecode::packet::VideoPacket::new(
FfmpegBuffer::copy_from_slice(&[1u8]).expect("body"),
VideoPacketExtra::new(0).with_side_data(vec![SideDataEntry::new(-1, vec![9u8])]),
))
.map(|_| ()),
Err(PacketBuildError::UnknownSideData(p)) if p.kind() == -1,
));
}
#[test]
fn a_side_data_only_packet_is_delivered_on_every_timed_arm() {
let packet = side_data_only_packet();
let tb = mediadecode::Timebase::default();
let video = video_packet_from_ffmpeg_in(&packet, tb)
.expect("wrappable")
.expect("a side-data-only packet is a packet");
assert!(video.data().as_ref().is_empty(), "no body, but a buffer");
assert_eq!(video.extra().side_data().len(), 1);
assert_eq!(video.extra().side_data()[0].kind(), NEW_EXTRADATA);
assert_eq!(video.extra().side_data()[0].data(), &[1, 2, 3, 4]);
let audio = audio_packet_from_ffmpeg_in(&packet, tb)
.expect("wrappable")
.expect("present");
assert!(audio.data().as_ref().is_empty());
assert_eq!(audio.extra().side_data()[0].data(), &[1, 2, 3, 4]);
let subtitle = subtitle_packet_from_ffmpeg_in(&packet, tb)
.expect("wrappable")
.expect("present");
assert!(subtitle.data().as_ref().is_empty());
assert_eq!(subtitle.extra().side_data()[0].data(), &[1, 2, 3, 4]);
let data = data_packet_from_ffmpeg_in(&packet, tb)
.expect("wrappable")
.expect("present");
assert!(data.data().as_ref().is_empty());
assert_eq!(data.extra().side_data()[0].data(), &[1, 2, 3, 4]);
assert!(
attachment_packet_from_ffmpeg(&packet)
.expect("wrappable")
.is_none(),
"an attachment with no bytes is no attachment",
);
}
#[test]
fn side_data_rides_a_packet_that_has_a_body_too() {
use ffmpeg_next::{
ffi::{AVPacketSideDataType, av_packet_new_side_data},
packet::Mut,
};
let mut packet = Packet::copy(&[7u8, 7, 7]);
unsafe {
let ptr = av_packet_new_side_data(
packet.as_mut_ptr(),
AVPacketSideDataType::AV_PKT_DATA_NEW_EXTRADATA,
2,
);
assert!(!ptr.is_null());
core::ptr::copy_nonoverlapping([9u8, 9].as_ptr(), ptr, 2);
}
let video = video_packet_from_ffmpeg_in(&packet, mediadecode::Timebase::default())
.expect("wrappable")
.expect("present");
assert_eq!(video.data().as_ref(), &[7, 7, 7]);
assert_eq!(video.extra().side_data()[0].data(), &[9, 9]);
}
#[test]
fn an_empty_packet_is_absent_and_a_real_one_is_present() {
let tb = mediadecode::Timebase::default();
let empty = Packet::empty();
assert!(
video_packet_from_ffmpeg_in(&empty, tb)
.expect("not a failure")
.is_none()
);
assert!(
attachment_packet_from_ffmpeg(&empty)
.expect("not a failure")
.is_none()
);
let real = Packet::copy(&[9u8, 8, 7]);
let wrapped = video_packet_from_ffmpeg_in(&real, tb)
.expect("wrappable")
.expect("present");
assert_eq!(wrapped.data().as_ref(), &[9, 8, 7]);
}
#[test]
fn nv12_round_trips() {
assert_eq!(
from_av_pixel_format(AVPixelFormat::AV_PIX_FMT_NV12 as i32),
PixelFormat::Nv12,
);
}
#[test]
fn p010be_maps_to_p010be() {
assert_eq!(
from_av_pixel_format(AVPixelFormat::AV_PIX_FMT_P010BE as i32),
PixelFormat::P010Be,
);
}
#[test]
fn unnamed_raw_maps_to_none() {
assert_eq!(from_av_pixel_format(-99_999), PixelFormat::None);
}
#[test]
fn av_pix_fmt_none_maps_to_none() {
assert_eq!(
from_av_pixel_format(AVPixelFormat::AV_PIX_FMT_NONE as i32),
PixelFormat::None,
);
}
#[test]
fn hw_formats_detected() {
assert!(is_hardware_pix_fmt(
AVPixelFormat::AV_PIX_FMT_VIDEOTOOLBOX as i32
));
assert!(is_hardware_pix_fmt(AVPixelFormat::AV_PIX_FMT_VAAPI as i32));
assert!(is_hardware_pix_fmt(AVPixelFormat::AV_PIX_FMT_CUDA as i32));
assert!(is_hardware_pix_fmt(AVPixelFormat::AV_PIX_FMT_D3D11 as i32));
}
#[test]
fn cpu_formats_not_detected_as_hw() {
assert!(!is_hardware_pix_fmt(AVPixelFormat::AV_PIX_FMT_NV12 as i32));
assert!(!is_hardware_pix_fmt(
AVPixelFormat::AV_PIX_FMT_YUV420P as i32
));
assert!(!is_hardware_pix_fmt(AVPixelFormat::AV_PIX_FMT_NONE as i32));
}
#[test]
fn hw_formats_map_to_none_in_pixel_format() {
assert_eq!(
from_av_pixel_format(AVPixelFormat::AV_PIX_FMT_VIDEOTOOLBOX as i32),
PixelFormat::None,
);
assert_eq!(
from_av_pixel_format(AVPixelFormat::AV_PIX_FMT_VAAPI as i32),
PixelFormat::None,
);
}
}