mediadecode/decoder.rs
1//! Decoder traits — push-style streams (FFmpeg / WebCodecs / ProRes
2//! RAW via VTDecompressionSession), pull-style frame sources
3//! (R3D / BRAW / ARRIRAW / X-OCN / Canon RAW Light), and the one-shot
4//! [`ImageDecoder`].
5//!
6//! # What the names say
7//!
8//! `Stream` in a name means the decoder has a *rhythm*: packets go in
9//! over time, frames come out over time, and the two are not in step —
10//! hence `send_packet` / `receive_frame` / `send_eof` / `flush`.
11//! [`VideoStreamDecoder`] and [`AudioStreamDecoder`] carry it.
12//! [`SubtitleDecoder`] and [`ImageDecoder`] do not, and their names say
13//! so: a subtitle cue and a still image each come out of exactly the
14//! packet that went in.
15//!
16//! All four are mirrored under [`crate::future`] with `async fn`
17//! methods, in both the `!Send` and the `Send`-bounded variant.
18//!
19//! # What the two calls answer
20//!
21//! Every `send_packet` / `send_eof` here answers [`Sent`] — *accepted*
22//! or *must drain* — and every `receive_frame` answers [`Received`] —
23//! *a frame*, *needs input*, or *ended*. `Err` is reserved for faults
24//! on both faces. The rationale, and the reason those two vocabularies
25//! are exhaustive while the backends' error types are not, is on the
26//! [`rhythm`](crate::rhythm) module.
27//!
28//! # The buffer seat
29//!
30//! Every trait here carries a `Buffer` associated type bounded only by
31//! `AsRef<[u8]>`, and none of them names a concrete carrier. What a
32//! backend may bind there is the
33//! [D-seat amputation contract](crate::adapter#the-d-seat-amputation-contract):
34//! owned, `Send + Sync`, clone-is-a-refcount-bump, with no
35//! backend-internal lifetime crossing the seam.
36//!
37//! # Construction is not on these traits
38//!
39//! Opening a decoder is each backend's own business and each
40//! backend's is different — codec parameters, a WebCodecs config
41//! dictionary, a clip handle. Putting a constructor on the trait would
42//! force one of those spellings onto all of them, which is the same
43//! stance [`crate::demuxer::Demuxer`] takes for the same reason.
44
45use crate::{
46 Received, Sent, Timebase, Timestamp,
47 adapter::{AudioAdapter, ImageAdapter, SubtitleAdapter, VideoAdapter},
48 demuxer::AttachmentPacket,
49 frame::{AudioFrame, ImageFrame, SubtitleFrame, VideoFrame},
50 packet::{AudioPacket, SubtitlePacket, VideoPacket},
51};
52
53/// Push-style video decoder. Caller submits compressed packets and
54/// drains decoded frames.
55///
56/// Backends: FFmpeg, WebCodecs, ProRes RAW (VideoToolbox).
57pub trait VideoStreamDecoder {
58 /// Backend-specific vocabulary.
59 type Adapter: VideoAdapter;
60 /// Buffer type held by the packets and frames this decoder
61 /// produces or accepts.
62 type Buffer: AsRef<[u8]>;
63 /// Decoder-specific error type.
64 type Error;
65
66 /// Submits one compressed packet.
67 ///
68 /// [`Sent::Accepted`] means the packet was consumed.
69 /// [`Sent::MustDrain`] means it was **not** — the session's output
70 /// must be drained through [`receive_frame`](Self::receive_frame)
71 /// and the same packet offered again. That is back pressure, not a
72 /// refusal, and it is why `Err` here can be read as a fault without
73 /// a second attempt.
74 fn send_packet(
75 &mut self,
76 packet: &VideoPacket<<Self::Adapter as VideoAdapter>::PacketExtra, Self::Buffer>,
77 ) -> Result<Sent, Self::Error>;
78
79 /// Drains one decoded frame into `dst`.
80 ///
81 /// [`Received::Frame`] means `dst` was written.
82 /// [`Received::NeedsInput`] and [`Received::Ended`] are the protocol's
83 /// other two answers, and neither is an error — `Err` is a fault.
84 fn receive_frame(
85 &mut self,
86 dst: &mut VideoFrame<
87 <Self::Adapter as VideoAdapter>::PixelFormat,
88 <Self::Adapter as VideoAdapter>::FrameExtra,
89 Self::Buffer,
90 >,
91 ) -> Result<Received, Self::Error>;
92
93 /// Signals end-of-stream.
94 ///
95 /// Answers [`Sent`] for the same reason
96 /// [`send_packet`](Self::send_packet) does: a session with undrained
97 /// output can be unable to take the signal yet, and
98 /// [`Sent::MustDrain`] means the end-of-stream was **not** recorded.
99 /// Drain and signal again.
100 fn send_eof(&mut self) -> Result<Sent, Self::Error>;
101
102 /// Flushes internal state. Not a submission — nothing is offered, so
103 /// there is nothing to be back-pressured.
104 fn flush(&mut self) -> Result<(), Self::Error>;
105}
106
107/// Pull-style video frame source. Caller requests frames by integer
108/// index. Clip-level metadata accessible via `clip_meta()`.
109///
110/// Backends: R3D, BRAW, ARRIRAW, Sony X-OCN, Canon Cinema RAW Light.
111pub trait VideoFrameSource {
112 /// Backend-specific vocabulary.
113 type Adapter: VideoAdapter;
114 /// Buffer type for the produced frames.
115 type Buffer: AsRef<[u8]>;
116 /// Backend-specific clip-level metadata bag (e.g. `R3dClipMeta`,
117 /// `ArriClipMeta`). Backends without clip metadata set this to `()`.
118 type ClipMeta;
119 /// Decoder-specific error type.
120 type Error;
121
122 /// Total frame count in the clip.
123 fn frame_count(&self) -> u64;
124 /// Video frame rate (frames per second as a `Timebase`).
125 fn frame_rate(&self) -> Timebase;
126 /// Total clip duration.
127 fn duration(&self) -> Timestamp;
128 /// Backend-specific clip-level metadata.
129 fn clip_meta(&self) -> &Self::ClipMeta;
130
131 /// Decodes one frame at `index` into `dst`.
132 fn decode_frame(
133 &mut self,
134 index: u64,
135 dst: &mut VideoFrame<
136 <Self::Adapter as VideoAdapter>::PixelFormat,
137 <Self::Adapter as VideoAdapter>::FrameExtra,
138 Self::Buffer,
139 >,
140 ) -> Result<(), Self::Error>;
141}
142
143/// Push-style audio decoder.
144pub trait AudioStreamDecoder {
145 /// Backend vocabulary.
146 type Adapter: AudioAdapter;
147 /// Buffer type.
148 type Buffer: AsRef<[u8]>;
149 /// Decoder-specific error.
150 type Error;
151 /// Submits a compressed audio packet. See
152 /// [`VideoStreamDecoder::send_packet`] — the two answers are the same
153 /// on every push face here.
154 fn send_packet(
155 &mut self,
156 packet: &AudioPacket<<Self::Adapter as AudioAdapter>::PacketExtra, Self::Buffer>,
157 ) -> Result<Sent, Self::Error>;
158 /// Drains a decoded frame into `dst`. See
159 /// [`VideoStreamDecoder::receive_frame`] — the three answers are the
160 /// same on every push face here.
161 fn receive_frame(
162 &mut self,
163 dst: &mut AudioFrame<
164 <Self::Adapter as AudioAdapter>::SampleFormat,
165 <Self::Adapter as AudioAdapter>::ChannelLayout,
166 <Self::Adapter as AudioAdapter>::FrameExtra,
167 Self::Buffer,
168 >,
169 ) -> Result<Received, Self::Error>;
170 /// Signals EOF.
171 fn send_eof(&mut self) -> Result<Sent, Self::Error>;
172 /// Flushes internal state.
173 fn flush(&mut self) -> Result<(), Self::Error>;
174}
175
176/// Pull-style audio frame source. Caller requests blocks by sample
177/// offset.
178///
179/// Backends: R3D, BRAW (audio in companion track of the same clip).
180pub trait AudioFrameSource {
181 /// Backend vocabulary.
182 type Adapter: AudioAdapter;
183 /// Buffer type.
184 type Buffer: AsRef<[u8]>;
185 /// Backend-specific clip-level metadata.
186 type ClipMeta;
187 /// Decoder-specific error.
188 type Error;
189 /// Total sample count across all channels.
190 fn sample_count(&self) -> u64;
191 /// Sample rate (Hz).
192 fn sample_rate(&self) -> u32;
193 /// Channel count.
194 fn channel_count(&self) -> u8;
195 /// Backend-specific clip metadata.
196 fn clip_meta(&self) -> &Self::ClipMeta;
197 /// Decodes a block starting at `sample_offset`, of `sample_count` samples.
198 fn decode_block(
199 &mut self,
200 sample_offset: u64,
201 sample_count: u32,
202 dst: &mut AudioFrame<
203 <Self::Adapter as AudioAdapter>::SampleFormat,
204 <Self::Adapter as AudioAdapter>::ChannelLayout,
205 <Self::Adapter as AudioAdapter>::FrameExtra,
206 Self::Buffer,
207 >,
208 ) -> Result<(), Self::Error>;
209}
210
211/// Push-style subtitle decoder. (No pull-style subtitle decoders
212/// exist in the wild — subtitle streams are linear and small.)
213pub trait SubtitleDecoder {
214 /// Backend vocabulary.
215 type Adapter: SubtitleAdapter;
216 /// Buffer type.
217 type Buffer: AsRef<[u8]>;
218 /// Decoder-specific error.
219 type Error;
220 /// Submits a compressed subtitle packet. See
221 /// [`VideoStreamDecoder::send_packet`].
222 fn send_packet(
223 &mut self,
224 packet: &SubtitlePacket<<Self::Adapter as SubtitleAdapter>::PacketExtra, Self::Buffer>,
225 ) -> Result<Sent, Self::Error>;
226 /// Drains a decoded subtitle frame into `dst`.
227 ///
228 /// A backend whose underlying API produces a cue inline with the
229 /// packet still answers all three: [`Received::NeedsInput`] until a
230 /// packet has produced one, and [`Received::Ended`] once
231 /// [`send_eof`](Self::send_eof) has been signalled and no cue is
232 /// held. A subtitle decoder with no tail to drain is still a session
233 /// with an end, and saying so is what lets a generic drain loop stop.
234 fn receive_frame(
235 &mut self,
236 dst: &mut SubtitleFrame<<Self::Adapter as SubtitleAdapter>::FrameExtra, Self::Buffer>,
237 ) -> Result<Received, Self::Error>;
238 /// Signals EOF.
239 fn send_eof(&mut self) -> Result<Sent, Self::Error>;
240 /// Flushes internal state.
241 fn flush(&mut self) -> Result<(), Self::Error>;
242}
243
244/// One-shot still-image decoder — cover art, an embedded thumbnail, a
245/// poster frame.
246///
247/// **No `Stream` in the name, and no rhythm to go with it.** An
248/// attachment track delivers exactly one packet (see
249/// [`Demuxer`](crate::demuxer::Demuxer)'s attachment contract), that
250/// packet is a whole file, and decoding it produces exactly one
251/// picture. There is nothing to queue, nothing to drain, and no
252/// end-of-stream to signal — so [`decode`](Self::decode) takes the
253/// packet and returns the frame, instead of the
254/// `send_packet` / `receive_frame` split the two `*StreamDecoder`
255/// traits need. [`SubtitleDecoder`] keeps that split only because
256/// FFmpeg's own subtitle API is push-shaped; nothing forces it here.
257///
258/// **The frame has no timestamps**, because
259/// [`ImageFrame`] has no seats for them.
260///
261/// # Where the input comes from
262///
263/// A container's still images arrive as
264/// [`TrackKind::Attachment`](crate::demuxer::TrackKind::Attachment)
265/// tracks: the track row says what codec the picture is in, and the
266/// track's one [`AttachmentPacket`] carries its bytes. So the row
267/// opens the decoder — off-trait, per the module docs — and the packet
268/// is what `decode` is handed.
269///
270/// # `&mut self`
271///
272/// Decoding one image needs no state across calls; the exclusive
273/// borrow is here because a backend's decoder handle is a mutable
274/// resource (FFmpeg's `AVCodecContext` is), and because it lets a
275/// backend reuse one open decoder across several attachments of the
276/// same codec rather than reopening per picture.
277pub trait ImageDecoder {
278 /// Backend-specific vocabulary.
279 type Adapter: ImageAdapter;
280 /// Buffer type held by the packet this decoder accepts and the frame
281 /// it produces. See the module docs for what may be bound here.
282 type Buffer: AsRef<[u8]>;
283 /// Decoder-specific error type.
284 type Error;
285
286 /// Decodes one attachment payload — a whole image file — into a
287 /// still.
288 ///
289 /// Backends signal "these bytes are not a picture this decoder can
290 /// read" through a backend-specific `Error` variant, never by
291 /// returning an empty frame. That is **not** the receive-path
292 /// convention wearing different clothes: [`Received`] names the
293 /// states of a *session* — nothing yet, over — and a one-shot decode
294 /// has neither. "These bytes are not a picture" is a fact about the
295 /// payload the caller handed over, which is what an error is for.
296 fn decode(
297 &mut self,
298 packet: &AttachmentPacket<<Self::Adapter as ImageAdapter>::PacketExtra, Self::Buffer>,
299 ) -> Result<
300 ImageFrame<
301 <Self::Adapter as ImageAdapter>::PixelFormat,
302 <Self::Adapter as ImageAdapter>::FrameExtra,
303 Self::Buffer,
304 >,
305 Self::Error,
306 >;
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312 use crate::Timebase;
313 use core::num::NonZeroI32;
314
315 pub(crate) struct VLoop;
316 impl VideoAdapter for VLoop {
317 type CodecId = u32;
318 type PixelFormat = u32;
319 type PacketExtra = ();
320 type FrameExtra = ();
321 }
322
323 /// Trivial loopback impl — confirms the trait can be implemented.
324 pub(crate) struct LoopVideoStream;
325
326 #[derive(Debug)]
327 pub(crate) struct LoopError;
328
329 impl VideoStreamDecoder for LoopVideoStream {
330 type Adapter = VLoop;
331 type Buffer = &'static [u8];
332 type Error = LoopError;
333
334 fn send_packet(&mut self, _: &VideoPacket<(), &'static [u8]>) -> Result<Sent, LoopError> {
335 Ok(Sent::Accepted)
336 }
337 fn receive_frame(
338 &mut self,
339 _: &mut VideoFrame<u32, (), &'static [u8]>,
340 ) -> Result<Received, LoopError> {
341 Ok(Received::NeedsInput)
342 }
343 fn send_eof(&mut self) -> Result<Sent, LoopError> {
344 Ok(Sent::Accepted)
345 }
346 fn flush(&mut self) -> Result<(), LoopError> {
347 Ok(())
348 }
349 }
350
351 pub(crate) struct LoopVideoSource;
352
353 impl VideoFrameSource for LoopVideoSource {
354 type Adapter = VLoop;
355 type Buffer = &'static [u8];
356 type ClipMeta = ();
357 type Error = LoopError;
358
359 fn frame_count(&self) -> u64 {
360 0
361 }
362 fn frame_rate(&self) -> Timebase {
363 Timebase::new(30, NonZeroI32::new(1).unwrap())
364 }
365 fn duration(&self) -> Timestamp {
366 Timestamp::new(0, self.frame_rate())
367 }
368 fn clip_meta(&self) -> &() {
369 &()
370 }
371 fn decode_frame(
372 &mut self,
373 _: u64,
374 _: &mut VideoFrame<u32, (), &'static [u8]>,
375 ) -> Result<(), LoopError> {
376 Err(LoopError)
377 }
378 }
379
380 #[test]
381 fn video_traits_are_implementable() {
382 fn _stream<D: VideoStreamDecoder>() {}
383 fn _source<D: VideoFrameSource>() {}
384 _stream::<LoopVideoStream>();
385 _source::<LoopVideoSource>();
386 }
387
388 pub(crate) struct ALoop;
389 impl AudioAdapter for ALoop {
390 type CodecId = u32;
391 type SampleFormat = u32;
392 type ChannelLayout = u32;
393 type PacketExtra = ();
394 type FrameExtra = ();
395 }
396
397 pub(crate) struct LoopAudioStream;
398
399 impl AudioStreamDecoder for LoopAudioStream {
400 type Adapter = ALoop;
401 type Buffer = &'static [u8];
402 type Error = LoopError;
403 fn send_packet(&mut self, _: &AudioPacket<(), &'static [u8]>) -> Result<Sent, LoopError> {
404 Ok(Sent::Accepted)
405 }
406 fn receive_frame(
407 &mut self,
408 _: &mut AudioFrame<u32, u32, (), &'static [u8]>,
409 ) -> Result<Received, LoopError> {
410 Ok(Received::NeedsInput)
411 }
412 fn send_eof(&mut self) -> Result<Sent, LoopError> {
413 Ok(Sent::Accepted)
414 }
415 fn flush(&mut self) -> Result<(), LoopError> {
416 Ok(())
417 }
418 }
419
420 pub(crate) struct LoopAudioSource;
421
422 impl AudioFrameSource for LoopAudioSource {
423 type Adapter = ALoop;
424 type Buffer = &'static [u8];
425 type ClipMeta = ();
426 type Error = LoopError;
427 fn sample_count(&self) -> u64 {
428 0
429 }
430 fn sample_rate(&self) -> u32 {
431 48_000
432 }
433 fn channel_count(&self) -> u8 {
434 2
435 }
436 fn clip_meta(&self) -> &() {
437 &()
438 }
439 fn decode_block(
440 &mut self,
441 _: u64,
442 _: u32,
443 _: &mut AudioFrame<u32, u32, (), &'static [u8]>,
444 ) -> Result<(), LoopError> {
445 Err(LoopError)
446 }
447 }
448
449 #[test]
450 fn audio_traits_are_implementable() {
451 fn _stream<D: AudioStreamDecoder>() {}
452 fn _source<D: AudioFrameSource>() {}
453 _stream::<LoopAudioStream>();
454 _source::<LoopAudioSource>();
455 }
456
457 pub(crate) struct SLoop;
458 impl SubtitleAdapter for SLoop {
459 type CodecId = u32;
460 type PacketExtra = ();
461 type FrameExtra = ();
462 }
463
464 pub(crate) struct LoopSubtitleStream;
465
466 impl SubtitleDecoder for LoopSubtitleStream {
467 type Adapter = SLoop;
468 type Buffer = &'static [u8];
469 type Error = LoopError;
470 fn send_packet(&mut self, _: &SubtitlePacket<(), &'static [u8]>) -> Result<Sent, LoopError> {
471 Ok(Sent::Accepted)
472 }
473 fn receive_frame(
474 &mut self,
475 _: &mut SubtitleFrame<(), &'static [u8]>,
476 ) -> Result<Received, LoopError> {
477 Ok(Received::NeedsInput)
478 }
479 fn send_eof(&mut self) -> Result<Sent, LoopError> {
480 Ok(Sent::Accepted)
481 }
482 fn flush(&mut self) -> Result<(), LoopError> {
483 Ok(())
484 }
485 }
486
487 #[test]
488 fn subtitle_decoder_is_implementable() {
489 fn _decoder<D: SubtitleDecoder>() {}
490 _decoder::<LoopSubtitleStream>();
491 }
492
493 pub(crate) struct ILoop;
494 impl ImageAdapter for ILoop {
495 type CodecId = u32;
496 type PixelFormat = u32;
497 type PacketExtra = ();
498 type FrameExtra = ();
499 }
500
501 pub(crate) struct LoopImage;
502
503 impl ImageDecoder for LoopImage {
504 type Adapter = ILoop;
505 type Buffer = &'static [u8];
506 type Error = LoopError;
507
508 fn decode(
509 &mut self,
510 _: &AttachmentPacket<(), &'static [u8]>,
511 ) -> Result<ImageFrame<u32, (), &'static [u8]>, LoopError> {
512 Err(LoopError)
513 }
514 }
515
516 #[test]
517 fn image_decoder_is_implementable() {
518 fn _decoder<D: ImageDecoder>() {}
519 _decoder::<LoopImage>();
520 }
521
522 #[test]
523 fn the_one_shot_seam_takes_a_packet_and_answers_a_frame() {
524 // The shape the register turns on: no `send_*`, no `receive_*`, no
525 // `flush`. One call in, one picture out.
526 let mut decoder = LoopImage;
527 let packet: AttachmentPacket<(), &'static [u8]> = AttachmentPacket::new(&[][..], ());
528 assert!(decoder.decode(&packet).is_err());
529 }
530}