Skip to main content

mediadecode_ffmpeg/
backend.rs

1use derive_more::IsVariant;
2use ffmpeg_next::ffi::{AVHWDeviceType, AVPixelFormat};
3
4/// Hardware decoding backend.
5///
6/// `hwdecode` only manages **hardware** decoders — software fallback is
7/// out of scope. If no backend in [`probe_order`] for the current platform
8/// can decode a stream, [`crate::VideoDecoder::open`] returns
9/// [`crate::Error::AllBackendsFailed`] and the caller decides how to fall
10/// back (e.g. by opening an `ffmpeg::decoder::Video` directly).
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IsVariant)]
12pub enum Backend {
13  /// Apple VideoToolbox (macOS, iOS, iPadOS, tvOS, visionOS).
14  VideoToolbox,
15  /// Linux Video Acceleration API (Intel / AMD GPUs).
16  Vaapi,
17  /// NVIDIA NVDEC via CUDA (Linux / Windows on NVIDIA hardware).
18  Cuda,
19  /// Microsoft Direct3D 11 Video Acceleration (Windows).
20  D3d11va,
21}
22
23impl Backend {
24  /// `AVHWDeviceType` corresponding to this backend.
25  pub(crate) fn av_hwdevice_type(self) -> AVHWDeviceType {
26    match self {
27      Self::VideoToolbox => AVHWDeviceType::AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
28      Self::Vaapi => AVHWDeviceType::AV_HWDEVICE_TYPE_VAAPI,
29      Self::Cuda => AVHWDeviceType::AV_HWDEVICE_TYPE_CUDA,
30      Self::D3d11va => AVHWDeviceType::AV_HWDEVICE_TYPE_D3D11VA,
31    }
32  }
33
34  /// Hardware pixel format the codec is expected to produce when this
35  /// backend is in use. (The post-`av_hwframe_transfer_data` CPU format is
36  /// typically `NV12` or `P010LE`; this is the *pre-transfer* sentinel.)
37  ///
38  /// Returns a `AVPixelFormat` value constructed from a hardcoded constant
39  /// in our bindings — never reads an enum value supplied by FFmpeg, so
40  /// no enum-discriminant UB risk.
41  pub(crate) fn hw_pixel_format(self) -> AVPixelFormat {
42    match self {
43      Self::VideoToolbox => AVPixelFormat::AV_PIX_FMT_VIDEOTOOLBOX,
44      Self::Vaapi => AVPixelFormat::AV_PIX_FMT_VAAPI,
45      Self::Cuda => AVPixelFormat::AV_PIX_FMT_CUDA,
46      Self::D3d11va => AVPixelFormat::AV_PIX_FMT_D3D11,
47    }
48  }
49}
50
51/// Probe order for `VideoDecoder::open` on the current target. Hardware
52/// backends only, in preference order. Empty for platforms with no known
53/// HW backend; on those `open()` returns `AllBackendsFailed` immediately.
54pub(crate) fn probe_order() -> &'static [Backend] {
55  #[cfg(target_vendor = "apple")]
56  {
57    &[Backend::VideoToolbox]
58  }
59  #[cfg(target_os = "linux")]
60  {
61    &[Backend::Vaapi, Backend::Cuda]
62  }
63  #[cfg(target_os = "windows")]
64  {
65    &[Backend::D3d11va, Backend::Cuda]
66  }
67  #[cfg(not(any(target_vendor = "apple", target_os = "linux", target_os = "windows",)))]
68  {
69    &[]
70  }
71}
72
73#[cfg(test)]
74mod tests {
75  use super::*;
76
77  #[test]
78  fn all_backends_have_hwdevice_type_and_pix_fmt() {
79    for b in [
80      Backend::VideoToolbox,
81      Backend::Vaapi,
82      Backend::Cuda,
83      Backend::D3d11va,
84    ] {
85      let _ = b.av_hwdevice_type();
86      let _ = b.hw_pixel_format();
87    }
88  }
89
90  #[cfg(any(
91    target_os = "macos",
92    target_os = "ios",
93    target_os = "tvos",
94    target_os = "visionos",
95  ))]
96  #[test]
97  fn apple_probe_order() {
98    assert_eq!(probe_order(), &[Backend::VideoToolbox]);
99  }
100
101  #[cfg(target_os = "linux")]
102  #[test]
103  fn linux_probe_order() {
104    assert_eq!(probe_order(), &[Backend::Vaapi, Backend::Cuda]);
105  }
106
107  #[cfg(target_os = "windows")]
108  #[test]
109  fn windows_probe_order() {
110    assert_eq!(probe_order(), &[Backend::D3d11va, Backend::Cuda]);
111  }
112}