From a5437e1ad12b443b9f173329145490d5c37b2eb8 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 12 Feb 2021 18:35:15 +0100 Subject: [PATCH] Display frame via KMS --- main.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 109 insertions(+), 6 deletions(-) diff --git a/main.c b/main.c index 2a35ba1..18cb5b1 100644 --- a/main.c +++ b/main.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -8,6 +9,8 @@ #include #include #include +#include +#include static enum AVPixelFormat hw_pix_fmt = AV_PIX_FMT_NONE; @@ -64,6 +67,44 @@ static enum AVPixelFormat get_hw_format(AVCodecContext *ctx, return AV_PIX_FMT_NONE; } +static uint32_t pick_crtc(int drm_fd) { + drmModeRes *res = drmModeGetResources(drm_fd); + if (!res) { + perror("drmModeGetResources"); + return 0; + } + + drmModeConnector *conn = NULL; + for (int i = 0; i < res->count_connectors; i++) { + drmModeConnector *c = drmModeGetConnector(drm_fd, res->connectors[i]); + if (c->connection == DRM_MODE_CONNECTED) { + conn = c; + break; + } + drmModeFreeConnector(c); + } + if (!conn) { + fprintf(stderr, "Failed to find connected connector\n"); + return 0; + } + if (conn->encoder_id == 0) { + fprintf(stderr, "Connector is not connected to an encoder\n"); + return 0; + } + + drmModeFreeResources(res); + + drmModeEncoder *enc = drmModeGetEncoder(drm_fd, conn->encoder_id); + if (enc->crtc_id == 0) { + fprintf(stderr, "Encoder is not connected to a CRTC\n"); + return 0; + } + uint32_t crtc_id = enc->crtc_id; + drmModeFreeEncoder(enc); + + return crtc_id; +} + static int decode_frame(AVFormatContext *input_ctx, int video_stream, AVCodecContext *decoder_ctx, AVFrame **frame_ptr) { int ret; @@ -137,6 +178,52 @@ static void print_drm_frame_desc(const AVDRMFrameDescriptor *drm_frame_desc) { } } +static uint32_t import_drm_frame_desc(const AVDRMFrameDescriptor *drm_frame_desc, + int drm_fd, int width, int height) { + // VA-API drivers may use separate layers with one plane each, or a single + // layer with multiple planes. We need to handle both. + uint32_t drm_format = get_drm_frame_format(drm_frame_desc); + if (drm_format == DRM_FORMAT_INVALID) { + fprintf(stderr, "Failed to get DRM frame format\n"); + return 0; + } + fprintf(stderr, "DRM format: 0x%X\n", drm_format); + + uint32_t handles[4]; + uint32_t pitches[4]; + uint32_t offsets[4]; + int k = 0; + for (int i = 0; i < drm_frame_desc->nb_layers; i++) { + const AVDRMLayerDescriptor *drm_layer = &drm_frame_desc->layers[i]; + for (int j = 0; j < drm_layer->nb_planes; j++) { + const AVDRMPlaneDescriptor *drm_plane = &drm_layer->planes[j]; + const AVDRMObjectDescriptor *drm_object = &drm_frame_desc->objects[drm_plane->object_index]; + + uint32_t handle = 0; + int ret = drmPrimeFDToHandle(drm_fd, drm_object->fd, &handle); + if (ret < 0) { + perror("drmPrimeFDToHandle"); + return 0; + } + + handles[k] = handle; + pitches[k] = drm_plane->pitch; + offsets[k] = drm_plane->offset; + k++; + } + } + + uint32_t fb_id = 0; + int ret = drmModeAddFB2(drm_fd, width, height, drm_format, + handles, pitches, offsets, &fb_id, 0); + if (ret < 0) { + perror("drmModeAddFB2"); + return 0; + } + + return fb_id; +} + int main(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "usage: %s \n", argv[0]); @@ -218,6 +305,17 @@ int main(int argc, char *argv[]) { return 1; } + int drm_fd = open("/dev/dri/card0", O_RDWR); + if (drm_fd < 0) { + fprintf(stderr, "Failed to open DRM primary node\n"); + return 1; + } + + uint32_t crtc_id = pick_crtc(drm_fd); + if (crtc_id == 0) { + return 1; + } + AVFrame *frame = NULL; ret = decode_frame(input_ctx, video_stream, decoder_ctx, &frame); if (ret < 0) { @@ -240,14 +338,19 @@ int main(int argc, char *argv[]) { AVDRMFrameDescriptor *drm_frame_desc = (void *)drm_frame->data[0]; print_drm_frame_desc(drm_frame_desc); - // VA-API drivers may use separate layers with one plane each, or a single - // layer with multiple planes. We need to handle both. - uint32_t drm_format = get_drm_frame_format(drm_frame_desc); - if (drm_format == DRM_FORMAT_INVALID) { - fprintf(stderr, "Failed to get DRM frame format\n"); + uint32_t fb_id = import_drm_frame_desc(drm_frame_desc, drm_fd, + drm_frame->width, drm_frame->height); + if (!fb_id) { return 1; } - fprintf(stderr, "DRM format: 0x%X\n", drm_format); + + ret = drmModePageFlip(drm_fd, crtc_id, fb_id, 0, NULL); + if (ret < 0) { + perror("drmModePageFlip"); + return 1; + } + + sleep(5); av_frame_free(&drm_frame); av_frame_free(&frame); -- 2.34.2