Loading...
Searching...
No Matches
DrmPrimeWrap.hpp
1#pragma once
2
3// SPDX-License-Identifier: GPL-3.0-or-later
4//
5// Protocol-agnostic AVDRMFrameDescriptor construction for DMA-BUF video
6// buffers. Any transport providing dmabuf fds plus per-plane chunk metadata
7// (PipeWire today;
8// TODO: videoio vendors and GStreamer can reuse this for their DRM-prime
9// capture paths) can build an AV_PIX_FMT_DRM_PRIME AVFrame from it.
10//
11// The caller owns frame-release semantics: buildDrmDescriptor() returns a
12// calloc'd descriptor the caller must either free() or hand to
13// av_buffer_create with a release callback (see wrapDescriptorAsDrmPrime).
14
15#if defined(__linux__)
16
17extern "C" {
18#include <libavutil/buffer.h>
19#include <libavutil/frame.h>
20#include <libavutil/hwcontext_drm.h>
21}
22
23#include <Gfx/Graph/interop/DrmFourcc.hpp>
24
25#include <algorithm>
26#include <cstdint>
27#include <cstdlib>
28
29namespace score::gfx::interop
30{
31
34struct DrmPlaneSpan
35{
36 int fd{-1};
37 uint32_t maxsize{0};
38 int32_t stride{0};
39 uint32_t offset{0};
40};
41
54inline AVDRMFrameDescriptor* buildDrmDescriptor(
55 const DrmPlaneSpan* blocks, int nBlocks, uint32_t fourcc,
56 uint64_t modifier, int width, int height) noexcept
57{
58 if(!blocks || nBlocks < 1 || fourcc == 0)
59 return nullptr;
60
61 auto* desc = static_cast<AVDRMFrameDescriptor*>(
62 std::calloc(1, sizeof(AVDRMFrameDescriptor)));
63 if(!desc)
64 return nullptr;
65
66 if(nBlocks == 1)
67 {
68 desc->nb_objects = 1;
69 desc->objects[0].fd = blocks[0].fd;
70 desc->objects[0].size = blocks[0].maxsize;
71 desc->objects[0].format_modifier = modifier;
72
73 desc->nb_layers = 1;
74 desc->layers[0].format = fourcc;
75
76 const int srcStride = blocks[0].stride;
77 const std::size_t srcOffset = blocks[0].offset;
78
79 switch(fourcc)
80 {
81 case DRM_NV12:
82 case DRM_P010:
83 {
84 // 2 planes: Y at offset 0, UV interleaved at offset Y_size.
85 const int pitch = srcStride > 0
86 ? srcStride
87 : (fourcc == DRM_P010 ? width * 2 : width);
88 const std::size_t y_size = std::size_t(pitch) * height;
89 desc->layers[0].nb_planes = 2;
90 desc->layers[0].planes[0].object_index = 0;
91 desc->layers[0].planes[0].offset = srcOffset;
92 desc->layers[0].planes[0].pitch = pitch;
93 desc->layers[0].planes[1].object_index = 0;
94 desc->layers[0].planes[1].offset = srcOffset + y_size;
95 desc->layers[0].planes[1].pitch = pitch; // UV interleaved: same pitch
96 break;
97 }
98 case DRM_YUV420:
99 case DRM_YVU420:
100 {
101 // 3 planes at offsets 0, y_size, y_size + uv_size. For YVU420 the
102 // middle plane is V — the fourcc already says so; no swap here.
103 const int yPitch = srcStride > 0 ? srcStride : width;
104 const int uvPitch = yPitch / 2;
105 const std::size_t y_size = std::size_t(yPitch) * height;
106 const std::size_t uv_size = std::size_t(uvPitch) * (height / 2);
107 desc->layers[0].nb_planes = 3;
108 desc->layers[0].planes[0].object_index = 0;
109 desc->layers[0].planes[0].offset = srcOffset;
110 desc->layers[0].planes[0].pitch = yPitch;
111 desc->layers[0].planes[1].object_index = 0;
112 desc->layers[0].planes[1].offset = srcOffset + y_size;
113 desc->layers[0].planes[1].pitch = uvPitch;
114 desc->layers[0].planes[2].object_index = 0;
115 desc->layers[0].planes[2].offset = srcOffset + y_size + uv_size;
116 desc->layers[0].planes[2].pitch = uvPitch;
117 break;
118 }
119 default:
120 {
121 // Single-plane packed.
122 desc->layers[0].nb_planes = 1;
123 desc->layers[0].planes[0].object_index = 0;
124 desc->layers[0].planes[0].offset = srcOffset;
125 desc->layers[0].planes[0].pitch = srcStride;
126 break;
127 }
128 }
129 }
130 else
131 {
132 // Split planes across separate DMA-BUFs: N objects + 1 layer with one
133 // plane per object. Vulkan (VK_EXT_image_drm_format_modifier) and EGL
134 // (EGL_EXT_image_dma_buf_import_modifiers) both accept this shape.
135 const int n = std::min(nBlocks, int(AV_DRM_MAX_PLANES));
136 desc->nb_objects = n;
137 desc->nb_layers = 1;
138 desc->layers[0].format = fourcc;
139 desc->layers[0].nb_planes = n;
140 for(int i = 0; i < n; ++i)
141 {
142 desc->objects[i].fd = blocks[i].fd;
143 desc->objects[i].size = blocks[i].maxsize;
144 desc->objects[i].format_modifier = modifier;
145 desc->layers[0].planes[i].object_index = i;
146 desc->layers[0].planes[i].offset = blocks[i].offset;
147 desc->layers[0].planes[i].pitch = blocks[i].stride;
148 }
149 }
150 return desc;
151}
152
157inline AVFrame* wrapDescriptorAsDrmPrime(
158 AVDRMFrameDescriptor* desc, int width, int height,
159 void (*release)(void*, uint8_t*), void* opaque) noexcept
160{
161 AVFrame* f = av_frame_alloc();
162 if(!f)
163 {
164 std::free(desc);
165 return nullptr;
166 }
167 f->format = AV_PIX_FMT_DRM_PRIME;
168 f->width = width;
169 f->height = height;
170 f->data[0] = reinterpret_cast<uint8_t*>(desc);
171 f->buf[0] = av_buffer_create(
172 reinterpret_cast<uint8_t*>(desc), sizeof(*desc), release, opaque,
173 AV_BUFFER_FLAG_READONLY);
174 if(!f->buf[0])
175 {
176 std::free(desc);
177 av_frame_free(&f);
178 return nullptr;
179 }
180 return f;
181}
182
183} // namespace score::gfx::interop
184
185#endif // __linux__