Skip to main content

ff_encode/video/codec_options/
dnxhd.rs

1//! Avid DNxHD / DNxHR per-codec encoding options.
2
3/// DNxHD / DNxHR encoding variant.
4///
5/// Legacy DNxHD variants (`Dnxhd*`) are constrained to 1920×1080 or 1280×720
6/// and require a fixed bitrate. DNxHR variants (`Dnxhr*`) work at any resolution.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
8pub enum DnxhdVariant {
9    // ── DNxHD (legacy fixed-bitrate, 1920×1080 or 1280×720 only) ─────────────
10    /// 1080i/p 115 Mbps, 8-bit yuv422p.
11    Dnxhd115,
12    /// 1080i/p 145 Mbps, 8-bit yuv422p.
13    Dnxhd145,
14    /// 1080p 220 Mbps, 8-bit yuv422p.
15    Dnxhd220,
16    /// 1080p 220 Mbps, 10-bit yuv422p10le.
17    Dnxhd220x,
18    // ── DNxHR (resolution-agnostic) ───────────────────────────────────────────
19    /// Low Bandwidth, 8-bit yuv422p.
20    DnxhrLb,
21    /// Standard Quality, 8-bit yuv422p (default).
22    #[default]
23    DnxhrSq,
24    /// High Quality, 8-bit yuv422p.
25    DnxhrHq,
26    /// High Quality 10-bit, yuv422p10le.
27    DnxhrHqx,
28    /// 4:4:4 12-bit, yuv444p10le.
29    DnxhrR444,
30}
31
32impl DnxhdVariant {
33    /// Returns the `vprofile` string passed to the `dnxhd` encoder.
34    pub(in crate::video) fn vprofile_str(self) -> &'static str {
35        match self {
36            Self::Dnxhd115 | Self::Dnxhd145 | Self::Dnxhd220 | Self::Dnxhd220x => "dnxhd",
37            Self::DnxhrLb => "dnxhr_lb",
38            Self::DnxhrSq => "dnxhr_sq",
39            Self::DnxhrHq => "dnxhr_hq",
40            Self::DnxhrHqx => "dnxhr_hqx",
41            Self::DnxhrR444 => "dnxhr_444",
42        }
43    }
44
45    /// Returns the required pixel format for this variant.
46    pub(in crate::video) fn pixel_format(self) -> ff_format::PixelFormat {
47        use ff_format::PixelFormat;
48        match self {
49            Self::Dnxhd115
50            | Self::Dnxhd145
51            | Self::Dnxhd220
52            | Self::DnxhrLb
53            | Self::DnxhrSq
54            | Self::DnxhrHq => PixelFormat::Yuv422p,
55            Self::Dnxhd220x | Self::DnxhrHqx => PixelFormat::Yuv422p10le,
56            Self::DnxhrR444 => PixelFormat::Yuv444p10le,
57        }
58    }
59
60    /// For legacy DNxHD variants, returns the required fixed bitrate in bps.
61    ///
62    /// DNxHR variants return `None` — the encoder selects the bitrate automatically.
63    pub(in crate::video) fn fixed_bitrate_bps(self) -> Option<i64> {
64        match self {
65            Self::Dnxhd115 => Some(115_000_000),
66            Self::Dnxhd145 => Some(145_000_000),
67            Self::Dnxhd220 | Self::Dnxhd220x => Some(220_000_000),
68            _ => None,
69        }
70    }
71
72    /// Returns `true` for legacy DNxHD variants that require 1920×1080 or 1280×720.
73    pub(in crate::video) fn is_dnxhd(self) -> bool {
74        matches!(
75            self,
76            Self::Dnxhd115 | Self::Dnxhd145 | Self::Dnxhd220 | Self::Dnxhd220x
77        )
78    }
79}
80
81/// Avid DNxHD / DNxHR per-codec options.
82///
83/// Output should use a `.mxf` or `.mov` container. Legacy DNxHD variants
84/// (`Dnxhd*`) are validated in `build()` to require 1920×1080 or 1280×720.
85#[derive(Debug, Clone, Default)]
86pub struct DnxhdOptions {
87    /// DNxHD/DNxHR encoding variant.
88    pub variant: DnxhdVariant,
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[test]
96    fn dnxhd_options_default_should_have_dnxhr_sq_variant() {
97        let opts = DnxhdOptions::default();
98        assert_eq!(opts.variant, DnxhdVariant::DnxhrSq);
99    }
100
101    #[test]
102    fn dnxhd_variant_vprofile_str_should_match_spec() {
103        assert_eq!(DnxhdVariant::Dnxhd115.vprofile_str(), "dnxhd");
104        assert_eq!(DnxhdVariant::Dnxhd145.vprofile_str(), "dnxhd");
105        assert_eq!(DnxhdVariant::Dnxhd220.vprofile_str(), "dnxhd");
106        assert_eq!(DnxhdVariant::Dnxhd220x.vprofile_str(), "dnxhd");
107        assert_eq!(DnxhdVariant::DnxhrLb.vprofile_str(), "dnxhr_lb");
108        assert_eq!(DnxhdVariant::DnxhrSq.vprofile_str(), "dnxhr_sq");
109        assert_eq!(DnxhdVariant::DnxhrHq.vprofile_str(), "dnxhr_hq");
110        assert_eq!(DnxhdVariant::DnxhrHqx.vprofile_str(), "dnxhr_hqx");
111        assert_eq!(DnxhdVariant::DnxhrR444.vprofile_str(), "dnxhr_444");
112    }
113
114    #[test]
115    fn dnxhd_variant_pixel_format_should_match_spec() {
116        use ff_format::PixelFormat;
117        assert_eq!(DnxhdVariant::Dnxhd115.pixel_format(), PixelFormat::Yuv422p);
118        assert_eq!(DnxhdVariant::Dnxhd145.pixel_format(), PixelFormat::Yuv422p);
119        assert_eq!(DnxhdVariant::Dnxhd220.pixel_format(), PixelFormat::Yuv422p);
120        assert_eq!(
121            DnxhdVariant::Dnxhd220x.pixel_format(),
122            PixelFormat::Yuv422p10le
123        );
124        assert_eq!(DnxhdVariant::DnxhrLb.pixel_format(), PixelFormat::Yuv422p);
125        assert_eq!(DnxhdVariant::DnxhrSq.pixel_format(), PixelFormat::Yuv422p);
126        assert_eq!(DnxhdVariant::DnxhrHq.pixel_format(), PixelFormat::Yuv422p);
127        assert_eq!(
128            DnxhdVariant::DnxhrHqx.pixel_format(),
129            PixelFormat::Yuv422p10le
130        );
131        assert_eq!(
132            DnxhdVariant::DnxhrR444.pixel_format(),
133            PixelFormat::Yuv444p10le
134        );
135    }
136
137    #[test]
138    fn dnxhd_variant_fixed_bitrate_should_return_none_for_dnxhr() {
139        assert_eq!(
140            DnxhdVariant::Dnxhd115.fixed_bitrate_bps(),
141            Some(115_000_000)
142        );
143        assert_eq!(
144            DnxhdVariant::Dnxhd145.fixed_bitrate_bps(),
145            Some(145_000_000)
146        );
147        assert_eq!(
148            DnxhdVariant::Dnxhd220.fixed_bitrate_bps(),
149            Some(220_000_000)
150        );
151        assert_eq!(
152            DnxhdVariant::Dnxhd220x.fixed_bitrate_bps(),
153            Some(220_000_000)
154        );
155        assert!(DnxhdVariant::DnxhrLb.fixed_bitrate_bps().is_none());
156        assert!(DnxhdVariant::DnxhrSq.fixed_bitrate_bps().is_none());
157        assert!(DnxhdVariant::DnxhrHq.fixed_bitrate_bps().is_none());
158        assert!(DnxhdVariant::DnxhrHqx.fixed_bitrate_bps().is_none());
159        assert!(DnxhdVariant::DnxhrR444.fixed_bitrate_bps().is_none());
160    }
161
162    #[test]
163    fn dnxhd_variant_is_dnxhd_should_return_true_only_for_legacy_variants() {
164        assert!(DnxhdVariant::Dnxhd115.is_dnxhd());
165        assert!(DnxhdVariant::Dnxhd145.is_dnxhd());
166        assert!(DnxhdVariant::Dnxhd220.is_dnxhd());
167        assert!(DnxhdVariant::Dnxhd220x.is_dnxhd());
168        assert!(!DnxhdVariant::DnxhrLb.is_dnxhd());
169        assert!(!DnxhdVariant::DnxhrSq.is_dnxhd());
170        assert!(!DnxhdVariant::DnxhrHq.is_dnxhd());
171        assert!(!DnxhdVariant::DnxhrHqx.is_dnxhd());
172        assert!(!DnxhdVariant::DnxhrR444.is_dnxhd());
173    }
174}