Skip to main content

mediadecode_ffmpeg/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![cfg_attr(docsrs, allow(unused_attributes))]
4#![deny(missing_docs)]
5// The core crate carries this same allow, for this same reason, and 0.9
6// is where this crate joined it: a signature here reads
7// `VideoPacket<VideoPacketExtra, FfmpegBytes>` because every one of those
8// three names is load-bearing — the household, the backend's extras,
9// and the owned carrier the D-seat amputation contract requires.
10// `clippy::type_complexity` counts nesting, and the fix it asks for is
11// an alias that hides exactly the word this release exists to make
12// visible. 0.8 had that alias; it was called `FfmpegBuffer`.
13#![allow(clippy::type_complexity)]
14
15mod adapter;
16mod audio;
17mod backend;
18pub mod boundary;
19mod buffer;
20mod carrier;
21pub mod channel_layout;
22mod codec_id;
23mod container;
24pub mod convert;
25mod decoder;
26mod demuxer;
27mod error;
28pub mod extras;
29#[cfg(test)]
30mod fault_subprocess;
31mod ffi;
32mod footprint;
33mod frame;
34mod image;
35pub mod limits;
36mod measured;
37mod pixdesc;
38mod reader_guard;
39#[cfg(feature = "resample")]
40mod resampler;
41mod sample_format;
42mod subtitle;
43pub mod ticket;
44mod video;
45mod view;
46mod vtscale;
47
48pub use adapter::Ffmpeg;
49pub use audio::{AudioDecodeError, CarrierAudioStreamDecoder};
50pub use backend::Backend;
51pub use boundary::{
52  MediaKind, PacketBuildError, SendPayloadTooLarge, SendSideDataTooLarge,
53  attachment_packet_from_ffmpeg, audio_packet_from_ffmpeg, audio_packet_from_ffmpeg_in,
54  data_packet_from_ffmpeg_in, empty_audio_frame, empty_owned_audio_frame,
55  empty_owned_subtitle_frame, empty_owned_video_frame, empty_subtitle_frame, empty_video_frame,
56  ffmpeg_packet_from_audio_packet, ffmpeg_packet_from_owned_audio_packet,
57  ffmpeg_packet_from_owned_subtitle_packet, ffmpeg_packet_from_owned_video_packet,
58  ffmpeg_packet_from_subtitle_packet, ffmpeg_packet_from_video_packet, from_av_pixel_format,
59  is_hardware_pix_fmt, owned_attachment_packet_from_ffmpeg, owned_audio_packet_from_ffmpeg_in,
60  owned_data_packet_from_ffmpeg_in, owned_subtitle_packet_from_ffmpeg_in,
61  owned_video_packet_from_ffmpeg_in, subtitle_packet_from_ffmpeg, subtitle_packet_from_ffmpeg_in,
62  video_packet_from_ffmpeg, video_packet_from_ffmpeg_in,
63};
64
65pub use buffer::{FfmpegBytes, PacketBufferError, TrustedPayload};
66pub(crate) use carrier::CarrierOps;
67pub use carrier::{FfmpegCarrier, Owned, View};
68pub use view::FfmpegBuffer;
69
70/// The demuxer, on the **view** lane — the ordinary road.
71///
72/// Packets carry [`FfmpegBuffer`] views onto libavformat's own
73/// allocations: nothing is copied, and a consumer that reads a packet
74/// and drops it never pays for bytes it did not keep. This is what a
75/// direct consumer wants, which is why it is what the bare name means.
76///
77/// Reach for [`FfmpegOwnedDemuxer`] when a packet has to **travel** —
78/// across a graph, between threads that both read it, into a cache
79/// that outlives the decoder. See [the carrier lanes][lanes] for the
80/// tradeoff table.
81///
82/// [lanes]: mediadecode::adapter#the-two-carrier-lanes
83pub type FfmpegDemuxer = demuxer::CarrierDemuxer<View>;
84
85/// The demuxer on the **owned** lane: every byte copied once at the
86/// boundary into memory Rust owns.
87///
88/// The same type as [`FfmpegDemuxer`] on the other carrier, with the
89/// same constructors — `FfmpegOwnedDemuxer::open(&path)`. Packets are
90/// `Send + Sync + 'static` and owe nothing to the session that produced
91/// them, which is what a graph needs and what a view cannot give.
92pub type FfmpegOwnedDemuxer = demuxer::CarrierDemuxer<Owned>;
93
94/// The audio decoder on the **view** lane.
95pub type FfmpegAudioStreamDecoder = audio::CarrierAudioStreamDecoder<View>;
96/// The audio decoder on the **owned** lane.
97pub type FfmpegOwnedAudioStreamDecoder = audio::CarrierAudioStreamDecoder<Owned>;
98
99/// The subtitle decoder on the **view** lane.
100pub type FfmpegSubtitleStreamDecoder = subtitle::CarrierSubtitleStreamDecoder<View>;
101/// The subtitle decoder on the **owned** lane.
102pub type FfmpegOwnedSubtitleStreamDecoder = subtitle::CarrierSubtitleStreamDecoder<Owned>;
103
104/// The still-image decoder on the **view** lane.
105pub type FfmpegImageDecoder = image::CarrierImageDecoder<View>;
106/// The still-image decoder on the **owned** lane.
107pub type FfmpegOwnedImageDecoder = image::CarrierImageDecoder<Owned>;
108
109/// The video stream decoder on the **view** lane.
110pub type FfmpegVideoStreamDecoder = video::CarrierVideoStreamDecoder<View>;
111/// The video stream decoder on the **owned** lane.
112pub type FfmpegOwnedVideoStreamDecoder = video::CarrierVideoStreamDecoder<Owned>;
113pub use channel_layout::{
114  ChannelLayoutFault, channel_layout_description_from_ffmpeg, channel_layout_from_ffmpeg,
115  channel_order_from_ffmpeg,
116};
117pub use codec_id::CodecId;
118pub use container::ContainerFormat;
119pub use decoder::VideoDecoder;
120pub use demuxer::{
121  CarrierDemuxer, ChapterAlloc, ChapterTimebaseInvalid, ChapterTitleAlloc,
122  ChapterTitleBudgetExhausted, ChapterTitleTooLong, DemuxError, ParametersChannelMap,
123  ParametersLayoutShape, ProbeBudgetExhausted, TooManyChapters, TrackMetadataAlloc,
124  TrackMetadataBudgetExhausted, TrackMetadataTooLong, TrackTableAlloc, TrackTimebaseInvalid,
125};
126pub use error::{
127  Error, FrameBudgetExceeded, FrameMedium, HwSurfaceTooLarge, HwTransferTooLarge, Result,
128};
129pub use frame::Frame;
130pub use image::{CarrierImageDecoder, Corrupt, CorruptSource, ImageDecodeError, InputTooLarge};
131pub use limits::{
132  DEFAULT_MAX_ATTACHMENT_BYTES, DEFAULT_MAX_CHAPTERS, DEFAULT_MAX_CODEC_PARAMETER_BYTES,
133  DEFAULT_MAX_FRAME_BYTES, DEFAULT_MAX_IMAGE_INPUT_BYTES, DEFAULT_MAX_IMAGE_SIDE_DATA_BYTES,
134  DEFAULT_MAX_PACKET_BYTES, DEFAULT_MAX_PIXELS, DEFAULT_MAX_PROBE_BYTES, DEFAULT_MAX_STREAMS,
135  DEFAULT_MAX_TOTAL_ATTACHMENT_BYTES, DEFAULT_MAX_TOTAL_CHAPTER_TITLE_BYTES,
136  DEFAULT_MAX_TOTAL_CODEC_PARAMETER_BYTES, DEFAULT_MAX_TOTAL_STREAM_METADATA_BYTES, DecoderLimits,
137  DemuxLimits, FrameLimits, PacketLimits,
138};
139#[cfg(feature = "resample")]
140#[cfg_attr(docsrs, doc(cfg(feature = "resample")))]
141pub use resampler::{
142  CarrierResampler, OutputTooLarge, ResampleError, ResampleSpec, SpecEnd,
143  UnsupportedChannelCount as UnsupportedSpecChannelCount,
144};
145/// The resampler, on the **view** lane — planes shared out of its own
146/// output frame.
147///
148/// Its output frames are the resampler's, not a decoder's: it allocates
149/// one per conversion and never writes into a buffer it has handed out,
150/// so the pool-hostage warning that applies to decoder frames does not
151/// apply here. What does apply is `!Sync` and the amputation contract —
152/// use [`FfmpegOwnedResampler`] for a frame that has to travel.
153#[cfg(feature = "resample")]
154#[cfg_attr(docsrs, doc(cfg(feature = "resample")))]
155pub type FfmpegResampler = resampler::CarrierResampler<View>;
156
157/// The resampler, on the **owned** lane — every produced plane copied
158/// out of the output frame.
159#[cfg(feature = "resample")]
160#[cfg_attr(docsrs, doc(cfg(feature = "resample")))]
161pub type FfmpegOwnedResampler = resampler::CarrierResampler<Owned>;
162
163pub use sample_format::SampleFormat;
164pub use subtitle::{CarrierSubtitleStreamDecoder, SubtitleDecodeError};
165pub use ticket::{ChannelLayoutTicket, CodecTicket, CustomChannel, DolbyVisionConfig, Ratio};
166pub use video::{CarrierVideoStreamDecoder, DecodePath, VideoDecodeError};
167
168// Every bare alias below binds [`FfmpegBuffer`] in the `D` seat — the
169// view lane, the ordinary road: a decoder's output read where it lands
170// and dropped. The `Owned*` family binds [`FfmpegBytes`] and is what a
171// payload takes when it has to **travel** — outlive the decoder, cross
172// into a graph, be shared across threads.
173//
174// Each spells its carrier out rather than hiding it behind a neutral
175// name. That is deliberate, and it is the one lesson 0.8's version of
176// this block failed to teach: 0.8 also called this type `FfmpegBuffer`,
177// but the D seat was *only* ever that type, so a consumer reading
178// `VideoFrame` could not tell that holding one held libavcodec's memory
179// open. It did. Naming both carriers in both families is what makes the
180// question answerable at the use site — and [`FfmpegBytes`] answers it
181// by being nothing of ours: owned, `Send + Sync`, no FFmpeg lifetime
182// attached, the core's D-seat amputation contract satisfied by a type
183// out of `alloc`.
184
185/// Compressed video packet pre-parameterized with this crate's extras
186/// and view carrier — the type [`FfmpegVideoStreamDecoder`] consumes
187/// via [`mediadecode::decoder::VideoStreamDecoder::send_packet`].
188pub type VideoPacket = mediadecode::packet::VideoPacket<extras::VideoPacketExtra, FfmpegBuffer>;
189
190/// Compressed audio packet pre-parameterized with this crate's extras
191/// and view carrier.
192pub type AudioPacket = mediadecode::packet::AudioPacket<extras::AudioPacketExtra, FfmpegBuffer>;
193
194/// Compressed subtitle packet pre-parameterized with this crate's
195/// extras and view carrier.
196pub type SubtitlePacket =
197  mediadecode::packet::SubtitlePacket<extras::SubtitlePacketExtra, FfmpegBuffer>;
198
199/// Decoded video frame pre-parameterized with this crate's pixel
200/// format / extras / view carrier.
201///
202/// Its planes are windows into the decoder's own frame buffer wherever
203/// the geometry proves they may be — see the frame row of the lane
204/// table in [`mediadecode::adapter`]. **A frame held is a pool slot
205/// held**: on a hardware or fixed-pool decoder, retaining these past
206/// the next `receive_frame` starves the decoder. Use [`OwnedVideoFrame`]
207/// when a frame has to outlive the decode loop.
208pub type VideoFrame =
209  mediadecode::frame::VideoFrame<mediadecode::PixelFormat, extras::VideoFrameExtra, FfmpegBuffer>;
210
211/// Decoded audio frame pre-parameterized with this crate's sample
212/// format / channel layout / extras / view carrier.
213///
214/// Each plane is a window over exactly the samples the decoder wrote —
215/// never the allocator's alignment padding past them.
216pub type AudioFrame = mediadecode::frame::AudioFrame<
217  SampleFormat,
218  mediaframe::audio::ChannelLayoutDescription,
219  extras::AudioFrameExtra,
220  FfmpegBuffer,
221>;
222
223/// Decoded subtitle frame pre-parameterized with this crate's
224/// extras / view carrier.
225pub type SubtitleFrame =
226  mediadecode::frame::SubtitleFrame<extras::SubtitleFrameExtra, FfmpegBuffer>;
227
228/// Decoded still image pre-parameterized with this crate's pixel
229/// format / extras / view carrier — what [`FfmpegImageDecoder`]
230/// produces.
231pub type ImageFrame =
232  mediadecode::frame::ImageFrame<mediadecode::PixelFormat, extras::ImageFrameExtra, FfmpegBuffer>;
233
234/// Timed opaque-data packet pre-parameterized with this crate's extras
235/// and view carrier.
236pub type DataPacket = mediadecode::demuxer::DataPacket<extras::DataPacketExtra, FfmpegBuffer>;
237
238/// Attachment payload pre-parameterized with this crate's extras and
239/// view carrier — a font, or the cover art [`FfmpegImageDecoder`]
240/// decodes.
241pub type AttachmentPacket =
242  mediadecode::demuxer::AttachmentPacket<extras::AttachmentPacketExtra, FfmpegBuffer>;
243
244/// The five-arm demux envelope [`FfmpegDemuxer`] delivers.
245pub type DemuxedPacket = mediadecode::demuxer::DemuxedPacket<Ffmpeg, FfmpegBuffer>;
246
247// --- The owned lane's alias family -----------------------------------
248//
249// The same shapes on the copying carrier, named explicitly because the
250// bare names mean the view lane — the ordinary road for a direct
251// consumer. Reach for these when a packet has to travel.
252//
253// Note what is **not** doubled: the `*Extra` types and `SideDataEntry`
254// stay monomorphic on both lanes. Side data has no `AVBufferRef` to
255// share — `AVPacketSideData` and `AVFrameSideData` payloads are plain
256// allocations — so both lanes copy it, and a second family of extras
257// would have been two names for one representation. The carrier
258// parameter reaches the **payload**, not the annotations.
259
260/// [`VideoPacket`] on the owned lane.
261pub type OwnedVideoPacket = mediadecode::packet::VideoPacket<extras::VideoPacketExtra, FfmpegBytes>;
262
263/// [`AudioPacket`] on the owned lane.
264pub type OwnedAudioPacket = mediadecode::packet::AudioPacket<extras::AudioPacketExtra, FfmpegBytes>;
265
266/// [`SubtitlePacket`] on the owned lane.
267pub type OwnedSubtitlePacket =
268  mediadecode::packet::SubtitlePacket<extras::SubtitlePacketExtra, FfmpegBytes>;
269
270/// [`DataPacket`] on the owned lane.
271pub type OwnedDataPacket = mediadecode::demuxer::DataPacket<extras::DataPacketExtra, FfmpegBytes>;
272
273/// [`AttachmentPacket`] on the owned lane.
274pub type OwnedAttachmentPacket =
275  mediadecode::demuxer::AttachmentPacket<extras::AttachmentPacketExtra, FfmpegBytes>;
276
277/// [`DemuxedPacket`] on the owned lane.
278pub type OwnedDemuxedPacket = mediadecode::demuxer::DemuxedPacket<Ffmpeg, FfmpegBytes>;
279
280/// [`VideoFrame`] on the owned lane — planes copied out of the
281/// decoder's buffer, so the frame outlives it and the pool slot goes
282/// straight back.
283pub type OwnedVideoFrame =
284  mediadecode::frame::VideoFrame<mediadecode::PixelFormat, extras::VideoFrameExtra, FfmpegBytes>;
285
286/// [`AudioFrame`] on the owned lane.
287pub type OwnedAudioFrame = mediadecode::frame::AudioFrame<
288  SampleFormat,
289  mediaframe::audio::ChannelLayoutDescription,
290  extras::AudioFrameExtra,
291  FfmpegBytes,
292>;
293
294/// [`SubtitleFrame`] on the owned lane.
295pub type OwnedSubtitleFrame =
296  mediadecode::frame::SubtitleFrame<extras::SubtitleFrameExtra, FfmpegBytes>;
297
298/// [`ImageFrame`] on the owned lane — what [`FfmpegOwnedImageDecoder`]
299/// produces.
300pub type OwnedImageFrame =
301  mediadecode::frame::ImageFrame<mediadecode::PixelFormat, extras::ImageFrameExtra, FfmpegBytes>;
302
303/// One row of the track table [`FfmpegDemuxer::tracks`] returns.
304///
305/// [`FfmpegDemuxer::tracks`]: mediadecode::demuxer::Demuxer::tracks
306pub type TrackInfo = mediadecode::demuxer::TrackInfo<Ffmpeg>;
307
308/// A track's per-kind codec parameters, as [`TrackInfo`] carries them.
309pub type TrackParams = mediadecode::demuxer::TrackParams<Ffmpeg>;
310
311/// One row of the chapter table [`FfmpegDemuxer::chapters`] returns.
312///
313/// [`FfmpegDemuxer::chapters`]: mediadecode::demuxer::Demuxer::chapters
314pub type Chapter = mediadecode::demuxer::Chapter<Ffmpeg>;
315
316/// Asserts a submission was taken, and answers nothing.
317///
318/// The `#[must_use]` on [`mediadecode::Sent`] is deliberate teeth: a
319/// test that submits and ignores the answer is a test that would not
320/// notice a decoder quietly asking to be drained. Most of this crate's
321/// tests submit into a session they have just emptied, where
322/// [`Sent::MustDrain`](mediadecode::Sent::MustDrain) is a real
323/// surprise — so they say so here rather than dropping it.
324#[cfg(test)]
325#[track_caller]
326pub(crate) fn accepted<E: core::fmt::Debug>(
327  status: core::result::Result<mediadecode::Sent, E>,
328  what: &str,
329) {
330  assert_eq!(
331    status.unwrap_or_else(|e| panic!("{what}: {e:?}")),
332    mediadecode::Sent::Accepted,
333    "{what}: the session asked to be drained where the test expected room",
334  );
335}