| @@ -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; | |||
| @@ -897,6 +906,173 @@ 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[] = { | |||
| DRM_MAP(NV12, 2, DRM_FORMAT_R8, DRM_FORMAT_RG88), | |||
| DRM_MAP(NV12, 1, DRM_FORMAT_NV12), | |||
| #ifdef VA_FOURCC_P010 | |||
| DRM_MAP(P010, 2, DRM_FORMAT_R16, DRM_FORMAT_RG1616), | |||
| #endif | |||
| DRM_MAP(BGRA, 1, DRM_FORMAT_BGRA8888), | |||
| DRM_MAP(BGRX, 1, DRM_FORMAT_BGRX8888), | |||
| DRM_MAP(RGBA, 1, DRM_FORMAT_RGBA8888), | |||
| DRM_MAP(RGBX, 1, DRM_FORMAT_RGBX8888), | |||
| #ifdef VA_FOURCC_ABGR | |||
| DRM_MAP(ABGR, 1, DRM_FORMAT_ABGR8888), | |||
| DRM_MAP(XBGR, 1, DRM_FORMAT_XBGR8888), | |||
| #endif | |||
| DRM_MAP(ARGB, 1, DRM_FORMAT_ARGB8888), | |||
| DRM_MAP(XRGB, 1, DRM_FORMAT_XRGB8888), | |||
| }; | |||
| #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; | |||
| } | |||
| #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 +1175,56 @@ 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; | |||
| VAAPIDevicePriv *priv; | |||
| 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); | |||
| } | |||
| 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); | |||
| } | |||
| 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); | |||
| } | |||
| return 0; | |||
| } | |||
| #endif | |||
| return AVERROR(ENOSYS); | |||
| } | |||
| const HWContextType ff_hwcontext_type_vaapi = { | |||
| .type = AV_HWDEVICE_TYPE_VAAPI, | |||
| .name = "VAAPI", | |||
| @@ -1010,6 +1236,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 +1246,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[]) { | |||