1#![forbid(unsafe_code)]
47
48use crate::include::common::bitdepth::{BitDepth8, BitDepth16};
49use crate::include::dav1d::data::Rav1dData;
50use crate::include::dav1d::dav1d::{Rav1dDecodeFrameType, Rav1dInloopFilterType, Rav1dSettings};
51use crate::include::dav1d::headers::{
52 Rav1dColorPrimaries, Rav1dMatrixCoefficients, Rav1dPixelLayout, Rav1dTransferCharacteristics,
53};
54use crate::include::dav1d::picture::{Rav1dPicture, Rav1dPictureDataComponentInner};
55use crate::src::c_arc::CArc;
56use crate::src::disjoint_mut::DisjointImmutGuard;
57use crate::src::error::Rav1dError;
58use crate::src::internal::Rav1dContext;
59use std::ops::Deref;
60use std::sync::Arc;
61
62#[derive(Debug, Clone, PartialEq, Eq)]
64pub enum Error {
65 InvalidSettings(&'static str),
66 InitFailed,
67 OutOfMemory,
68 InvalidData,
69 NeedMoreData,
70 Other(String),
71}
72
73impl std::fmt::Display for Error {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 match self {
76 Self::InvalidSettings(msg) => write!(f, "invalid settings: {}", msg),
77 Self::InitFailed => write!(f, "decoder initialization failed"),
78 Self::OutOfMemory => write!(f, "out of memory"),
79 Self::InvalidData => write!(f, "invalid data"),
80 Self::NeedMoreData => write!(f, "need more data"),
81 Self::Other(msg) => write!(f, "decode error: {}", msg),
82 }
83 }
84}
85
86impl std::error::Error for Error {}
87
88impl From<Rav1dError> for Error {
89 fn from(err: Rav1dError) -> Self {
90 match err {
91 Rav1dError::EAGAIN => Self::NeedMoreData,
92 Rav1dError::ENOMEM => Self::OutOfMemory,
93 Rav1dError::EINVAL => Self::InvalidData,
94 Rav1dError::EGeneric => Self::Other("generic error".to_string()),
95 _ => Self::Other(format!("{:?}", err)),
96 }
97 }
98}
99
100pub type Result<T, E = Error> = std::result::Result<T, E>;
101
102#[derive(Clone, Debug)]
107#[non_exhaustive]
108pub struct Settings {
109 pub threads: u32,
123
124 pub apply_grain: bool,
126
127 pub frame_size_limit: u32,
132
133 pub all_layers: bool,
135
136 pub operating_point: u8,
138
139 pub output_invisible_frames: bool,
141
142 pub inloop_filters: InloopFilters,
144
145 pub decode_frame_type: DecodeFrameType,
147
148 pub max_frame_delay: u32,
157
158 pub strict_std_compliance: bool,
160
161 pub cpu_level: CpuLevel,
169}
170
171impl Default for Settings {
172 fn default() -> Self {
173 Self {
174 threads: 1,
180 apply_grain: true,
181 frame_size_limit: 8192 * 4320, all_layers: true,
183 operating_point: 0,
184 max_frame_delay: 0,
185 output_invisible_frames: false,
186 inloop_filters: InloopFilters::all(),
187 decode_frame_type: DecodeFrameType::All,
188 strict_std_compliance: false,
189 cpu_level: CpuLevel::Native,
190 }
191 }
192}
193
194impl From<Settings> for Rav1dSettings {
195 fn from(settings: Settings) -> Self {
196 Self {
197 n_threads: settings.threads as i32,
198 max_frame_delay: settings.max_frame_delay as i32,
199 apply_grain: settings.apply_grain,
200 operating_point: settings.operating_point,
201 all_layers: settings.all_layers,
202 frame_size_limit: settings.frame_size_limit,
203 allocator: Default::default(),
204 logger: None,
205 strict_std_compliance: settings.strict_std_compliance,
206 output_invisible_frames: settings.output_invisible_frames,
207 inloop_filters: settings.inloop_filters.into(),
208 decode_frame_type: settings.decode_frame_type.into(),
209 }
210 }
211}
212
213#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
215pub struct InloopFilters {
216 bits: u8,
217}
218
219impl InloopFilters {
220 pub const DEBLOCK: Self = Self {
221 bits: Rav1dInloopFilterType::DEBLOCK.bits(),
222 };
223 pub const CDEF: Self = Self {
224 bits: Rav1dInloopFilterType::CDEF.bits(),
225 };
226 pub const RESTORATION: Self = Self {
227 bits: Rav1dInloopFilterType::RESTORATION.bits(),
228 };
229
230 pub const fn all() -> Self {
232 Self {
233 bits: Rav1dInloopFilterType::DEBLOCK.bits()
234 | Rav1dInloopFilterType::CDEF.bits()
235 | Rav1dInloopFilterType::RESTORATION.bits(),
236 }
237 }
238
239 pub const fn none() -> Self {
241 Self { bits: 0 }
242 }
243
244 pub const fn contains(&self, other: Self) -> bool {
245 (self.bits & other.bits) == other.bits
246 }
247
248 pub const fn union(self, other: Self) -> Self {
249 Self {
250 bits: self.bits | other.bits,
251 }
252 }
253}
254
255impl From<InloopFilters> for Rav1dInloopFilterType {
256 fn from(filters: InloopFilters) -> Self {
257 Self::from_bits_retain(filters.bits)
258 }
259}
260
261#[derive(Clone, Copy, Debug, PartialEq, Eq)]
263pub enum DecodeFrameType {
264 All,
266 Reference,
268 Intra,
270 Key,
272}
273
274impl From<DecodeFrameType> for Rav1dDecodeFrameType {
275 fn from(ft: DecodeFrameType) -> Self {
276 match ft {
277 DecodeFrameType::All => Self::All,
278 DecodeFrameType::Reference => Self::Reference,
279 DecodeFrameType::Intra => Self::Intra,
280 DecodeFrameType::Key => Self::Key,
281 }
282 }
283}
284
285#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
306#[non_exhaustive]
307#[derive(Default)]
308pub enum CpuLevel {
309 Scalar,
311
312 X86V2,
315
316 X86V3,
319
320 X86V4,
323
324 Neon,
326
327 NeonDotprod,
329
330 NeonI8mm,
332
333 #[default]
335 Native,
336}
337
338impl CpuLevel {
339 pub const fn to_mask(self) -> u32 {
344 match self {
345 Self::Scalar => 0,
346
347 Self::X86V2 => {
349 (1 << 0) | (1 << 1) | (1 << 2) }
351 Self::X86V3 => {
352 (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) }
354 Self::X86V4 => {
355 (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) }
357
358 Self::Neon => 1 << 0,
360 Self::NeonDotprod => (1 << 0) | (1 << 1),
361 Self::NeonI8mm => (1 << 0) | (1 << 1) | (1 << 2),
362
363 Self::Native => u32::MAX,
364 }
365 }
366
367 pub fn platform_levels() -> &'static [CpuLevel] {
373 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
374 {
375 &[
376 CpuLevel::Scalar,
377 CpuLevel::X86V2,
378 CpuLevel::X86V3,
379 CpuLevel::X86V4,
380 CpuLevel::Native,
381 ]
382 }
383 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
384 {
385 &[
386 CpuLevel::Scalar,
387 CpuLevel::Neon,
388 CpuLevel::NeonDotprod,
389 CpuLevel::NeonI8mm,
390 CpuLevel::Native,
391 ]
392 }
393 #[cfg(not(any(
394 target_arch = "x86",
395 target_arch = "x86_64",
396 target_arch = "arm",
397 target_arch = "aarch64",
398 )))]
399 {
400 &[CpuLevel::Scalar, CpuLevel::Native]
401 }
402 }
403
404 pub const fn name(self) -> &'static str {
406 match self {
407 Self::Scalar => "scalar",
408 Self::X86V2 => "x86-64-v2",
409 Self::X86V3 => "x86-64-v3",
410 Self::X86V4 => "x86-64-v4",
411 Self::Neon => "neon",
412 Self::NeonDotprod => "neon-dotprod",
413 Self::NeonI8mm => "neon-i8mm",
414 Self::Native => "native",
415 }
416 }
417}
418
419impl std::fmt::Display for CpuLevel {
420 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
421 f.write_str(self.name())
422 }
423}
424
425pub struct Decoder {
446 ctx: Arc<Rav1dContext>,
447 worker_handles: Vec<std::thread::JoinHandle<()>>,
448}
449
450pub const fn is_unchecked() -> bool {
457 cfg!(feature = "unchecked")
458}
459
460impl Decoder {
461 pub fn new() -> Result<Self> {
463 Self::with_settings(Settings::default())
464 }
465
466 pub fn with_settings(settings: Settings) -> Result<Self> {
468 crate::src::cpu::rav1d_set_cpu_flags_mask(settings.cpu_level.to_mask());
470
471 let rav1d_settings: Rav1dSettings = settings.into();
472 let (ctx, worker_handles) =
473 crate::src::lib::rav1d_open(&rav1d_settings).map_err(|_| Error::InitFailed)?;
474 Ok(Self {
475 ctx,
476 worker_handles,
477 })
478 }
479
480 pub fn decode(&mut self, data: &[u8]) -> Result<Option<Frame>> {
505 let mut rav1d_data = if !data.is_empty() {
507 let owned = data.to_vec().into_boxed_slice();
509 let cbox = crate::src::c_box::CBox::from_box(owned);
510 let carc = CArc::wrap(cbox).map_err(|_| Error::OutOfMemory)?;
511 Rav1dData {
512 data: Some(carc),
513 m: Default::default(),
514 }
515 } else {
516 Rav1dData {
517 data: None,
518 m: Default::default(),
519 }
520 };
521
522 crate::src::lib::rav1d_send_data(&self.ctx, &mut rav1d_data)?;
524
525 let mut pic = Rav1dPicture::default();
527 match crate::src::lib::rav1d_get_picture(&self.ctx, &mut pic) {
528 Ok(()) => Ok(Some(Frame { inner: pic })),
529 Err(Rav1dError::EAGAIN) => Ok(None),
530 Err(e) => Err(e.into()),
531 }
532 }
533
534 pub fn get_frame(&mut self) -> Result<Option<Frame>> {
540 let mut pic = Rav1dPicture::default();
541 match crate::src::lib::rav1d_get_picture(&self.ctx, &mut pic) {
542 Ok(()) => Ok(Some(Frame { inner: pic })),
543 Err(Rav1dError::EAGAIN) => Ok(None),
544 Err(e) => Err(e.into()),
545 }
546 }
547
548 pub fn flush(&mut self) -> Result<Vec<Frame>> {
553 crate::src::lib::rav1d_flush(&self.ctx);
554
555 let mut frames = Vec::new();
556 loop {
557 let mut pic = Rav1dPicture::default();
558 match crate::src::lib::rav1d_get_picture(&self.ctx, &mut pic) {
559 Ok(()) => frames.push(Frame { inner: pic }),
560 Err(Rav1dError::EAGAIN) => break,
561 Err(e) => return Err(e.into()),
562 }
563 }
564 Ok(frames)
565 }
566}
567
568impl Drop for Decoder {
569 fn drop(&mut self) {
570 self.ctx.tell_worker_threads_to_die();
572
573 for handle in self.worker_handles.drain(..) {
579 match handle.join() {
580 Ok(()) => {}
581 Err(e) => {
582 eprintln!("rav1d worker thread panicked during shutdown: {:?}", e);
583 }
584 }
585 }
586
587 }
590}
591
592#[derive(Clone)]
597pub struct Frame {
598 inner: Rav1dPicture,
599}
600
601impl Frame {
602 pub fn width(&self) -> u32 {
604 self.inner.p.w as u32
605 }
606
607 pub fn height(&self) -> u32 {
609 self.inner.p.h as u32
610 }
611
612 pub fn bit_depth(&self) -> u8 {
614 self.inner.p.bpc
615 }
616
617 pub fn pixel_layout(&self) -> PixelLayout {
619 self.inner.p.layout.into()
620 }
621
622 pub fn planes(&self) -> Planes<'_> {
626 match self.bit_depth() {
627 8 => Planes::Depth8(Planes8 { frame: self }),
628 10 | 12 => Planes::Depth16(Planes16 { frame: self }),
629 _ => unreachable!("invalid bit depth: {}", self.bit_depth()),
630 }
631 }
632
633 pub fn color_info(&self) -> ColorInfo {
635 let seq_hdr = self.inner.seq_hdr.as_ref().expect("missing seq_hdr");
636 ColorInfo {
637 primaries: seq_hdr.rav1d.pri.into(),
638 transfer_characteristics: seq_hdr.rav1d.trc.into(),
639 matrix_coefficients: seq_hdr.rav1d.mtrx.into(),
640 color_range: if seq_hdr.rav1d.color_range != 0 {
641 ColorRange::Full
642 } else {
643 ColorRange::Limited
644 },
645 }
646 }
647
648 pub fn content_light(&self) -> Option<ContentLightLevel> {
650 self.inner
651 .content_light
652 .as_ref()
653 .map(|arc| ContentLightLevel {
654 max_content_light_level: arc.max_content_light_level,
655 max_frame_average_light_level: arc.max_frame_average_light_level,
656 })
657 }
658
659 pub fn mastering_display(&self) -> Option<MasteringDisplay> {
661 self.inner
662 .mastering_display
663 .as_ref()
664 .map(|arc| MasteringDisplay {
665 primaries: arc.primaries,
666 white_point: arc.white_point,
667 max_luminance: arc.max_luminance,
668 min_luminance: arc.min_luminance,
669 })
670 }
671
672 pub fn timestamp(&self) -> i64 {
674 self.inner.m.timestamp
675 }
676
677 pub fn duration(&self) -> i64 {
679 self.inner.m.duration
680 }
681}
682
683#[derive(Clone, Copy, Debug, PartialEq, Eq)]
685pub enum PixelLayout {
686 I400,
688 I420,
690 I422,
692 I444,
694}
695
696impl From<Rav1dPixelLayout> for PixelLayout {
697 fn from(layout: Rav1dPixelLayout) -> Self {
698 match layout {
699 Rav1dPixelLayout::I400 => Self::I400,
700 Rav1dPixelLayout::I420 => Self::I420,
701 Rav1dPixelLayout::I422 => Self::I422,
702 Rav1dPixelLayout::I444 => Self::I444,
703 }
704 }
705}
706
707pub enum Planes<'a> {
711 Depth8(Planes8<'a>),
712 Depth16(Planes16<'a>),
713}
714
715pub struct Planes8<'a> {
717 frame: &'a Frame,
718}
719
720impl<'a> Planes8<'a> {
721 pub fn y(&self) -> PlaneView8<'a> {
723 let data = self
724 .frame
725 .inner
726 .data
727 .as_ref()
728 .expect("missing picture data");
729 let guard = data.data[0].slice::<BitDepth8, _>(..);
730
731 let stride = self.frame.inner.stride[0] as usize;
732 let buffer_height = guard.len().checked_div(stride).unwrap_or(0);
733 let height = (self.frame.height() as usize).min(buffer_height);
735
736 PlaneView8 {
737 guard,
738 stride,
739 width: self.frame.width() as usize,
740 height,
741 }
742 }
743
744 pub fn u(&self) -> Option<PlaneView8<'a>> {
746 if self.frame.pixel_layout() == PixelLayout::I400 {
747 return None;
748 }
749
750 let (w, h) = self.chroma_dimensions();
751 let data = self
752 .frame
753 .inner
754 .data
755 .as_ref()
756 .expect("missing picture data");
757 let guard = data.data[1].slice::<BitDepth8, _>(..);
758
759 let stride = self.frame.inner.stride[1] as usize;
760 let buffer_height = guard.len().checked_div(stride).unwrap_or(0);
761 let height = h.min(buffer_height);
762
763 Some(PlaneView8 {
764 guard,
765 stride,
766 width: w,
767 height,
768 })
769 }
770
771 pub fn v(&self) -> Option<PlaneView8<'a>> {
773 if self.frame.pixel_layout() == PixelLayout::I400 {
774 return None;
775 }
776
777 let (w, h) = self.chroma_dimensions();
778 let data = self
779 .frame
780 .inner
781 .data
782 .as_ref()
783 .expect("missing picture data");
784 let guard = data.data[2].slice::<BitDepth8, _>(..);
785
786 let stride = self.frame.inner.stride[1] as usize;
787 let buffer_height = guard.len().checked_div(stride).unwrap_or(0);
788 let height = h.min(buffer_height);
789
790 Some(PlaneView8 {
791 guard,
792 stride,
793 width: w,
794 height,
795 })
796 }
797
798 fn chroma_dimensions(&self) -> (usize, usize) {
799 let w = self.frame.width() as usize;
800 let h = self.frame.height() as usize;
801 match self.frame.pixel_layout() {
802 PixelLayout::I420 => (w.div_ceil(2), h.div_ceil(2)),
803 PixelLayout::I422 => (w.div_ceil(2), h),
804 PixelLayout::I444 => (w, h),
805 PixelLayout::I400 => (0, 0),
806 }
807 }
808}
809
810pub struct Planes16<'a> {
812 frame: &'a Frame,
813}
814
815impl<'a> Planes16<'a> {
816 pub fn y(&self) -> PlaneView16<'a> {
818 let data = self
819 .frame
820 .inner
821 .data
822 .as_ref()
823 .expect("missing picture data");
824 let guard = data.data[0].slice::<BitDepth16, _>(..);
825
826 let stride = self.frame.inner.stride[0] as usize / 2;
828 let buffer_height = guard.len().checked_div(stride).unwrap_or(0);
829 let height = (self.frame.height() as usize).min(buffer_height);
830
831 PlaneView16 {
832 guard,
833 stride,
834 width: self.frame.width() as usize,
835 height,
836 }
837 }
838
839 pub fn u(&self) -> Option<PlaneView16<'a>> {
841 if self.frame.pixel_layout() == PixelLayout::I400 {
842 return None;
843 }
844
845 let (w, h) = self.chroma_dimensions();
846 let data = self
847 .frame
848 .inner
849 .data
850 .as_ref()
851 .expect("missing picture data");
852 let guard = data.data[1].slice::<BitDepth16, _>(..);
853
854 let stride = self.frame.inner.stride[1] as usize / 2;
856 let buffer_height = guard.len().checked_div(stride).unwrap_or(0);
857 let height = h.min(buffer_height);
858
859 Some(PlaneView16 {
860 guard,
861 stride,
862 width: w,
863 height,
864 })
865 }
866
867 pub fn v(&self) -> Option<PlaneView16<'a>> {
869 if self.frame.pixel_layout() == PixelLayout::I400 {
870 return None;
871 }
872
873 let (w, h) = self.chroma_dimensions();
874 let data = self
875 .frame
876 .inner
877 .data
878 .as_ref()
879 .expect("missing picture data");
880 let guard = data.data[2].slice::<BitDepth16, _>(..);
881
882 let stride = self.frame.inner.stride[1] as usize / 2;
884 let buffer_height = guard.len().checked_div(stride).unwrap_or(0);
885 let height = h.min(buffer_height);
886
887 Some(PlaneView16 {
888 guard,
889 stride,
890 width: w,
891 height,
892 })
893 }
894
895 fn chroma_dimensions(&self) -> (usize, usize) {
896 let w = self.frame.width() as usize;
897 let h = self.frame.height() as usize;
898 match self.frame.pixel_layout() {
899 PixelLayout::I420 => (w.div_ceil(2), h.div_ceil(2)),
900 PixelLayout::I422 => (w.div_ceil(2), h),
901 PixelLayout::I444 => (w, h),
902 PixelLayout::I400 => (0, 0),
903 }
904 }
905}
906
907pub struct PlaneView8<'a> {
911 guard: DisjointImmutGuard<'a, Rav1dPictureDataComponentInner, [u8]>,
912 stride: usize,
913 width: usize,
914 height: usize,
915}
916
917impl<'a> PlaneView8<'a> {
918 pub fn row(&self, y: usize) -> &[u8] {
924 assert!(
925 y < self.height,
926 "row index {} out of bounds (height: {})",
927 y,
928 self.height
929 );
930 let start = y * self.stride;
931 &self.guard[start..start + self.width]
932 }
933
934 pub fn pixel(&self, x: usize, y: usize) -> u8 {
940 assert!(
941 x < self.width && y < self.height,
942 "pixel coordinates ({}, {}) out of bounds ({}x{})",
943 x,
944 y,
945 self.width,
946 self.height
947 );
948 self.guard[y * self.stride + x]
949 }
950
951 pub fn rows(&'a self) -> impl Iterator<Item = &'a [u8]> + 'a {
953 (0..self.height).map(move |y| self.row(y))
954 }
955
956 pub fn as_slice(&self) -> &[u8] {
958 self.guard.deref()
959 }
960
961 pub fn width(&self) -> usize {
962 self.width
963 }
964
965 pub fn height(&self) -> usize {
966 self.height
967 }
968
969 pub fn stride(&self) -> usize {
970 self.stride
971 }
972}
973
974pub struct PlaneView16<'a> {
976 guard: DisjointImmutGuard<'a, Rav1dPictureDataComponentInner, [u16]>,
977 stride: usize,
978 width: usize,
979 height: usize,
980}
981
982impl<'a> PlaneView16<'a> {
983 pub fn row(&self, y: usize) -> &[u16] {
989 assert!(
990 y < self.height,
991 "row index {} out of bounds (height: {})",
992 y,
993 self.height
994 );
995 let start = y * self.stride;
996 &self.guard[start..start + self.width]
997 }
998
999 pub fn pixel(&self, x: usize, y: usize) -> u16 {
1005 assert!(
1006 x < self.width && y < self.height,
1007 "pixel coordinates ({}, {}) out of bounds ({}x{})",
1008 x,
1009 y,
1010 self.width,
1011 self.height
1012 );
1013 self.guard[y * self.stride + x]
1014 }
1015
1016 pub fn rows(&'a self) -> impl Iterator<Item = &'a [u16]> + 'a {
1018 (0..self.height).map(move |y| self.row(y))
1019 }
1020
1021 pub fn as_slice(&self) -> &[u16] {
1023 self.guard.deref()
1024 }
1025
1026 pub fn width(&self) -> usize {
1027 self.width
1028 }
1029
1030 pub fn height(&self) -> usize {
1031 self.height
1032 }
1033
1034 pub fn stride(&self) -> usize {
1035 self.stride
1036 }
1037}
1038
1039#[derive(Clone, Copy, Debug)]
1041pub struct ColorInfo {
1042 pub primaries: ColorPrimaries,
1043 pub transfer_characteristics: TransferCharacteristics,
1044 pub matrix_coefficients: MatrixCoefficients,
1045 pub color_range: ColorRange,
1046}
1047
1048#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1050#[repr(u8)]
1051pub enum ColorPrimaries {
1052 Unknown = 0,
1053 BT709 = 1,
1054 Unspecified = 2,
1055 BT470M = 4,
1056 BT470BG = 5,
1057 BT601 = 6,
1058 SMPTE240 = 7,
1059 Film = 8,
1060 BT2020 = 9,
1061 XYZ = 10,
1062 SMPTE431 = 11,
1063 SMPTE432 = 12,
1064 EBU3213 = 22,
1065}
1066
1067impl From<Rav1dColorPrimaries> for ColorPrimaries {
1068 fn from(pri: Rav1dColorPrimaries) -> Self {
1069 match pri.0 {
1070 1 => Self::BT709,
1071 4 => Self::BT470M,
1072 5 => Self::BT470BG,
1073 6 => Self::BT601,
1074 7 => Self::SMPTE240,
1075 8 => Self::Film,
1076 9 => Self::BT2020,
1077 10 => Self::XYZ,
1078 11 => Self::SMPTE431,
1079 12 => Self::SMPTE432,
1080 22 => Self::EBU3213,
1081 _ => Self::Unspecified,
1082 }
1083 }
1084}
1085
1086#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1088#[repr(u8)]
1089pub enum TransferCharacteristics {
1090 Reserved = 0,
1091 BT709 = 1,
1092 Unspecified = 2,
1093 BT470M = 4,
1094 BT470BG = 5,
1095 BT601 = 6,
1096 SMPTE240 = 7,
1097 Linear = 8,
1098 Log100 = 9,
1099 Log100Sqrt10 = 10,
1100 IEC61966 = 11,
1101 BT1361 = 12,
1102 SRGB = 13,
1103 BT2020_10bit = 14,
1104 BT2020_12bit = 15,
1105 SMPTE2084 = 16,
1107 SMPTE428 = 17,
1108 HLG = 18,
1110}
1111
1112impl From<Rav1dTransferCharacteristics> for TransferCharacteristics {
1113 fn from(trc: Rav1dTransferCharacteristics) -> Self {
1114 match trc.0 {
1115 1 => Self::BT709,
1116 4 => Self::BT470M,
1117 5 => Self::BT470BG,
1118 6 => Self::BT601,
1119 7 => Self::SMPTE240,
1120 8 => Self::Linear,
1121 9 => Self::Log100,
1122 10 => Self::Log100Sqrt10,
1123 11 => Self::IEC61966,
1124 12 => Self::BT1361,
1125 13 => Self::SRGB,
1126 14 => Self::BT2020_10bit,
1127 15 => Self::BT2020_12bit,
1128 16 => Self::SMPTE2084,
1129 17 => Self::SMPTE428,
1130 18 => Self::HLG,
1131 _ => Self::Unspecified,
1132 }
1133 }
1134}
1135
1136#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1138#[repr(u8)]
1139pub enum MatrixCoefficients {
1140 Identity = 0,
1141 BT709 = 1,
1142 Unspecified = 2,
1143 Reserved = 3,
1144 FCC = 4,
1145 BT470BG = 5,
1146 BT601 = 6,
1147 SMPTE240 = 7,
1148 YCgCo = 8,
1149 BT2020NCL = 9,
1150 BT2020CL = 10,
1151 SMPTE2085 = 11,
1152 ChromaDerivedNCL = 12,
1153 ChromaDerivedCL = 13,
1154 ICtCp = 14,
1155}
1156
1157impl From<Rav1dMatrixCoefficients> for MatrixCoefficients {
1158 fn from(mtrx: Rav1dMatrixCoefficients) -> Self {
1159 match mtrx.0 {
1160 0 => Self::Identity,
1161 1 => Self::BT709,
1162 4 => Self::FCC,
1163 5 => Self::BT470BG,
1164 6 => Self::BT601,
1165 7 => Self::SMPTE240,
1166 8 => Self::YCgCo,
1167 9 => Self::BT2020NCL,
1168 10 => Self::BT2020CL,
1169 11 => Self::SMPTE2085,
1170 12 => Self::ChromaDerivedNCL,
1171 13 => Self::ChromaDerivedCL,
1172 14 => Self::ICtCp,
1173 _ => Self::Unspecified,
1174 }
1175 }
1176}
1177
1178#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1180pub enum ColorRange {
1181 Limited,
1183 Full,
1185}
1186
1187#[derive(Clone, Copy, Debug)]
1189pub struct ContentLightLevel {
1190 pub max_content_light_level: u16,
1192 pub max_frame_average_light_level: u16,
1194}
1195
1196#[derive(Clone, Copy, Debug)]
1198pub struct MasteringDisplay {
1199 pub primaries: [[u16; 2]; 3],
1202 pub white_point: [u16; 2],
1204 pub max_luminance: u32,
1206 pub min_luminance: u32,
1208}
1209
1210impl MasteringDisplay {
1211 pub fn max_luminance_nits(&self) -> f64 {
1213 self.max_luminance as f64 / 10000.0
1214 }
1215
1216 pub fn min_luminance_nits(&self) -> f64 {
1218 self.min_luminance as f64 / 10000.0
1219 }
1220
1221 pub fn primary_chromaticity(&self, index: usize) -> [f64; 2] {
1225 assert!(index < 3, "primary index must be 0-2");
1226 [
1227 self.primaries[index][0] as f64 / 50000.0,
1228 self.primaries[index][1] as f64 / 50000.0,
1229 ]
1230 }
1231
1232 pub fn white_point_chromaticity(&self) -> [f64; 2] {
1234 [
1235 self.white_point[0] as f64 / 50000.0,
1236 self.white_point[1] as f64 / 50000.0,
1237 ]
1238 }
1239}
1240
1241pub fn enabled_features() -> String {
1251 let mut features = Vec::new();
1252
1253 if cfg!(feature = "asm") {
1254 features.push("asm");
1255 }
1256 if cfg!(feature = "partial_asm") {
1257 features.push("partial_asm");
1258 }
1259 if cfg!(feature = "c-ffi") {
1260 features.push("c-ffi");
1261 }
1262 if cfg!(feature = "unchecked") {
1263 features.push("unchecked");
1264 }
1265 if cfg!(feature = "bitdepth_8") {
1266 features.push("bitdepth_8");
1267 }
1268 if cfg!(feature = "bitdepth_16") {
1269 features.push("bitdepth_16");
1270 }
1271
1272 if cfg!(feature = "asm") {
1274 features.push("safety:asm");
1275 } else if cfg!(feature = "partial_asm") {
1276 features.push("safety:partial-asm");
1277 } else if cfg!(feature = "c-ffi") {
1278 features.push("safety:c-ffi");
1279 } else if cfg!(feature = "unchecked") {
1280 features.push("safety:unchecked");
1281 } else {
1282 features.push("safety:forbid-unsafe");
1283 }
1284
1285 features.join(", ")
1286}