]> git.sesse.net Git - ffmpeg/blob - libavutil/hwcontext_drm.c
ceacf683a08b3b3cdf6e33a886b395da7e4aaa95
[ffmpeg] / libavutil / hwcontext_drm.c
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 <fcntl.h>
20 #include <sys/mman.h>
21 #include <unistd.h>
22 #include <linux/dma-buf.h>
23 #include <sys/ioctl.h>
24
25 #include <drm.h>
26 #include <xf86drm.h>
27
28 #include "avassert.h"
29 #include "hwcontext.h"
30 #include "hwcontext_drm.h"
31 #include "hwcontext_internal.h"
32 #include "imgutils.h"
33
34
35 static void drm_device_free(AVHWDeviceContext *hwdev)
36 {
37     AVDRMDeviceContext *hwctx = hwdev->hwctx;
38
39     close(hwctx->fd);
40 }
41
42 static int drm_device_create(AVHWDeviceContext *hwdev, const char *device,
43                              AVDictionary *opts, int flags)
44 {
45     AVDRMDeviceContext *hwctx = hwdev->hwctx;
46     drmVersionPtr version;
47
48     hwctx->fd = open(device, O_RDWR);
49     if (hwctx->fd < 0)
50         return AVERROR(errno);
51
52     version = drmGetVersion(hwctx->fd);
53     if (!version) {
54         av_log(hwdev, AV_LOG_ERROR, "Failed to get version information "
55                "from %s: probably not a DRM device?\n", device);
56         close(hwctx->fd);
57         return AVERROR(EINVAL);
58     }
59
60     av_log(hwdev, AV_LOG_VERBOSE, "Opened DRM device %s: driver %s "
61            "version %d.%d.%d.\n", device, version->name,
62            version->version_major, version->version_minor,
63            version->version_patchlevel);
64
65     drmFreeVersion(version);
66
67     hwdev->free = &drm_device_free;
68
69     return 0;
70 }
71
72 static int drm_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
73 {
74     frame->buf[0] = av_buffer_pool_get(hwfc->pool);
75     if (!frame->buf[0])
76         return AVERROR(ENOMEM);
77
78     frame->data[0] = (uint8_t*)frame->buf[0]->data;
79
80     frame->format = AV_PIX_FMT_DRM_PRIME;
81     frame->width  = hwfc->width;
82     frame->height = hwfc->height;
83
84     return 0;
85 }
86
87 typedef struct DRMMapping {
88     // Address and length of each mmap()ed region.
89     int nb_regions;
90     int sync_flags;
91     int object[AV_DRM_MAX_PLANES];
92     void *address[AV_DRM_MAX_PLANES];
93     size_t length[AV_DRM_MAX_PLANES];
94 } DRMMapping;
95
96 static void drm_unmap_frame(AVHWFramesContext *hwfc,
97                             HWMapDescriptor *hwmap)
98 {
99     DRMMapping *map = hwmap->priv;
100     struct dma_buf_sync sync = { .flags = DMA_BUF_SYNC_END | map->sync_flags };
101     int i, ret;
102
103     for (i = 0; i < map->nb_regions; i++) {
104         ret = ioctl(map->object[i], DMA_BUF_IOCTL_SYNC, &sync);
105         if (ret)
106             av_log(hwfc, AV_LOG_ERROR, "Failed to issue ioctl sync to DRM object "
107                    "%d: %d.\n", map->object[i], errno);
108         munmap(map->address[i], map->length[i]);
109     }
110
111     av_free(map);
112 }
113
114 static int drm_map_frame(AVHWFramesContext *hwfc,
115                          AVFrame *dst, const AVFrame *src, int flags)
116 {
117     const AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor*)src->data[0];
118     struct dma_buf_sync sync_start = { 0 };
119     DRMMapping *map;
120     int err, i, p, plane;
121     int mmap_prot;
122     void *addr;
123
124     map = av_mallocz(sizeof(*map));
125     if (!map)
126         return AVERROR(ENOMEM);
127
128     mmap_prot = 0;
129     if (flags & AV_HWFRAME_MAP_READ) {
130         mmap_prot |= PROT_READ;
131         map->sync_flags |= DMA_BUF_SYNC_READ;
132     }
133     if (flags & AV_HWFRAME_MAP_WRITE) {
134         mmap_prot |= PROT_WRITE;
135         map->sync_flags |= DMA_BUF_SYNC_WRITE;
136     }
137
138     sync_start.flags = DMA_BUF_SYNC_START | map->sync_flags;
139
140     av_assert0(desc->nb_objects <= AV_DRM_MAX_PLANES);
141     for (i = 0; i < desc->nb_objects; i++) {
142         addr = mmap(NULL, desc->objects[i].size, mmap_prot, MAP_SHARED,
143                     desc->objects[i].fd, 0);
144         if (addr == MAP_FAILED) {
145             err = AVERROR(errno);
146             av_log(hwfc, AV_LOG_ERROR, "Failed to map DRM object %d to "
147                    "memory: %d.\n", desc->objects[i].fd, errno);
148             goto fail;
149         }
150
151         map->address[i] = addr;
152         map->length[i]  = desc->objects[i].size;
153         map->object[i] = desc->objects[i].fd;
154
155         err = ioctl(desc->objects[i].fd, DMA_BUF_IOCTL_SYNC, &sync_start);
156         if (err) {
157             err = AVERROR(errno);
158             av_log(hwfc, AV_LOG_ERROR, "Failed to issue ioctl sync to DRM object "
159                    "%d: %d.\n", desc->objects[i].fd, errno);
160             goto fail;
161         }
162     }
163     map->nb_regions = i;
164
165     plane = 0;
166     for (i = 0; i < desc->nb_layers; i++) {
167         const AVDRMLayerDescriptor *layer = &desc->layers[i];
168         for (p = 0; p < layer->nb_planes; p++) {
169             dst->data[plane] =
170                 (uint8_t*)map->address[layer->planes[p].object_index] +
171                                        layer->planes[p].offset;
172             dst->linesize[plane] =     layer->planes[p].pitch;
173             ++plane;
174         }
175     }
176     av_assert0(plane <= AV_DRM_MAX_PLANES);
177
178     dst->width  = src->width;
179     dst->height = src->height;
180
181     err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
182                                 &drm_unmap_frame, map);
183     if (err < 0)
184         goto fail;
185
186     return 0;
187
188 fail:
189     for (i = 0; i < desc->nb_objects; i++) {
190         if (map->address[i])
191             munmap(map->address[i], map->length[i]);
192     }
193     av_free(map);
194     return err;
195 }
196
197 static int drm_transfer_get_formats(AVHWFramesContext *ctx,
198                                     enum AVHWFrameTransferDirection dir,
199                                     enum AVPixelFormat **formats)
200 {
201     enum AVPixelFormat *pix_fmts;
202
203     pix_fmts = av_malloc_array(2, sizeof(*pix_fmts));
204     if (!pix_fmts)
205         return AVERROR(ENOMEM);
206
207     pix_fmts[0] = ctx->sw_format;
208     pix_fmts[1] = AV_PIX_FMT_NONE;
209
210     *formats = pix_fmts;
211     return 0;
212 }
213
214 static int drm_transfer_data_from(AVHWFramesContext *hwfc,
215                                   AVFrame *dst, const AVFrame *src)
216 {
217     AVFrame *map;
218     int err;
219
220     if (dst->width > hwfc->width || dst->height > hwfc->height)
221         return AVERROR(EINVAL);
222
223     map = av_frame_alloc();
224     if (!map)
225         return AVERROR(ENOMEM);
226     map->format = dst->format;
227
228     err = drm_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
229     if (err)
230         goto fail;
231
232     map->width  = dst->width;
233     map->height = dst->height;
234
235     err = av_frame_copy(dst, map);
236     if (err)
237         goto fail;
238
239     err = 0;
240 fail:
241     av_frame_free(&map);
242     return err;
243 }
244
245 static int drm_transfer_data_to(AVHWFramesContext *hwfc,
246                                 AVFrame *dst, const AVFrame *src)
247 {
248     AVFrame *map;
249     int err;
250
251     if (src->width > hwfc->width || src->height > hwfc->height)
252         return AVERROR(EINVAL);
253
254     map = av_frame_alloc();
255     if (!map)
256         return AVERROR(ENOMEM);
257     map->format = src->format;
258
259     err = drm_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE |
260                                         AV_HWFRAME_MAP_OVERWRITE);
261     if (err)
262         goto fail;
263
264     map->width  = src->width;
265     map->height = src->height;
266
267     err = av_frame_copy(map, src);
268     if (err)
269         goto fail;
270
271     err = 0;
272 fail:
273     av_frame_free(&map);
274     return err;
275 }
276
277 static int drm_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
278                         const AVFrame *src, int flags)
279 {
280     int err;
281
282     if (hwfc->sw_format != dst->format)
283         return AVERROR(ENOSYS);
284
285     err = drm_map_frame(hwfc, dst, src, flags);
286     if (err)
287         return err;
288
289     err = av_frame_copy_props(dst, src);
290     if (err)
291         return err;
292
293     return 0;
294 }
295
296 const HWContextType ff_hwcontext_type_drm = {
297     .type                   = AV_HWDEVICE_TYPE_DRM,
298     .name                   = "DRM",
299
300     .device_hwctx_size      = sizeof(AVDRMDeviceContext),
301
302     .device_create          = &drm_device_create,
303
304     .frames_get_buffer      = &drm_get_buffer,
305
306     .transfer_get_formats   = &drm_transfer_get_formats,
307     .transfer_data_to       = &drm_transfer_data_to,
308     .transfer_data_from     = &drm_transfer_data_from,
309     .map_from               = &drm_map_from,
310
311     .pix_fmts = (const enum AVPixelFormat[]) {
312         AV_PIX_FMT_DRM_PRIME,
313         AV_PIX_FMT_NONE
314     },
315 };