QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
GstCudaVideoBuffer.cc
Go to the documentation of this file.
2
3#if defined(QGC_HAS_GST_CUDA_GPU_PATH)
4
5#include <gst/allocators/gstdmabuf.h>
6#include <gst/cuda/gstcuda.h>
7#include <gst/video/video.h>
8#include <memory>
9#include <unistd.h>
10
12
13namespace GstCudaVideoBuffer {
14namespace {
15
16// gst_cuda_memory_export only succeeds for cuMemCreate/cuMemMap-backed allocations (ALLOC_MMAP); the common
17// cuMemAlloc/pitch decoder default cannot be shared and returns FALSE -> nullptr -> factory CPU latch.
18int exportCudaMemoryToFd(GstMemory* mem)
19{
20 if (!gst_is_cuda_memory(mem)) {
21 return -1;
22 }
23 auto* cudaMem = reinterpret_cast<GstCudaMemory*>(mem);
24
25 // Linux: os_handle is an int* receiving a POSIX file descriptor (CU_MEM_HANDLE_TYPE_POSIX_FILE_DESCRIPTOR).
26 // gst_cuda_memory_export hands us an owned fd; we transfer it to the dmabuf allocator below.
27 int fd = -1;
28 if (!gst_cuda_memory_export(cudaMem, &fd) || fd < 0) {
29 return -1;
30 }
31 return fd;
32}
33
34} // namespace
35
36std::unique_ptr<GstHwVideoBuffer> exportToDmaBuf(GstSample* sample, const GstVideoInfo& info, QVideoFrameFormat format,
37 const HwVideoBufferContext& context)
38{
39 if (context.dmaBufEglDisplay == EGL_NO_DISPLAY) {
40 return nullptr; // No EGLImage display to import the exported fd; fall back to CPU.
41 }
42
43 GstBuffer* buffer = gst_sample_get_buffer(sample);
44 if (!buffer) {
45 return nullptr;
46 }
47
48 // Single contiguous CUDA allocation per frame (NV12/P010 planes share one device buffer); multi-memory CUDA
49 // layouts are out of scope, so only memory 0 is exported.
50 GstMemory* mem0 = gst_buffer_peek_memory(buffer, 0);
51 const int fd = exportCudaMemoryToFd(mem0);
52 if (fd < 0) {
53 return nullptr;
54 }
55
56 GstAllocator* allocator = gst_dmabuf_allocator_new();
57 if (!allocator) {
58 close(fd);
59 return nullptr;
60 }
61
62 // Allocator takes ownership of fd; on success it is closed when dmaMem (and the GstBuffer wrapping it) is unreffed.
63 GstMemory* dmaMem = gst_dmabuf_allocator_alloc(allocator, fd, gst_memory_get_sizes(mem0, nullptr, nullptr));
64 if (!dmaMem) {
65 close(fd);
66 gst_object_unref(allocator);
67 return nullptr;
68 }
69
70 GstBuffer* dmaBuffer = gst_buffer_new();
71 if (!dmaBuffer) {
72 gst_memory_unref(dmaMem);
73 gst_object_unref(allocator);
74 return nullptr;
75 }
76 gst_buffer_append_memory(dmaBuffer, dmaMem);
77
78 // Carry the source GstVideoMeta so GstDmaBufVideoBuffer reads correct per-plane offsets/strides without mmapping.
79 if (GstVideoMeta* srcMeta = gst_buffer_get_video_meta(buffer)) {
80 gst_buffer_add_video_meta_full(dmaBuffer, GST_VIDEO_FRAME_FLAG_NONE, srcMeta->format, srcMeta->width,
81 srcMeta->height, srcMeta->n_planes, srcMeta->offset, srcMeta->stride);
82 } else {
83 gst_buffer_add_video_meta(dmaBuffer, GST_VIDEO_FRAME_FLAG_NONE, GST_VIDEO_INFO_FORMAT(&info),
84 GST_VIDEO_INFO_WIDTH(&info), GST_VIDEO_INFO_HEIGHT(&info));
85 }
86
87 // Reuse the source caps: the exported fd is LINEAR device memory, so the original (non-DRM) caps drive a
88 // modifier-0 import in GstDmaBufVideoBuffer.
89 GstCaps* caps = gst_sample_get_caps(sample);
90 GstSample* dmaSample = gst_sample_new(dmaBuffer, caps, nullptr, nullptr);
91 gst_buffer_unref(dmaBuffer);
92 if (!dmaSample) {
93 gst_object_unref(allocator);
94 return nullptr;
95 }
96
97 // GstHwVideoBuffer refs the sample; our local refs (sample + allocator) drop here.
98 auto buf = std::make_unique<GstDmaBufVideoBuffer>(dmaSample, info, format, context.dmaBufEglDisplay);
99 gst_sample_unref(dmaSample);
100 gst_object_unref(allocator);
101 return buf;
102}
103
104} // namespace GstCudaVideoBuffer
105
106#endif // QGC_HAS_GST_CUDA_GPU_PATH
bool close(int deviceId)
Platform context for the factory; encapsulates EGL handles so callers don't need path-specific ifdefs...