Skip to main content

mediadecode_ffmpeg/
extras.rs

1//! Backend-specific `*Extra` carriers used as the
2//! `mediadecode::*Adapter::*Extra` associated types.
3//!
4//! Fields are private; values are read through getters and set through
5//! `with_*` (consuming builders) / `set_*` (in-place mutators) — the
6//! crate-wide encapsulation convention. `const fn` is used wherever
7//! the field type permits (i.e. anything but `Vec`).
8
9use std::vec::Vec;
10
11/// Per-`VideoPacket` extras.
12#[derive(Clone, Debug, Default)]
13pub struct VideoPacketExtra {
14  stream_index: i32,
15  byte_pos: Option<i64>,
16  side_data: Vec<SideDataEntry>,
17}
18
19impl VideoPacketExtra {
20  /// Constructs a `VideoPacketExtra` with the given stream index.
21  /// `byte_pos` defaults to `None` and `side_data` to empty.
22  #[cfg_attr(not(tarpaulin), inline(always))]
23  pub const fn new(stream_index: i32) -> Self {
24    Self {
25      stream_index,
26      byte_pos: None,
27      side_data: Vec::new(),
28    }
29  }
30
31  /// Returns the source `AVStream.index`.
32  #[cfg_attr(not(tarpaulin), inline(always))]
33  pub const fn stream_index(&self) -> i32 {
34    self.stream_index
35  }
36
37  /// Returns the byte position of the packet in the input file, or
38  /// `None` if unknown.
39  #[cfg_attr(not(tarpaulin), inline(always))]
40  pub const fn byte_pos(&self) -> Option<i64> {
41    self.byte_pos
42  }
43
44  /// Returns the raw side-data entries from `AVPacket.side_data`.
45  #[cfg_attr(not(tarpaulin), inline(always))]
46  pub fn side_data(&self) -> &[SideDataEntry] {
47    self.side_data.as_slice()
48  }
49
50  /// Sets the stream index (consuming builder).
51  #[cfg_attr(not(tarpaulin), inline(always))]
52  #[must_use]
53  pub const fn with_stream_index(mut self, value: i32) -> Self {
54    self.stream_index = value;
55    self
56  }
57  /// Sets the byte position (consuming builder).
58  #[cfg_attr(not(tarpaulin), inline(always))]
59  #[must_use]
60  pub const fn with_byte_pos(mut self, value: Option<i64>) -> Self {
61    self.byte_pos = value;
62    self
63  }
64  /// Sets the side-data list (consuming builder).
65  #[cfg_attr(not(tarpaulin), inline(always))]
66  #[must_use]
67  pub fn with_side_data(mut self, value: Vec<SideDataEntry>) -> Self {
68    self.side_data = value;
69    self
70  }
71
72  /// Sets the stream index in place.
73  #[cfg_attr(not(tarpaulin), inline(always))]
74  pub const fn set_stream_index(&mut self, value: i32) -> &mut Self {
75    self.stream_index = value;
76    self
77  }
78  /// Sets the byte position in place.
79  #[cfg_attr(not(tarpaulin), inline(always))]
80  pub const fn set_byte_pos(&mut self, value: Option<i64>) -> &mut Self {
81    self.byte_pos = value;
82    self
83  }
84  /// Sets the side-data list in place.
85  #[cfg_attr(not(tarpaulin), inline(always))]
86  pub fn set_side_data(&mut self, value: Vec<SideDataEntry>) -> &mut Self {
87    self.side_data = value;
88    self
89  }
90}
91
92/// Per-`VideoFrame` extras carrying everything the unified
93/// `mediadecode::ColorInfo` doesn't already cover.
94#[derive(Clone, Debug, Default)]
95pub struct VideoFrameExtra {
96  sample_aspect_ratio: Option<(u32, u32)>,
97  picture_type: PictureType,
98  key_frame: bool,
99  interlaced: bool,
100  top_field_first: bool,
101  best_effort_timestamp: Option<i64>,
102  mastering_display: Option<MasteringDisplay>,
103  content_light_level: Option<ContentLightLevel>,
104  smpte_timecode: Vec<u32>,
105  side_data: Vec<SideDataEntry>,
106}
107
108impl VideoFrameExtra {
109  /// Constructs an empty `VideoFrameExtra` (all fields at default).
110  #[cfg_attr(not(tarpaulin), inline(always))]
111  pub const fn new() -> Self {
112    Self {
113      sample_aspect_ratio: None,
114      picture_type: PictureType::Unspecified,
115      key_frame: false,
116      interlaced: false,
117      top_field_first: false,
118      best_effort_timestamp: None,
119      mastering_display: None,
120      content_light_level: None,
121      smpte_timecode: Vec::new(),
122      side_data: Vec::new(),
123    }
124  }
125
126  /// Sample aspect ratio (par numerator / denominator), `None` if 1:1
127  /// or unspecified.
128  #[cfg_attr(not(tarpaulin), inline(always))]
129  pub const fn sample_aspect_ratio(&self) -> Option<(u32, u32)> {
130    self.sample_aspect_ratio
131  }
132  /// Frame picture type (I/P/B/etc.).
133  #[cfg_attr(not(tarpaulin), inline(always))]
134  pub const fn picture_type(&self) -> PictureType {
135    self.picture_type
136  }
137  /// `True` if this frame is a key frame.
138  #[cfg_attr(not(tarpaulin), inline(always))]
139  pub const fn key_frame(&self) -> bool {
140    self.key_frame
141  }
142  /// `True` if the frame is interlaced.
143  #[cfg_attr(not(tarpaulin), inline(always))]
144  pub const fn interlaced(&self) -> bool {
145    self.interlaced
146  }
147  /// `True` if the top field is first (only meaningful with `interlaced`).
148  #[cfg_attr(not(tarpaulin), inline(always))]
149  pub const fn top_field_first(&self) -> bool {
150    self.top_field_first
151  }
152  /// FFmpeg's heuristic best-effort PTS, or `None` if unknown.
153  #[cfg_attr(not(tarpaulin), inline(always))]
154  pub const fn best_effort_timestamp(&self) -> Option<i64> {
155    self.best_effort_timestamp
156  }
157  /// HDR10 mastering-display metadata, if present on the source frame.
158  #[cfg_attr(not(tarpaulin), inline(always))]
159  pub const fn mastering_display(&self) -> Option<MasteringDisplay> {
160    self.mastering_display
161  }
162  /// HDR10 content-light-level.
163  #[cfg_attr(not(tarpaulin), inline(always))]
164  pub const fn content_light_level(&self) -> Option<ContentLightLevel> {
165    self.content_light_level
166  }
167  /// SMPTE ST 12-M timecode entries (raw 32-bit BCD-packed values).
168  #[cfg_attr(not(tarpaulin), inline(always))]
169  pub fn smpte_timecode(&self) -> &[u32] {
170    self.smpte_timecode.as_slice()
171  }
172  /// Raw side-data entries from `AVFrame.side_data`.
173  #[cfg_attr(not(tarpaulin), inline(always))]
174  pub fn side_data(&self) -> &[SideDataEntry] {
175    self.side_data.as_slice()
176  }
177
178  /// Sets the sample aspect ratio (consuming builder).
179  #[cfg_attr(not(tarpaulin), inline(always))]
180  pub const fn with_sample_aspect_ratio(mut self, value: Option<(u32, u32)>) -> Self {
181    self.sample_aspect_ratio = value;
182    self
183  }
184  /// Sets the picture type (consuming builder).
185  #[cfg_attr(not(tarpaulin), inline(always))]
186  #[must_use]
187  pub const fn with_picture_type(mut self, value: PictureType) -> Self {
188    self.picture_type = value;
189    self
190  }
191  /// Sets the key-frame flag (consuming builder).
192  #[cfg_attr(not(tarpaulin), inline(always))]
193  #[must_use]
194  pub const fn with_key_frame(mut self, value: bool) -> Self {
195    self.key_frame = value;
196    self
197  }
198  /// Sets the interlaced flag (consuming builder).
199  #[cfg_attr(not(tarpaulin), inline(always))]
200  #[must_use]
201  pub const fn with_interlaced(mut self, value: bool) -> Self {
202    self.interlaced = value;
203    self
204  }
205  /// Sets the top-field-first flag (consuming builder).
206  #[cfg_attr(not(tarpaulin), inline(always))]
207  #[must_use]
208  pub const fn with_top_field_first(mut self, value: bool) -> Self {
209    self.top_field_first = value;
210    self
211  }
212  /// Sets the best-effort timestamp (consuming builder).
213  #[cfg_attr(not(tarpaulin), inline(always))]
214  #[must_use]
215  pub const fn with_best_effort_timestamp(mut self, value: Option<i64>) -> Self {
216    self.best_effort_timestamp = value;
217    self
218  }
219  /// Sets the mastering-display metadata (consuming builder).
220  #[cfg_attr(not(tarpaulin), inline(always))]
221  #[must_use]
222  pub const fn with_mastering_display(mut self, value: Option<MasteringDisplay>) -> Self {
223    self.mastering_display = value;
224    self
225  }
226  /// Sets the content-light-level metadata (consuming builder).
227  #[cfg_attr(not(tarpaulin), inline(always))]
228  #[must_use]
229  pub const fn with_content_light_level(mut self, value: Option<ContentLightLevel>) -> Self {
230    self.content_light_level = value;
231    self
232  }
233  /// Sets the SMPTE timecode list (consuming builder).
234  #[cfg_attr(not(tarpaulin), inline(always))]
235  #[must_use]
236  pub fn with_smpte_timecode(mut self, value: Vec<u32>) -> Self {
237    self.smpte_timecode = value;
238    self
239  }
240  /// Sets the side-data list (consuming builder).
241  #[cfg_attr(not(tarpaulin), inline(always))]
242  #[must_use]
243  pub fn with_side_data(mut self, value: Vec<SideDataEntry>) -> Self {
244    self.side_data = value;
245    self
246  }
247
248  /// Sets the sample aspect ratio in place.
249  #[cfg_attr(not(tarpaulin), inline(always))]
250  pub const fn set_sample_aspect_ratio(&mut self, value: Option<(u32, u32)>) -> &mut Self {
251    self.sample_aspect_ratio = value;
252    self
253  }
254  /// Sets the picture type in place.
255  #[cfg_attr(not(tarpaulin), inline(always))]
256  pub const fn set_picture_type(&mut self, value: PictureType) -> &mut Self {
257    self.picture_type = value;
258    self
259  }
260  /// Sets the key-frame flag in place.
261  #[cfg_attr(not(tarpaulin), inline(always))]
262  pub const fn set_key_frame(&mut self, value: bool) -> &mut Self {
263    self.key_frame = value;
264    self
265  }
266  /// Sets the interlaced flag in place.
267  #[cfg_attr(not(tarpaulin), inline(always))]
268  pub const fn set_interlaced(&mut self, value: bool) -> &mut Self {
269    self.interlaced = value;
270    self
271  }
272  /// Sets the top-field-first flag in place.
273  #[cfg_attr(not(tarpaulin), inline(always))]
274  pub const fn set_top_field_first(&mut self, value: bool) -> &mut Self {
275    self.top_field_first = value;
276    self
277  }
278  /// Sets the best-effort timestamp in place.
279  #[cfg_attr(not(tarpaulin), inline(always))]
280  pub const fn set_best_effort_timestamp(&mut self, value: Option<i64>) -> &mut Self {
281    self.best_effort_timestamp = value;
282    self
283  }
284  /// Sets the mastering-display metadata in place.
285  #[cfg_attr(not(tarpaulin), inline(always))]
286  pub const fn set_mastering_display(&mut self, value: Option<MasteringDisplay>) -> &mut Self {
287    self.mastering_display = value;
288    self
289  }
290  /// Sets the content-light-level metadata in place.
291  #[cfg_attr(not(tarpaulin), inline(always))]
292  pub const fn set_content_light_level(&mut self, value: Option<ContentLightLevel>) -> &mut Self {
293    self.content_light_level = value;
294    self
295  }
296  /// Sets the SMPTE timecode list in place.
297  #[cfg_attr(not(tarpaulin), inline(always))]
298  pub fn set_smpte_timecode(&mut self, value: Vec<u32>) -> &mut Self {
299    self.smpte_timecode = value;
300    self
301  }
302  /// Sets the side-data list in place.
303  #[cfg_attr(not(tarpaulin), inline(always))]
304  pub fn set_side_data(&mut self, value: Vec<SideDataEntry>) -> &mut Self {
305    self.side_data = value;
306    self
307  }
308}
309
310/// Per-`AudioPacket` extras.
311#[derive(Clone, Debug, Default)]
312pub struct AudioPacketExtra {
313  stream_index: i32,
314  byte_pos: Option<i64>,
315  side_data: Vec<SideDataEntry>,
316}
317
318impl AudioPacketExtra {
319  /// Constructs an `AudioPacketExtra` with the given stream index.
320  #[cfg_attr(not(tarpaulin), inline(always))]
321  pub const fn new(stream_index: i32) -> Self {
322    Self {
323      stream_index,
324      byte_pos: None,
325      side_data: Vec::new(),
326    }
327  }
328
329  /// Returns the source `AVStream.index`.
330  #[cfg_attr(not(tarpaulin), inline(always))]
331  pub const fn stream_index(&self) -> i32 {
332    self.stream_index
333  }
334  /// Returns the byte position, or `None` if unknown.
335  #[cfg_attr(not(tarpaulin), inline(always))]
336  pub const fn byte_pos(&self) -> Option<i64> {
337    self.byte_pos
338  }
339  /// Returns the raw side-data entries.
340  #[cfg_attr(not(tarpaulin), inline(always))]
341  pub fn side_data(&self) -> &[SideDataEntry] {
342    self.side_data.as_slice()
343  }
344
345  /// Sets the stream index (consuming builder).
346  #[cfg_attr(not(tarpaulin), inline(always))]
347  #[must_use]
348  pub const fn with_stream_index(mut self, value: i32) -> Self {
349    self.stream_index = value;
350    self
351  }
352  /// Sets the byte position (consuming builder).
353  #[cfg_attr(not(tarpaulin), inline(always))]
354  #[must_use]
355  pub const fn with_byte_pos(mut self, value: Option<i64>) -> Self {
356    self.byte_pos = value;
357    self
358  }
359  /// Sets the side-data list (consuming builder).
360  #[cfg_attr(not(tarpaulin), inline(always))]
361  #[must_use]
362  pub fn with_side_data(mut self, value: Vec<SideDataEntry>) -> Self {
363    self.side_data = value;
364    self
365  }
366
367  /// Sets the stream index in place.
368  #[cfg_attr(not(tarpaulin), inline(always))]
369  pub const fn set_stream_index(&mut self, value: i32) -> &mut Self {
370    self.stream_index = value;
371    self
372  }
373  /// Sets the byte position in place.
374  #[cfg_attr(not(tarpaulin), inline(always))]
375  pub const fn set_byte_pos(&mut self, value: Option<i64>) -> &mut Self {
376    self.byte_pos = value;
377    self
378  }
379  /// Sets the side-data list in place.
380  #[cfg_attr(not(tarpaulin), inline(always))]
381  pub fn set_side_data(&mut self, value: Vec<SideDataEntry>) -> &mut Self {
382    self.side_data = value;
383    self
384  }
385}
386
387/// Per-`AudioFrame` extras.
388#[derive(Clone, Debug, Default)]
389pub struct AudioFrameExtra {
390  best_effort_timestamp: Option<i64>,
391  side_data: Vec<SideDataEntry>,
392}
393
394impl AudioFrameExtra {
395  /// Constructs an empty `AudioFrameExtra`.
396  #[cfg_attr(not(tarpaulin), inline(always))]
397  pub const fn new() -> Self {
398    Self {
399      best_effort_timestamp: None,
400      side_data: Vec::new(),
401    }
402  }
403
404  /// FFmpeg's heuristic best-effort PTS, or `None` if unknown.
405  #[cfg_attr(not(tarpaulin), inline(always))]
406  pub const fn best_effort_timestamp(&self) -> Option<i64> {
407    self.best_effort_timestamp
408  }
409  /// Returns the raw side-data entries.
410  #[cfg_attr(not(tarpaulin), inline(always))]
411  pub fn side_data(&self) -> &[SideDataEntry] {
412    self.side_data.as_slice()
413  }
414
415  /// Sets the best-effort timestamp (consuming builder).
416  #[cfg_attr(not(tarpaulin), inline(always))]
417  #[must_use]
418  pub const fn with_best_effort_timestamp(mut self, value: Option<i64>) -> Self {
419    self.best_effort_timestamp = value;
420    self
421  }
422  /// Sets the side-data list (consuming builder).
423  #[cfg_attr(not(tarpaulin), inline(always))]
424  #[must_use]
425  pub fn with_side_data(mut self, value: Vec<SideDataEntry>) -> Self {
426    self.side_data = value;
427    self
428  }
429
430  /// Sets the best-effort timestamp in place.
431  #[cfg_attr(not(tarpaulin), inline(always))]
432  pub const fn set_best_effort_timestamp(&mut self, value: Option<i64>) -> &mut Self {
433    self.best_effort_timestamp = value;
434    self
435  }
436  /// Sets the side-data list in place.
437  #[cfg_attr(not(tarpaulin), inline(always))]
438  pub fn set_side_data(&mut self, value: Vec<SideDataEntry>) -> &mut Self {
439    self.side_data = value;
440    self
441  }
442}
443
444/// Per-`SubtitlePacket` extras.
445#[derive(Clone, Debug, Default)]
446pub struct SubtitlePacketExtra {
447  stream_index: i32,
448  language: Option<[u8; 3]>,
449  forced: bool,
450}
451
452impl SubtitlePacketExtra {
453  /// Constructs a `SubtitlePacketExtra` with the given stream index.
454  #[cfg_attr(not(tarpaulin), inline(always))]
455  pub const fn new(stream_index: i32) -> Self {
456    Self {
457      stream_index,
458      language: None,
459      forced: false,
460    }
461  }
462
463  /// Returns the source `AVStream.index`.
464  #[cfg_attr(not(tarpaulin), inline(always))]
465  pub const fn stream_index(&self) -> i32 {
466    self.stream_index
467  }
468  /// Returns the ISO 639-2/T language tag, or `None` if unspecified.
469  #[cfg_attr(not(tarpaulin), inline(always))]
470  pub const fn language(&self) -> Option<[u8; 3]> {
471    self.language
472  }
473  /// Returns whether this subtitle stream is marked "forced".
474  #[cfg_attr(not(tarpaulin), inline(always))]
475  pub const fn forced(&self) -> bool {
476    self.forced
477  }
478
479  /// Sets the stream index (consuming builder).
480  #[cfg_attr(not(tarpaulin), inline(always))]
481  #[must_use]
482  pub const fn with_stream_index(mut self, value: i32) -> Self {
483    self.stream_index = value;
484    self
485  }
486  /// Sets the language tag (consuming builder).
487  #[cfg_attr(not(tarpaulin), inline(always))]
488  #[must_use]
489  pub const fn with_language(mut self, value: Option<[u8; 3]>) -> Self {
490    self.language = value;
491    self
492  }
493  /// Sets the forced flag (consuming builder).
494  #[cfg_attr(not(tarpaulin), inline(always))]
495  #[must_use]
496  pub const fn with_forced(mut self, value: bool) -> Self {
497    self.forced = value;
498    self
499  }
500
501  /// Sets the stream index in place.
502  #[cfg_attr(not(tarpaulin), inline(always))]
503  pub const fn set_stream_index(&mut self, value: i32) -> &mut Self {
504    self.stream_index = value;
505    self
506  }
507  /// Sets the language tag in place.
508  #[cfg_attr(not(tarpaulin), inline(always))]
509  pub const fn set_language(&mut self, value: Option<[u8; 3]>) -> &mut Self {
510    self.language = value;
511    self
512  }
513  /// Sets the forced flag in place.
514  #[cfg_attr(not(tarpaulin), inline(always))]
515  pub const fn set_forced(&mut self, value: bool) -> &mut Self {
516    self.forced = value;
517    self
518  }
519}
520
521/// Per-`SubtitleFrame` extras.
522#[derive(Clone, Debug, Default)]
523pub struct SubtitleFrameExtra {
524  start_display_time: u32,
525  end_display_time: u32,
526}
527
528impl SubtitleFrameExtra {
529  /// Constructs a `SubtitleFrameExtra`.
530  #[cfg_attr(not(tarpaulin), inline(always))]
531  pub const fn new(start_display_time: u32, end_display_time: u32) -> Self {
532    Self {
533      start_display_time,
534      end_display_time,
535    }
536  }
537
538  /// `AVSubtitle.start_display_time` — milliseconds from `pts`.
539  #[cfg_attr(not(tarpaulin), inline(always))]
540  pub const fn start_display_time(&self) -> u32 {
541    self.start_display_time
542  }
543  /// `AVSubtitle.end_display_time` — milliseconds from `pts`.
544  #[cfg_attr(not(tarpaulin), inline(always))]
545  pub const fn end_display_time(&self) -> u32 {
546    self.end_display_time
547  }
548
549  /// Sets the start display time (consuming builder).
550  #[cfg_attr(not(tarpaulin), inline(always))]
551  #[must_use]
552  pub const fn with_start_display_time(mut self, value: u32) -> Self {
553    self.start_display_time = value;
554    self
555  }
556  /// Sets the end display time (consuming builder).
557  #[cfg_attr(not(tarpaulin), inline(always))]
558  #[must_use]
559  pub const fn with_end_display_time(mut self, value: u32) -> Self {
560    self.end_display_time = value;
561    self
562  }
563
564  /// Sets the start display time in place.
565  #[cfg_attr(not(tarpaulin), inline(always))]
566  pub const fn set_start_display_time(&mut self, value: u32) -> &mut Self {
567    self.start_display_time = value;
568    self
569  }
570  /// Sets the end display time in place.
571  #[cfg_attr(not(tarpaulin), inline(always))]
572  pub const fn set_end_display_time(&mut self, value: u32) -> &mut Self {
573    self.end_display_time = value;
574    self
575  }
576}
577
578/// Picture type per `AVFrame.pict_type`.
579#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
580#[non_exhaustive]
581pub enum PictureType {
582  /// Unspecified / unset.
583  #[default]
584  Unspecified,
585  /// Intra (I-frame).
586  I,
587  /// Predicted (P-frame).
588  P,
589  /// Bi-directional predicted (B-frame).
590  B,
591  /// S(GMC)-VOP from MPEG-4.
592  S,
593  /// Switching Intra (H.264).
594  Si,
595  /// Switching Predicted (H.264).
596  Sp,
597  /// Bi-predicted intra (BI-frame).
598  Bi,
599}
600
601/// Raw side-data entry carrying the FFmpeg type id and the unparsed
602/// byte buffer. Type ids correspond to FFmpeg's
603/// `AV_FRAME_DATA_*` / `AV_PKT_DATA_*` constants — see
604/// `libavutil/frame.h` and `libavcodec/packet.h`.
605#[derive(Clone, Debug)]
606pub struct SideDataEntry {
607  kind: i32,
608  data: Vec<u8>,
609}
610
611impl SideDataEntry {
612  /// Constructs a `SideDataEntry`.
613  #[cfg_attr(not(tarpaulin), inline(always))]
614  pub const fn new(kind: i32, data: Vec<u8>) -> Self {
615    Self { kind, data }
616  }
617
618  /// FFmpeg side-data type id.
619  #[cfg_attr(not(tarpaulin), inline(always))]
620  pub const fn kind(&self) -> i32 {
621    self.kind
622  }
623  /// Side-data payload as raw bytes.
624  #[cfg_attr(not(tarpaulin), inline(always))]
625  pub fn data(&self) -> &[u8] {
626    self.data.as_slice()
627  }
628
629  /// Sets the type id (consuming builder).
630  #[cfg_attr(not(tarpaulin), inline(always))]
631  #[must_use]
632  pub const fn with_kind(mut self, value: i32) -> Self {
633    self.kind = value;
634    self
635  }
636  /// Sets the payload (consuming builder).
637  #[cfg_attr(not(tarpaulin), inline(always))]
638  #[must_use]
639  pub fn with_data(mut self, value: Vec<u8>) -> Self {
640    self.data = value;
641    self
642  }
643
644  /// Sets the type id in place.
645  #[cfg_attr(not(tarpaulin), inline(always))]
646  pub const fn set_kind(&mut self, value: i32) -> &mut Self {
647    self.kind = value;
648    self
649  }
650  /// Sets the payload in place.
651  #[cfg_attr(not(tarpaulin), inline(always))]
652  pub fn set_data(&mut self, value: Vec<u8>) -> &mut Self {
653    self.data = value;
654    self
655  }
656}
657
658/// HDR10 mastering display metadata.
659#[derive(Copy, Clone, Debug, PartialEq)]
660pub struct MasteringDisplay {
661  display_primaries: [(u32, u32); 3],
662  white_point: (u32, u32),
663  max_luminance: (u32, u32),
664  min_luminance: (u32, u32),
665}
666
667impl MasteringDisplay {
668  /// Constructs a `MasteringDisplay`.
669  #[cfg_attr(not(tarpaulin), inline(always))]
670  pub const fn new(
671    display_primaries: [(u32, u32); 3],
672    white_point: (u32, u32),
673    max_luminance: (u32, u32),
674    min_luminance: (u32, u32),
675  ) -> Self {
676    Self {
677      display_primaries,
678      white_point,
679      max_luminance,
680      min_luminance,
681    }
682  }
683
684  /// Display primary chromaticities `(x, y)` for R, G, B in CIE 1931
685  /// (each as `(num, den)` rational, with `den` non-zero).
686  #[cfg_attr(not(tarpaulin), inline(always))]
687  pub const fn display_primaries(&self) -> [(u32, u32); 3] {
688    self.display_primaries
689  }
690  /// White-point chromaticity `(x, y)` as rationals.
691  #[cfg_attr(not(tarpaulin), inline(always))]
692  pub const fn white_point(&self) -> (u32, u32) {
693    self.white_point
694  }
695  /// Maximum luminance in `0.0001 cd/m²` units (rational `(num, den)`).
696  #[cfg_attr(not(tarpaulin), inline(always))]
697  pub const fn max_luminance(&self) -> (u32, u32) {
698    self.max_luminance
699  }
700  /// Minimum luminance in `0.0001 cd/m²` units.
701  #[cfg_attr(not(tarpaulin), inline(always))]
702  pub const fn min_luminance(&self) -> (u32, u32) {
703    self.min_luminance
704  }
705
706  /// Sets the display primaries (consuming builder).
707  #[cfg_attr(not(tarpaulin), inline(always))]
708  pub const fn with_display_primaries(mut self, value: [(u32, u32); 3]) -> Self {
709    self.display_primaries = value;
710    self
711  }
712  /// Sets the white point (consuming builder).
713  #[cfg_attr(not(tarpaulin), inline(always))]
714  pub const fn with_white_point(mut self, value: (u32, u32)) -> Self {
715    self.white_point = value;
716    self
717  }
718  /// Sets the max luminance (consuming builder).
719  #[cfg_attr(not(tarpaulin), inline(always))]
720  pub const fn with_max_luminance(mut self, value: (u32, u32)) -> Self {
721    self.max_luminance = value;
722    self
723  }
724  /// Sets the min luminance (consuming builder).
725  #[cfg_attr(not(tarpaulin), inline(always))]
726  pub const fn with_min_luminance(mut self, value: (u32, u32)) -> Self {
727    self.min_luminance = value;
728    self
729  }
730
731  /// Sets the display primaries in place.
732  #[cfg_attr(not(tarpaulin), inline(always))]
733  pub const fn set_display_primaries(&mut self, value: [(u32, u32); 3]) -> &mut Self {
734    self.display_primaries = value;
735    self
736  }
737  /// Sets the white point in place.
738  #[cfg_attr(not(tarpaulin), inline(always))]
739  pub const fn set_white_point(&mut self, value: (u32, u32)) -> &mut Self {
740    self.white_point = value;
741    self
742  }
743  /// Sets the max luminance in place.
744  #[cfg_attr(not(tarpaulin), inline(always))]
745  pub const fn set_max_luminance(&mut self, value: (u32, u32)) -> &mut Self {
746    self.max_luminance = value;
747    self
748  }
749  /// Sets the min luminance in place.
750  #[cfg_attr(not(tarpaulin), inline(always))]
751  pub const fn set_min_luminance(&mut self, value: (u32, u32)) -> &mut Self {
752    self.min_luminance = value;
753    self
754  }
755}
756
757/// HDR10 content light level (`AV_FRAME_DATA_CONTENT_LIGHT_LEVEL`).
758#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
759pub struct ContentLightLevel {
760  max_cll: u32,
761  max_fall: u32,
762}
763
764impl ContentLightLevel {
765  /// Constructs a `ContentLightLevel`.
766  #[cfg_attr(not(tarpaulin), inline(always))]
767  pub const fn new(max_cll: u32, max_fall: u32) -> Self {
768    Self { max_cll, max_fall }
769  }
770
771  /// Maximum content light level (cd/m²).
772  #[cfg_attr(not(tarpaulin), inline(always))]
773  pub const fn max_cll(&self) -> u32 {
774    self.max_cll
775  }
776  /// Maximum frame-average light level (cd/m²).
777  #[cfg_attr(not(tarpaulin), inline(always))]
778  pub const fn max_fall(&self) -> u32 {
779    self.max_fall
780  }
781
782  /// Sets `max_cll` (consuming builder).
783  #[cfg_attr(not(tarpaulin), inline(always))]
784  #[must_use]
785  pub const fn with_max_cll(mut self, value: u32) -> Self {
786    self.max_cll = value;
787    self
788  }
789  /// Sets `max_fall` (consuming builder).
790  #[cfg_attr(not(tarpaulin), inline(always))]
791  #[must_use]
792  pub const fn with_max_fall(mut self, value: u32) -> Self {
793    self.max_fall = value;
794    self
795  }
796
797  /// Sets `max_cll` in place.
798  #[cfg_attr(not(tarpaulin), inline(always))]
799  pub const fn set_max_cll(&mut self, value: u32) -> &mut Self {
800    self.max_cll = value;
801    self
802  }
803  /// Sets `max_fall` in place.
804  #[cfg_attr(not(tarpaulin), inline(always))]
805  pub const fn set_max_fall(&mut self, value: u32) -> &mut Self {
806    self.max_fall = value;
807    self
808  }
809}
810
811#[cfg(test)]
812mod tests {
813  use super::*;
814
815  #[test]
816  fn defaults_construct() {
817    let v = VideoPacketExtra::default();
818    assert_eq!(v.stream_index(), 0);
819    assert!(v.side_data().is_empty());
820
821    let f = VideoFrameExtra::default();
822    assert_eq!(f.picture_type(), PictureType::Unspecified);
823    assert!(!f.key_frame());
824    assert!(f.mastering_display().is_none());
825
826    let s = SubtitleFrameExtra::default();
827    assert_eq!(s.start_display_time(), 0);
828    assert_eq!(s.end_display_time(), 0);
829  }
830
831  #[test]
832  fn picture_type_default_is_unspecified() {
833    assert_eq!(PictureType::default(), PictureType::Unspecified);
834  }
835
836  #[test]
837  fn side_data_entry_carries_bytes() {
838    let entry = SideDataEntry::new(12345, vec![1, 2, 3, 4]);
839    assert_eq!(entry.kind(), 12345);
840    assert_eq!(entry.data(), &[1, 2, 3, 4]);
841  }
842
843  #[test]
844  fn content_light_level_default_is_zero() {
845    let cll = ContentLightLevel::default();
846    assert_eq!(cll.max_cll(), 0);
847    assert_eq!(cll.max_fall(), 0);
848  }
849
850  #[test]
851  fn builders_chain() {
852    let v = VideoPacketExtra::new(7)
853      .with_byte_pos(Some(1234))
854      .with_side_data(vec![SideDataEntry::new(1, vec![0xAB])]);
855    assert_eq!(v.stream_index(), 7);
856    assert_eq!(v.byte_pos(), Some(1234));
857    assert_eq!(v.side_data().len(), 1);
858  }
859
860  #[test]
861  fn setters_chain() {
862    let mut v = VideoPacketExtra::default();
863    v.set_stream_index(3).set_byte_pos(Some(99));
864    assert_eq!(v.stream_index(), 3);
865    assert_eq!(v.byte_pos(), Some(99));
866  }
867}