1#[cfg(feature = "cuda")]
2pub mod cuda;
3#[cfg(feature = "metal")]
4pub mod metal;
5#[cfg(feature = "vulkan")]
6pub mod vulkan;
7
8#[cfg(feature = "cuda")]
9pub use cuda::CudaDecodedFrame;
10#[cfg(all(feature = "cuda", target_os = "linux"))]
11pub use cuda::{
12 CudaContext, CudaDeviceAllocation, CudaDriver, CudaNv12ToRgbaConverter,
13 ImportedCudaExternalImage, import_owned_vulkan_opaque_fd_image, import_vulkan_opaque_fd_image,
14};
15#[cfg(feature = "metal")]
16pub use metal::{
17 MetalDecodedFrame, MetalPixelBufferFrame, MetalPixelBufferPool, MetalTextureCache,
18 Objc2MetalDevice, Objc2MetalTexture,
19};
20#[cfg(feature = "vulkan")]
21pub use vulkan::VulkanVideoFrame;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum GpuBackend {
25 Cuda,
26 Metal,
27 Vulkan,
28}
29
30#[derive(Debug)]
31pub enum GpuVideoInput<'a> {
32 #[cfg(feature = "cuda")]
33 Cuda(&'a CudaVideoFrame),
34 #[cfg(feature = "metal")]
35 Metal(&'a Objc2MetalTexture),
36 #[cfg(feature = "metal")]
37 MetalPixelBuffer(&'a MetalPixelBufferFrame),
38 #[cfg(feature = "vulkan")]
39 Vulkan(&'a VulkanVideoFrame),
40 #[doc(hidden)]
41 __NonExhaustive(std::marker::PhantomData<&'a ()>),
42}
43
44impl<'a> GpuVideoInput<'a> {
45 pub fn backend(&self) -> GpuBackend {
46 match self {
47 #[cfg(feature = "cuda")]
48 Self::Cuda(_) => GpuBackend::Cuda,
49 #[cfg(feature = "metal")]
50 Self::Metal(_) | Self::MetalPixelBuffer(_) => GpuBackend::Metal,
51 #[cfg(feature = "vulkan")]
52 Self::Vulkan(_) => GpuBackend::Vulkan,
53 Self::__NonExhaustive(_) => unreachable!("hidden GPU frame variant"),
54 }
55 }
56
57 pub fn dimensions(&self) -> (u32, u32) {
58 match self {
59 #[cfg(feature = "cuda")]
60 Self::Cuda(frame) => frame.dimensions(),
61 #[cfg(feature = "metal")]
62 Self::Metal(texture) => {
63 use objc2_metal::MTLTexture;
64
65 (texture.width() as u32, texture.height() as u32)
66 }
67 #[cfg(feature = "metal")]
68 Self::MetalPixelBuffer(frame) => frame.dimensions(),
69 #[cfg(feature = "vulkan")]
70 Self::Vulkan(frame) => frame.dimensions(),
71 Self::__NonExhaustive(_) => unreachable!("hidden GPU frame variant"),
72 }
73 }
74
75 pub fn pts(&self) -> Option<i64> {
76 match self {
77 #[cfg(feature = "cuda")]
78 Self::Cuda(frame) => frame.pts(),
79 #[cfg(feature = "metal")]
80 Self::Metal(_) => None,
81 #[cfg(feature = "metal")]
82 Self::MetalPixelBuffer(frame) => frame.pts(),
83 #[cfg(feature = "vulkan")]
84 Self::Vulkan(frame) => frame.pts(),
85 Self::__NonExhaustive(_) => unreachable!("hidden GPU frame variant"),
86 }
87 }
88
89 pub fn estimated_rgba_bytes(&self) -> u64 {
90 let (width, height) = self.dimensions();
91 u64::from(width)
92 .saturating_mul(u64::from(height))
93 .saturating_mul(4)
94 }
95}
96
97#[derive(Debug)]
98#[non_exhaustive]
99pub enum GpuVideoFrame {
100 #[cfg(feature = "cuda")]
101 Cuda(CudaDecodedFrame),
102 #[cfg(feature = "metal")]
103 Metal(MetalDecodedFrame),
104 #[cfg(feature = "vulkan")]
105 Vulkan(VulkanVideoFrame),
106 #[doc(hidden)]
107 __NonExhaustive,
108}
109
110impl GpuVideoFrame {
111 pub fn backend(&self) -> GpuBackend {
112 match self {
113 #[cfg(feature = "cuda")]
114 Self::Cuda(_) => GpuBackend::Cuda,
115 #[cfg(feature = "metal")]
116 Self::Metal(_) => GpuBackend::Metal,
117 #[cfg(feature = "vulkan")]
118 Self::Vulkan(_) => GpuBackend::Vulkan,
119 Self::__NonExhaustive => unreachable!("hidden GPU frame variant"),
120 }
121 }
122
123 pub fn dimensions(&self) -> (u32, u32) {
124 match self {
125 #[cfg(feature = "cuda")]
126 Self::Cuda(frame) => frame.dimensions(),
127 #[cfg(feature = "metal")]
128 Self::Metal(frame) => frame.dimensions(),
129 #[cfg(feature = "vulkan")]
130 Self::Vulkan(frame) => frame.dimensions(),
131 Self::__NonExhaustive => unreachable!("hidden GPU frame variant"),
132 }
133 }
134
135 pub fn pts(&self) -> Option<i64> {
136 match self {
137 #[cfg(feature = "cuda")]
138 Self::Cuda(frame) => frame.pts(),
139 #[cfg(feature = "metal")]
140 Self::Metal(frame) => frame.pts(),
141 #[cfg(feature = "vulkan")]
142 Self::Vulkan(frame) => frame.pts(),
143 Self::__NonExhaustive => unreachable!("hidden GPU frame variant"),
144 }
145 }
146
147 pub fn estimated_rgba_bytes(&self) -> u64 {
148 let (width, height) = self.dimensions();
149 u64::from(width)
150 .saturating_mul(u64::from(height))
151 .saturating_mul(4)
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 #[cfg(feature = "cuda")]
158 #[test]
159 fn cuda_video_frame_reports_shape() {
160 use super::*;
161
162 let frame = CudaVideoFrame::from_device_ptr(0xCAFE, 1920, 1080, 8192, Some(7));
163 let input = GpuVideoInput::Cuda(&frame);
164
165 assert_eq!(input.backend(), GpuBackend::Cuda);
166 assert_eq!(input.dimensions(), (1920, 1080));
167 assert_eq!(frame.device_ptr(), 0xCAFE);
168 assert_eq!(frame.pitch(), 8192);
169 assert_eq!(frame.pts(), Some(7));
170 }
171
172 #[cfg(feature = "vulkan")]
173 #[test]
174 fn vulkan_video_frame_reports_shape() {
175 use ash::vk::Handle;
176
177 use super::*;
178
179 let frame = VulkanVideoFrame::new(
180 ash::vk::Image::from_raw(11),
181 ash::vk::ImageView::from_raw(22),
182 ash::vk::DeviceMemory::from_raw(33),
183 ash::vk::Format::R8G8B8A8_UNORM,
184 ash::vk::Extent3D {
185 width: 640,
186 height: 480,
187 depth: 1,
188 },
189 );
190 let input = GpuVideoInput::Vulkan(&frame);
191
192 assert_eq!(input.backend(), GpuBackend::Vulkan);
193 assert_eq!(input.dimensions(), (640, 480));
194 assert_eq!(frame.image().as_raw(), 11);
195 assert_eq!(frame.image_view().as_raw(), 22);
196 assert_eq!(frame.memory().as_raw(), 33);
197 assert_eq!(frame.format(), ash::vk::Format::R8G8B8A8_UNORM);
198 }
199}
200#[cfg(feature = "cuda")]
201pub use cuda::{
202 CudaExternalMemoryHandle, CudaExternalSemaphoreHandle, CudaVideoFrame, VulkanToCudaExport,
203};