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