Skip to main content

mediadecode_ffmpeg/
carrier.rs

1//! The carrier-strategy seam: which lane a demuxer or decoder captures
2//! into.
3//!
4//! This crate ships **two first-class carriers**, and neither is a
5//! feature flag on the other:
6//!
7//! * [`Owned`] captures into [`FfmpegBytes`](crate::FfmpegBytes) — every
8//!   byte copied once at the boundary into memory Rust owns. `Send +
9//!   Sync`, lifetime answerable to nobody, safe to fan out across a
10//!   graph. This is the default, and the lane the
11//!   [amputation contract][law] governs.
12//! * [`View`] captures into [`FfmpegBuffer`](crate::FfmpegBuffer) — a
13//!   refcounted view onto the allocation FFmpeg already made. `Send`
14//!   and **not** `Sync`, lifetime pinned to the backend's pools,
15//!   zero-copy.
16//!
17//! # Why a sealed seam and not an open trait
18//!
19//! A carrier strategy is not an extension point. Implementing one
20//! correctly means knowing what an `AVBufferRef` guarantees, which
21//! plane extents are initialised, and which of this crate's proofs run
22//! before a capture is allowed to happen — knowledge that lives in this
23//! crate and cannot be documented into a third party. A third strategy
24//! would also have to answer the questions this seam does not ask,
25//! because only two lanes exist to ask them of.
26//!
27//! So the trait is **sealed**: public to name and to bound on, closed
28//! to implement. If a third lane is ever wanted, it is added here,
29//! where it can be held to the same proofs — the seam being closed is
30//! what makes that a change to one file rather than a change to a
31//! contract.
32//!
33//! # Why the operations are not public either
34//!
35//! Sealing stopped outsiders *implementing* the seam. It did not stop
36//! them *calling* it, and for a while that was a hole with teeth: the
37//! operations take offsets, lengths and row geometry which the unsafe
38//! layer beneath them acts on, so their invariants live in the caller.
39//! `View::from_rows(1, 64, |_| &[0])` was safe code, compiled, and
40//! copied sixty-four bytes out of a one-byte slice.
41//!
42//! Two answers, applied together:
43//!
44//! * every operation moved onto the private supertrait in
45//!   [`sealed`], which no path outside this crate can name and
46//!   therefore no call outside this crate can reach. What stays public
47//!   is the *name* of a lane and the ability to bound on it — which is
48//!   all a consumer ever needed;
49//! * and where a length still arrives from a caller, it is **checked**
50//!   rather than asserted. A `debug_assert` is a note to the author; it
51//!   is not a bounds check, and it is not there at all in the profile
52//!   that matters.
53//!
54//! The rule the two share: an invariant that lives in the caller is an
55//! invariant the caller must be inside this crate to be trusted with.
56//!
57//! # Where the bound sits
58//!
59//! At the narrowest site each one can be written at, and no wider:
60//!
61//! * **Structural** on the types whose *fields* name `C::Buffer` —
62//!   [`CarrierDemuxer`](crate::demuxer::CarrierDemuxer) holds built
63//!   tracks, [`CarrierResampler`](crate::resampler::CarrierResampler)
64//!   holds a queue of frames. A struct whose field type is a projection
65//!   cannot be well-formed without the bound that gives the projection
66//!   meaning, so writing it there is not a choice.
67//! * **Behavioural** on the decoders, which carry `C` only as a
68//!   `PhantomData` marker: their struct declarations take a bare `C`
69//!   and the bound sits on the impls whose methods capture.
70//! * **Absent** from `*Extra`, `SideDataEntry` and every side-data
71//!   collector. Side data has no `AVBufferRef` to share, so both lanes
72//!   copy it and the parameter never reaches those types at all.
73//!
74//! The lanes are then named by alias rather than by a defaulted type
75//! parameter, because a default is used when a type is *written* and
76//! never inferred from a call — `Demuxer::open(path)` would have had no
77//! default to fall back on.
78//!
79//! [law]: mediadecode::adapter#the-d-seat-amputation-contract
80
81use ffmpeg_next::ffi::AVBufferRef;
82
83use crate::{FfmpegBytes, view::FfmpegBuffer};
84
85pub(crate) use ops::{BodyRoute, CarrierOps};
86
87pub(crate) mod sealed {
88  /// Closes [`super::FfmpegCarrier`] to outside implementations, and
89  /// **carries nothing**.
90  ///
91  /// An earlier round put the operations here, reasoning that a trait
92  /// no outside path can name is a trait no outside call can reach.
93  /// That was wrong in a way worth recording: associated items resolve
94  /// through a *bound*, not through a path, so downstream code written
95  /// as `fn f<C: FfmpegCarrier>()` type-checked `C::capture(..)` and
96  /// `C::from_rows(..)` perfectly well — the seal stopped
97  /// implementations and nothing else. The operations now live on
98  /// [`CarrierOps`](super::CarrierOps), which is **not** a supertrait
99  /// of anything public, so no bound a downstream crate can write
100  /// reaches them.
101  pub trait Sealed {}
102}
103
104pub(crate) mod ops {
105  use ffmpeg_next::ffi::AVBufferRef;
106
107  /// Which body a rebuilt `AVPacket` gets.
108  ///
109  /// The distinction exists because the two roads have different
110  /// exposure. A packet a caller is *handed* is a public
111  /// `ffmpeg_next::Packet`, and that type can lend `&mut [u8]` — so its
112  /// body must be storage nobody else can read, which means a copy on
113  /// either lane. A packet built to be **submitted and dropped** inside
114  /// one crate-private call never escapes to anywhere a `&mut` can be
115  /// taken from it, so the view lane may share there. The one decoder
116  /// that hands back what it is sent, the video decoder's hardware
117  /// probe, keeps copies of its own (`decoder::try_clone_packet`), so
118  /// nothing it hands back shares the carrier's buffer either.
119  #[derive(Debug, Clone, Copy, PartialEq, Eq)]
120  pub enum BodyRoute {
121    /// The body is copied into storage the packet alone owns. For a
122    /// packet handed to a caller.
123    Copy,
124    /// The body may share the carrier's buffer, if this lane can prove
125    /// that is safe for a decoder to read. Only ever asked for from a
126    /// scoped submission.
127    Submission,
128  }
129
130  /// Every operation a lane performs — and the reason none of them is
131  /// reachable from outside this crate.
132  ///
133  /// This trait is deliberately **not** a supertrait of
134  /// [`FfmpegCarrier`](super::FfmpegCarrier). A supertrait would put
135  /// its items in scope for any bound that names the subtrait, and
136  /// `fn f<C: FfmpegCarrier>()` written in a downstream crate would
137  /// then type-check `C::from_rows(1, 64, |_| &[0])` — safe code, an
138  /// out-of-bounds read, in somebody else's crate. Sealing prevents
139  /// implementing; only an unreachable *bound* prevents calling.
140  ///
141  /// So the two are joined nowhere: `FfmpegCarrier` names a lane and
142  /// says what it carries, this says what it can do, and the functions
143  /// in this crate that need the second ask for it by a name no
144  /// downstream crate can write.
145  pub trait CarrierOps: super::FfmpegCarrier {
146    /// A carrier over no bytes.
147    ///
148    /// Allocation-free on both lanes; it is what an unpopulated plane
149    /// slot holds, and eight per frame is not a place to put a
150    /// failure mode.
151    fn empty() -> Self::Buffer;
152
153    /// A carrier over bytes that live outside any `AVBufferRef`.
154    ///
155    /// Subtitle rect text and a rect's own palette are plain
156    /// allocations with no refcount to share, so **both** lanes copy
157    /// here. The view lane says so rather than implying its whole road
158    /// is zero-copy.
159    ///
160    /// `None` when the copy cannot be allocated. Fallible on both lanes
161    /// so that neither can answer an allocation failure with an empty
162    /// carrier — a plane that silently became zero bytes is a frame
163    /// whose header is a lie.
164    fn from_bytes(bytes: &[u8]) -> Option<Self::Buffer>;
165
166    /// A carrier over `len` bytes at `offset` inside the live
167    /// `AVBufferRef` `buf`.
168    ///
169    /// The owned lane copies them out; the view lane takes a reference.
170    /// Either way the caller has already proved the extent lies inside
171    /// the buffer — this is the capture, not the judgement.
172    ///
173    /// Nothing may be assumed about the bytes **after** the captured
174    /// range: this is the road frame planes and palettes take. See
175    /// [`Self::capture_packet_payload`] for the one that may.
176    ///
177    /// `None` when the capture itself fails: an allocation on the owned
178    /// lane, a refcount on the view lane.
179    ///
180    /// # Safety
181    ///
182    /// `buf` must be a live `AVBufferRef`, and `offset + len` must be
183    /// within its `size`.
184    unsafe fn capture(buf: *mut AVBufferRef, offset: usize, len: usize) -> Option<Self::Buffer>;
185
186    /// [`Self::capture`] for a payload taken out of an `AVPacket`'s own
187    /// buffer.
188    ///
189    /// The only difference is what the carrier records about itself:
190    /// libavformat allocates `AV_INPUT_BUFFER_PADDING_SIZE` zeroed
191    /// bytes behind every packet it produces, and this is the one
192    /// capture that may claim them. That claim is what the send leg
193    /// checks before it shares anything with a decoder.
194    ///
195    /// # Safety
196    ///
197    /// As [`Self::capture`], and `buf` must be the buffer of an
198    /// `AVPacket` whose payload this range is.
199    unsafe fn capture_packet_payload(
200      buf: *mut AVBufferRef,
201      offset: usize,
202      len: usize,
203    ) -> Option<Self::Buffer>;
204
205    /// A carrier over `rows` runs of `row_bytes`, gathered from a
206    /// **padded** plane.
207    ///
208    /// Copied on **both** lanes, and this is the one place the view
209    /// lane cannot share on principle rather than on plumbing. A padded
210    /// plane is `linesize` wide and only its first `row_bytes` per row
211    /// are the decoder's output; the rest is allocator scratch that
212    /// nothing wrote. A carrier is an `AsRef<[u8]>`, so sharing the
213    /// padded span would form a slice over uninitialised memory —
214    /// undefined before a consumer reads a byte of it, and the same
215    /// information leak the owned lane refused when it stopped
216    /// exporting `linesize`.
217    ///
218    /// So a padded plane is compacted, on either lane, and arrives with
219    /// `row_bytes` as its stride. `None` when the gather cannot be
220    /// allocated, or when a row is not exactly `row_bytes` wide.
221    fn from_rows<'a>(
222      rows: usize,
223      row_bytes: usize,
224      row: impl FnMut(usize) -> &'a [u8],
225    ) -> Option<Self::Buffer>;
226
227    /// A capture claimed **before** the bytes exist, whose length is
228    /// settled after they do. See [`Self::reserve`].
229    type Reserved;
230
231    /// Claims `cap` bytes at `offset` inside `buf`, without reading
232    /// them.
233    ///
234    /// For a producer that allocates its own output frame and then
235    /// hands it to FFmpeg to fill — the resampler — this is what keeps
236    /// every fallible step on the *near* side of the conversion. `swr`
237    /// consumes its input as it runs, so a failure after it has run
238    /// leaves a session no caller can retry; reserving first and
239    /// committing after means the only thing left to do once the bytes
240    /// are there is name how many of them are real.
241    ///
242    /// The view lane takes its reference here — the refcount is what
243    /// can fail, and it fails before anything is consumed. The owned
244    /// lane reserves nothing and copies at [`Self::commit`], because
245    /// copying uninitialised capacity is exactly the read this crate
246    /// refuses everywhere else.
247    ///
248    /// # Safety
249    ///
250    /// `buf` must be a live `AVBufferRef`, `offset + cap` must be
251    /// within its `size`, and the buffer must stay alive until the
252    /// matching [`Self::commit`].
253    unsafe fn reserve(buf: *mut AVBufferRef, offset: usize, cap: usize) -> Option<Self::Reserved>;
254
255    /// Settles a [`Self::reserve`] at its true length. Infallible.
256    ///
257    /// # Safety
258    ///
259    /// `len` must be at most the `cap` the reservation was taken with,
260    /// those `len` bytes must now be initialised, and the buffer must
261    /// still be alive.
262    /// **Infallible, and that is the whole reason [`Self::reserve`]
263    /// exists.** By the time this runs `swr` has consumed its input and
264    /// written its output; an allocation that failed here would leave a
265    /// caller with samples that are simply gone and nothing to retry
266    /// with. The owned lane therefore takes its allocation in
267    /// `reserve`, before the conversion, and this only copies into it
268    /// and names the length; the view lane only narrows a reference it
269    /// already holds.
270    unsafe fn commit(reserved: Self::Reserved, len: usize) -> Self::Buffer;
271
272    /// Builds the body of an `AVPacket` on the way **into** a decoder.
273    ///
274    /// [`BodyRoute::Copy`] always copies, on either lane. Only
275    /// [`BodyRoute::Submission`] lets the view lane share, and only
276    /// where it can prove a decoder may read past the payload — see
277    /// `boundary::share_or_copy`.
278    ///
279    /// This is why the reverse builders are one family rather than
280    /// two: every judgement they make — the `TRUSTED` refusal, the
281    /// side-data caps, the send budget — is about sizes and shapes,
282    /// which are the same on both lanes. Only the body differs, and
283    /// only here.
284    fn packet_body(
285      body: &Self::Buffer,
286      route: BodyRoute,
287    ) -> std::result::Result<ffmpeg_next::Packet, ffmpeg_next::Error>;
288  }
289}
290
291/// How a lane turns FFmpeg's bytes into a carrier.
292///
293/// **Sealed, and almost empty by design.** Naming a lane, bounding on
294/// one, and asking what it carries are public; performing a capture is
295/// not. Every operation lives on a private trait that is *not* a
296/// supertrait of this one — see the [module docs](self) for why that
297/// distinction is the whole of the wall.
298///
299/// What a downstream crate can do — name a lane, hold the types
300/// parameterized by one, be generic over it, and drive either:
301///
302/// ```
303/// use mediadecode_ffmpeg::{FfmpegBuffer, FfmpegBytes, FfmpegCarrier, Owned, View};
304///
305/// // Name what a lane carries, and be generic over the lane.
306/// fn carried<C: FfmpegCarrier>(buffer: &C::Buffer) -> usize {
307///   buffer.as_ref().len()
308/// }
309/// let _: fn(&FfmpegBytes) -> usize = carried::<Owned>;
310/// let _: fn(&FfmpegBuffer) -> usize = carried::<View>;
311/// ```
312///
313/// Holding a lane-parameterized type generically — the public structs
314/// carry **only** this bound, so a consumer's own generic code can pass
315/// them around:
316///
317/// ```
318/// use mediadecode::demuxer::TrackInfo;
319/// use mediadecode_ffmpeg::{
320///   CarrierAudioStreamDecoder, CarrierDemuxer, CarrierVideoStreamDecoder, Ffmpeg,
321///   FfmpegCarrier, Owned, View,
322/// };
323///
324/// fn tracks_of<C: FfmpegCarrier>(demuxer: &CarrierDemuxer<C>) -> usize {
325///   // A field read is not an operation on the lane, so this is
326///   // exactly as generic as it looks.
327///   core::mem::size_of_val(demuxer)
328/// }
329///
330/// fn hold<C: FfmpegCarrier>(
331///   _demuxer: &CarrierDemuxer<C>,
332///   _audio: &CarrierAudioStreamDecoder<C>,
333///   _video: &CarrierVideoStreamDecoder<C>,
334/// ) {
335/// }
336///
337/// let _: fn(&CarrierDemuxer<View>) -> usize = tracks_of::<View>;
338/// let _: fn(&CarrierDemuxer<Owned>) -> usize = tracks_of::<Owned>;
339/// let _ = hold::<View>;
340/// let _ = hold::<Owned>;
341/// let _: fn() -> Vec<TrackInfo<Ffmpeg>> = || Vec::new();
342/// ```
343///
344/// And calling at either concrete lane, through the aliases or through
345/// the `Carrier*` names directly:
346///
347/// ```no_run
348/// use mediadecode::demuxer::Demuxer;
349/// use mediadecode_ffmpeg::{
350///   CarrierDemuxer, FfmpegDemuxer, FfmpegOwnedDemuxer, Owned, View,
351/// };
352///
353/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
354/// let mut viewed = FfmpegDemuxer::open("clip.mkv")?;
355/// let mut owned = CarrierDemuxer::<Owned>::open("clip.mkv")?;
356/// let _ = viewed.tracks().len();
357/// let _ = owned.next_packet()?;
358/// let _: fn(&std::path::Path) -> _ = CarrierDemuxer::<View>::open::<std::path::Path>;
359/// let _: fn(&std::path::Path) -> _ = FfmpegOwnedDemuxer::open::<std::path::Path>;
360/// # Ok(())
361/// # }
362/// ```
363///
364/// What no bound reaches is the **operations** — see below. Being
365/// generic over the lane and *driving* it are different asks: the
366/// second needs the operations, so a consumer writes lane-generic
367/// helpers over `C::Buffer` and instantiates the doors at the two
368/// concrete lanes. That is the trade the wall costs, and it is
369/// deliberate.
370///
371/// What it cannot, and must not be able to: every one of these takes an
372/// extent, a geometry or a provenance claim that only this crate is in
373/// a position to establish. `from_rows` is the sharpest — it is
374/// **safe**, and a caller who could reach it could ask for sixty-four
375/// bytes out of a one-byte row.
376///
377/// ```compile_fail,E0599
378/// use mediadecode_ffmpeg::FfmpegCarrier;
379/// fn downstream<C: FfmpegCarrier>() -> C::Buffer {
380///   C::empty()
381/// }
382/// ```
383///
384/// ```compile_fail,E0599
385/// use mediadecode_ffmpeg::FfmpegCarrier;
386/// fn downstream<C: FfmpegCarrier>(row: &[u8]) -> Option<C::Buffer> {
387///   C::from_rows(1, 64, |_| row)
388/// }
389/// ```
390///
391/// ```compile_fail,E0599
392/// use mediadecode_ffmpeg::FfmpegCarrier;
393/// unsafe fn downstream<C: FfmpegCarrier>(
394///   buf: *mut ffmpeg_next::ffi::AVBufferRef,
395/// ) -> Option<C::Buffer> {
396///   unsafe { C::capture(buf, 0, 64) }
397/// }
398/// ```
399///
400/// ```compile_fail,E0599
401/// use mediadecode_ffmpeg::FfmpegCarrier;
402/// unsafe fn downstream<C: FfmpegCarrier>(
403///   buf: *mut ffmpeg_next::ffi::AVBufferRef,
404/// ) -> Option<C::Buffer> {
405///   // The provenance claim: minting this downstream would let a frame
406///   // plane pass itself off as a padded packet payload.
407///   unsafe { C::capture_packet_payload(buf, 0, 64) }
408/// }
409/// ```
410///
411/// ```compile_fail,E0599
412/// use mediadecode_ffmpeg::FfmpegCarrier;
413/// unsafe fn downstream<C: FfmpegCarrier>(buf: *mut ffmpeg_next::ffi::AVBufferRef) {
414///   let reserved = unsafe { C::reserve(buf, 0, 64) };
415/// }
416/// ```
417///
418/// ```compile_fail,E0599
419/// use mediadecode_ffmpeg::FfmpegCarrier;
420/// unsafe fn downstream<C: FfmpegCarrier>(reserved: ()) -> C::Buffer {
421///   unsafe { C::commit(reserved, 64) }
422/// }
423/// ```
424///
425/// The generic bodies behind the per-lane faces are equally out of
426/// reach. They carry the operations' bound, so reaching one at a
427/// concrete lane would be a way around the wall that never names it:
428///
429/// ```compile_fail,E0624
430/// use mediadecode_ffmpeg::{CarrierAudioStreamDecoder, DecoderLimits, Owned};
431/// fn downstream(parameters: ffmpeg_next::codec::Parameters) {
432///   let _ = CarrierAudioStreamDecoder::<Owned>::open_impl(
433///     parameters,
434///     mediadecode::Timebase::default(),
435///     DecoderLimits::default(),
436///   );
437/// }
438/// ```
439///
440/// ```compile_fail,E0624
441/// use mediadecode_ffmpeg::{CarrierDemuxer, View};
442/// fn downstream() {
443///   let _ = CarrierDemuxer::<View>::open_impl("clip.mkv");
444/// }
445/// ```
446pub trait FfmpegCarrier: sealed::Sealed + Copy + Clone + core::fmt::Debug + 'static {
447  /// The carrier this lane produces.
448  ///
449  /// `Send` but not necessarily `Sync`: the view lane is `Send`-only,
450  /// and requiring `Sync` here would have closed the seam to it.
451  ///
452  /// A **type**, and the only thing on this trait — naming
453  /// `<View as FfmpegCarrier>::Buffer` tells a consumer what a lane
454  /// hands them, and tells them nothing they could misuse. Everything
455  /// that *acts* is on the private ops trait.
456  type Buffer: AsRef<[u8]> + Clone + Send + 'static;
457}
458
459/// The **owned** lane: every byte copied once at the boundary.
460///
461/// The default carrier, and the one the [amputation contract][law]
462/// governs. Frames and packets on this lane are `Send + Sync +
463/// 'static`, clone by refcount, and owe nothing to the decoder that
464/// produced them.
465///
466/// [law]: mediadecode::adapter#the-d-seat-amputation-contract
467#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
468pub struct Owned;
469
470/// The **view** lane: refcounted zero-copy handles onto FFmpeg's own
471/// allocations.
472///
473/// `Send` and not `Sync`, with a lifetime pinned to the backend's
474/// buffer pools — a frame held is a pool slot held. See
475/// [the carrier lanes][lanes] for the tradeoff table and for why graph
476/// traffic belongs on [`Owned`].
477///
478/// [lanes]: mediadecode::adapter#the-two-carrier-lanes
479#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
480pub struct View;
481
482impl sealed::Sealed for Owned {}
483impl sealed::Sealed for View {}
484
485impl FfmpegCarrier for Owned {
486  type Buffer = FfmpegBytes;
487}
488
489impl FfmpegCarrier for View {
490  type Buffer = FfmpegBuffer;
491}
492
493impl ops::CarrierOps for Owned {
494  fn empty() -> Self::Buffer {
495    FfmpegBytes::empty()
496  }
497
498  fn from_bytes(bytes: &[u8]) -> Option<Self::Buffer> {
499    FfmpegBytes::try_copy_from_slice(bytes)
500  }
501
502  fn from_rows<'a>(
503    rows: usize,
504    row_bytes: usize,
505    row: impl FnMut(usize) -> &'a [u8],
506  ) -> Option<Self::Buffer> {
507    FfmpegBytes::from_rows(rows, row_bytes, row)
508  }
509
510  /// The plane's start. Nothing is claimed and nothing can fail:
511  /// this lane's cost is a copy, and the copy happens at `commit` once
512  /// the bytes are real.
513  /// The source FFmpeg will write into, and **the destination carrier,
514  /// allocated up front**.
515  ///
516  /// The allocation used to happen in `commit`, after `swr` had already
517  /// consumed the input — so a refusal there lost converted samples
518  /// with no way to retry. It happens here instead, where a refusal
519  /// costs nothing but the attempt.
520  type Reserved = (*const u8, triomphe::UniqueArc<[u8]>);
521
522  unsafe fn reserve(buf: *mut AVBufferRef, offset: usize, cap: usize) -> Option<Self::Reserved> {
523    // SAFETY: `buf` is live per the contract and `offset` is inside it.
524    let data = unsafe { (*buf).data };
525    if data.is_null() {
526      return None;
527    }
528    // The fallible half, taken before the conversion runs.
529    let destination = FfmpegBytes::reserve(cap)?;
530    // SAFETY: `offset` is within the buffer per the contract.
531    Some((unsafe { data.add(offset).cast_const() }, destination))
532  }
533
534  unsafe fn commit((source, mut destination): Self::Reserved, len: usize) -> Self::Buffer {
535    let len = len.min(destination.len());
536    if len == 0 {
537      return FfmpegBytes::empty();
538    }
539    // SAFETY: the caller promises `len` initialised bytes at `source`
540    // inside a buffer still alive; `destination` was allocated with at
541    // least `len` bytes in `reserve` and is unique, so the two cannot
542    // overlap.
543    unsafe {
544      core::ptr::copy_nonoverlapping(source, destination.as_mut_ptr(), len);
545    }
546    FfmpegBytes::from_reservation(destination, len)
547  }
548
549  /// Both routes copy. This lane's carrier is Rust-owned memory with no
550  /// `AVBufferRef` behind it to hand back, so there is nothing to share
551  /// on either road and no distinction to draw.
552  fn packet_body(
553    body: &Self::Buffer,
554    _route: BodyRoute,
555  ) -> std::result::Result<ffmpeg_next::Packet, ffmpeg_next::Error> {
556    crate::boundary::try_packet_copy(body.as_ref())
557  }
558
559  unsafe fn capture(buf: *mut AVBufferRef, offset: usize, len: usize) -> Option<Self::Buffer> {
560    if len == 0 {
561      return Some(FfmpegBytes::empty());
562    }
563    // SAFETY: the caller proved `offset + len <= (*buf).size` against a
564    // live buffer, so the range is inside an allocation FFmpeg holds.
565    let bytes = unsafe {
566      let data = (*buf).data;
567      if data.is_null() {
568        return None;
569      }
570      core::slice::from_raw_parts(data.add(offset).cast_const(), len)
571    };
572    FfmpegBytes::try_copy_from_slice(bytes)
573  }
574
575  /// Indistinguishable from [`capture`](Self::capture) here: this lane
576  /// copies the payload out, so what follows it in FFmpeg's allocation
577  /// is not a fact about the carrier.
578  unsafe fn capture_packet_payload(
579    buf: *mut AVBufferRef,
580    offset: usize,
581    len: usize,
582  ) -> Option<Self::Buffer> {
583    // SAFETY: the caller's contract is the one `capture` states.
584    unsafe { <Self as ops::CarrierOps>::capture(buf, offset, len) }
585  }
586}
587
588impl ops::CarrierOps for View {
589  fn empty() -> Self::Buffer {
590    FfmpegBuffer::empty()
591  }
592
593  fn from_bytes(bytes: &[u8]) -> Option<Self::Buffer> {
594    // No `AVBufferRef` to share, so this lane copies too — into one of
595    // its own, so the carrier type stays uniform.
596    FfmpegBuffer::copy_from_slice(bytes)
597  }
598
599  fn from_rows<'a>(
600    rows: usize,
601    row_bytes: usize,
602    row: impl FnMut(usize) -> &'a [u8],
603  ) -> Option<Self::Buffer> {
604    FfmpegBuffer::from_rows(rows, row_bytes, row)
605  }
606
607  /// The view itself, taken at full capacity. The refcount — the only
608  /// step that can fail — is therefore already paid when the bytes
609  /// arrive.
610  type Reserved = FfmpegBuffer;
611
612  unsafe fn reserve(buf: *mut AVBufferRef, offset: usize, cap: usize) -> Option<Self::Reserved> {
613    // SAFETY: the caller proved the extent; `view_of` proves it again.
614    // A reservation is over an output frame this crate allocated, not
615    // over a packet, so it carries no padding claim.
616    unsafe { FfmpegBuffer::view_of(buf, offset, cap, crate::view::Origin::Foreign) }
617  }
618
619  unsafe fn commit(mut reserved: Self::Reserved, len: usize) -> Self::Buffer {
620    // No bytes move: the reference is held already and this only names
621    // how much of it is real output. The capacity past `len` is
622    // untouched allocator memory, and narrowing here is what keeps it
623    // out of every span this carrier hands out.
624    reserved.shrink_to(len);
625    reserved
626  }
627
628  fn packet_body(
629    body: &Self::Buffer,
630    route: BodyRoute,
631  ) -> std::result::Result<ffmpeg_next::Packet, ffmpeg_next::Error> {
632    match route {
633      // **A packet handed to a caller never shares.** `ffmpeg_next::
634      // Packet` lends `&mut [u8]` through `data_mut`, and the carrier
635      // it was built from still lends `&[u8]` — two live references to
636      // one allocation, one of them mutable, from entirely safe code.
637      // Copying here is what makes that unconstructible.
638      BodyRoute::Copy => crate::boundary::try_packet_copy(body.as_ref()),
639      // **A scoped submission may share**, on every road: the packet is
640      // dropped inside the call, and the one decoder that hands back
641      // what it is sent — the video decoder's hardware probe — keeps
642      // copies.
643      BodyRoute::Submission => crate::boundary::share_or_copy(body),
644    }
645  }
646
647  unsafe fn capture(buf: *mut AVBufferRef, offset: usize, len: usize) -> Option<Self::Buffer> {
648    // SAFETY: the caller proved the extent; `view_of` proves it again
649    // against the buffer's own `size`, because a constructor that
650    // trusts its arguments is one bad caller away from a view over
651    // somebody else's memory.
652    unsafe { FfmpegBuffer::view_of(buf, offset, len, crate::view::Origin::Foreign) }
653  }
654
655  unsafe fn capture_packet_payload(
656    buf: *mut AVBufferRef,
657    offset: usize,
658    len: usize,
659  ) -> Option<Self::Buffer> {
660    // SAFETY: as `capture`, with the caller additionally promising this
661    // range is an `AVPacket`'s payload — which is what entitles the
662    // carrier to claim the padding behind it. The caller has also
663    // already refused any buffer with more than one reference
664    // (`buffer::payload_of`), so nothing else can be writing here.
665    unsafe { FfmpegBuffer::view_of(buf, offset, len, crate::view::Origin::PacketPayload) }
666  }
667}