Skip to main content

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}