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