FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavutil/hwcontext_drm.c
Date: 2024-05-04 02:01:39
Exec Total Coverage
Lines: 0 143 0.0%
Functions: 0 9 0.0%
Branches: 0 64 0.0%

Line Branch Exec Source
1 /*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include "config.h"
20
21 #include <fcntl.h>
22 #include <sys/mman.h>
23 #include <unistd.h>
24
25 /* This was introduced in version 4.6. And may not exist all without an
26 * optional package. So to prevent a hard dependency on needing the Linux
27 * kernel headers to compile, make this optional. */
28 #if HAVE_LINUX_DMA_BUF_H
29 #include <linux/dma-buf.h>
30 #include <sys/ioctl.h>
31 #endif
32
33 #include <drm.h>
34 #include <xf86drm.h>
35
36 #include "avassert.h"
37 #include "hwcontext.h"
38 #include "hwcontext_drm.h"
39 #include "hwcontext_internal.h"
40 #include "imgutils.h"
41 #include "mem.h"
42
43
44 static void drm_device_free(AVHWDeviceContext *hwdev)
45 {
46 AVDRMDeviceContext *hwctx = hwdev->hwctx;
47
48 close(hwctx->fd);
49 }
50
51 static int drm_device_create(AVHWDeviceContext *hwdev, const char *device,
52 AVDictionary *opts, int flags)
53 {
54 AVDRMDeviceContext *hwctx = hwdev->hwctx;
55 drmVersionPtr version;
56
57 hwctx->fd = open(device, O_RDWR);
58 if (hwctx->fd < 0)
59 return AVERROR(errno);
60
61 version = drmGetVersion(hwctx->fd);
62 if (!version) {
63 av_log(hwdev, AV_LOG_ERROR, "Failed to get version information "
64 "from %s: probably not a DRM device?\n", device);
65 close(hwctx->fd);
66 return AVERROR(EINVAL);
67 }
68
69 av_log(hwdev, AV_LOG_VERBOSE, "Opened DRM device %s: driver %s "
70 "version %d.%d.%d.\n", device, version->name,
71 version->version_major, version->version_minor,
72 version->version_patchlevel);
73
74 drmFreeVersion(version);
75
76 hwdev->free = &drm_device_free;
77
78 return 0;
79 }
80
81 static int drm_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
82 {
83 frame->buf[0] = av_buffer_pool_get(hwfc->pool);
84 if (!frame->buf[0])
85 return AVERROR(ENOMEM);
86
87 frame->data[0] = (uint8_t*)frame->buf[0]->data;
88
89 frame->format = AV_PIX_FMT_DRM_PRIME;
90 frame->width = hwfc->width;
91 frame->height = hwfc->height;
92
93 return 0;
94 }
95
96 typedef struct DRMMapping {
97 // Address and length of each mmap()ed region.
98 int nb_regions;
99 int sync_flags;
100 int object[AV_DRM_MAX_PLANES];
101 void *address[AV_DRM_MAX_PLANES];
102 size_t length[AV_DRM_MAX_PLANES];
103 } DRMMapping;
104
105 static void drm_unmap_frame(AVHWFramesContext *hwfc,
106 HWMapDescriptor *hwmap)
107 {
108 DRMMapping *map = hwmap->priv;
109
110 for (int i = 0; i < map->nb_regions; i++) {
111 #if HAVE_LINUX_DMA_BUF_H
112 struct dma_buf_sync sync = { .flags = DMA_BUF_SYNC_END | map->sync_flags };
113 ioctl(map->object[i], DMA_BUF_IOCTL_SYNC, &sync);
114 #endif
115 munmap(map->address[i], map->length[i]);
116 }
117
118 av_free(map);
119 }
120
121 static int drm_map_frame(AVHWFramesContext *hwfc,
122 AVFrame *dst, const AVFrame *src, int flags)
123 {
124 const AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor*)src->data[0];
125 #if HAVE_LINUX_DMA_BUF_H
126 struct dma_buf_sync sync_start = { 0 };
127 #endif
128 DRMMapping *map;
129 int err, i, p, plane;
130 int mmap_prot;
131 void *addr;
132
133 map = av_mallocz(sizeof(*map));
134 if (!map)
135 return AVERROR(ENOMEM);
136
137 mmap_prot = 0;
138 if (flags & AV_HWFRAME_MAP_READ)
139 mmap_prot |= PROT_READ;
140 if (flags & AV_HWFRAME_MAP_WRITE)
141 mmap_prot |= PROT_WRITE;
142
143 #if HAVE_LINUX_DMA_BUF_H
144 if (flags & AV_HWFRAME_MAP_READ)
145 map->sync_flags |= DMA_BUF_SYNC_READ;
146 if (flags & AV_HWFRAME_MAP_WRITE)
147 map->sync_flags |= DMA_BUF_SYNC_WRITE;
148 sync_start.flags = DMA_BUF_SYNC_START | map->sync_flags;
149 #endif
150
151 av_assert0(desc->nb_objects <= AV_DRM_MAX_PLANES);
152 for (i = 0; i < desc->nb_objects; i++) {
153 addr = mmap(NULL, desc->objects[i].size, mmap_prot, MAP_SHARED,
154 desc->objects[i].fd, 0);
155 if (addr == MAP_FAILED) {
156 err = AVERROR(errno);
157 av_log(hwfc, AV_LOG_ERROR, "Failed to map DRM object %d to "
158 "memory: %d.\n", desc->objects[i].fd, errno);
159 goto fail;
160 }
161
162 map->address[i] = addr;
163 map->length[i] = desc->objects[i].size;
164 map->object[i] = desc->objects[i].fd;
165
166 #if HAVE_LINUX_DMA_BUF_H
167 /* We're not checking for errors here because the kernel may not
168 * support the ioctl, in which case its okay to carry on */
169 ioctl(desc->objects[i].fd, DMA_BUF_IOCTL_SYNC, &sync_start);
170 #endif
171 }
172 map->nb_regions = i;
173
174 plane = 0;
175 for (i = 0; i < desc->nb_layers; i++) {
176 const AVDRMLayerDescriptor *layer = &desc->layers[i];
177 for (p = 0; p < layer->nb_planes; p++) {
178 dst->data[plane] =
179 (uint8_t*)map->address[layer->planes[p].object_index] +
180 layer->planes[p].offset;
181 dst->linesize[plane] = layer->planes[p].pitch;
182 ++plane;
183 }
184 }
185 av_assert0(plane <= AV_DRM_MAX_PLANES);
186
187 dst->width = src->width;
188 dst->height = src->height;
189
190 err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
191 &drm_unmap_frame, map);
192 if (err < 0)
193 goto fail;
194
195 return 0;
196
197 fail:
198 for (i = 0; i < desc->nb_objects; i++) {
199 if (map->address[i])
200 munmap(map->address[i], map->length[i]);
201 }
202 av_free(map);
203 return err;
204 }
205
206 static int drm_transfer_get_formats(AVHWFramesContext *ctx,
207 enum AVHWFrameTransferDirection dir,
208 enum AVPixelFormat **formats)
209 {
210 enum AVPixelFormat *pix_fmts;
211
212 pix_fmts = av_malloc_array(2, sizeof(*pix_fmts));
213 if (!pix_fmts)
214 return AVERROR(ENOMEM);
215
216 pix_fmts[0] = ctx->sw_format;
217 pix_fmts[1] = AV_PIX_FMT_NONE;
218
219 *formats = pix_fmts;
220 return 0;
221 }
222
223 static int drm_transfer_data_from(AVHWFramesContext *hwfc,
224 AVFrame *dst, const AVFrame *src)
225 {
226 AVFrame *map;
227 int err;
228
229 if (dst->width > hwfc->width || dst->height > hwfc->height)
230 return AVERROR(EINVAL);
231
232 map = av_frame_alloc();
233 if (!map)
234 return AVERROR(ENOMEM);
235 map->format = dst->format;
236
237 err = drm_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
238 if (err)
239 goto fail;
240
241 map->width = dst->width;
242 map->height = dst->height;
243
244 err = av_frame_copy(dst, map);
245 if (err)
246 goto fail;
247
248 err = 0;
249 fail:
250 av_frame_free(&map);
251 return err;
252 }
253
254 static int drm_transfer_data_to(AVHWFramesContext *hwfc,
255 AVFrame *dst, const AVFrame *src)
256 {
257 AVFrame *map;
258 int err;
259
260 if (src->width > hwfc->width || src->height > hwfc->height)
261 return AVERROR(EINVAL);
262
263 map = av_frame_alloc();
264 if (!map)
265 return AVERROR(ENOMEM);
266 map->format = src->format;
267
268 err = drm_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE |
269 AV_HWFRAME_MAP_OVERWRITE);
270 if (err)
271 goto fail;
272
273 map->width = src->width;
274 map->height = src->height;
275
276 err = av_frame_copy(map, src);
277 if (err)
278 goto fail;
279
280 err = 0;
281 fail:
282 av_frame_free(&map);
283 return err;
284 }
285
286 static int drm_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
287 const AVFrame *src, int flags)
288 {
289 int err;
290
291 if (hwfc->sw_format != dst->format)
292 return AVERROR(ENOSYS);
293
294 err = drm_map_frame(hwfc, dst, src, flags);
295 if (err)
296 return err;
297
298 err = av_frame_copy_props(dst, src);
299 if (err)
300 return err;
301
302 return 0;
303 }
304
305 const HWContextType ff_hwcontext_type_drm = {
306 .type = AV_HWDEVICE_TYPE_DRM,
307 .name = "DRM",
308
309 .device_hwctx_size = sizeof(AVDRMDeviceContext),
310
311 .device_create = &drm_device_create,
312
313 .frames_get_buffer = &drm_get_buffer,
314
315 .transfer_get_formats = &drm_transfer_get_formats,
316 .transfer_data_to = &drm_transfer_data_to,
317 .transfer_data_from = &drm_transfer_data_from,
318 .map_from = &drm_map_from,
319
320 .pix_fmts = (const enum AVPixelFormat[]) {
321 AV_PIX_FMT_DRM_PRIME,
322 AV_PIX_FMT_NONE
323 },
324 };
325