summaryrefslogtreecommitdiff
path: root/libavutil/hwcontext_vaapi.c
diff options
context:
space:
mode:
Diffstat (limited to 'libavutil/hwcontext_vaapi.c')
-rw-r--r--libavutil/hwcontext_vaapi.c399
1 files changed, 374 insertions, 25 deletions
diff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c
index 2260518ca6..40a85d288c 100644
--- a/libavutil/hwcontext_vaapi.c
+++ b/libavutil/hwcontext_vaapi.c
@@ -1,18 +1,18 @@
/*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -25,6 +25,11 @@
# include <va/va_drm.h>
#endif
+#if CONFIG_LIBDRM
+# include <va/va_drmcommon.h>
+# include <drm_fourcc.h>
+#endif
+
#include <fcntl.h>
#if HAVE_UNISTD_H
# include <unistd.h>
@@ -41,6 +46,10 @@
#include "pixdesc.h"
#include "pixfmt.h"
+#if CONFIG_LIBDRM
+# include "hwcontext_drm.h"
+#endif
+
typedef struct VAAPIDevicePriv {
#if HAVE_VAAPI_X11
Display *x11_display;
@@ -104,15 +113,15 @@ static const struct {
MAP(P010, YUV420_10BPP, P010),
#endif
MAP(BGRA, RGB32, BGRA),
- //MAP(BGRX, RGB32, BGR0),
+ MAP(BGRX, RGB32, BGR0),
MAP(RGBA, RGB32, RGBA),
- //MAP(RGBX, RGB32, RGB0),
+ MAP(RGBX, RGB32, RGB0),
#ifdef VA_FOURCC_ABGR
MAP(ABGR, RGB32, ABGR),
- //MAP(XBGR, RGB32, 0BGR),
+ MAP(XBGR, RGB32, 0BGR),
#endif
MAP(ARGB, RGB32, ARGB),
- //MAP(XRGB, RGB32, 0RGB),
+ MAP(XRGB, RGB32, 0RGB),
};
#undef MAP
@@ -875,8 +884,8 @@ fail:
return err;
}
-static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
- const AVFrame *src, int flags)
+static int vaapi_map_to_memory(AVHWFramesContext *hwfc, AVFrame *dst,
+ const AVFrame *src, int flags)
{
int err;
@@ -897,6 +906,279 @@ static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
return 0;
}
+#if CONFIG_LIBDRM
+
+#define DRM_MAP(va, layers, ...) { \
+ VA_FOURCC_ ## va, \
+ layers, \
+ { __VA_ARGS__ } \
+ }
+static const struct {
+ uint32_t va_fourcc;
+ int nb_layer_formats;
+ uint32_t layer_formats[AV_DRM_MAX_PLANES];
+} vaapi_drm_format_map[] = {
+#ifdef DRM_FORMAT_R8
+ DRM_MAP(NV12, 2, DRM_FORMAT_R8, DRM_FORMAT_RG88),
+#endif
+ DRM_MAP(NV12, 1, DRM_FORMAT_NV12),
+#if defined(VA_FOURCC_P010) && defined(DRM_FORMAT_R16)
+ DRM_MAP(P010, 2, DRM_FORMAT_R16, DRM_FORMAT_RG1616),
+#endif
+ DRM_MAP(BGRA, 1, DRM_FORMAT_ARGB8888),
+ DRM_MAP(BGRX, 1, DRM_FORMAT_XRGB8888),
+ DRM_MAP(RGBA, 1, DRM_FORMAT_ABGR8888),
+ DRM_MAP(RGBX, 1, DRM_FORMAT_XBGR8888),
+#ifdef VA_FOURCC_ABGR
+ DRM_MAP(ABGR, 1, DRM_FORMAT_RGBA8888),
+ DRM_MAP(XBGR, 1, DRM_FORMAT_RGBX8888),
+#endif
+ DRM_MAP(ARGB, 1, DRM_FORMAT_BGRA8888),
+ DRM_MAP(XRGB, 1, DRM_FORMAT_BGRX8888),
+};
+#undef DRM_MAP
+
+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;
+
+ av_log(dst_fc, AV_LOG_DEBUG, "Destroy surface %#x.\n", surface_id);
+
+ 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;
+ uint32_t va_fourcc, va_rt_format;
+ int err, i, j, k;
+
+ 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 a single DRM object.\n");
+ return AVERROR(EINVAL);
+ }
+
+ va_fourcc = 0;
+ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
+ if (desc->nb_layers != vaapi_drm_format_map[i].nb_layer_formats)
+ continue;
+ for (j = 0; j < desc->nb_layers; j++) {
+ if (desc->layers[j].format !=
+ vaapi_drm_format_map[i].layer_formats[j])
+ break;
+ }
+ if (j != desc->nb_layers)
+ continue;
+ va_fourcc = vaapi_drm_format_map[i].va_fourcc;
+ break;
+ }
+ if (!va_fourcc) {
+ av_log(dst_fc, AV_LOG_ERROR, "DRM format not supported "
+ "by VAAPI.\n");
+ return AVERROR(EINVAL);
+ }
+
+ av_log(dst_fc, AV_LOG_DEBUG, "Map DRM object %d to VAAPI as "
+ "%08x.\n", desc->objects[0].fd, va_fourcc);
+
+ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++) {
+ if (vaapi_format_map[i].fourcc == va_fourcc)
+ va_rt_format = vaapi_format_map[i].rt_format;
+ }
+
+ buffer_handle = desc->objects[0].fd;
+ buffer_desc.pixel_format = va_fourcc;
+ buffer_desc.width = src_fc->width;
+ buffer_desc.height = src_fc->height;
+ buffer_desc.data_size = desc->objects[0].size;
+ buffer_desc.buffers = &buffer_handle;
+ buffer_desc.num_buffers = 1;
+ buffer_desc.flags = 0;
+
+ k = 0;
+ for (i = 0; i < desc->nb_layers; i++) {
+ for (j = 0; j < desc->layers[i].nb_planes; j++) {
+ buffer_desc.pitches[k] = desc->layers[i].planes[j].pitch;
+ buffer_desc.offsets[k] = desc->layers[i].planes[j].offset;
+ ++k;
+ }
+ }
+ buffer_desc.num_planes = k;
+
+ vas = vaCreateSurfaces(dst_dev->display, va_rt_format,
+ 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);
+ }
+ av_log(dst_fc, AV_LOG_DEBUG, "Create surface %#x.\n", surface_id);
+
+ 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;
+}
+
+static void vaapi_unmap_to_drm(AVHWFramesContext *dst_fc,
+ HWMapDescriptor *hwmap)
+{
+ AVDRMFrameDescriptor *drm_desc = hwmap->priv;
+ int i;
+
+ for (i = 0; i < drm_desc->nb_objects; i++)
+ close(drm_desc->objects[i].fd);
+
+ av_freep(&drm_desc);
+}
+
+static int vaapi_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
+ const AVFrame *src, int flags)
+{
+#if CONFIG_VAAPI_1
+ AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+ VASurfaceID surface_id;
+ VAStatus vas;
+ VADRMPRIMESurfaceDescriptor va_desc;
+ AVDRMFrameDescriptor *drm_desc = NULL;
+ int err, i, j;
+
+ surface_id = (VASurfaceID)(uintptr_t)src->data[3];
+
+ vas = vaExportSurfaceHandle(hwctx->display, surface_id,
+ VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
+ VA_EXPORT_SURFACE_READ_ONLY |
+ VA_EXPORT_SURFACE_SEPARATE_LAYERS,
+ &va_desc);
+ if (vas != VA_STATUS_SUCCESS) {
+ if (vas == VA_STATUS_ERROR_UNIMPLEMENTED)
+ return AVERROR(ENOSYS);
+ av_log(hwfc, AV_LOG_ERROR, "Failed to export surface %#x: "
+ "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
+ return AVERROR(EIO);
+ }
+
+ drm_desc = av_mallocz(sizeof(*drm_desc));
+ if (!drm_desc) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ // By some bizarre coincidence, these structures are very similar...
+ drm_desc->nb_objects = va_desc.num_objects;
+ for (i = 0; i < va_desc.num_objects; i++) {
+ drm_desc->objects[i].fd = va_desc.objects[i].fd;
+ drm_desc->objects[i].size = va_desc.objects[i].size;
+ drm_desc->objects[i].format_modifier =
+ va_desc.objects[i].drm_format_modifier;
+ }
+ drm_desc->nb_layers = va_desc.num_layers;
+ for (i = 0; i < va_desc.num_layers; i++) {
+ drm_desc->layers[i].format = va_desc.layers[i].drm_format;
+ drm_desc->layers[i].nb_planes = va_desc.layers[i].num_planes;
+ for (j = 0; j < va_desc.layers[i].num_planes; j++) {
+ drm_desc->layers[i].planes[j].object_index =
+ va_desc.layers[i].object_index[j];
+ drm_desc->layers[i].planes[j].offset =
+ va_desc.layers[i].offset[j];
+ drm_desc->layers[i].planes[j].pitch =
+ va_desc.layers[i].pitch[j];
+ }
+ }
+
+ err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
+ &vaapi_unmap_to_drm, drm_desc);
+ if (err < 0)
+ goto fail;
+
+ dst->width = src->width;
+ dst->height = src->height;
+ dst->data[0] = (uint8_t*)drm_desc;
+
+ return 0;
+
+fail:
+ for (i = 0; i < va_desc.num_objects; i++)
+ close(va_desc.objects[i].fd);
+ av_freep(&drm_desc);
+ return err;
+#else
+ // Older versions without vaExportSurfaceHandle() are not supported -
+ // in theory this is possible with a combination of vaDeriveImage()
+ // and vaAcquireBufferHandle(), but it doesn't carry enough metadata
+ // to actually use the result in a generic way.
+ return AVERROR(ENOSYS);
+#endif
+}
+#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 int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
+ const AVFrame *src, int flags)
+{
+ switch (dst->format) {
+#if CONFIG_LIBDRM
+ case AV_PIX_FMT_DRM_PRIME:
+ return vaapi_map_to_drm(hwfc, dst, src, flags);
+#endif
+ default:
+ return vaapi_map_to_memory(hwfc, dst, src, flags);
+ }
+}
+
static void vaapi_device_free(AVHWDeviceContext *ctx)
{
AVVAAPIDeviceContext *hwctx = ctx->hwctx;
@@ -916,14 +1198,53 @@ static void vaapi_device_free(AVHWDeviceContext *ctx)
av_freep(&priv);
}
+#if CONFIG_VAAPI_1
+static void vaapi_device_log_error(void *context, const char *message)
+{
+ AVHWDeviceContext *ctx = context;
+
+ av_log(ctx, AV_LOG_ERROR, "libva: %s", message);
+}
+
+static void vaapi_device_log_info(void *context, const char *message)
+{
+ AVHWDeviceContext *ctx = context;
+
+ av_log(ctx, AV_LOG_VERBOSE, "libva: %s", message);
+}
+#endif
+
+static int vaapi_device_connect(AVHWDeviceContext *ctx,
+ VADisplay display)
+{
+ AVVAAPIDeviceContext *hwctx = ctx->hwctx;
+ int major, minor;
+ VAStatus vas;
+
+#if CONFIG_VAAPI_1
+ vaSetErrorCallback(display, &vaapi_device_log_error, ctx);
+ vaSetInfoCallback (display, &vaapi_device_log_info, ctx);
+#endif
+
+ hwctx->display = display;
+
+ 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));
+ return AVERROR(EIO);
+ }
+ av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: "
+ "version %d.%d\n", major, minor);
+
+ return 0;
+}
+
static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
AVDictionary *opts, int flags)
{
- AVVAAPIDeviceContext *hwctx = ctx->hwctx;
VAAPIDevicePriv *priv;
- VADisplay display = 0;
- VAStatus vas;
- int major, minor;
+ VADisplay display = NULL;
priv = av_mallocz(sizeof(*priv));
if (!priv)
@@ -985,18 +1306,45 @@ static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
return AVERROR(EINVAL);
}
- hwctx->display = display;
+ return vaapi_device_connect(ctx, display);
+}
- 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));
- return AVERROR(EIO);
- }
- av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: "
- "version %d.%d\n", major, minor);
+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;
+ VADisplay *display;
+ VAAPIDevicePriv *priv;
+
+ 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);
+ }
- return 0;
+ priv = av_mallocz(sizeof(*priv));
+ if (!priv)
+ return AVERROR(ENOMEM);
+
+ // Inherits the fd from the source context, which will close it.
+ priv->drm_fd = -1;
+
+ ctx->user_opaque = priv;
+ ctx->free = &vaapi_device_free;
+
+ 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);
+ }
+
+ return vaapi_device_connect(ctx, display);
+ }
+#endif
+ return AVERROR(ENOSYS);
}
const HWContextType ff_hwcontext_type_vaapi = {
@@ -1010,6 +1358,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 +1368,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[]) {