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