1#[derive(Debug, Clone)]
18pub enum VideoCodecOptions {
19 H264(H264Options),
21 H265(H265Options),
23 Av1(Av1Options),
25 Av1Svt(SvtAv1Options),
27 Vp9(Vp9Options),
29 ProRes(ProResOptions),
31 Dnxhd(DnxhdOptions),
33}
34
35#[derive(Debug, Clone)]
39pub struct H264Options {
40 pub profile: H264Profile,
42 pub level: Option<u32>,
46 pub bframes: u32,
48 pub gop_size: u32,
50 pub refs: u32,
52 pub preset: Option<H264Preset>,
59 pub tune: Option<H264Tune>,
64}
65
66impl Default for H264Options {
67 fn default() -> Self {
68 Self {
69 profile: H264Profile::High,
70 level: None,
71 bframes: 2,
72 gop_size: 250,
73 refs: 3,
74 preset: None,
75 tune: None,
76 }
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
82pub enum H264Profile {
83 Baseline,
85 Main,
87 #[default]
89 High,
90 High10,
92}
93
94impl H264Profile {
95 pub(super) fn as_str(self) -> &'static str {
96 match self {
97 Self::Baseline => "baseline",
98 Self::Main => "main",
99 Self::High => "high",
100 Self::High10 => "high10",
101 }
102 }
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub enum H264Preset {
112 Ultrafast,
114 Superfast,
116 Veryfast,
118 Faster,
120 Fast,
122 Medium,
124 Slow,
126 Slower,
128 Veryslow,
130 Placebo,
132}
133
134impl H264Preset {
135 pub(super) fn as_str(self) -> &'static str {
136 match self {
137 Self::Ultrafast => "ultrafast",
138 Self::Superfast => "superfast",
139 Self::Veryfast => "veryfast",
140 Self::Faster => "faster",
141 Self::Fast => "fast",
142 Self::Medium => "medium",
143 Self::Slow => "slow",
144 Self::Slower => "slower",
145 Self::Veryslow => "veryslow",
146 Self::Placebo => "placebo",
147 }
148 }
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub enum H264Tune {
158 Film,
160 Animation,
162 Grain,
164 Stillimage,
166 Psnr,
168 Ssim,
170 Fastdecode,
172 Zerolatency,
174}
175
176impl H264Tune {
177 pub(super) fn as_str(self) -> &'static str {
178 match self {
179 Self::Film => "film",
180 Self::Animation => "animation",
181 Self::Grain => "grain",
182 Self::Stillimage => "stillimage",
183 Self::Psnr => "psnr",
184 Self::Ssim => "ssim",
185 Self::Fastdecode => "fastdecode",
186 Self::Zerolatency => "zerolatency",
187 }
188 }
189}
190
191#[derive(Debug, Clone)]
195pub struct H265Options {
196 pub profile: H265Profile,
198 pub tier: H265Tier,
200 pub level: Option<u32>,
204 pub preset: Option<String>,
210 pub x265_params: Option<String>,
217}
218
219impl Default for H265Options {
220 fn default() -> Self {
221 Self {
222 profile: H265Profile::Main,
223 tier: H265Tier::Main,
224 level: None,
225 preset: None,
226 x265_params: None,
227 }
228 }
229}
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
233pub enum H265Profile {
234 #[default]
236 Main,
237 Main10,
239}
240
241impl H265Profile {
242 pub(super) fn as_str(self) -> &'static str {
243 match self {
244 Self::Main => "main",
245 Self::Main10 => "main10",
246 }
247 }
248}
249
250#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
252pub enum H265Tier {
253 #[default]
255 Main,
256 High,
258}
259
260impl H265Tier {
261 pub(super) fn as_str(self) -> &'static str {
262 match self {
263 Self::Main => "main",
264 Self::High => "high",
265 }
266 }
267}
268
269#[derive(Debug, Clone)]
273pub struct Av1Options {
274 pub cpu_used: u8,
276 pub tile_rows: u8,
278 pub tile_cols: u8,
280 pub usage: Av1Usage,
282}
283
284impl Default for Av1Options {
285 fn default() -> Self {
286 Self {
287 cpu_used: 4,
288 tile_rows: 0,
289 tile_cols: 0,
290 usage: Av1Usage::VoD,
291 }
292 }
293}
294
295#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
297pub enum Av1Usage {
298 #[default]
300 VoD,
301 RealTime,
303}
304
305impl Av1Usage {
306 pub(super) fn as_str(self) -> &'static str {
307 match self {
308 Self::VoD => "vod",
309 Self::RealTime => "realtime",
310 }
311 }
312}
313
314#[derive(Debug, Clone)]
322pub struct SvtAv1Options {
323 pub preset: u8,
325 pub tile_rows: u8,
327 pub tile_cols: u8,
329 pub svtav1_params: Option<String>,
334}
335
336impl Default for SvtAv1Options {
337 fn default() -> Self {
338 Self {
339 preset: 8,
340 tile_rows: 0,
341 tile_cols: 0,
342 svtav1_params: None,
343 }
344 }
345}
346
347#[derive(Debug, Clone, Default)]
351pub struct Vp9Options {
352 pub cpu_used: i8,
354 pub cq_level: Option<u8>,
360 pub tile_columns: u8,
362 pub tile_rows: u8,
364 pub row_mt: bool,
366}
367
368#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
375pub enum ProResProfile {
376 Proxy,
378 Lt,
380 #[default]
382 Standard,
383 Hq,
385 P4444,
387 P4444Xq,
389}
390
391impl ProResProfile {
392 pub(super) fn profile_id(self) -> u8 {
394 match self {
395 Self::Proxy => 0,
396 Self::Lt => 1,
397 Self::Standard => 2,
398 Self::Hq => 3,
399 Self::P4444 => 4,
400 Self::P4444Xq => 5,
401 }
402 }
403
404 pub(super) fn is_4444(self) -> bool {
406 matches!(self, Self::P4444 | Self::P4444Xq)
407 }
408}
409
410#[derive(Debug, Clone)]
415pub struct ProResOptions {
416 pub profile: ProResProfile,
418 pub vendor: Option<[u8; 4]>,
423}
424
425impl Default for ProResOptions {
426 fn default() -> Self {
427 Self {
428 profile: ProResProfile::Standard,
429 vendor: None,
430 }
431 }
432}
433
434#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
441pub enum DnxhdVariant {
442 Dnxhd115,
445 Dnxhd145,
447 Dnxhd220,
449 Dnxhd220x,
451 DnxhrLb,
454 #[default]
456 DnxhrSq,
457 DnxhrHq,
459 DnxhrHqx,
461 DnxhrR444,
463}
464
465impl DnxhdVariant {
466 pub(super) fn vprofile_str(self) -> &'static str {
468 match self {
469 Self::Dnxhd115 | Self::Dnxhd145 | Self::Dnxhd220 | Self::Dnxhd220x => "dnxhd",
470 Self::DnxhrLb => "dnxhr_lb",
471 Self::DnxhrSq => "dnxhr_sq",
472 Self::DnxhrHq => "dnxhr_hq",
473 Self::DnxhrHqx => "dnxhr_hqx",
474 Self::DnxhrR444 => "dnxhr_444",
475 }
476 }
477
478 pub(super) fn pixel_format(self) -> ff_format::PixelFormat {
480 use ff_format::PixelFormat;
481 match self {
482 Self::Dnxhd115
483 | Self::Dnxhd145
484 | Self::Dnxhd220
485 | Self::DnxhrLb
486 | Self::DnxhrSq
487 | Self::DnxhrHq => PixelFormat::Yuv422p,
488 Self::Dnxhd220x | Self::DnxhrHqx => PixelFormat::Yuv422p10le,
489 Self::DnxhrR444 => PixelFormat::Yuv444p10le,
490 }
491 }
492
493 pub(super) fn fixed_bitrate_bps(self) -> Option<i64> {
497 match self {
498 Self::Dnxhd115 => Some(115_000_000),
499 Self::Dnxhd145 => Some(145_000_000),
500 Self::Dnxhd220 | Self::Dnxhd220x => Some(220_000_000),
501 _ => None,
502 }
503 }
504
505 pub(super) fn is_dnxhd(self) -> bool {
507 matches!(
508 self,
509 Self::Dnxhd115 | Self::Dnxhd145 | Self::Dnxhd220 | Self::Dnxhd220x
510 )
511 }
512}
513
514#[derive(Debug, Clone, Default)]
519pub struct DnxhdOptions {
520 pub variant: DnxhdVariant,
522}
523
524#[cfg(test)]
527mod tests {
528 use super::*;
529
530 #[test]
531 fn h264_profile_should_return_correct_str() {
532 assert_eq!(H264Profile::Baseline.as_str(), "baseline");
533 assert_eq!(H264Profile::Main.as_str(), "main");
534 assert_eq!(H264Profile::High.as_str(), "high");
535 assert_eq!(H264Profile::High10.as_str(), "high10");
536 }
537
538 #[test]
539 fn h265_profile_should_return_correct_str() {
540 assert_eq!(H265Profile::Main.as_str(), "main");
541 assert_eq!(H265Profile::Main10.as_str(), "main10");
542 }
543
544 #[test]
545 fn h265_tier_should_return_correct_str() {
546 assert_eq!(H265Tier::Main.as_str(), "main");
547 assert_eq!(H265Tier::High.as_str(), "high");
548 }
549
550 #[test]
551 fn av1_usage_should_return_correct_str() {
552 assert_eq!(Av1Usage::VoD.as_str(), "vod");
553 assert_eq!(Av1Usage::RealTime.as_str(), "realtime");
554 }
555
556 #[test]
557 fn h264_options_default_should_have_high_profile() {
558 let opts = H264Options::default();
559 assert_eq!(opts.profile, H264Profile::High);
560 assert_eq!(opts.level, None);
561 assert_eq!(opts.bframes, 2);
562 assert_eq!(opts.gop_size, 250);
563 assert_eq!(opts.refs, 3);
564 assert!(opts.preset.is_none());
565 assert!(opts.tune.is_none());
566 }
567
568 #[test]
569 fn h264_preset_should_return_correct_str() {
570 assert_eq!(H264Preset::Ultrafast.as_str(), "ultrafast");
571 assert_eq!(H264Preset::Superfast.as_str(), "superfast");
572 assert_eq!(H264Preset::Veryfast.as_str(), "veryfast");
573 assert_eq!(H264Preset::Faster.as_str(), "faster");
574 assert_eq!(H264Preset::Fast.as_str(), "fast");
575 assert_eq!(H264Preset::Medium.as_str(), "medium");
576 assert_eq!(H264Preset::Slow.as_str(), "slow");
577 assert_eq!(H264Preset::Slower.as_str(), "slower");
578 assert_eq!(H264Preset::Veryslow.as_str(), "veryslow");
579 assert_eq!(H264Preset::Placebo.as_str(), "placebo");
580 }
581
582 #[test]
583 fn h264_tune_should_return_correct_str() {
584 assert_eq!(H264Tune::Film.as_str(), "film");
585 assert_eq!(H264Tune::Animation.as_str(), "animation");
586 assert_eq!(H264Tune::Grain.as_str(), "grain");
587 assert_eq!(H264Tune::Stillimage.as_str(), "stillimage");
588 assert_eq!(H264Tune::Psnr.as_str(), "psnr");
589 assert_eq!(H264Tune::Ssim.as_str(), "ssim");
590 assert_eq!(H264Tune::Fastdecode.as_str(), "fastdecode");
591 assert_eq!(H264Tune::Zerolatency.as_str(), "zerolatency");
592 }
593
594 #[test]
595 fn h265_options_default_should_have_main_profile() {
596 let opts = H265Options::default();
597 assert_eq!(opts.profile, H265Profile::Main);
598 assert_eq!(opts.tier, H265Tier::Main);
599 assert_eq!(opts.level, None);
600 assert!(opts.preset.is_none());
601 assert!(opts.x265_params.is_none());
602 }
603
604 #[test]
605 fn h265_preset_should_be_none_by_default() {
606 let opts = H265Options::default();
607 assert!(opts.preset.is_none());
608 }
609
610 #[test]
611 fn h265_x265_params_should_be_none_by_default() {
612 let opts = H265Options::default();
613 assert!(opts.x265_params.is_none());
614 }
615
616 #[test]
617 fn av1_options_default_should_have_vod_usage() {
618 let opts = Av1Options::default();
619 assert_eq!(opts.cpu_used, 4);
620 assert_eq!(opts.tile_rows, 0);
621 assert_eq!(opts.tile_cols, 0);
622 assert_eq!(opts.usage, Av1Usage::VoD);
623 }
624
625 #[test]
626 fn video_codec_options_enum_variants_are_accessible() {
627 let _h264 = VideoCodecOptions::H264(H264Options::default());
628 let _h265 = VideoCodecOptions::H265(H265Options::default());
629 let _av1 = VideoCodecOptions::Av1(Av1Options::default());
630 let _av1svt = VideoCodecOptions::Av1Svt(SvtAv1Options::default());
631 let _vp9 = VideoCodecOptions::Vp9(Vp9Options::default());
632 let _prores = VideoCodecOptions::ProRes(ProResOptions::default());
633 let _dnxhd = VideoCodecOptions::Dnxhd(DnxhdOptions::default());
634 }
635
636 #[test]
637 fn vp9_options_default_should_have_cpu_used_0() {
638 let opts = Vp9Options::default();
639 assert_eq!(opts.cpu_used, 0);
640 assert_eq!(opts.tile_columns, 0);
641 assert_eq!(opts.tile_rows, 0);
642 assert!(!opts.row_mt);
643 }
644
645 #[test]
646 fn vp9_options_default_should_have_no_cq_level() {
647 let opts = Vp9Options::default();
648 assert!(opts.cq_level.is_none());
649 }
650
651 #[test]
652 fn svtav1_options_default_should_have_preset_8() {
653 let opts = SvtAv1Options::default();
654 assert_eq!(opts.preset, 8);
655 assert_eq!(opts.tile_rows, 0);
656 assert_eq!(opts.tile_cols, 0);
657 assert!(opts.svtav1_params.is_none());
658 }
659
660 #[test]
661 fn prores_options_default_should_have_standard_profile() {
662 let opts = ProResOptions::default();
663 assert_eq!(opts.profile, ProResProfile::Standard);
664 assert!(opts.vendor.is_none());
665 }
666
667 #[test]
668 fn prores_profile_ids_should_match_spec() {
669 assert_eq!(ProResProfile::Proxy.profile_id(), 0);
670 assert_eq!(ProResProfile::Lt.profile_id(), 1);
671 assert_eq!(ProResProfile::Standard.profile_id(), 2);
672 assert_eq!(ProResProfile::Hq.profile_id(), 3);
673 assert_eq!(ProResProfile::P4444.profile_id(), 4);
674 assert_eq!(ProResProfile::P4444Xq.profile_id(), 5);
675 }
676
677 #[test]
678 fn prores_profile_is_4444_should_return_true_for_4444_variants() {
679 assert!(!ProResProfile::Proxy.is_4444());
680 assert!(!ProResProfile::Lt.is_4444());
681 assert!(!ProResProfile::Standard.is_4444());
682 assert!(!ProResProfile::Hq.is_4444());
683 assert!(ProResProfile::P4444.is_4444());
684 assert!(ProResProfile::P4444Xq.is_4444());
685 }
686
687 #[test]
688 fn dnxhd_options_default_should_have_dnxhr_sq_variant() {
689 let opts = DnxhdOptions::default();
690 assert_eq!(opts.variant, DnxhdVariant::DnxhrSq);
691 }
692
693 #[test]
694 fn dnxhd_variant_vprofile_str_should_match_spec() {
695 assert_eq!(DnxhdVariant::Dnxhd115.vprofile_str(), "dnxhd");
696 assert_eq!(DnxhdVariant::Dnxhd145.vprofile_str(), "dnxhd");
697 assert_eq!(DnxhdVariant::Dnxhd220.vprofile_str(), "dnxhd");
698 assert_eq!(DnxhdVariant::Dnxhd220x.vprofile_str(), "dnxhd");
699 assert_eq!(DnxhdVariant::DnxhrLb.vprofile_str(), "dnxhr_lb");
700 assert_eq!(DnxhdVariant::DnxhrSq.vprofile_str(), "dnxhr_sq");
701 assert_eq!(DnxhdVariant::DnxhrHq.vprofile_str(), "dnxhr_hq");
702 assert_eq!(DnxhdVariant::DnxhrHqx.vprofile_str(), "dnxhr_hqx");
703 assert_eq!(DnxhdVariant::DnxhrR444.vprofile_str(), "dnxhr_444");
704 }
705
706 #[test]
707 fn dnxhd_variant_pixel_format_should_match_spec() {
708 use ff_format::PixelFormat;
709 assert_eq!(DnxhdVariant::Dnxhd115.pixel_format(), PixelFormat::Yuv422p);
710 assert_eq!(DnxhdVariant::Dnxhd145.pixel_format(), PixelFormat::Yuv422p);
711 assert_eq!(DnxhdVariant::Dnxhd220.pixel_format(), PixelFormat::Yuv422p);
712 assert_eq!(
713 DnxhdVariant::Dnxhd220x.pixel_format(),
714 PixelFormat::Yuv422p10le
715 );
716 assert_eq!(DnxhdVariant::DnxhrLb.pixel_format(), PixelFormat::Yuv422p);
717 assert_eq!(DnxhdVariant::DnxhrSq.pixel_format(), PixelFormat::Yuv422p);
718 assert_eq!(DnxhdVariant::DnxhrHq.pixel_format(), PixelFormat::Yuv422p);
719 assert_eq!(
720 DnxhdVariant::DnxhrHqx.pixel_format(),
721 PixelFormat::Yuv422p10le
722 );
723 assert_eq!(
724 DnxhdVariant::DnxhrR444.pixel_format(),
725 PixelFormat::Yuv444p10le
726 );
727 }
728
729 #[test]
730 fn dnxhd_variant_fixed_bitrate_should_return_none_for_dnxhr() {
731 assert_eq!(
732 DnxhdVariant::Dnxhd115.fixed_bitrate_bps(),
733 Some(115_000_000)
734 );
735 assert_eq!(
736 DnxhdVariant::Dnxhd145.fixed_bitrate_bps(),
737 Some(145_000_000)
738 );
739 assert_eq!(
740 DnxhdVariant::Dnxhd220.fixed_bitrate_bps(),
741 Some(220_000_000)
742 );
743 assert_eq!(
744 DnxhdVariant::Dnxhd220x.fixed_bitrate_bps(),
745 Some(220_000_000)
746 );
747 assert!(DnxhdVariant::DnxhrLb.fixed_bitrate_bps().is_none());
748 assert!(DnxhdVariant::DnxhrSq.fixed_bitrate_bps().is_none());
749 assert!(DnxhdVariant::DnxhrHq.fixed_bitrate_bps().is_none());
750 assert!(DnxhdVariant::DnxhrHqx.fixed_bitrate_bps().is_none());
751 assert!(DnxhdVariant::DnxhrR444.fixed_bitrate_bps().is_none());
752 }
753
754 #[test]
755 fn dnxhd_variant_is_dnxhd_should_return_true_only_for_legacy_variants() {
756 assert!(DnxhdVariant::Dnxhd115.is_dnxhd());
757 assert!(DnxhdVariant::Dnxhd145.is_dnxhd());
758 assert!(DnxhdVariant::Dnxhd220.is_dnxhd());
759 assert!(DnxhdVariant::Dnxhd220x.is_dnxhd());
760 assert!(!DnxhdVariant::DnxhrLb.is_dnxhd());
761 assert!(!DnxhdVariant::DnxhrSq.is_dnxhd());
762 assert!(!DnxhdVariant::DnxhrHq.is_dnxhd());
763 assert!(!DnxhdVariant::DnxhrHqx.is_dnxhd());
764 assert!(!DnxhdVariant::DnxhrR444.is_dnxhd());
765 }
766}