Skip to main content

mediadecode_ffmpeg/
adapter.rs

1//! `Ffmpeg` adapter — implements [`mediadecode::VideoAdapter`],
2//! [`mediadecode::AudioAdapter`], [`mediadecode::SubtitleAdapter`],
3//! [`mediadecode::adapter::ImageAdapter`] and
4//! [`mediadecode::demuxer::DemuxAdapter`] for this crate.
5//!
6//! The adapter is a zero-sized type whose sole purpose is to bind the
7//! associated types together so the rest of the API (Packet / Frame /
8//! Decoder) reads cleanly: `VideoPacket<Ffmpeg, FfmpegBytes>` etc.
9//!
10//! `DemuxAdapter` bundles the other three, and `Ffmpeg` fills all four
11//! seats with itself — the demux tier's `CodecId` bound (one
12//! codec-identifier namespace across a container's whole track table) is
13//! trivially satisfied when every family already binds
14//! [`crate::CodecId`].
15
16use mediadecode::{
17  PixelFormat,
18  adapter::{AudioAdapter, ImageAdapter, SubtitleAdapter, VideoAdapter},
19  demuxer::DemuxAdapter,
20};
21use mediaframe::audio::ChannelLayoutDescription;
22use smol_bytes::Utf8Bytes;
23
24use crate::{
25  codec_id::CodecId,
26  extras::{
27    AttachmentPacketExtra, AudioFrameExtra, AudioPacketExtra, DataPacketExtra, ImageFrameExtra,
28    SubtitleFrameExtra, SubtitlePacketExtra, TrackExtra, VideoFrameExtra, VideoPacketExtra,
29  },
30  sample_format::SampleFormat,
31};
32
33/// Zero-sized type carrying the FFmpeg adapter's vocabulary.
34///
35/// Used as the `A` parameter on `mediadecode::VideoPacket<A, B>` /
36/// `Frame<A, B>` (and audio / subtitle counterparts) when this crate's
37/// decoders are in play. Construction is `Ffmpeg` (unit struct);
38/// nothing about the adapter is stateful.
39#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
40pub struct Ffmpeg;
41
42impl VideoAdapter for Ffmpeg {
43  type CodecId = CodecId;
44  type PixelFormat = PixelFormat;
45  type PacketExtra = VideoPacketExtra;
46  type FrameExtra = VideoFrameExtra;
47}
48
49impl AudioAdapter for Ffmpeg {
50  type CodecId = CodecId;
51  type SampleFormat = SampleFormat;
52  type ChannelLayout = ChannelLayoutDescription;
53  type PacketExtra = AudioPacketExtra;
54  type FrameExtra = AudioFrameExtra;
55}
56
57impl SubtitleAdapter for Ffmpeg {
58  type CodecId = CodecId;
59  type PacketExtra = SubtitlePacketExtra;
60  type FrameExtra = SubtitleFrameExtra;
61}
62
63impl ImageAdapter for Ffmpeg {
64  type CodecId = CodecId;
65  type PixelFormat = PixelFormat;
66  // The packet an image decoder is fed is the attachment packet the
67  // demuxer hands out — one type, both seats, so a cover-art payload
68  // goes straight from `next_packet` into `decode` with nothing to
69  // convert in between.
70  type PacketExtra = AttachmentPacketExtra;
71  type FrameExtra = ImageFrameExtra;
72}
73
74impl DemuxAdapter for Ffmpeg {
75  type CodecId = CodecId;
76  type Video = Ffmpeg;
77  type Audio = Ffmpeg;
78  type Subtitle = Ffmpeg;
79  type DataExtra = DataPacketExtra;
80  type AttachmentExtra = AttachmentPacketExtra;
81  type TrackExtra = TrackExtra;
82  // `AVStream.metadata` hands out borrowed `&str` that dies with the
83  // format context, so a track row has to own its identity strings.
84  // **`Utf8Bytes`, and the reason is allocation failure rather than
85  // size.** A metadata value is read out of a container, so its length
86  // is attacker-controlled; the demuxer therefore measures it, charges
87  // it against a budget, and builds it in a `String` reserved with
88  // `try_reserve_exact`, so exhaustion is a named error rather than an
89  // abort.
90  //
91  // That whole chain is only worth anything if the last step — handing
92  // the buffer to this carrier — allocates nothing more. `SmolStr` sat
93  // here before and could not: its constructor takes a `&str` and
94  // copies into a fresh `Arc<str>` for anything past 23 bytes, so a
95  // second, infallible allocation of an attacker-sized value happened
96  // *after* the fallible one, with the first still live. Failing there
97  // aborted the process, which is exactly what the budget existed to
98  // prevent.
99  //
100  // `Utf8Bytes::from(String)` **moves** the buffer instead: short
101  // values go inline (`smol_bytes::INLINE_CAP`, no allocation at all)
102  // and longer ones reach `bytes::Bytes::from(Vec<u8>)`, which takes
103  // the vector's own allocation over rather than copying it. See
104  // `demuxer::lossy_text` for the one residue that remains and why it
105  // is not attacker-scaled.
106  //
107  // It is also the carrier every text seat in this household is
108  // supposed to be on.
109  type Text = Utf8Bytes;
110}
111
112#[cfg(test)]
113mod tests {
114  use super::*;
115
116  /// Compile-time proof that the three trait impls' associated types
117  /// resolve correctly when the `Ffmpeg` adapter parameterizes
118  /// mediadecode's generic types.
119  #[test]
120  fn adapter_parameterizes_mediadecode_types() {
121    use crate::FfmpegBytes;
122
123    use mediadecode::{
124      adapter::{AudioAdapter, SubtitleAdapter, VideoAdapter},
125      packet::{AudioPacket, SubtitlePacket, VideoPacket},
126    };
127
128    fn _video_packet_resolves(
129      _: &VideoPacket<Ffmpeg, FfmpegBytes>,
130      _: <Ffmpeg as VideoAdapter>::CodecId,
131      _: <Ffmpeg as VideoAdapter>::PixelFormat,
132      _: &<Ffmpeg as VideoAdapter>::PacketExtra,
133      _: &<Ffmpeg as VideoAdapter>::FrameExtra,
134    ) {
135    }
136
137    fn _audio_packet_resolves(
138      _: &AudioPacket<Ffmpeg, FfmpegBytes>,
139      _: <Ffmpeg as AudioAdapter>::CodecId,
140      _: <Ffmpeg as AudioAdapter>::SampleFormat,
141      _: &<Ffmpeg as AudioAdapter>::ChannelLayout,
142      _: &<Ffmpeg as AudioAdapter>::PacketExtra,
143      _: &<Ffmpeg as AudioAdapter>::FrameExtra,
144    ) {
145    }
146
147    fn _subtitle_packet_resolves(
148      _: &SubtitlePacket<Ffmpeg, FfmpegBytes>,
149      _: <Ffmpeg as SubtitleAdapter>::CodecId,
150      _: &<Ffmpeg as SubtitleAdapter>::PacketExtra,
151      _: &<Ffmpeg as SubtitleAdapter>::FrameExtra,
152    ) {
153    }
154  }
155
156  #[test]
157  fn ffmpeg_is_zero_sized() {
158    use core::mem::size_of;
159    assert_eq!(size_of::<Ffmpeg>(), 0);
160  }
161}