From patchwork Sun Jul 5 15:49:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Thompson X-Patchwork-Id: 20825 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 58AB544B1CB for ; Sun, 5 Jul 2020 19:00:11 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 336F968A3D6; Sun, 5 Jul 2020 19:00:11 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm1-f67.google.com (mail-wm1-f67.google.com [209.85.128.67]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id DF1A1680330 for ; Sun, 5 Jul 2020 19:00:04 +0300 (EEST) Received: by mail-wm1-f67.google.com with SMTP id q15so36591286wmj.2 for ; Sun, 05 Jul 2020 09:00:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jkqxz-net.20150623.gappssmtp.com; s=20150623; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=WQ1XOBEdQwQt9NPihIgMdRZbi4EzVzpsqN02b3dkjK0=; b=YCtHgd1dPfFN/pGL5+H3O/ia++H0j1Syht1AtvIKIio1Qkj/oW7Yc3yYauCmGRZ3gx bXESaAmJAYXFA+VyaYStmvC+1osOfcatHs5xYEZymIDAIN3PfoVh1KXZzkma0J8lEqcA ugFH84MU66oL85IC/0f1npIKTjdk2bx5OK79OYNYVQBdQ4US/m49lueHxTcgMtvKCZqD rpSOv+MPj9YVN8WjTGNBXtzN/eUGWBFYYhSnOHAYyIi/ms9RiLwLCxuT7FblZrWSExEH /lyVwsHpdQNB3IbOoUqVhHe81rePDitlN1DKUhnIMwWA8CO1RdfONQOAOuuc2NJ9h0hi j5Ug== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=WQ1XOBEdQwQt9NPihIgMdRZbi4EzVzpsqN02b3dkjK0=; b=JAogDUKnv3+MLYgocYq7Ay3T/18SdNCHqlziH7LsftAG5le4ZdRBLK5ncEn4C5MQYQ r4iQFg3JqgtE926CxpBBvaZtvHFCiavaST0+nUYzTMETUB4RxWxAKzaHjW2ZVnt+0WgW tm49HPeRe2tL8OL+DhhJadhlWgCkXwVkTCjD5Wlj02CmaKPfgK6paBKOd64+oF+UG7lO VYNka6LMijtxfMIGQH29mW0EHtfn7y2QaQWD1PbxD9r/XhtjZkLPw7Mef5MGsytarYAb E8dFY7SqLCH/ZA7Gsw/+cd1eGw6R/z2TN1g+Yu3xphZX51Pq2U0dIMcuhEqVIUbyPNHg /TuA== X-Gm-Message-State: AOAM530bU2xMvaw0c9QczzQ1/W3L15yI0Yn5mRulofiU9ePvq67J5E5Z lqYP5W+WwVDgGpPOcL9MM42SNpg3YZE= X-Google-Smtp-Source: ABdhPJyiIFD0DSjQ6VKtBqrPrQ0Hfg8w2hfJ38aHMgxe5kDIvabuqndTIP/G29EYHetYB1tNZBxYQA== X-Received: by 2002:a1c:3bc2:: with SMTP id i185mr46158992wma.33.1593964318676; Sun, 05 Jul 2020 08:51:58 -0700 (PDT) Received: from localhost.localdomain (cpc91242-cmbg18-2-0-cust650.5-4.cable.virginm.net. [82.8.130.139]) by smtp.gmail.com with ESMTPSA id j4sm16070985wrp.51.2020.07.05.08.51.57 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 05 Jul 2020 08:51:58 -0700 (PDT) From: Mark Thompson To: ffmpeg-devel@ffmpeg.org Date: Sun, 5 Jul 2020 16:49:44 +0100 Message-Id: <20200705154946.401280-2-sw@jkqxz.net> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20200705154946.401280-1-sw@jkqxz.net> References: <20200705154946.401280-1-sw@jkqxz.net> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 2/4] kmsgrab: Use GetFB2 if available X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" The most useful feature here is the ability to automatically extract the framebuffer format and modifiers. It also makes support for multi-plane framebuffers possible, though they will need to be added to the format table to work (not tested by me). This requires libdrm 2.4.101 (from April 2020) to build, so it includes a configure check to allow compatibility with existing distributions. Even with libdrm support, it still won't do anything at runtime if you are running Linux < 5.7 (before June 2020). --- This has been hanging around for a while waiting for the GETFB2 ioctl() to actually make it into stable Linux, which it now is with 5.7. configure | 4 + libavdevice/kmsgrab.c | 221 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 203 insertions(+), 22 deletions(-) diff --git a/configure b/configure index bdfd731602..2900acc687 100755 --- a/configure +++ b/configure @@ -2323,6 +2323,7 @@ HAVE_LIST=" $THREADS_LIST $TOOLCHAIN_FEATURES $TYPES_LIST + libdrm_getfb2 makeinfo makeinfo_html opencl_d3d11 @@ -6631,6 +6632,9 @@ test_cpp <= 0.35.0" "va/va.h" vaInitialize diff --git a/libavdevice/kmsgrab.c b/libavdevice/kmsgrab.c index 47ba15ca07..3e89c3f445 100644 --- a/libavdevice/kmsgrab.c +++ b/libavdevice/kmsgrab.c @@ -27,6 +27,11 @@ #include #include +// Required for compatibility when building against libdrm < 2.4.83. +#ifndef DRM_FORMAT_MOD_INVALID +#define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1) +#endif + #include "libavutil/hwcontext.h" #include "libavutil/hwcontext_drm.h" #include "libavutil/internal.h" @@ -45,6 +50,7 @@ typedef struct KMSGrabContext { AVBufferRef *device_ref; AVHWDeviceContext *device; AVDRMDeviceContext *hwctx; + int fb2_available; AVBufferRef *frames_ref; AVHWFramesContext *frames; @@ -68,8 +74,10 @@ typedef struct KMSGrabContext { static void kmsgrab_free_desc(void *opaque, uint8_t *data) { AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor*)data; + int i; - close(desc->objects[0].fd); + for (i = 0; i < desc->nb_objects; i++) + close(desc->objects[i].fd); av_free(desc); } @@ -142,6 +150,114 @@ fail: return err; } +#if HAVE_LIBDRM_GETFB2 +static int kmsgrab_get_fb2(AVFormatContext *avctx, + drmModePlane *plane, + AVDRMFrameDescriptor *desc) +{ + KMSGrabContext *ctx = avctx->priv_data; + drmModeFB2 *fb; + int err, i, nb_objects; + + fb = drmModeGetFB2(ctx->hwctx->fd, plane->fb_id); + if (!fb) { + av_log(avctx, AV_LOG_ERROR, "Failed to get framebuffer " + "%"PRIu32".\n", plane->fb_id); + return AVERROR(EIO); + } + if (fb->pixel_format != ctx->drm_format) { + av_log(avctx, AV_LOG_ERROR, "Plane %"PRIu32" framebuffer " + "format changed: now %"PRIx32".\n", + ctx->plane_id, fb->pixel_format); + err = AVERROR(EIO); + goto fail; + } + if (fb->modifier != ctx->drm_format_modifier) { + av_log(avctx, AV_LOG_ERROR, "Plane %"PRIu32" framebuffer " + "format modifier changed: now %"PRIx64".\n", + ctx->plane_id, fb->modifier); + err = AVERROR(EIO); + goto fail; + } + if (fb->width != ctx->width || fb->height != ctx->height) { + av_log(avctx, AV_LOG_ERROR, "Plane %"PRIu32" framebuffer " + "dimensions changed: now %"PRIu32"x%"PRIu32".\n", + ctx->plane_id, fb->width, fb->height); + err = AVERROR(EIO); + goto fail; + } + if (!fb->handles[0]) { + av_log(avctx, AV_LOG_ERROR, "No handle set on framebuffer.\n"); + err = AVERROR(EIO); + goto fail; + } + + *desc = (AVDRMFrameDescriptor) { + .nb_layers = 1, + .layers[0] = { + .format = ctx->drm_format, + }, + }; + + nb_objects = 0; + for (i = 0; i < 4 && fb->handles[i]; i++) { + size_t size; + int dup = 0, j, obj; + + size = fb->offsets[i] + fb->height * fb->pitches[i]; + + for (j = 0; j < i; j++) { + if (fb->handles[i] == fb->handles[j]) { + dup = 1; + break; + } + } + if (dup) { + obj = desc->layers[0].planes[j].object_index; + + if (desc->objects[j].size < size) + desc->objects[j].size = size; + + desc->layers[0].planes[i] = (AVDRMPlaneDescriptor) { + .object_index = obj, + .offset = fb->offsets[i], + .pitch = fb->pitches[i], + }; + + } else { + int fd; + err = drmPrimeHandleToFD(ctx->hwctx->fd, fb->handles[i], + O_RDONLY, &fd); + if (err < 0) { + err = AVERROR(errno); + av_log(avctx, AV_LOG_ERROR, "Failed to get PRIME fd from " + "framebuffer handle: %s.\n", strerror(errno)); + goto fail; + } + + obj = nb_objects++; + desc->objects[obj] = (AVDRMObjectDescriptor) { + .fd = fd, + .size = size, + .format_modifier = fb->modifier, + }; + desc->layers[0].planes[i] = (AVDRMPlaneDescriptor) { + .object_index = obj, + .offset = fb->offsets[i], + .pitch = fb->pitches[i], + }; + } + } + desc->nb_objects = nb_objects; + desc->layers[0].nb_planes = i; + + err = 0; +fail: + drmModeFreeFB2(fb); + return err; +} +#endif + static int kmsgrab_read_packet(AVFormatContext *avctx, AVPacket *pkt) { KMSGrabContext *ctx = avctx->priv_data; @@ -184,7 +300,12 @@ static int kmsgrab_read_packet(AVFormatContext *avctx, AVPacket *pkt) goto fail; } - err = kmsgrab_get_fb(avctx, plane, desc); +#if HAVE_LIBDRM_GETFB2 + if (ctx->fb2_available) + err = kmsgrab_get_fb2(avctx, plane, desc); + else +#endif + err = kmsgrab_get_fb(avctx, plane, desc); if (err < 0) goto fail; @@ -278,6 +399,9 @@ static av_cold int kmsgrab_read_header(AVFormatContext *avctx) drmModePlaneRes *plane_res = NULL; drmModePlane *plane = NULL; drmModeFB *fb = NULL; +#if HAVE_LIBDRM_GETFB2 + drmModeFB2 *fb2 = NULL; +#endif AVStream *stream; int err, i; @@ -379,28 +503,78 @@ static av_cold int kmsgrab_read_header(AVFormatContext *avctx) ctx->plane_id = plane->plane_id; - fb = drmModeGetFB(ctx->hwctx->fd, plane->fb_id); - if (!fb) { - err = errno; +#if HAVE_LIBDRM_GETFB2 + fb2 = drmModeGetFB2(ctx->hwctx->fd, plane->fb_id); + if (!fb2 && errno == ENOSYS) { + av_log(avctx, AV_LOG_INFO, "GETFB2 not supported, " + "will try to use GETFB instead.\n"); + } else if (!fb2) { + err = AVERROR(err); av_log(avctx, AV_LOG_ERROR, "Failed to get " "framebuffer %"PRIu32": %s.\n", - plane->fb_id, strerror(err)); - err = AVERROR(err); + plane->fb_id, strerror(errno)); goto fail; + } else { + av_log(avctx, AV_LOG_INFO, "Template framebuffer is " + "%"PRIu32": %"PRIu32"x%"PRIu32" " + "format %"PRIx32" modifier %"PRIx64" flags %"PRIx32".\n", + fb2->fb_id, fb2->width, fb2->height, + fb2->pixel_format, fb2->modifier, fb2->flags); + + ctx->width = fb2->width; + ctx->height = fb2->height; + + if (!fb2->handles[0]) { + av_log(avctx, AV_LOG_ERROR, "No handle set on framebuffer: " + "maybe you need some additional capabilities?\n"); + err = AVERROR(EINVAL); + goto fail; + } + if (ctx->drm_format != fb2->pixel_format) { + av_log(avctx, AV_LOG_ERROR, "Framebuffer pixel format " + "%"PRIx32" does not match expected format.\n", + fb2->pixel_format); + err = AVERROR(EINVAL); + goto fail; + } + if (ctx->drm_format_modifier != DRM_FORMAT_MOD_INVALID && + ctx->drm_format_modifier != fb2->modifier) { + av_log(avctx, AV_LOG_ERROR, "Framebuffer format modifier " + "%"PRIx64" does not match expected modifier.\n", + fb2->modifier); + err = AVERROR(EINVAL); + goto fail; + } else { + ctx->drm_format_modifier = fb2->modifier; + } + ctx->fb2_available = 1; } +#endif - av_log(avctx, AV_LOG_INFO, "Template framebuffer is %"PRIu32": " - "%"PRIu32"x%"PRIu32" %"PRIu32"bpp %"PRIu32"b depth.\n", - fb->fb_id, fb->width, fb->height, fb->bpp, fb->depth); + if (!ctx->fb2_available) { + fb = drmModeGetFB(ctx->hwctx->fd, plane->fb_id); + if (!fb) { + err = errno; + av_log(avctx, AV_LOG_ERROR, "Failed to get " + "framebuffer %"PRIu32": %s.\n", + plane->fb_id, strerror(err)); + err = AVERROR(err); + goto fail; + } - ctx->width = fb->width; - ctx->height = fb->height; + av_log(avctx, AV_LOG_INFO, "Template framebuffer is %"PRIu32": " + "%"PRIu32"x%"PRIu32" %"PRIu32"bpp %"PRIu32"b depth.\n", + fb->fb_id, fb->width, fb->height, fb->bpp, fb->depth); - if (!fb->handle) { - av_log(avctx, AV_LOG_ERROR, "No handle set on framebuffer: " - "maybe you need some additional capabilities?\n"); - err = AVERROR(EINVAL); - goto fail; + ctx->width = fb->width; + ctx->height = fb->height; + + if (!fb->handle) { + av_log(avctx, AV_LOG_ERROR, "No handle set on framebuffer: " + "maybe you need some additional capabilities?\n"); + err = AVERROR(EINVAL); + goto fail; + } } stream = avformat_new_stream(avctx, NULL); @@ -411,8 +585,8 @@ static av_cold int kmsgrab_read_header(AVFormatContext *avctx) stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; stream->codecpar->codec_id = AV_CODEC_ID_WRAPPED_AVFRAME; - stream->codecpar->width = fb->width; - stream->codecpar->height = fb->height; + stream->codecpar->width = ctx->width; + stream->codecpar->height = ctx->height; stream->codecpar->format = AV_PIX_FMT_DRM_PRIME; avpriv_set_pts_info(stream, 64, 1, 1000000); @@ -426,8 +600,8 @@ static av_cold int kmsgrab_read_header(AVFormatContext *avctx) ctx->frames->format = AV_PIX_FMT_DRM_PRIME; ctx->frames->sw_format = ctx->format, - ctx->frames->width = fb->width; - ctx->frames->height = fb->height; + ctx->frames->width = ctx->width; + ctx->frames->height = ctx->height; err = av_hwframe_ctx_init(ctx->frames_ref); if (err < 0) { @@ -444,6 +618,9 @@ fail: drmModeFreePlaneResources(plane_res); drmModeFreePlane(plane); drmModeFreeFB(fb); +#if HAVE_LIBDRM_GETFB2 + drmModeFreeFB2(fb2); +#endif return err; } @@ -468,7 +645,7 @@ static const AVOption options[] = { { .i64 = AV_PIX_FMT_BGR0 }, 0, UINT32_MAX, FLAGS }, { "format_modifier", "DRM format modifier for framebuffer", OFFSET(drm_format_modifier), AV_OPT_TYPE_INT64, - { .i64 = DRM_FORMAT_MOD_NONE }, 0, INT64_MAX, FLAGS }, + { .i64 = DRM_FORMAT_MOD_INVALID }, 0, INT64_MAX, FLAGS }, { "crtc_id", "CRTC ID to define capture source", OFFSET(source_crtc), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, UINT32_MAX, FLAGS },