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::{
buffer::{
FfmpegBytes, PacketBufferError, SideDataAlloc as BufferSideDataAlloc, SideDataArray,
SideDataBytes, SideDataEntries, SideDataPayload, UnrepresentableFlags,
},
carrier::BodyRoute,
convert::{SIDE_DATA_MAX_ENTRIES, SIDE_DATA_MAX_TOTAL_BYTES},
extras::{
AttachmentPacketExtra, AudioFrameExtra, AudioPacketExtra, DataPacketExtra, SideDataEntry,
SubtitleFrameExtra, SubtitlePacketExtra, VideoFrameExtra, VideoPacketExtra,
},
limits::PacketLimits,
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
)
}
pub(crate) 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,
}),
}
}
pub(crate) 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(Copy, Clone, Debug, PartialEq, Eq, thiserror::Error)]
#[error("a {bytes}-byte packet payload exceeds the {limit}-byte budget on the way into FFmpeg")]
pub struct SendPayloadTooLarge {
bytes: usize,
limit: usize,
}
impl SendPayloadTooLarge {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(bytes: usize, limit: usize) -> Self {
Self { bytes, limit }
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn bytes(&self) -> usize {
self.bytes
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn limit(&self) -> usize {
self.limit
}
}
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error, IsVariant, Unwrap, TryUnwrap)]
#[unwrap(ref, ref_mut)]
#[try_unwrap(ref, ref_mut)]
pub enum PacketBuildError {
#[error(transparent)]
SendPayloadTooLarge(#[from] SendPayloadTooLarge),
#[error(transparent)]
Ffmpeg(#[from] ffmpeg_next::Error),
#[error(transparent)]
UnknownSideData(#[from] UnknownSideData),
#[error(transparent)]
SideDataAlloc(#[from] SideDataAlloc),
#[error(transparent)]
TrustedPayload(#[from] crate::buffer::TrustedPayload),
#[error(transparent)]
SendSideDataTooLarge(#[from] SendSideDataTooLarge),
}
fn check_side_data_budget(entries: &[SideDataEntry]) -> Result<(), PacketBuildError> {
use crate::convert::{SIDE_DATA_MAX_ENTRIES, SIDE_DATA_MAX_TOTAL_BYTES};
if entries.len() > SIDE_DATA_MAX_ENTRIES {
return Err(PacketBuildError::SendSideDataTooLarge(
SendSideDataTooLarge::new_entries(entries.len(), SIDE_DATA_MAX_ENTRIES),
));
}
let mut total: usize = 0;
for entry in entries {
total = total.saturating_add(entry.data().len());
if total > SIDE_DATA_MAX_TOTAL_BYTES {
return Err(PacketBuildError::SendSideDataTooLarge(
SendSideDataTooLarge::new_bytes(total, SIDE_DATA_MAX_TOTAL_BYTES),
));
}
}
Ok(())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[error("packet side data is over the {what} ceiling: {value} against {limit}")]
pub struct SendSideDataTooLarge {
what: &'static str,
value: usize,
limit: usize,
}
impl SendSideDataTooLarge {
#[inline]
pub const fn new_entries(value: usize, limit: usize) -> Self {
Self {
what: "entry-count",
value,
limit,
}
}
#[inline]
pub const fn new_bytes(value: usize, limit: usize) -> Self {
Self {
what: "byte",
value,
limit,
}
}
#[inline]
pub const fn value(&self) -> usize {
self.value
}
#[inline]
pub const fn limit(&self) -> usize {
self.limit
}
#[inline]
pub const fn what(&self) -> &'static str {
self.what
}
}
fn refuse_trusted(flags: MdPacketFlags, len: usize) -> std::result::Result<(), PacketBuildError> {
if flags.bits() & crate::buffer::TRUSTED_BIT != 0 {
return Err(PacketBuildError::TrustedPayload(
crate::buffer::TrustedPayload::new(len),
));
}
Ok(())
}
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(crate) fn share_or_copy(
body: &crate::FfmpegBuffer,
) -> std::result::Result<Packet, ffmpeg_next::Error> {
use ffmpeg_next::packet::Mut;
const PADDING: usize = ffmpeg_next::ffi::AV_INPUT_BUFFER_PADDING_SIZE as usize;
if body.is_empty() {
return try_packet_copy(&[]);
}
if body.origin() != crate::view::Origin::PacketPayload {
return try_packet_copy(body.as_ref());
}
let buf = body.as_av_buffer_ref();
if buf.is_null() {
return try_packet_copy(body.as_ref());
}
let capacity = unsafe { (*buf).size };
let end = body.offset().saturating_add(body.len());
let padded = capacity
.checked_sub(end)
.is_some_and(|slack| slack >= PADDING);
if !padded {
return try_packet_copy(body.as_ref());
}
let mut out = Packet::empty();
unsafe {
let raw = out.as_mut_ptr();
let shared = ffmpeg_next::ffi::av_buffer_ref(buf.cast_mut());
if shared.is_null() {
return Err(ffmpeg_next::Error::Other {
errno: libc::ENOMEM,
});
}
(*raw).buf = shared;
(*raw).data = (*shared).data.add(body.offset());
(*raw).size = c_int::try_from(body.len()).map_err(|_| ffmpeg_next::Error::Other {
errno: libc::EINVAL,
})?;
}
Ok(out)
}
pub fn ffmpeg_packet_from_video_packet(
packet: &mediadecode::packet::VideoPacket<VideoPacketExtra, crate::FfmpegBuffer>,
limits: PacketLimits,
) -> std::result::Result<Packet, PacketBuildError> {
build_video_packet::<crate::View>(packet, limits, BodyRoute::Copy)
}
pub fn ffmpeg_packet_from_owned_video_packet(
packet: &mediadecode::packet::VideoPacket<VideoPacketExtra, FfmpegBytes>,
limits: PacketLimits,
) -> std::result::Result<Packet, PacketBuildError> {
build_video_packet::<crate::Owned>(packet, limits, BodyRoute::Copy)
}
pub(crate) fn with_ffmpeg_video_packet<C: crate::FfmpegCarrier + crate::CarrierOps, T>(
packet: &mediadecode::packet::VideoPacket<VideoPacketExtra, C::Buffer>,
limits: PacketLimits,
route: BodyRoute,
submit: impl FnOnce(&Packet) -> T,
) -> std::result::Result<T, PacketBuildError> {
let av_packet = build_video_packet::<C>(packet, limits, route)?;
let out = submit(&av_packet);
drop(av_packet);
Ok(out)
}
fn build_video_packet<C: crate::FfmpegCarrier + crate::CarrierOps>(
packet: &mediadecode::packet::VideoPacket<VideoPacketExtra, C::Buffer>,
limits: PacketLimits,
route: BodyRoute,
) -> std::result::Result<Packet, PacketBuildError> {
let body = packet.data().as_ref();
refuse_trusted(packet.flags(), body.len())?;
check_side_data_budget(packet.extra().side_data())?;
if body.len() > limits.max_packet_bytes() {
return Err(PacketBuildError::SendPayloadTooLarge(
SendPayloadTooLarge::new(body.len(), limits.max_packet_bytes()),
));
}
let mut out = C::packet_body(packet.data(), route)?;
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, crate::FfmpegBuffer>,
limits: PacketLimits,
) -> std::result::Result<Packet, PacketBuildError> {
build_audio_packet::<crate::View>(packet, limits, BodyRoute::Copy)
}
pub fn ffmpeg_packet_from_owned_audio_packet(
packet: &mediadecode::packet::AudioPacket<AudioPacketExtra, FfmpegBytes>,
limits: PacketLimits,
) -> std::result::Result<Packet, PacketBuildError> {
build_audio_packet::<crate::Owned>(packet, limits, BodyRoute::Copy)
}
pub(crate) fn with_ffmpeg_audio_packet<C: crate::FfmpegCarrier + crate::CarrierOps, T>(
packet: &mediadecode::packet::AudioPacket<AudioPacketExtra, C::Buffer>,
limits: PacketLimits,
route: BodyRoute,
submit: impl FnOnce(&Packet) -> T,
) -> std::result::Result<T, PacketBuildError> {
let av_packet = build_audio_packet::<C>(packet, limits, route)?;
let out = submit(&av_packet);
drop(av_packet);
Ok(out)
}
fn build_audio_packet<C: crate::FfmpegCarrier + crate::CarrierOps>(
packet: &mediadecode::packet::AudioPacket<AudioPacketExtra, C::Buffer>,
limits: PacketLimits,
route: BodyRoute,
) -> std::result::Result<Packet, PacketBuildError> {
let body = packet.data().as_ref();
refuse_trusted(packet.flags(), body.len())?;
check_side_data_budget(packet.extra().side_data())?;
if body.len() > limits.max_packet_bytes() {
return Err(PacketBuildError::SendPayloadTooLarge(
SendPayloadTooLarge::new(body.len(), limits.max_packet_bytes()),
));
}
let mut out = C::packet_body(packet.data(), route)?;
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, crate::FfmpegBuffer>,
limits: PacketLimits,
) -> std::result::Result<Packet, PacketBuildError> {
build_subtitle_packet::<crate::View>(packet, limits, BodyRoute::Copy)
}
pub fn ffmpeg_packet_from_owned_subtitle_packet(
packet: &mediadecode::packet::SubtitlePacket<SubtitlePacketExtra, FfmpegBytes>,
limits: PacketLimits,
) -> std::result::Result<Packet, PacketBuildError> {
build_subtitle_packet::<crate::Owned>(packet, limits, BodyRoute::Copy)
}
pub(crate) fn with_ffmpeg_subtitle_packet<C: crate::FfmpegCarrier + crate::CarrierOps, T>(
packet: &mediadecode::packet::SubtitlePacket<SubtitlePacketExtra, C::Buffer>,
limits: PacketLimits,
route: BodyRoute,
submit: impl FnOnce(&Packet) -> T,
) -> std::result::Result<T, PacketBuildError> {
let av_packet = build_subtitle_packet::<C>(packet, limits, route)?;
let out = submit(&av_packet);
drop(av_packet);
Ok(out)
}
fn build_subtitle_packet<C: crate::FfmpegCarrier + crate::CarrierOps>(
packet: &mediadecode::packet::SubtitlePacket<SubtitlePacketExtra, C::Buffer>,
limits: PacketLimits,
route: BodyRoute,
) -> std::result::Result<Packet, PacketBuildError> {
let body = packet.data().as_ref();
refuse_trusted(packet.flags(), body.len())?;
check_side_data_budget(packet.extra().side_data())?;
if body.len() > limits.max_packet_bytes() {
return Err(PacketBuildError::SendPayloadTooLarge(
SendPayloadTooLarge::new(body.len(), limits.max_packet_bytes()),
));
}
let mut out = C::packet_body(packet.data(), route)?;
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, FfmpegBytes>>, PacketBufferError> {
video_packet_from_borrowed::<crate::Owned>(
packet,
mediadecode::Timebase::default(),
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
}
pub fn audio_packet_from_ffmpeg(
packet: &Packet,
) -> Result<Option<AudioPacket<AudioPacketExtra, FfmpegBytes>>, PacketBufferError> {
audio_packet_from_borrowed::<crate::Owned>(
packet,
mediadecode::Timebase::default(),
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
}
pub fn subtitle_packet_from_ffmpeg(
packet: &Packet,
) -> Result<Option<SubtitlePacket<SubtitlePacketExtra, FfmpegBytes>>, PacketBufferError> {
subtitle_packet_from_borrowed::<crate::Owned>(
packet,
mediadecode::Timebase::default(),
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
}
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 {
FfmpegBytes::empty()
} 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) });
FfmpegBytes::copy_from_slice(&buf)
};
out.push(SideDataEntry::new(kind, data));
}
Ok(out)
}
fn delivered_payload<C: crate::FfmpegCarrier + crate::CarrierOps>(
packet: &Packet,
side_data: &[SideDataEntry],
limits: PacketLimits,
provenance: crate::buffer::PayloadProvenance,
) -> Result<Option<C::Buffer>, PacketBufferError> {
use ffmpeg_next::packet::Ref;
if let Some(bytes) = unsafe {
crate::buffer::payload_of::<C>(packet.as_ptr(), limits.max_packet_bytes(), provenance)
}? {
return Ok(Some(bytes));
}
if side_data.is_empty() {
return Ok(None);
}
Ok(Some(C::empty()))
}
pub(crate) fn video_packet_from_ffmpeg_as<C: crate::FfmpegCarrier + crate::CarrierOps>(
source: Packet,
time_base: mediadecode::Timebase,
limits: PacketLimits,
provenance: crate::buffer::PayloadProvenance,
) -> Result<Option<VideoPacket<VideoPacketExtra, C::Buffer>>, PacketBufferError> {
video_packet_from_borrowed::<C>(&source, time_base, limits, provenance)
}
pub(crate) fn video_packet_from_borrowed<C: crate::FfmpegCarrier + crate::CarrierOps>(
packet: &Packet,
time_base: mediadecode::Timebase,
limits: PacketLimits,
provenance: crate::buffer::PayloadProvenance,
) -> Result<Option<VideoPacket<VideoPacketExtra, C::Buffer>>, PacketBufferError> {
let side_data = packet_side_data(packet)?;
let Some(buf) = delivered_payload::<C>(packet, &side_data, limits, provenance)? 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(crate) fn audio_packet_from_ffmpeg_as<C: crate::FfmpegCarrier + crate::CarrierOps>(
source: Packet,
time_base: mediadecode::Timebase,
limits: PacketLimits,
provenance: crate::buffer::PayloadProvenance,
) -> Result<Option<AudioPacket<AudioPacketExtra, C::Buffer>>, PacketBufferError> {
audio_packet_from_borrowed::<C>(&source, time_base, limits, provenance)
}
pub(crate) fn audio_packet_from_borrowed<C: crate::FfmpegCarrier + crate::CarrierOps>(
packet: &Packet,
time_base: mediadecode::Timebase,
limits: PacketLimits,
provenance: crate::buffer::PayloadProvenance,
) -> Result<Option<AudioPacket<AudioPacketExtra, C::Buffer>>, PacketBufferError> {
let side_data = packet_side_data(packet)?;
let Some(buf) = delivered_payload::<C>(packet, &side_data, limits, provenance)? 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(crate) fn subtitle_packet_from_ffmpeg_as<C: crate::FfmpegCarrier + crate::CarrierOps>(
source: Packet,
time_base: mediadecode::Timebase,
limits: PacketLimits,
provenance: crate::buffer::PayloadProvenance,
) -> Result<Option<SubtitlePacket<SubtitlePacketExtra, C::Buffer>>, PacketBufferError> {
subtitle_packet_from_borrowed::<C>(&source, time_base, limits, provenance)
}
pub(crate) fn subtitle_packet_from_borrowed<C: crate::FfmpegCarrier + crate::CarrierOps>(
packet: &Packet,
time_base: mediadecode::Timebase,
limits: PacketLimits,
provenance: crate::buffer::PayloadProvenance,
) -> Result<Option<SubtitlePacket<SubtitlePacketExtra, C::Buffer>>, PacketBufferError> {
let side_data = packet_side_data(packet)?;
let Some(buf) = delivered_payload::<C>(packet, &side_data, limits, provenance)? 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(crate) fn data_packet_from_ffmpeg_as<C: crate::FfmpegCarrier + crate::CarrierOps>(
source: Packet,
time_base: mediadecode::Timebase,
limits: PacketLimits,
provenance: crate::buffer::PayloadProvenance,
) -> Result<Option<DataPacket<DataPacketExtra, C::Buffer>>, PacketBufferError> {
data_packet_from_borrowed::<C>(&source, time_base, limits, provenance)
}
pub(crate) fn data_packet_from_borrowed<C: crate::FfmpegCarrier + crate::CarrierOps>(
packet: &Packet,
time_base: mediadecode::Timebase,
limits: PacketLimits,
provenance: crate::buffer::PayloadProvenance,
) -> Result<Option<DataPacket<DataPacketExtra, C::Buffer>>, PacketBufferError> {
let side_data = packet_side_data(packet)?;
let Some(buf) = delivered_payload::<C>(packet, &side_data, limits, provenance)? 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(crate) fn attachment_packet_from_ffmpeg_as<C: crate::FfmpegCarrier + crate::CarrierOps>(
source: Packet,
limits: PacketLimits,
provenance: crate::buffer::PayloadProvenance,
) -> Result<Option<AttachmentPacket<AttachmentPacketExtra, C::Buffer>>, PacketBufferError> {
attachment_packet_from_borrowed::<C>(&source, limits, provenance)
}
pub(crate) fn attachment_packet_from_borrowed<C: crate::FfmpegCarrier + crate::CarrierOps>(
packet: &Packet,
limits: PacketLimits,
provenance: crate::buffer::PayloadProvenance,
) -> Result<Option<AttachmentPacket<AttachmentPacketExtra, C::Buffer>>, PacketBufferError> {
use ffmpeg_next::packet::Ref;
let Some(buf) = (unsafe {
crate::buffer::payload_of::<C>(packet.as_ptr(), limits.max_packet_bytes(), provenance)
})?
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))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, IsVariant)]
pub enum MediaKind {
Video,
Audio,
Subtitle,
Data,
Attachment,
Unknown,
}
pub(crate) fn media_kind_from_raw(raw: i32) -> MediaKind {
use ffmpeg_next::ffi::AVMediaType::*;
match raw {
x if x == AVMEDIA_TYPE_VIDEO as i32 => MediaKind::Video,
x if x == AVMEDIA_TYPE_AUDIO as i32 => MediaKind::Audio,
x if x == AVMEDIA_TYPE_SUBTITLE as i32 => MediaKind::Subtitle,
x if x == AVMEDIA_TYPE_DATA as i32 => MediaKind::Data,
x if x == AVMEDIA_TYPE_ATTACHMENT as i32 => MediaKind::Attachment,
_ => MediaKind::Unknown,
}
}
pub(crate) fn media_kind_of(parameters: &ffmpeg_next::codec::Parameters) -> MediaKind {
let ptr = unsafe { parameters.as_ptr() };
if ptr.is_null() {
return MediaKind::Unknown;
}
let raw = unsafe { read_unaligned(addr_of!((*ptr).codec_type) as *const i32) };
media_kind_from_raw(raw)
}
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(crate) fn empty_video_frame_as<C: crate::FfmpegCarrier + crate::CarrierOps>()
-> VideoFrame<PixelFormat, VideoFrameExtra, C::Buffer> {
VideoFrame::new(
Dimensions::new(0, 0),
PixelFormat::None,
core::array::from_fn(|_| Plane::new(C::empty(), 0)),
0,
VideoFrameExtra::default(),
)
}
pub(crate) fn empty_audio_frame_as<C: crate::FfmpegCarrier + crate::CarrierOps>()
-> AudioFrame<SampleFormat, ChannelLayoutDescription, AudioFrameExtra, C::Buffer> {
AudioFrame::new(
0,
0,
0,
SampleFormat::NONE,
ChannelLayoutDescription::default(),
core::array::from_fn(|_| Plane::new(C::empty(), 0)),
0,
AudioFrameExtra::default(),
)
}
pub(crate) fn empty_subtitle_frame_as<C: crate::FfmpegCarrier + crate::CarrierOps>()
-> SubtitleFrame<SubtitleFrameExtra, C::Buffer> {
SubtitleFrame::new(
SubtitlePayload::Text(SubtitleText::new(C::empty(), None)),
SubtitleFrameExtra::default(),
)
}
pub fn video_packet_from_ffmpeg_in(
packet: Packet,
time_base: mediadecode::Timebase,
limits: PacketLimits,
) -> Result<Option<VideoPacket<VideoPacketExtra, crate::FfmpegBuffer>>, PacketBufferError> {
video_packet_from_ffmpeg_as::<crate::View>(
packet,
time_base,
limits,
crate::buffer::PayloadProvenance::CallerSupplied,
)
}
pub fn audio_packet_from_ffmpeg_in(
packet: Packet,
time_base: mediadecode::Timebase,
limits: PacketLimits,
) -> Result<Option<AudioPacket<AudioPacketExtra, crate::FfmpegBuffer>>, PacketBufferError> {
audio_packet_from_ffmpeg_as::<crate::View>(
packet,
time_base,
limits,
crate::buffer::PayloadProvenance::CallerSupplied,
)
}
pub fn subtitle_packet_from_ffmpeg_in(
packet: Packet,
time_base: mediadecode::Timebase,
limits: PacketLimits,
) -> Result<Option<SubtitlePacket<SubtitlePacketExtra, crate::FfmpegBuffer>>, PacketBufferError> {
subtitle_packet_from_ffmpeg_as::<crate::View>(
packet,
time_base,
limits,
crate::buffer::PayloadProvenance::CallerSupplied,
)
}
pub fn data_packet_from_ffmpeg_in(
packet: Packet,
time_base: mediadecode::Timebase,
limits: PacketLimits,
) -> Result<Option<DataPacket<DataPacketExtra, crate::FfmpegBuffer>>, PacketBufferError> {
data_packet_from_ffmpeg_as::<crate::View>(
packet,
time_base,
limits,
crate::buffer::PayloadProvenance::CallerSupplied,
)
}
pub fn owned_video_packet_from_ffmpeg_in(
packet: &Packet,
time_base: mediadecode::Timebase,
limits: PacketLimits,
) -> Result<Option<VideoPacket<VideoPacketExtra, FfmpegBytes>>, PacketBufferError> {
video_packet_from_borrowed::<crate::Owned>(
packet,
time_base,
limits,
crate::buffer::PayloadProvenance::CallerSupplied,
)
}
pub fn owned_audio_packet_from_ffmpeg_in(
packet: &Packet,
time_base: mediadecode::Timebase,
limits: PacketLimits,
) -> Result<Option<AudioPacket<AudioPacketExtra, FfmpegBytes>>, PacketBufferError> {
audio_packet_from_borrowed::<crate::Owned>(
packet,
time_base,
limits,
crate::buffer::PayloadProvenance::CallerSupplied,
)
}
pub fn owned_subtitle_packet_from_ffmpeg_in(
packet: &Packet,
time_base: mediadecode::Timebase,
limits: PacketLimits,
) -> Result<Option<SubtitlePacket<SubtitlePacketExtra, FfmpegBytes>>, PacketBufferError> {
subtitle_packet_from_borrowed::<crate::Owned>(
packet,
time_base,
limits,
crate::buffer::PayloadProvenance::CallerSupplied,
)
}
pub fn owned_data_packet_from_ffmpeg_in(
packet: &Packet,
time_base: mediadecode::Timebase,
limits: PacketLimits,
) -> Result<Option<DataPacket<DataPacketExtra, FfmpegBytes>>, PacketBufferError> {
data_packet_from_borrowed::<crate::Owned>(
packet,
time_base,
limits,
crate::buffer::PayloadProvenance::CallerSupplied,
)
}
pub fn owned_attachment_packet_from_ffmpeg(
packet: &Packet,
limits: PacketLimits,
) -> Result<Option<AttachmentPacket<AttachmentPacketExtra, FfmpegBytes>>, PacketBufferError> {
attachment_packet_from_borrowed::<crate::Owned>(
packet,
limits,
crate::buffer::PayloadProvenance::CallerSupplied,
)
}
#[must_use]
pub fn empty_video_frame() -> VideoFrame<PixelFormat, VideoFrameExtra, crate::FfmpegBuffer> {
empty_video_frame_as::<crate::View>()
}
#[must_use]
pub fn empty_owned_video_frame() -> VideoFrame<PixelFormat, VideoFrameExtra, FfmpegBytes> {
empty_video_frame_as::<crate::Owned>()
}
#[must_use]
pub fn empty_audio_frame()
-> AudioFrame<SampleFormat, ChannelLayoutDescription, AudioFrameExtra, crate::FfmpegBuffer> {
empty_audio_frame_as::<crate::View>()
}
#[must_use]
pub fn empty_owned_audio_frame()
-> AudioFrame<SampleFormat, ChannelLayoutDescription, AudioFrameExtra, FfmpegBytes> {
empty_audio_frame_as::<crate::Owned>()
}
#[must_use]
pub fn empty_subtitle_frame() -> SubtitleFrame<SubtitleFrameExtra, crate::FfmpegBuffer> {
empty_subtitle_frame_as::<crate::View>()
}
#[must_use]
pub fn empty_owned_subtitle_frame() -> SubtitleFrame<SubtitleFrameExtra, FfmpegBytes> {
empty_subtitle_frame_as::<crate::Owned>()
}
pub fn attachment_packet_from_ffmpeg(
packet: Packet,
limits: PacketLimits,
) -> Result<Option<AttachmentPacket<AttachmentPacketExtra, crate::FfmpegBuffer>>, PacketBufferError>
{
attachment_packet_from_ffmpeg_as::<crate::View>(
packet,
limits,
crate::buffer::PayloadProvenance::CallerSupplied,
)
}
#[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_borrowed::<crate::Owned>(
&forged,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied
),
Err(PacketBufferError::Bounds(_)),
));
assert!(matches!(
audio_packet_from_borrowed::<crate::Owned>(
&forged,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied
),
Err(PacketBufferError::Bounds(_)),
));
assert!(matches!(
subtitle_packet_from_borrowed::<crate::Owned>(
&forged,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied
),
Err(PacketBufferError::Bounds(_)),
));
assert!(matches!(
data_packet_from_borrowed::<crate::Owned>(
&forged,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied
),
Err(PacketBufferError::Bounds(_)),
));
assert!(matches!(
attachment_packet_from_borrowed::<crate::Owned>(
&forged,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied
),
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_borrowed::<crate::Owned>(
packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.map(|p| p.is_some()),
),
(
"audio",
audio_packet_from_borrowed::<crate::Owned>(
packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.map(|p| p.is_some()),
),
(
"subtitle",
subtitle_packet_from_borrowed::<crate::Owned>(
packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.map(|p| p.is_some()),
),
(
"data",
data_packet_from_borrowed::<crate::Owned>(
packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.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_borrowed::<crate::Owned>(
&marker.packet,
mediadecode::Timebase::default(),
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.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_borrowed::<crate::Owned>(
&oversized,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.map(|p| p.is_some()),
),
(
"audio",
audio_packet_from_borrowed::<crate::Owned>(
&oversized,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.map(|p| p.is_some()),
),
(
"subtitle",
subtitle_packet_from_borrowed::<crate::Owned>(
&oversized,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.map(|p| p.is_some()),
),
(
"data",
data_packet_from_borrowed::<crate::Owned>(
&oversized,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.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_borrowed::<crate::Owned>(
&oversized,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied
)
.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_borrowed::<crate::Owned>(
&at_cap,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.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_borrowed::<crate::Owned>(
&past,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied
)
.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_borrowed::<crate::Owned>(
&at_cap.packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.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_borrowed::<crate::Owned>(
&past.packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.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_borrowed::<crate::Owned>(&corrupt.packet, tb, PacketLimits::default(), crate::buffer::PayloadProvenance::CallerSupplied).map(|p| p.is_some()),
Err(PacketBufferError::SideDataEntries(p)) if p.count() == -3,
));
}
#[test]
fn a_trusted_packet_is_refused_on_both_legs() {
use ffmpeg_next::packet::Mut;
let tb = mediadecode::Timebase::default();
let mut packet = Packet::copy(&[1u8, 2, 3]);
unsafe {
(*packet.as_mut_ptr()).flags =
ffmpeg_next::ffi::AV_PKT_FLAG_KEY | ffmpeg_next::ffi::AV_PKT_FLAG_TRUSTED;
};
for (arm, taken) in [
(
"video",
video_packet_from_borrowed::<crate::Owned>(
&packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.map(|p| p.is_some()),
),
(
"audio",
audio_packet_from_borrowed::<crate::Owned>(
&packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.map(|p| p.is_some()),
),
(
"subtitle",
subtitle_packet_from_borrowed::<crate::Owned>(
&packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.map(|p| p.is_some()),
),
(
"data",
data_packet_from_borrowed::<crate::Owned>(
&packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.map(|p| p.is_some()),
),
(
"attachment",
attachment_packet_from_borrowed::<crate::Owned>(
&packet,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.map(|p| p.is_some()),
),
] {
match taken {
Err(PacketBufferError::TrustedPayload(p)) => assert_eq!(p.len(), 3, "{arm}"),
other => panic!("{arm}: a TRUSTED payload must not be copied out, got {other:?}"),
}
}
let clean = Packet::copy(&[1u8, 2, 3]);
let video = video_packet_from_borrowed::<crate::Owned>(
&clean,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.expect("a clean packet is carriable")
.expect("present");
let trusted = video
.clone()
.with_flags(MdPacketFlags::from_bits_retain(crate::buffer::TRUSTED_BIT));
assert!(
matches!(
ffmpeg_packet_from_owned_video_packet(&trusted, PacketLimits::default()),
Err(PacketBuildError::TrustedPayload(_)),
),
"a TRUSTED flag must not be written back onto an AVPacket",
);
}
#[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 _ = TRUSTED;
let raw = ffmpeg_next::ffi::AV_PKT_FLAG_KEY | DISCARD | 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_borrowed::<crate::Owned>(
&packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.expect("wrappable")
.expect("present");
assert_eq!(video.flags(), expected);
assert!(video.flags().contains(MdPacketFlags::DISCARD));
let audio = audio_packet_from_borrowed::<crate::Owned>(
&packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.expect("wrappable")
.expect("present");
assert_eq!(audio.flags(), expected);
let subtitle = subtitle_packet_from_borrowed::<crate::Owned>(
&packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.expect("wrappable")
.expect("present");
assert_eq!(subtitle.flags(), expected);
let data = data_packet_from_borrowed::<crate::Owned>(
&packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.expect("wrappable")
.expect("present");
assert_eq!(data.flags(), expected);
let attachment = attachment_packet_from_borrowed::<crate::Owned>(
&packet,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.expect("wrappable")
.expect("present");
assert_eq!(attachment.flags(), expected);
for (arm, rebuilt) in [
(
"video",
ffmpeg_packet_from_owned_video_packet(&video, PacketLimits::default()).expect("rebuilt"),
),
(
"audio",
ffmpeg_packet_from_owned_audio_packet(&audio, PacketLimits::default()).expect("rebuilt"),
),
(
"subtitle",
ffmpeg_packet_from_owned_subtitle_packet(&subtitle, PacketLimits::default())
.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_borrowed::<crate::Owned>(
&packet,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied
)
.map(|p| p.is_some()),
Err(PacketBufferError::UnrepresentableFlags(_)),
));
let _ = tb;
}
struct TestBuffer(*mut ffmpeg_next::ffi::AVBufferRef);
impl TestBuffer {
fn new(len: usize) -> Self {
unsafe {
let raw = ffmpeg_next::ffi::av_buffer_alloc(len as _);
assert!(!raw.is_null(), "av_buffer_alloc");
core::ptr::write_bytes((*raw).data, 0xAB, len);
Self(raw)
}
}
}
impl Drop for TestBuffer {
fn drop(&mut self) {
unsafe { ffmpeg_next::ffi::av_buffer_unref(&mut self.0) };
}
}
fn payload_address(packet: &Packet) -> usize {
use ffmpeg_next::packet::Ref;
unsafe { (*packet.as_ptr()).data as usize }
}
fn payload_references(packet: &Packet) -> i32 {
use ffmpeg_next::packet::Ref;
unsafe {
let buf = (*packet.as_ptr()).buf;
assert!(!buf.is_null(), "the fixture must be refcounted");
ffmpeg_next::ffi::av_buffer_get_ref_count(buf)
}
}
#[test]
fn a_shared_packet_buffer_is_refused_without_being_read() {
let mut original = Packet::copy(&[7u8; 4096]);
let mut shared = Packet::empty();
unsafe {
use ffmpeg_next::packet::{Mut, Ref};
assert_eq!(
ffmpeg_next::ffi::av_packet_ref(shared.as_mut_ptr(), original.as_ptr()),
0,
);
}
assert_eq!(
payload_address(&shared),
payload_address(&original),
"the premise: the two packets share one allocation",
);
assert_eq!(payload_references(&original), 2);
match video_packet_from_ffmpeg_as::<crate::View>(
shared,
mediadecode::Timebase::default(),
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
) {
Err(PacketBufferError::SharedPayload(refused)) => {
assert_eq!(refused.references(), 2);
}
other => panic!("a shared payload must be refused by name, got {other:?}"),
}
{
let slot = original.data_mut().expect("a writable payload");
slot[0] = 0xEE;
}
assert_eq!(
payload_references(&original),
1,
"the refusal must not have taken a reference of its own",
);
let mut shared_again = Packet::empty();
unsafe {
use ffmpeg_next::packet::{Mut, Ref};
assert_eq!(
ffmpeg_next::ffi::av_packet_ref(shared_again.as_mut_ptr(), original.as_ptr()),
0,
);
}
assert!(matches!(
owned_video_packet_from_ffmpeg_in(
&shared_again,
mediadecode::Timebase::default(),
PacketLimits::default(),
),
Err(PacketBufferError::SharedPayload(_)),
));
}
#[test]
fn a_clone_that_silently_shared_is_refused_by_name() {
crate::fault_subprocess::in_subprocess(
"boundary::tests::a_clone_that_silently_shared_is_refused_by_name",
|| {
let mut original = Packet::copy(&[3u8; 8192]);
crate::fault_subprocess::cap_ffmpeg_allocations(512);
let clone = original.clone();
crate::fault_subprocess::uncap_ffmpeg_allocations();
assert_eq!(
payload_address(&clone),
payload_address(&original),
"the premise this test exists for: a failed `make_writable` \
leaves the clone sharing, silently",
);
assert_eq!(payload_references(&original), 2);
match video_packet_from_ffmpeg_as::<crate::View>(
clone,
mediadecode::Timebase::default(),
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
) {
Err(PacketBufferError::SharedPayload(refused)) => {
assert_eq!(refused.references(), 2);
}
other => panic!("expected a named refusal, got {other:?}"),
}
{
let slot = original.data_mut().expect("a writable payload");
slot[0] = 0x5A;
}
assert_eq!(payload_references(&original), 1);
},
);
}
#[test]
fn only_a_packet_payload_with_provable_padding_is_shared_into_a_decoder() {
use crate::view::Origin;
use ffmpeg_next::packet::Ref;
const PADDING: usize = ffmpeg_next::ffi::AV_INPUT_BUFFER_PADDING_SIZE as usize;
let src = TestBuffer::new(512);
let body_len = 512 - PADDING;
let payload =
unsafe { crate::FfmpegBuffer::view_of(src.0, 0, body_len, Origin::PacketPayload) }
.expect("a view");
let shared = share_or_copy(&payload).expect("built");
assert_eq!(
unsafe { (*shared.as_ptr()).data as usize },
payload.as_ref().as_ptr() as usize,
"a packet payload with provable padding must be shared",
);
let plane =
unsafe { crate::FfmpegBuffer::view_of(src.0, 0, body_len, Origin::Foreign) }.expect("a view");
let copied = share_or_copy(&plane).expect("built");
assert_ne!(
unsafe { (*copied.as_ptr()).data as usize },
plane.as_ref().as_ptr() as usize,
"trailing capacity is not padding provenance — this must be copied",
);
assert_eq!(copied.data().unwrap_or(&[]), plane.as_ref());
let tight = unsafe { crate::FfmpegBuffer::view_of(src.0, 0, 511, Origin::PacketPayload) }
.expect("a view");
let copied = share_or_copy(&tight).expect("built");
assert_ne!(
unsafe { (*copied.as_ptr()).data as usize },
tight.as_ref().as_ptr() as usize,
"a payload with less than the padding behind it must be copied",
);
let mut narrowed = payload.clone();
narrowed.shrink_to(16);
let copied = share_or_copy(&narrowed).expect("built");
assert_ne!(
unsafe { (*copied.as_ptr()).data as usize },
narrowed.as_ref().as_ptr() as usize,
);
}
#[test]
fn an_empty_view_carrier_builds_a_payload_less_packet() {
let empty = crate::FfmpegBuffer::empty();
assert!(empty.as_av_buffer_ref().is_null(), "the premise");
let built = share_or_copy(&empty).expect("a payload-less packet");
assert_eq!(built.size(), 0);
let mut built = built;
attach_side_data(
&mut built,
&[SideDataEntry::new(
NEW_EXTRADATA,
FfmpegBytes::copy_from_slice(&[1u8, 2, 3, 4]),
)],
)
.expect("side data attaches to a payload-less packet");
}
#[test]
fn a_side_data_only_packet_round_trips_on_every_view_decoder_family() {
let tb = mediadecode::Timebase::default();
let source = side_data_only_packet();
let limits = PacketLimits::default();
let video = video_packet_from_ffmpeg_as::<crate::View>(
source.clone(),
tb,
limits,
crate::buffer::PayloadProvenance::CallerSupplied,
)
.expect("carried")
.expect("a side-data-only packet is a packet");
assert!(video.data().as_ref().is_empty(), "the premise: no payload");
assert_eq!(
ffmpeg_packet_from_video_packet(&video, limits)
.expect("built")
.size(),
0,
);
with_ffmpeg_video_packet::<crate::View, _>(&video, limits, BodyRoute::Submission, |av| {
assert_eq!(av.size(), 0);
})
.expect("submitted");
let audio = audio_packet_from_ffmpeg_as::<crate::View>(
source.clone(),
tb,
limits,
crate::buffer::PayloadProvenance::CallerSupplied,
)
.expect("carried")
.expect("a side-data-only packet is a packet");
assert!(audio.data().as_ref().is_empty());
assert_eq!(
ffmpeg_packet_from_audio_packet(&audio, limits)
.expect("built")
.size(),
0,
);
with_ffmpeg_audio_packet::<crate::View, _>(&audio, limits, BodyRoute::Submission, |av| {
assert_eq!(av.size(), 0);
})
.expect("submitted");
let subtitle = subtitle_packet_from_ffmpeg_as::<crate::View>(
source.clone(),
tb,
limits,
crate::buffer::PayloadProvenance::CallerSupplied,
)
.expect("carried")
.expect("a side-data-only packet is a packet");
assert!(subtitle.data().as_ref().is_empty());
assert_eq!(
ffmpeg_packet_from_subtitle_packet(&subtitle, limits)
.expect("built")
.size(),
0,
);
with_ffmpeg_subtitle_packet::<crate::View, _>(&subtitle, limits, BodyRoute::Submission, |av| {
assert_eq!(av.size(), 0);
})
.expect("submitted");
}
#[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_borrowed::<crate::Owned>(
source,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.expect("wrappable")
.expect("present");
let rebuilt =
ffmpeg_packet_from_owned_video_packet(&video, PacketLimits::default()).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_borrowed::<crate::Owned>(
source,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.expect("wrappable")
.expect("present");
let rebuilt =
ffmpeg_packet_from_owned_audio_packet(&audio, PacketLimits::default()).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_borrowed::<crate::Owned>(
source,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.expect("wrappable")
.expect("present");
let rebuilt = ffmpeg_packet_from_owned_subtitle_packet(&subtitle, PacketLimits::default())
.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(
FfmpegBytes::copy_from_slice(&[1u8]),
VideoPacketExtra::new(0).with_side_data(vec![SideDataEntry::new(
limit,
FfmpegBytes::copy_from_slice(&[9u8]),
)]),
);
match ffmpeg_packet_from_owned_video_packet(&packet, PacketLimits::default()).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_owned_video_packet(&mediadecode::packet::VideoPacket::new(
FfmpegBytes::copy_from_slice(&[1u8]),
VideoPacketExtra::new(0).with_side_data(vec![SideDataEntry::new(-1, FfmpegBytes::copy_from_slice(&[9u8]))]),
), PacketLimits::default())
.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_borrowed::<crate::Owned>(
&packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.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_borrowed::<crate::Owned>(
&packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.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_borrowed::<crate::Owned>(
&packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.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_borrowed::<crate::Owned>(
&packet,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.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_borrowed::<crate::Owned>(
&packet,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied
)
.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_borrowed::<crate::Owned>(
&packet,
mediadecode::Timebase::default(),
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.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_borrowed::<crate::Owned>(
&empty,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied
)
.expect("not a failure")
.is_none()
);
assert!(
attachment_packet_from_borrowed::<crate::Owned>(
&empty,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied
)
.expect("not a failure")
.is_none()
);
let real = Packet::copy(&[9u8, 8, 7]);
let wrapped = video_packet_from_borrowed::<crate::Owned>(
&real,
tb,
PacketLimits::default(),
crate::buffer::PayloadProvenance::CallerSupplied,
)
.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,
);
}
}