Skip to main content

lumen_ffmpeg/gpu/
mod.rs

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};