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}