This patch adds KMS/DRM output device for rendering a video stream using KMS/DRM dumb buffer. The proposed implementation is very basic, only bgr0 pixel format is currently supported (the most common format with KMS/DRM). To enable this output device you need to configure FFmpeg with --enable-libdrm. Example: ffmpeg -re -i INPUT -pix_fmt bgr0 -f kmsdumb /dev/dri/card0
Nicolas Caramelli
From 939bbbf9feeda3e94f61f2ca3b3c536702e2457a Mon Sep 17 00:00:00 2001 From: Nicolas Caramelli <caramelli.de...@gmail.com> Date: Sat, 16 Jan 2021 22:55:12 +0100 Subject: [PATCH] libavdevice: Add KMS/DRM output device Signed-off-by: Nicolas Caramelli <caramelli.de...@gmail.com> --- configure | 1 + doc/outdevs.texi | 14 +++ libavdevice/Makefile | 1 + libavdevice/alldevices.c | 1 + libavdevice/kmsdumb.c | 246 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 263 insertions(+) create mode 100644 libavdevice/kmsdumb.c diff --git a/configure b/configure index 900505756b..1baddbc665 100755 --- a/configure +++ b/configure @@ -3417,6 +3417,7 @@ gdigrab_indev_select="bmp_decoder" iec61883_indev_deps="libiec61883" jack_indev_deps="libjack" jack_indev_deps_any="sem_timedwait dispatch_dispatch_h" +kmsdumb_outdev_deps="libdrm" kmsgrab_indev_deps="libdrm" lavfi_indev_deps="avfilter" libcdio_indev_deps="libcdio" diff --git a/doc/outdevs.texi b/doc/outdevs.texi index aaf247995c..b458632c40 100644 --- a/doc/outdevs.texi +++ b/doc/outdevs.texi @@ -266,6 +266,20 @@ ffmpeg -re -i INPUT -c:v rawvideo -pix_fmt bgra -f fbdev /dev/fb0 See also @url{http://linux-fbdev.sourceforge.net/}, and fbset(1). +@section kmsdumb + +KMS/DRM output device. + +This output device allows one to show a video stream using KMS/DRM dumb buffer. +Only bgr0 pixel format is currently supported (the most common format with KMS/DRM). +To enable this output device you need to configure FFmpeg with --enable-libdrm. + +@subsection Examples +Play a file on KMS/DRM device @file{/dev/dri/card0}. +@example +ffmpeg -re -i INPUT -pix_fmt bgr0 -f kmsdumb /dev/dri/card0 +@end example + @section opengl OpenGL output device. diff --git a/libavdevice/Makefile b/libavdevice/Makefile index 0dfe47a1f4..965093dc0c 100644 --- a/libavdevice/Makefile +++ b/libavdevice/Makefile @@ -31,6 +31,7 @@ OBJS-$(CONFIG_FBDEV_OUTDEV) += fbdev_enc.o \ OBJS-$(CONFIG_GDIGRAB_INDEV) += gdigrab.o OBJS-$(CONFIG_IEC61883_INDEV) += iec61883.o OBJS-$(CONFIG_JACK_INDEV) += jack.o timefilter.o +OBJS-$(CONFIG_KMSDUMB_OUTDEV) += kmsdumb.o OBJS-$(CONFIG_KMSGRAB_INDEV) += kmsgrab.o OBJS-$(CONFIG_LAVFI_INDEV) += lavfi.o OBJS-$(CONFIG_OPENAL_INDEV) += openal-dec.o diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c index 92b27a1d14..9a982b60b1 100644 --- a/libavdevice/alldevices.c +++ b/libavdevice/alldevices.c @@ -39,6 +39,7 @@ extern AVOutputFormat ff_fbdev_muxer; extern AVInputFormat ff_gdigrab_demuxer; extern AVInputFormat ff_iec61883_demuxer; extern AVInputFormat ff_jack_demuxer; +extern AVOutputFormat ff_kmsdumb_muxer; extern AVInputFormat ff_kmsgrab_demuxer; extern AVInputFormat ff_lavfi_demuxer; extern AVInputFormat ff_openal_demuxer; diff --git a/libavdevice/kmsdumb.c b/libavdevice/kmsdumb.c new file mode 100644 index 0000000000..6312773ff5 --- /dev/null +++ b/libavdevice/kmsdumb.c @@ -0,0 +1,246 @@ +/* + * KMS/DRM ouput device + * + * This file is part of FFmpeg. + * + * 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. + * + * 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <fcntl.h> +#include <sys/mman.h> +#include <unistd.h> +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include "avdevice.h" + +typedef struct { + AVClass *class; + int fd; + uint32_t connector_id; + uint32_t encoder_id; + uint32_t crtc_id; + drmModeCrtc *crtc; + drmModeModeInfo mode_info; + uint32_t handle; + uint32_t width; + uint32_t height; + uint32_t pitch; + uint32_t size; + uint32_t fb_id; + uint8_t *data; +} KMSDumbContext; + +static av_cold int kmsdumb_write_trailer(AVFormatContext *s) +{ + KMSDumbContext *kmsdumb = s->priv_data; + struct drm_mode_destroy_dumb dreq; + + if (kmsdumb->data) { + munmap(kmsdumb->data, kmsdumb->size); + kmsdumb->data = NULL; + } + + if (kmsdumb->fb_id) { + drmModeRmFB(kmsdumb->fd, kmsdumb->fb_id); + kmsdumb->fb_id = 0; + } + + if (kmsdumb->handle) { + memset(&dreq, 0, sizeof(struct drm_mode_destroy_dumb)); + dreq.handle = kmsdumb->handle; + drmIoctl(kmsdumb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + kmsdumb->handle = 0; + } + + if (kmsdumb->crtc) { + drmModeSetCrtc(kmsdumb->fd, + kmsdumb->crtc->crtc_id, kmsdumb->crtc->buffer_id, + kmsdumb->crtc->x, kmsdumb->crtc->y, + &kmsdumb->connector_id, 1, &kmsdumb->crtc->mode); + drmModeFreeCrtc(kmsdumb->crtc); + kmsdumb->crtc = NULL; + } + + close(kmsdumb->fd); + + return 0; +} + +static av_cold int kmsdumb_write_header(AVFormatContext *s) +{ + KMSDumbContext *kmsdumb = s->priv_data; + drmModeRes *res; + drmModeConnector *connector; + drmModeEncoder *encoder; + struct drm_mode_create_dumb creq; + struct drm_mode_map_dumb mreq; + int ret; + + if (s->nb_streams > 1 || + s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO || + s->streams[0]->codecpar->codec_id != AV_CODEC_ID_RAWVIDEO) { + av_log(s, AV_LOG_ERROR, "Only supports one rawvideo stream\n"); + return AVERROR(EINVAL); + } + + if (s->streams[0]->codecpar->format != AV_PIX_FMT_BGR0) { + av_log(s, AV_LOG_ERROR, "Unsupported pixel format." + " Only AV_PIX_FMT_BGR0 is currently supported.\n"); + return AVERROR(EINVAL); + } + + if ((kmsdumb->fd = avpriv_open(s->url, O_RDWR)) == -1) { + ret = AVERROR(errno); + av_log(s, AV_LOG_ERROR, + "Failed to open DRM device '%s': %s\n", s->url, av_err2str(ret)); + return ret; + } + + res = drmModeGetResources(kmsdumb->fd); + if (!res) { + ret = AVERROR(errno); + av_log(s, AV_LOG_ERROR, + "Failed to get resources: %s\n", av_err2str(ret)); + goto fail; + } + kmsdumb->connector_id = res->connectors[0]; + drmModeFreeResources(res); + + connector = drmModeGetConnector(kmsdumb->fd, kmsdumb->connector_id); + if (!connector) { + ret = AVERROR(errno); + av_log(s, AV_LOG_ERROR, + "Failed to get connector: %s\n", av_err2str(ret)); + goto fail; + } + kmsdumb->encoder_id = connector->encoder_id; + memcpy(&kmsdumb->mode_info, &connector->modes[0], sizeof(drmModeModeInfo)); + drmModeFreeConnector(connector); + + encoder = drmModeGetEncoder(kmsdumb->fd, kmsdumb->encoder_id); + if (!encoder) { + ret = AVERROR(errno); + av_log(s, AV_LOG_ERROR, + "Failed to get encoder: %s\n", av_err2str(ret)); + goto fail; + } + kmsdumb->crtc_id = encoder->crtc_id; + drmModeFreeEncoder(encoder); + + kmsdumb->crtc = drmModeGetCrtc(kmsdumb->fd, kmsdumb->crtc_id); + if (!kmsdumb->crtc) { + ret = AVERROR(errno); + av_log(s, AV_LOG_ERROR, + "Failed to get CRTC: %s\n", av_err2str(ret)); + goto fail; + } + + memset(&creq, 0, sizeof(struct drm_mode_create_dumb)); + creq.width = kmsdumb->mode_info.hdisplay; + creq.height = kmsdumb->mode_info.vdisplay; + creq.bpp = 32; + if (drmIoctl(kmsdumb->fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq) == -1) { + ret = AVERROR(errno); + av_log(s, AV_LOG_ERROR, + "Failed to create dumb buffer: %s\n", av_err2str(ret)); + goto fail; + } + + kmsdumb->handle = creq.handle; + kmsdumb->width = creq.width; + kmsdumb->height = creq.height; + kmsdumb->pitch = creq.pitch; + kmsdumb->size = creq.size; + + if (drmModeAddFB(kmsdumb->fd, kmsdumb->width, kmsdumb->height, 24, 32, + kmsdumb->pitch, kmsdumb->handle, &kmsdumb->fb_id) == -1) { + ret = AVERROR(errno); + av_log(s, AV_LOG_ERROR, + "Failed to create framebuffer object: %s\n", av_err2str(ret)); + goto fail; + } + + memset(&mreq, 0, sizeof(struct drm_mode_map_dumb)); + mreq.handle = kmsdumb->handle; + if (drmIoctl(kmsdumb->fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq) == -1) { + ret = AVERROR(errno); + av_log(s, AV_LOG_ERROR, + "Failed to map dumb buffer: %s\n", av_err2str(ret)); + goto fail; + } + + kmsdumb->data = mmap(NULL, kmsdumb->size, PROT_WRITE, MAP_SHARED, + kmsdumb->fd, mreq.offset); + if (kmsdumb->data == MAP_FAILED) { + ret = AVERROR(errno); + av_log(s, AV_LOG_ERROR, + "Failed to mmap dumb buffer: %s\n", av_err2str(ret)); + goto fail; + } + + return 0; + fail: + kmsdumb_write_trailer(s); + return ret; +} + +static int kmsdumb_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + KMSDumbContext *kmsdumb = s->priv_data; + uint32_t video_width = s->streams[0]->codecpar->width; + uint32_t video_height = s->streams[0]->codecpar->height; + uint32_t disp_height = FFMIN(kmsdumb->height, video_height); + uint32_t bytes_to_copy = FFMIN(kmsdumb->width, video_width) * 4; + uint32_t video_pitch = video_width * 4; + uint8_t *pin = pkt->data; + uint8_t *pout = kmsdumb->data; + int i, ret; + + for (i = 0; i < disp_height; i++) { + memcpy(pout, pin, bytes_to_copy); + pout += kmsdumb->pitch; + pin += video_pitch; + } + + if (drmModeSetCrtc(kmsdumb->fd, kmsdumb->crtc_id, kmsdumb->fb_id, 0, 0, + &kmsdumb->connector_id, 1, &kmsdumb->mode_info) == -1) { + ret = AVERROR(errno); + av_log(s, AV_LOG_ERROR, "Failed to set CRTC: %s\n", av_err2str(ret)); + return ret; + } + + return 0; +} + +static const AVClass kmsdumb_class = { + .class_name = "kmsdumb outdev", + .item_name = av_default_item_name, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT, +}; + +AVOutputFormat ff_kmsdumb_muxer = { + .name = "kmsdumb", + .long_name = NULL_IF_CONFIG_SMALL("KMS/DRM dumb buffer"), + .priv_data_size = sizeof(KMSDumbContext), + .audio_codec = AV_CODEC_ID_NONE, + .video_codec = AV_CODEC_ID_RAWVIDEO, + .write_header = kmsdumb_write_header, + .write_packet = kmsdumb_write_packet, + .write_trailer = kmsdumb_write_trailer, + .flags = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS, + .priv_class = &kmsdumb_class, +}; -- 2.12.5
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".