diff mbox series

[FFmpeg-devel] libavdevice: Add KMS/DRM output device

Message ID CAN8TSYnUBP7x4vWac-51ZgsrajYD4aMg47tJCoa8uc1GsATCHA@mail.gmail.com
State New
Headers show
Series [FFmpeg-devel] libavdevice: Add KMS/DRM output device
Related show

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

Nicolas Caramelli Jan. 16, 2021, 10:12 p.m. UTC
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
Subject: [PATCH] libavdevice: Add KMS/DRM output device

Signed-off-by: Nicolas Caramelli <caramelli.devel@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

Comments

Mark Thompson Jan. 18, 2021, 11:37 p.m. UTC | #1
On 16/01/2021 22:12, Nicolas Caramelli wrote:
> 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

If you want to render things to a normal display device why not use a normal video player?  Or even ffplay?

IMO something like this would be of more value as a simple video player example with the documentation rather than including it as weirdly constrained library code which will see very little use.

(Note that I would argue against adding more general display output devices which are already present, like fb and xv, because they are of essentially no value to libavdevice users.  Removing legacy code is harder, though.)

- Mark
Anton Khirnov Jan. 19, 2021, 10:44 a.m. UTC | #2
Quoting Mark Thompson (2021-01-19 00:37:09)
> On 16/01/2021 22:12, Nicolas Caramelli wrote:
> > 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
> 
> If you want to render things to a normal display device why not use a normal video player?  Or even ffplay?
> 
> IMO something like this would be of more value as a simple video player example with the documentation rather than including it as weirdly constrained library code which will see very little use.
> 
> (Note that I would argue against adding more general display output devices which are already present, like fb and xv, because they are of essentially no value to libavdevice users.  Removing legacy code is harder, though.)

+1

IMO libavdevice output devices are worse than useless and should all be
removed. Libavformat API is barely passable for input, but it really is
NOT something you should use to drive outputs.
John Cox Jan. 19, 2021, 12:39 p.m. UTC | #3
On Mon, 18 Jan 2021 23:37:09 +0000, you wrote:
>On 16/01/2021 22:12, Nicolas Caramelli wrote:
>> 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
>
>If you want to render things to a normal display device why not use a normal video player?  Or even ffplay?
>
>IMO something like this would be of more value as a simple video player example with the documentation rather than including it as weirdly constrained library code which will see very little use.
>
>(Note that I would argue against adding more general display output devices which are already present, like fb and xv, because they are of essentially no value to libavdevice users.  Removing legacy code is harder, though.)

I take your point but I personally have found it very useful to have
simple display devices on the output of ffmpeg for testing purposes.
Though I guess that if I want that then the device should be bundled
with the application rather than in a library.

John Cox
Nicolas Caramelli Jan. 19, 2021, 5:59 p.m. UTC | #4
I appreciate to directly experiment ffmpeg without X11 or SDL
dependencies, just using the ffmpeg binary.
This is especially true when I work on embedded systems and I use it a lot.

Even though the fbdev output device is available, the Linux
Framebuffer is often considered obsolete in favor of KMS/DRM.
But if there are plans to remove output devices like fbdev or xv, I
understand that it is difficult to consider supporting a KMS/DRM
output ...

Nicolas Caramelli

Le mar. 19 janv. 2021 à 13:46, John Cox <jc@kynesim.co.uk> a écrit :
>
> On Mon, 18 Jan 2021 23:37:09 +0000, you wrote:
> >On 16/01/2021 22:12, Nicolas Caramelli wrote:
> >> 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
> >
> >If you want to render things to a normal display device why not use a normal video player?  Or even ffplay?
> >
> >IMO something like this would be of more value as a simple video player example with the documentation rather than including it as weirdly constrained library code which will see very little use.
> >
> >(Note that I would argue against adding more general display output devices which are already present, like fb and xv, because they are of essentially no value to libavdevice users.  Removing legacy code is harder, though.)
>
> I take your point but I personally have found it very useful to have
> simple display devices on the output of ffmpeg for testing purposes.
> Though I guess that if I want that then the device should be bundled
> with the application rather than in a library.
>
> John Cox
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Nicolas George Jan. 19, 2021, 7:04 p.m. UTC | #5
Anton Khirnov (12021-01-19):
> > (Note that I would argue against adding more general display output
> > devices which are already present, like fb and xv, because they are
> > of essentially no value to libavdevice users.  Removing legacy code
> > is harder, though.)
> 
> +1
> 
> IMO libavdevice output devices are worse than useless and should all be
> removed. Libavformat API is barely passable for input, but it really is
> NOT something you should use to drive outputs.

I use libavdevice in my projects, and these output devices have a lot of
value to me.

With this kind of user-hostile attitude where you disregard everybody's
use case except those you deem valuable, you almost killed the project
once. It was ten years ago, time to try again?
James Almer Jan. 19, 2021, 7:39 p.m. UTC | #6
On 1/19/2021 4:04 PM, Nicolas George wrote:
> Anton Khirnov (12021-01-19):
>>> (Note that I would argue against adding more general display output
>>> devices which are already present, like fb and xv, because they are
>>> of essentially no value to libavdevice users.  Removing legacy code
>>> is harder, though.)
>>
>> +1
>>
>> IMO libavdevice output devices are worse than useless and should all be
>> removed. Libavformat API is barely passable for input, but it really is
>> NOT something you should use to drive outputs.
> 
> I use libavdevice in my projects, and these output devices have a lot of
> value to me.
> 
> With this kind of user-hostile attitude where you disregard everybody's
> use case except those you deem valuable, you almost killed the project
> once. It was ten years ago, time to try again?

This entire paragraph was absolutely unnecessary and uncalled for as a 
reply to a personal opinion.
The first one alone was more than enough to let him and Mark know output 
devices have active users.
Mark Thompson Jan. 19, 2021, 8:02 p.m. UTC | #7
On 19/01/2021 19:04, Nicolas George wrote:
> Anton Khirnov (12021-01-19):
>>> (Note that I would argue against adding more general display output
>>> devices which are already present, like fb and xv, because they are
>>> of essentially no value to libavdevice users.  Removing legacy code
>>> is harder, though.)
> 
> I use libavdevice in my projects, and these output devices have a lot of
> value to me.
Can you explain in more detail your use of libavdevice here?

My general sense is that while they might sometimes be convenient to use if you are already working in a libav* context, they are so limited (by both the API and the implementation) that most users who want video output to a screen will turn more full-featured players to implement this.

(To be clear here, I am talking specifically about outputs to a screen like fbdev/xv, not about output to non-screen-like devices such as v4l2 which certainly do have other use-cases.)

Thanks,

- Mark
Marton Balint Jan. 19, 2021, 11:40 p.m. UTC | #8
On Tue, 19 Jan 2021, Anton Khirnov wrote:

> Quoting Mark Thompson (2021-01-19 00:37:09)
>> On 16/01/2021 22:12, Nicolas Caramelli wrote:
>> > 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
>> 
>> If you want to render things to a normal display device why not use a normal video player?  Or even ffplay?
>> 
>> IMO something like this would be of more value as a simple video player example with the documentation rather than including it as weirdly constrained library code which will see very little use.
>> 
>> (Note that I would argue against adding more general display output devices which are already present, like fb and xv, because they are of essentially no value to libavdevice users.  Removing legacy code is harder, though.)
>
> +1
>
> IMO libavdevice output devices are worse than useless and should all be
> removed.

Decklink is heavily used for both input and output.

Regards,
Marton
Nicolas George Jan. 20, 2021, 11:38 a.m. UTC | #9
James Almer (12021-01-19):
> On 1/19/2021 4:04 PM, Nicolas George wrote:
> > Anton Khirnov (12021-01-19):
> > > > (Note that I would argue against adding more general display output
> > > > devices which are already present, like fb and xv, because they are
> > > > of essentially no value to libavdevice users.  Removing legacy code
> > > > is harder, though.)

> > > IMO libavdevice output devices are worse than useless and should all be
> > > removed. Libavformat API is barely passable for input, but it really is
> > > NOT something you should use to drive outputs.
> > 
> > I use libavdevice in my projects, and these output devices have a lot of
> > value to me.
> > 
> > With this kind of user-hostile attitude where you disregard everybody's
> > use case except those you deem valuable, you almost killed the project
> > once. It was ten years ago, time to try again?
> 
> This entire paragraph was absolutely unnecessary and uncalled for as a reply
> to a personal opinion.
> The first one alone was more than enough to let him and Mark know output
> devices have active users.

The first paragraph was enough to reply to Mark's interrogation about
the usefulness of these devices.

The second paragraph was needed to let Anton know my displeasure at
seeing other people's work and effort, including mine, insulted by being
called "worse than useless". It would not have been necessary if you had
called Anton out for the insult.

And this is the tip of the iceberg. If we do not want FFmpeg to go the
way of libav, to become a mere wrapper library, forever following and
slowly falling into relevance, some things that are taboo here will need
to be said, especially about a certain unspoken exigence for purity.

Regards,
Nicolas George Jan. 20, 2021, 11:53 a.m. UTC | #10
Mark Thompson (12021-01-19):
> Can you explain in more detail your use of libavdevice here?
> 
> My general sense is that while they might sometimes be convenient to
> use if you are already working in a libav* context, they are so
> limited (by both the API and the implementation) that most users who
> want video output to a screen will turn more full-featured players to
> implement this.
> 
> (To be clear here, I am talking specifically about outputs to a screen
> like fbdev/xv, not about output to non-screen-like devices such as
> v4l2 which certainly do have other use-cases.)

For starter, since libavcodec and libavformat are supposed to be the
preferred way of dealing with audio and video, we should assume that
people who develop projects about it will likely have their data already
in AVFrame and AVPacket format, so the fact that these devices work with
the same kind of API is a huge plus.

Second, even though they are limited, they do the work, and for a lot of
projects that is plenty enough. Their existence in libavdevices avoids
reinventing the wheel, and their common API makes it easy to switch,
getting us portability for cheap.

Also, I will turn the board on you: why do YOU object to these
components?

Clearly, somebody found them valuable enough to work at implementing
them. Clearly, somebody found them valuable enough to work at reviewing
and applying the patches. If you do not see their usefulness, compile
with --disable-libavdevice, fine. But why do you spend your time arguing
about it?

Note that FFmpeg is frequently called "the Swiss army knife of
audio-video". Well, Swiss army knives have half a dozen blades that you
have no idea what they are for, and neither do mine, and who have
nonetheless saved somebody's life.

Regards,
Mark Thompson Jan. 20, 2021, 12:41 p.m. UTC | #11
On 20/01/2021 11:53, Nicolas George wrote:
> Mark Thompson (12021-01-19):
>> Can you explain in more detail your use of libavdevice here?
>>
>> My general sense is that while they might sometimes be convenient to
>> use if you are already working in a libav* context, they are so
>> limited (by both the API and the implementation) that most users who
>> want video output to a screen will turn more full-featured players to
>> implement this.
>>
>> (To be clear here, I am talking specifically about outputs to a screen
>> like fbdev/xv, not about output to non-screen-like devices such as
>> v4l2 which certainly do have other use-cases.)
> 
> For starter, since libavcodec and libavformat are supposed to be the
> preferred way of dealing with audio and video, we should assume that
> people who develop projects about it will likely have their data already
> in AVFrame and AVPacket format, so the fact that these devices work with
> the same kind of API is a huge plus.
> 
> Second, even though they are limited, they do the work, and for a lot of
> projects that is plenty enough. Their existence in libavdevices avoids
> reinventing the wheel, and their common API makes it easy to switch,
> getting us portability for cheap.
> 
> Also, I will turn the board on you: why do YOU object to these
> components?

Because the libavformat API is not suitable for (especially) these use-cases, so including them in the library feels like a hack that happened to be convenient for some subset of people even though it doesn't really fit and wants something else.

Those components do feel the worst to me, but this is really a general complaint about libavdevice as a whole.  I would prefer that is either mostly moved into the ffmpeg utility (where it has clear use and could be better integrated), or updated to have a device-oriented API (if there are external users, hence the question).

For an API, the things which immediately come to mind are:
* Handle frames as well as packets.
   * Including hardware frames - DRM objects from KMS/V4L2, D3D surfaces from Windows desktop duplication (which doesn't currently exist but should).
* Clear core option set - currently almost everything is set by inconsistent private options; things like pixel/sample format, frame/sample rate, geometry and hardware device should be common options to all.
* Asynchronicity - a big annoyance in current recording scenarios with the ffmpeg utility is that both audio and video capture block, and do so on the same thread which results in skipped frames.
* Capability probing - the existing method of options which log the capabilities are not very useful for API users.

(Not meant to be an exhaustive list.)

Thanks,

- Mark
Nicolas George Jan. 20, 2021, 1:15 p.m. UTC | #12
Mark Thompson (12021-01-20):
> Because the libavformat API is not suitable for (especially) these
> use-cases, so including them in the library feels like a hack that
> happened to be convenient for some subset of people even though it
> doesn't really fit and wants something else.

Again, I have used them in personal projects, and they do the work. The
API can be enhanced, clearly, and you have some ideas that match mine
about it.

But before discussing this further with you, I would like you to
acknowledge this: these devices are useful, removing or limiting them is
the wrong way to go; the right way is enhancing them.

Regards,
Anton Khirnov Jan. 23, 2021, 3:03 p.m. UTC | #13
Quoting Marton Balint (2021-01-20 00:40:30)
> 
> 
> On Tue, 19 Jan 2021, Anton Khirnov wrote:
> 
> > Quoting Mark Thompson (2021-01-19 00:37:09)
> >> On 16/01/2021 22:12, Nicolas Caramelli wrote:
> >> > 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
> >> 
> >> If you want to render things to a normal display device why not use a normal video player?  Or even ffplay?
> >> 
> >> IMO something like this would be of more value as a simple video player example with the documentation rather than including it as weirdly constrained library code which will see very little use.
> >> 
> >> (Note that I would argue against adding more general display output devices which are already present, like fb and xv, because they are of essentially no value to libavdevice users.  Removing legacy code is harder, though.)
> >
> > +1
> >
> > IMO libavdevice output devices are worse than useless and should all be
> > removed.
> 
> Decklink is heavily used for both input and output.

But does anyone use anything else than ffmpeg.c for that?

The question here is not whether this functionality should exist at all,
but whether libavdevice/libavformat is the right place for it.
Nicolas George Jan. 25, 2021, 4:20 p.m. UTC | #14
Anton Khirnov (12021-01-23):
> But does anyone use anything else than ffmpeg.c for that?
> 
> The question here is not whether this functionality should exist at all,
> but whether libavdevice/libavformat is the right place for it.

If something is useful in ffmpeg.c, then it is likely to be useful in
other projects as well. Projects that we do not know about.

All complex features should be in libraries, the fftools should be
minimalistic interfaces.

Regards,
diff mbox series

Patch

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,
+};