@@ -19,12 +19,15 @@
#include "config.h"
#include <fcntl.h>
+#include <sys/ioctl.h>
#include <sys/mman.h>
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <drm.h>
+#include <drm_fourcc.h>
+#include <drm_mode.h>
#include <xf86drm.h>
#include "avassert.h"
@@ -71,6 +74,150 @@ static int drm_device_create(AVHWDeviceContext *hwdev, const char *device,
return 0;
}
+static int drm_frames_get_constraints(AVHWDeviceContext *hwdev,
+ const void *hwconfig,
+ AVHWFramesConstraints *constraints)
+{
+ // Hard-coded to NV12 with no size limits.
+
+ constraints->valid_hw_formats =
+ av_malloc_array(2, sizeof(*constraints->valid_hw_formats));
+ if (!constraints->valid_hw_formats)
+ return AVERROR(ENOMEM);
+
+ constraints->valid_hw_formats[0] = AV_PIX_FMT_DRM_PRIME;
+ constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
+
+ constraints->valid_sw_formats =
+ av_malloc_array(2, sizeof(*constraints->valid_sw_formats));
+ if (!constraints->valid_sw_formats)
+ return AVERROR(ENOMEM);
+
+ constraints->valid_sw_formats[0] = AV_PIX_FMT_NV12;
+ constraints->valid_sw_formats[1] = AV_PIX_FMT_NONE;
+
+ return 0;
+}
+
+static void drm_pool_free(void *opaque, uint8_t *data)
+{
+ AVHWFramesContext *hwfc = opaque;
+ AVDRMDeviceContext *dev = hwfc->device_ctx->hwctx;
+ AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor*)data;
+ struct drm_mode_destroy_dumb destroy;
+ uint32_t handle;
+ int err, i;
+
+ for (i = 0; i < desc->nb_objects; i++) {
+ err = drmPrimeFDToHandle(dev->fd, desc->objects[0].fd, &handle);
+ if (err < 0) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to retrieve DRM buffer handle "
+ "from fd %d: %d.\n", desc->objects[0].fd, errno);
+ continue;
+ }
+
+ destroy.handle = handle;
+ ioctl(dev->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
+ }
+
+ av_free(desc);
+}
+
+static AVBufferRef *drm_pool_alloc(void *opaque, int size)
+{
+ AVHWFramesContext *hwfc = opaque;
+ AVDRMDeviceContext *dev = hwfc->device_ctx->hwctx;
+ AVDRMFrameDescriptor *desc;
+ AVBufferRef *ref;
+ struct drm_mode_create_dumb create;
+ int err, fd;
+ int aligned_height;
+
+ desc = av_mallocz(sizeof(*desc));
+ if (!desc)
+ return NULL;
+
+ // This is to satisfy possible alignment constraints on the offset
+ // of the second plane (import to VAAPI on Intel requires 16-line
+ // alignment here).
+ aligned_height = FFALIGN(hwfc->height, 16);
+
+ create.height = aligned_height * 3 / 2;
+ create.width = hwfc->width;
+ create.bpp = 8;
+ create.flags = 0;
+
+ err = ioctl(dev->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);
+ if (err < 0) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to create DRM buffer: "
+ "%d.\n", errno);
+ return NULL;
+ }
+
+ err = drmPrimeHandleToFD(dev->fd, create.handle, DRM_RDWR, &fd);
+ if (err < 0) {
+ struct drm_mode_destroy_dumb destroy;
+
+ av_log(hwfc, AV_LOG_ERROR, "Failed to map DRM buffer to fd: "
+ "%d.\n", errno);
+
+ destroy.handle = create.handle;
+ ioctl(dev->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
+
+ return NULL;
+ }
+
+ *desc = (AVDRMFrameDescriptor) {
+ .nb_objects = 1,
+ .objects[0] = {
+ .fd = fd,
+ .size = create.size,
+ .format_modifier = 0,
+ },
+ .nb_layers = 2,
+ .layers[0] = {
+ .format = DRM_FORMAT_R8,
+ .nb_planes = 1,
+ .planes[0] = {
+ .object_index = 0,
+ .offset = 0,
+ .pitch = create.pitch,
+ },
+ },
+ .layers[1] = {
+ .format = DRM_FORMAT_RG88,
+ .nb_planes = 1,
+ .planes[0] = {
+ .object_index = 0,
+ .offset = aligned_height * create.pitch,
+ .pitch = create.pitch,
+ },
+ },
+ };
+
+ ref = av_buffer_create((uint8_t*)desc, sizeof(*desc),
+ &drm_pool_free, hwfc, 0);
+ if (!ref) {
+ drm_pool_free(hwfc, (uint8_t*)desc);
+ return NULL;
+ }
+
+ return ref;
+}
+
+static int drm_frames_init(AVHWFramesContext *hwfc)
+{
+ if (!hwfc->pool) {
+ hwfc->internal->pool_internal =
+ av_buffer_pool_init2(sizeof(AVDRMFrameDescriptor), hwfc,
+ &drm_pool_alloc, NULL);
+ if (!hwfc->internal->pool_internal)
+ return AVERROR(ENOMEM);
+ }
+
+ return 0;
+}
+
static int drm_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
{
frame->buf[0] = av_buffer_pool_get(hwfc->pool);
@@ -280,6 +427,8 @@ const HWContextType ff_hwcontext_type_drm = {
.device_create = &drm_device_create,
+ .frames_get_constraints = &drm_frames_get_constraints,
+ .frames_init = &drm_frames_init,
.frames_get_buffer = &drm_get_buffer,
.transfer_get_formats = &drm_transfer_get_formats,
@@ -41,6 +41,11 @@
#include "pixdesc.h"
#include "pixfmt.h"
+#if CONFIG_LIBDRM
+#include <va/va_drmcommon.h>
+# include "hwcontext_drm.h"
+#endif
+
typedef struct VAAPIDevicePriv {
#if HAVE_VAAPI_X11
Display *x11_display;
@@ -897,6 +902,114 @@ static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
return 0;
}
+#if CONFIG_LIBDRM
+
+static void vaapi_unmap_from_drm(AVHWFramesContext *dst_fc,
+ HWMapDescriptor *hwmap)
+{
+ AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
+
+ VASurfaceID surface_id = (VASurfaceID)(uintptr_t)hwmap->priv;
+
+ vaDestroySurfaces(dst_dev->display, &surface_id, 1);
+}
+
+static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
+ const AVFrame *src, int flags)
+{
+ AVHWFramesContext *dst_fc =
+ (AVHWFramesContext*)dst->hw_frames_ctx->data;
+ AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
+ const AVDRMFrameDescriptor *desc;
+ VASurfaceID surface_id;
+ VAStatus vas;
+ int err, plane;
+
+ unsigned long buffer_handle;
+ VASurfaceAttribExternalBuffers buffer_desc;
+ VASurfaceAttrib attrs[2] = {
+ {
+ .type = VASurfaceAttribMemoryType,
+ .flags = VA_SURFACE_ATTRIB_SETTABLE,
+ .value.type = VAGenericValueTypeInteger,
+ .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME,
+ },
+ {
+ .type = VASurfaceAttribExternalBufferDescriptor,
+ .flags = VA_SURFACE_ATTRIB_SETTABLE,
+ .value.type = VAGenericValueTypePointer,
+ .value.value.p = &buffer_desc,
+ }
+ };
+
+ desc = (AVDRMFrameDescriptor*)src->data[0];
+
+ if (desc->nb_objects != 1) {
+ av_log(dst_fc, AV_LOG_ERROR, "VAAPI can only map frames "
+ "made from single DRM objects.\n");
+ return AVERROR(EINVAL);
+ }
+
+ av_log(dst_fc, AV_LOG_DEBUG, "Map DRM object %d to VAAPI.\n",
+ desc->objects[0].fd);
+
+ buffer_handle = desc->objects[0].fd;
+ buffer_desc.pixel_format = VA_FOURCC_NV12;
+ buffer_desc.width = src_fc->width;
+ buffer_desc.height = src_fc->height;
+ buffer_desc.data_size = desc->objects[0].size;
+
+ buffer_desc.num_planes = 2;
+ for (plane = 0; plane < 2; plane++) {
+ buffer_desc.pitches[plane] = desc->layers[plane].planes[0].pitch;
+ buffer_desc.offsets[plane] = desc->layers[plane].planes[0].offset;
+ }
+
+ buffer_desc.buffers = &buffer_handle;
+ buffer_desc.num_buffers = 1;
+ buffer_desc.flags = 0;
+
+ vas = vaCreateSurfaces(dst_dev->display,
+ VA_RT_FORMAT_YUV420,
+ src->width, src->height,
+ &surface_id, 1,
+ attrs, FF_ARRAY_ELEMS(attrs));
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(dst_fc, AV_LOG_ERROR, "Failed to create surface from DRM "
+ "object: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR(EIO);
+ }
+
+ err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
+ &vaapi_unmap_from_drm,
+ (void*)(uintptr_t)surface_id);
+ if (err < 0)
+ return err;
+
+ dst->width = src->width;
+ dst->height = src->height;
+ dst->data[3] = (uint8_t*)(uintptr_t)surface_id;
+
+ av_log(dst_fc, AV_LOG_DEBUG, "Mapped DRM object %d to "
+ "surface %#x.\n", desc->objects[0].fd, surface_id);
+
+ return 0;
+}
+#endif
+
+static int vaapi_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
+ const AVFrame *src, int flags)
+{
+ switch (src->format) {
+#if CONFIG_LIBDRM
+ case AV_PIX_FMT_DRM_PRIME:
+ return vaapi_map_from_drm(hwfc, dst, src, flags);
+#endif
+ default:
+ return AVERROR(ENOSYS);
+ }
+}
+
static void vaapi_device_free(AVHWDeviceContext *ctx)
{
AVVAAPIDeviceContext *hwctx = ctx->hwctx;
@@ -999,6 +1112,45 @@ static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
return 0;
}
+static int vaapi_device_derive(AVHWDeviceContext *ctx,
+ AVHWDeviceContext *src_ctx, int flags)
+{
+#if CONFIG_LIBDRM
+ if (src_ctx->type == AV_HWDEVICE_TYPE_DRM) {
+ AVDRMDeviceContext *src_hwctx = src_ctx->hwctx;
+ AVVAAPIDeviceContext *hwctx = ctx->hwctx;
+ VADisplay *display;
+ VAStatus vas;
+ int major, minor;
+
+ if (src_hwctx->fd < 0) {
+ av_log(ctx, AV_LOG_ERROR, "DRM instance requires an associated "
+ "device to derive a VA display from.\n");
+ return AVERROR(EINVAL);
+ }
+
+ display = vaGetDisplayDRM(src_hwctx->fd);
+ if (!display) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to open a VA display from "
+ "DRM device.\n");
+ return AVERROR(EIO);
+ }
+
+ vas = vaInitialize(display, &major, &minor);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI "
+ "connection: %d (%s).\n", vas, vaErrorStr(vas));
+ vaTerminate(display);
+ return AVERROR(EIO);
+ }
+
+ hwctx->display = display;
+ return 0;
+ }
+#endif
+ return AVERROR(ENOSYS);
+}
+
const HWContextType ff_hwcontext_type_vaapi = {
.type = AV_HWDEVICE_TYPE_VAAPI,
.name = "VAAPI",
@@ -1010,6 +1162,7 @@ const HWContextType ff_hwcontext_type_vaapi = {
.frames_priv_size = sizeof(VAAPIFramesContext),
.device_create = &vaapi_device_create,
+ .device_derive = &vaapi_device_derive,
.device_init = &vaapi_device_init,
.device_uninit = &vaapi_device_uninit,
.frames_get_constraints = &vaapi_frames_get_constraints,
@@ -1019,7 +1172,7 @@ const HWContextType ff_hwcontext_type_vaapi = {
.transfer_get_formats = &vaapi_transfer_get_formats,
.transfer_data_to = &vaapi_transfer_data_to,
.transfer_data_from = &vaapi_transfer_data_from,
- .map_to = NULL,
+ .map_to = &vaapi_map_to,
.map_from = &vaapi_map_from,
.pix_fmts = (const enum AVPixelFormat[]) {