Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package wf-recorder for openSUSE:Factory 
checked in at 2023-08-23 14:58:41
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/wf-recorder (Old)
 and      /work/SRC/openSUSE:Factory/.wf-recorder.new.1766 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "wf-recorder"

Wed Aug 23 14:58:41 2023 rev:5 rq:1105455 version:0.4.0+git0

Changes:
--------
--- /work/SRC/openSUSE:Factory/wf-recorder/wf-recorder.changes  2023-01-17 
17:35:54.465386143 +0100
+++ /work/SRC/openSUSE:Factory/.wf-recorder.new.1766/wf-recorder.changes        
2023-08-23 14:59:52.106231945 +0200
@@ -1,0 +2,21 @@
+Wed Aug 23 09:45:48 UTC 2023 - amme...@gmail.com
+
+- Update to version 0.4.0+git0:
+  * meson.build: bump version to 0.4.0
+  * don't ask for overwrite for the char device (#141)
+  * add --no-dmabuf option (#225)
+  * Break early when exit_main_loop is set in encoder thread (#223)
+  * Dynamically increase number of used buffers (#221)
+  * Add X2RGB10 format mapping for DMA-BUFs (#224)
+  * Add support for 16 bit deep formats (#184)
+  * Revert "Use VP9+Opus on MKV for recordings"
+  * Revert "Use VP8+Vorbis on WebM for recordings (#202)"
+  * Use codec format which best matches input (#215)
+  * Retry capture on failure
+  * Use gbm_bo_create_with_modifiers when possible
+  * Reduce memory usage with DMA-BUF capture (#219)
+  * Use DMA-BUF with HW encoding (#206)
+  * Only flush when video codec has delay (#209)
+  * ci: add to safe directories so that git parse-rev works (#210)
+
+-------------------------------------------------------------------

Old:
----
  wf-recorder-0.3.0+git19.tar.gz

New:
----
  wf-recorder-0.4.0+git0.obscpio
  wf-recorder-0.4.0+git0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ wf-recorder.spec ++++++
--- /var/tmp/diff_new_pack.IJJEGe/_old  2023-08-23 14:59:52.842233260 +0200
+++ /var/tmp/diff_new_pack.IJJEGe/_new  2023-08-23 14:59:52.846233268 +0200
@@ -18,7 +18,7 @@
 
 
 Name:           wf-recorder
-Version:        0.3.0+git19
+Version:        0.4.0+git0
 Release:        0%{?dist}
 Summary:        Utility program for screen recording of wlroots-based 
compositors
 License:        MIT
@@ -27,10 +27,12 @@
 Source0:        %{name}-%{version}.tar.gz
 BuildRequires:  gcc
 BuildRequires:  gcc-c++
-BuildRequires:  meson >= 0.47.0
+BuildRequires:  meson >= 0.54.0
 BuildRequires:  pkgconfig
+BuildRequires:  pkgconfig(gbm)
 BuildRequires:  pkgconfig(libavcodec)
 BuildRequires:  pkgconfig(libavdevice)
+BuildRequires:  pkgconfig(libavfilter)
 BuildRequires:  pkgconfig(libavformat)
 BuildRequires:  pkgconfig(libavutil)
 BuildRequires:  pkgconfig(libpulse-simple)

++++++ _service ++++++
--- /var/tmp/diff_new_pack.IJJEGe/_old  2023-08-23 14:59:52.902233368 +0200
+++ /var/tmp/diff_new_pack.IJJEGe/_new  2023-08-23 14:59:52.906233375 +0200
@@ -2,12 +2,12 @@
   <service name="obs_scm" mode="disabled">
     <param name="scm">git</param>
     <param name="url">https://github.com/ammen99/wf-recorder.git</param>
-    <param name="revision">460d454b1efd380a3f732f6fd70c7a5e265381f6</param>
+    <param name="revision">7e42d6c4dcb650808cc9ec3b7c2375764e5a2662</param>
     <param name="versionformat">@PARENT_TAG@+git@TAG_OFFSET@</param>
     <param name="versionrewrite-pattern">v(.*)</param>
     <param name="versionrewrite-replacement">\1</param>
     <param name="changesgenerate">enable</param>
-    <param name="changesauthor">llyyr.pub...@gmail.com</param>
+    <param name="changesauthor">amme...@gmail.com</param>
   </service>
   <service mode="disabled" name="tar" />
   <service mode="disabled" name="recompress">

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.IJJEGe/_old  2023-08-23 14:59:52.934233425 +0200
+++ /var/tmp/diff_new_pack.IJJEGe/_new  2023-08-23 14:59:52.938233432 +0200
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://github.com/ammen99/wf-recorder.git</param>
-              <param 
name="changesrevision">460d454b1efd380a3f732f6fd70c7a5e265381f6</param></service></servicedata>
+              <param 
name="changesrevision">7e42d6c4dcb650808cc9ec3b7c2375764e5a2662</param></service></servicedata>
 (No newline at EOF)
 

++++++ wf-recorder-0.3.0+git19.tar.gz -> wf-recorder-0.4.0+git0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/wf-recorder-0.3.0+git19/.github/workflows/build.yaml 
new/wf-recorder-0.4.0+git0/.github/workflows/build.yaml
--- old/wf-recorder-0.3.0+git19/.github/workflows/build.yaml    2023-01-02 
10:18:42.000000000 +0100
+++ new/wf-recorder-0.4.0+git0/.github/workflows/build.yaml     2023-08-23 
10:52:14.000000000 +0200
@@ -21,9 +21,11 @@
               'pkgconfig(wayland-client)' 'pkgconfig(wayland-protocols)' 
'pkgconfig(libpulse-simple)'
               'pkgconfig(libavutil)' 'pkgconfig(libavcodec)' 
'pkgconfig(libavformat)'
               'pkgconfig(libavdevice)' 'pkgconfig(libavfilter)' 
'pkgconfig(libswresample)'
+              'pkgconfig(gbm)'
       - uses: actions/checkout@v2
         with:
           fetch-depth: 0  # Shallow clones speed things up
+      - run: git config --global --add safe.directory '*' # Needed for git 
rev-parse
       - name: meson configure
         run: meson ./Build
       - name: compile with ninja
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/wf-recorder-0.3.0+git19/README.md 
new/wf-recorder-0.4.0+git0/README.md
--- old/wf-recorder-0.3.0+git19/README.md       2023-01-02 10:18:42.000000000 
+0100
+++ new/wf-recorder-0.4.0+git0/README.md        2023-08-23 10:52:14.000000000 
+0200
@@ -63,12 +63,12 @@
 meson build --prefix=/usr --buildtype=release
 ninja -C build
 ```
-Optionally configure with `-Ddefault_codec='codec'`. The default is libvpx. 
Now you can just run `./build/wf-recorder` or install it with `sudo ninja -C 
build install`.
+Optionally configure with `-Ddefault_codec='codec'`. The default is libx264. 
Now you can just run `./build/wf-recorder` or install it with `sudo ninja -C 
build install`.
 
 The man page can be read with `man ./manpage/wf-recorder.1`.
 
 # Usage
-In its simplest form, run `wf-recorder` to start recording and use Ctrl+C to 
stop. This will create a file called `recording.webm` in the current working 
directory using the default codec.
+In its simplest form, run `wf-recorder` to start recording and use Ctrl+C to 
stop. This will create a file called `recording.mp4` in the current working 
directory using the default codec.
 
 Use `-f <filename>` to specify the output file. In case of multiple outputs, 
you'll first be prompted to select the output you want to record. If you know 
the output name beforehand, you can use the `-o <output name>` option.
 
@@ -81,7 +81,7 @@
 You can record screen and sound simultaneously with
 
 ```
-wf-recorder --audio --file=recording_with_audio.webm
+wf-recorder --audio --file=recording_with_audio.mp4
 ```
 
 To specify an audio device, use the `-a<device>` or `--audio=<device>` options.
@@ -108,8 +108,8 @@
 wf-recorder --muxer=v4l2 --codec=rawvideo --file=/dev/video2
 ```
 
-To use GPU encoding, use a VAAPI codec (for ex. `vp8_vaapi`) and specify a GPU 
device to use with the `-d` option:
+To use GPU encoding, use a VAAPI codec (for ex. `h264_vaapi`) and specify a 
GPU device to use with the `-d` option:
 ```
-wf-recorder -f test-vaapi.webm -c vp8_vaapi -d /dev/dri/renderD128
+wf-recorder -f test-vaapi.mkv -c h264_vaapi -d /dev/dri/renderD128
 ```
 Some drivers report support for rgb0 data for vaapi input but really only 
support yuv planar formats. In this case, use the `-x yuv420p` or 
`--pixel-format yuv420p` option in addition to the vaapi options to convert the 
data to yuv planar data before sending it to the GPU.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/wf-recorder-0.3.0+git19/manpage/wf-recorder.1 
new/wf-recorder-0.4.0+git0/manpage/wf-recorder.1
--- old/wf-recorder-0.3.0+git19/manpage/wf-recorder.1   2023-01-02 
10:18:42.000000000 +0100
+++ new/wf-recorder-0.4.0+git0/manpage/wf-recorder.1    2023-08-23 
10:52:14.000000000 +0200
@@ -13,6 +13,7 @@
 .Op Fl c, -codec Ar output_codec
 .Op Fl r, -framerate Ar framerate
 .Op Fl d, -device Ar encoding_device
+.Op Fl -no-dmabuf
 .Op Fl D, -no-damage
 .Op Fl f Ar filename.ext
 .Op Fl F Ar filter_string
@@ -43,7 +44,7 @@
 .Ql Ctrl+C
 to stop.
 This will create a file called
-.Ql recording.webm
+.Ql recording.mp4
 in the current working directory using the default
 .Ar codec.
 .Pp
@@ -88,6 +89,11 @@
 option in addition to the vaapi options to convert the
 data in software, before sending it to the GPU.
 .Pp
+.It Fl -no-dmabuf
+By default, wf-recorder will try to use only GPU buffers and copies if using a 
GPU encoder.
+However, this can cause issues on some systems.
+In such cases, this option will disable the GPU copy and force a CPU one.
+.Pp
 .It Fl D , -no-damage
 By default, wf-recorder will request a new frame from the compositor
 only when the screen updates. This results in a much smaller output
@@ -111,7 +117,7 @@
 .Dl $ ffmpeg -muxers
 .Pp
 .It Fl F , -filter Ar filter_string
-Set the ffmpeg filter to use. VAAPI requires `hwupload,scale_vaapi=nv12` to 
work.
+Set the ffmpeg filter to use. VAAPI requires 
`scale_vaapi=format=nv12:out_range=full` to work.
 .Pp
 .It Fl g , -geometry Ar screen_geometry
 Selects a specific part of the screen. The format is "x,y WxH".
@@ -165,7 +171,7 @@
 .Dl $ wf-recorder -g "$(slurp)"
 .Pp
 You can record screen and sound simultaneously with
-.Dl $ wf-recorder --audio --file=recording_with_audio.webm
+.Dl $ wf-recorder --audio --file=recording_with_audio.mp4
 .Pp
 To specify an audio device, use the
 .Fl -a<DEVICE>
@@ -190,12 +196,12 @@
 .Dl $ wf-recorder --muxer=v4l2 --codec=rawvideo --file=/dev/video2
 .Pp
 To use GPU encoding, use a VAAPI codec (for ex.
-.Ql vp8_vaapi
+.Ql h264_vaapi
 ) and specify a GPU
 device to use with the
 .Fl d
 option:
-.Dl $ wf-recorder -f test-vaapi.webm -c vp8_vaapi -d /dev/dri/renderD128
+.Dl $ wf-recorder -f test-vaapi.mkv -c h264_vaapi -d /dev/dri/renderD128
 .Pp
 Some drivers report support for
 .Ql rgb0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/wf-recorder-0.3.0+git19/meson.build 
new/wf-recorder-0.4.0+git0/meson.build
--- old/wf-recorder-0.3.0+git19/meson.build     2023-01-02 10:18:42.000000000 
+0100
+++ new/wf-recorder-0.4.0+git0/meson.build      2023-08-23 10:52:14.000000000 
+0200
@@ -2,7 +2,7 @@
        'wf-recorder',
     'c',
        'cpp',
-       version: '0.3.0',
+       version: '0.4.0',
        license: 'MIT',
        meson_version: '>=0.54.0',
        default_options: [
@@ -43,7 +43,7 @@
 
 project_sources = ['src/frame-writer.cpp', 'src/main.cpp', 'src/averr.c']
 
-wayland_client = dependency('wayland-client')
+wayland_client = dependency('wayland-client', version: '>=1.20')
 wayland_protos = dependency('wayland-protocols', version: '>=1.14')
 
 pulse = dependency('libpulse-simple', required : get_option('pulse'))
@@ -60,6 +60,7 @@
 libavfilter = dependency('libavfilter')
 swr = dependency('libswresample')
 threads = dependency('threads')
+gbm = dependency('gbm')
 
 conf_data.set('HAVE_LIBAVDEVICE', libavdevice.found())
 
@@ -75,7 +76,7 @@
 dependencies = [
     wayland_client, wayland_protos,
     libavutil, libavcodec, libavformat, libavdevice, libavfilter,
-    wf_protos, threads, pulse, swr
+    wf_protos, threads, pulse, swr, gbm
 ]
 
 executable('wf-recorder', project_sources,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/wf-recorder-0.3.0+git19/meson_options.txt 
new/wf-recorder-0.4.0+git0/meson_options.txt
--- old/wf-recorder-0.3.0+git19/meson_options.txt       2023-01-02 
10:18:42.000000000 +0100
+++ new/wf-recorder-0.4.0+git0/meson_options.txt        2023-08-23 
10:52:14.000000000 +0200
@@ -1,6 +1,6 @@
-option('default_codec', type: 'string', value: 'libvpx', description: 'Codec 
that will be used by default')
-option('default_audio_codec', type: 'string', value: 'libvorbis', description: 
'Audio codec that will be used by default')
+option('default_codec', type: 'string', value: 'libx264', description: 'Codec 
that will be used by default')
+option('default_audio_codec', type: 'string', value: 'aac', description: 
'Audio codec that will be used by default')
 option('default_audio_sample_rate', type: 'integer', value: 48000, 
description: 'Audio sample rate that will be used by default')
-option('default_container_format', type: 'string', value: 'webm', description: 
'Container file format that will be used by default')
+option('default_container_format', type: 'string', value: 'mkv', description: 
'Container file format that will be used by default')
 option('fallback_audio_sample_fmt', type: 'string', value: 's16', description: 
'Fallback audio sample format that will be used if wf-recorder cannot determine 
the sample formats supported by a codec')
 option('pulse', type: 'feature', value: 'auto', description: 'Enable 
Pulseaudio')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/wf-recorder-0.3.0+git19/proto/meson.build 
new/wf-recorder-0.4.0+git0/proto/meson.build
--- old/wf-recorder-0.3.0+git19/proto/meson.build       2023-01-02 
10:18:42.000000000 +0100
+++ new/wf-recorder-0.4.0+git0/proto/meson.build        2023-08-23 
10:52:14.000000000 +0200
@@ -15,7 +15,9 @@
 
 client_protocols = [
     [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
-    'wlr-screencopy-unstable-v1.xml'
+    [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'],
+    'wlr-screencopy-unstable-v1.xml',
+    'wl-drm.xml'
 ]
 
 wl_protos_client_src = []
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/wf-recorder-0.3.0+git19/proto/wl-drm.xml 
new/wf-recorder-0.4.0+git0/proto/wl-drm.xml
--- old/wf-recorder-0.3.0+git19/proto/wl-drm.xml        1970-01-01 
01:00:00.000000000 +0100
+++ new/wf-recorder-0.4.0+git0/proto/wl-drm.xml 2023-08-23 10:52:14.000000000 
+0200
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="drm">
+
+  <copyright>
+    Copyright © 2008-2011 Kristian Høgsberg
+    Copyright © 2010-2011 Intel Corporation
+
+    Permission to use, copy, modify, distribute, and sell this
+    software and its documentation for any purpose is hereby granted
+    without fee, provided that\n the above copyright notice appear in
+    all copies and that both that copyright notice and this permission
+    notice appear in supporting documentation, and that the name of
+    the copyright holders not be used in advertising or publicity
+    pertaining to distribution of the software without specific,
+    written prior permission.  The copyright holders make no
+    representations about the suitability of this software for any
+    purpose.  It is provided "as is" without express or implied
+    warranty.
+
+    THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+    SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+    FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+    AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+    THIS SOFTWARE.
+  </copyright>
+
+  <!-- drm support. This object is created by the server and published
+       using the display's global event. -->
+  <interface name="wl_drm" version="2">
+    <enum name="error">
+      <entry name="authenticate_fail" value="0"/>
+      <entry name="invalid_format" value="1"/>
+      <entry name="invalid_name" value="2"/>
+    </enum>
+
+    <enum name="format">
+      <!-- The drm format codes match the #defines in drm_fourcc.h.
+           The formats actually supported by the compositor will be
+           reported by the format event. New codes must not be added,
+           unless directly taken from drm_fourcc.h. -->
+      <entry name="c8" value="0x20203843"/>
+      <entry name="rgb332" value="0x38424752"/>
+      <entry name="bgr233" value="0x38524742"/>
+      <entry name="xrgb4444" value="0x32315258"/>
+      <entry name="xbgr4444" value="0x32314258"/>
+      <entry name="rgbx4444" value="0x32315852"/>
+      <entry name="bgrx4444" value="0x32315842"/>
+      <entry name="argb4444" value="0x32315241"/>
+      <entry name="abgr4444" value="0x32314241"/>
+      <entry name="rgba4444" value="0x32314152"/>
+      <entry name="bgra4444" value="0x32314142"/>
+      <entry name="xrgb1555" value="0x35315258"/>
+      <entry name="xbgr1555" value="0x35314258"/>
+      <entry name="rgbx5551" value="0x35315852"/>
+      <entry name="bgrx5551" value="0x35315842"/>
+      <entry name="argb1555" value="0x35315241"/>
+      <entry name="abgr1555" value="0x35314241"/>
+      <entry name="rgba5551" value="0x35314152"/>
+      <entry name="bgra5551" value="0x35314142"/>
+      <entry name="rgb565" value="0x36314752"/>
+      <entry name="bgr565" value="0x36314742"/>
+      <entry name="rgb888" value="0x34324752"/>
+      <entry name="bgr888" value="0x34324742"/>
+      <entry name="xrgb8888" value="0x34325258"/>
+      <entry name="xbgr8888" value="0x34324258"/>
+      <entry name="rgbx8888" value="0x34325852"/>
+      <entry name="bgrx8888" value="0x34325842"/>
+      <entry name="argb8888" value="0x34325241"/>
+      <entry name="abgr8888" value="0x34324241"/>
+      <entry name="rgba8888" value="0x34324152"/>
+      <entry name="bgra8888" value="0x34324142"/>
+      <entry name="xrgb2101010" value="0x30335258"/>
+      <entry name="xbgr2101010" value="0x30334258"/>
+      <entry name="rgbx1010102" value="0x30335852"/>
+      <entry name="bgrx1010102" value="0x30335842"/>
+      <entry name="argb2101010" value="0x30335241"/>
+      <entry name="abgr2101010" value="0x30334241"/>
+      <entry name="rgba1010102" value="0x30334152"/>
+      <entry name="bgra1010102" value="0x30334142"/>
+      <entry name="yuyv" value="0x56595559"/>
+      <entry name="yvyu" value="0x55595659"/>
+      <entry name="uyvy" value="0x59565955"/>
+      <entry name="vyuy" value="0x59555956"/>
+      <entry name="ayuv" value="0x56555941"/>
+      <entry name="xyuv8888" value="0x56555958"/>
+      <entry name="nv12" value="0x3231564e"/>
+      <entry name="nv21" value="0x3132564e"/>
+      <entry name="nv16" value="0x3631564e"/>
+      <entry name="nv61" value="0x3136564e"/>
+      <entry name="yuv410" value="0x39565559"/>
+      <entry name="yvu410" value="0x39555659"/>
+      <entry name="yuv411" value="0x31315559"/>
+      <entry name="yvu411" value="0x31315659"/>
+      <entry name="yuv420" value="0x32315559"/>
+      <entry name="yvu420" value="0x32315659"/>
+      <entry name="yuv422" value="0x36315559"/>
+      <entry name="yvu422" value="0x36315659"/>
+      <entry name="yuv444" value="0x34325559"/>
+      <entry name="yvu444" value="0x34325659"/>
+      <entry name="abgr16f" value="0x48344241"/>
+      <entry name="xbgr16f" value="0x48344258"/>
+    </enum>
+
+    <!-- Call this request with the magic received from drmGetMagic().
+         It will be passed on to the drmAuthMagic() or
+         DRIAuthConnection() call.  This authentication must be
+         completed before create_buffer could be used. -->
+    <request name="authenticate">
+      <arg name="id" type="uint"/>
+    </request>
+
+    <!-- Create a wayland buffer for the named DRM buffer.  The DRM
+         surface must have a name using the flink ioctl -->
+    <request name="create_buffer">
+      <arg name="id" type="new_id" interface="wl_buffer"/>
+      <arg name="name" type="uint"/>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+      <arg name="stride" type="uint"/>
+      <arg name="format" type="uint"/>
+    </request>
+
+    <!-- Create a wayland buffer for the named DRM buffer.  The DRM
+         surface must have a name using the flink ioctl -->
+    <request name="create_planar_buffer">
+      <arg name="id" type="new_id" interface="wl_buffer"/>
+      <arg name="name" type="uint"/>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+      <arg name="format" type="uint"/>
+      <arg name="offset0" type="int"/>
+      <arg name="stride0" type="int"/>
+      <arg name="offset1" type="int"/>
+      <arg name="stride1" type="int"/>
+      <arg name="offset2" type="int"/>
+      <arg name="stride2" type="int"/>
+    </request>
+
+    <!-- Notification of the path of the drm device which is used by
+         the server.  The client should use this device for creating
+         local buffers.  Only buffers created from this device should
+         be be passed to the server using this drm object's
+         create_buffer request. -->
+    <event name="device">
+      <arg name="name" type="string"/>
+    </event>
+
+    <event name="format">
+      <arg name="format" type="uint"/>
+    </event>
+
+    <!-- Raised if the authenticate request succeeded -->
+    <event name="authenticated"/>
+
+    <enum name="capability" since="2">
+      <description summary="wl_drm capability bitmask">
+        Bitmask of capabilities.
+      </description>
+      <entry name="prime" value="1" summary="wl_drm prime available"/>
+    </enum>
+
+    <event name="capabilities">
+      <arg name="value" type="uint"/>
+    </event>
+
+    <!-- Version 2 additions -->
+
+    <!-- Create a wayland buffer for the prime fd.  Use for regular and planar
+         buffers.  Pass 0 for offset and stride for unused planes. -->
+    <request name="create_prime_buffer" since="2">
+      <arg name="id" type="new_id" interface="wl_buffer"/>
+      <arg name="name" type="fd"/>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+      <arg name="format" type="uint"/>
+      <arg name="offset0" type="int"/>
+      <arg name="stride0" type="int"/>
+      <arg name="offset1" type="int"/>
+      <arg name="stride1" type="int"/>
+      <arg name="offset2" type="int"/>
+      <arg name="stride2" type="int"/>
+    </request>
+
+  </interface>
+
+</protocol>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/wf-recorder-0.3.0+git19/proto/wlr-screencopy-unstable-v1.xml 
new/wf-recorder-0.4.0+git0/proto/wlr-screencopy-unstable-v1.xml
--- old/wf-recorder-0.3.0+git19/proto/wlr-screencopy-unstable-v1.xml    
2023-01-02 10:18:42.000000000 +0100
+++ new/wf-recorder-0.4.0+git0/proto/wlr-screencopy-unstable-v1.xml     
2023-08-23 10:52:14.000000000 +0200
@@ -38,7 +38,7 @@
     interface version number is reset.
   </description>
 
-  <interface name="zwlr_screencopy_manager_v1" version="2">
+  <interface name="zwlr_screencopy_manager_v1" version="3">
     <description summary="manager to inform clients and begin capturing">
       This object is a manager which offers requests to start capturing from a
       source.
@@ -80,13 +80,18 @@
     </request>
   </interface>
 
-  <interface name="zwlr_screencopy_frame_v1" version="2">
+  <interface name="zwlr_screencopy_frame_v1" version="3">
     <description summary="a frame ready for copy">
       This object represents a single frame.
 
-      When created, a "buffer" event will be sent. The client will then be able
-      to send a "copy" request. If the capture is successful, the compositor
-      will send a "flags" followed by a "ready" event.
+      When created, a series of buffer events will be sent, each representing a
+      supported buffer type. The "buffer_done" event is sent afterwards to
+      indicate that all supported buffer types have been enumerated. The client
+      will then be able to send a "copy" request. If the capture is successful,
+      the compositor will send a "flags" followed by a "ready" event.
+
+      For objects version 2 or lower, wl_shm buffers are always supported, ie.
+      the "buffer" event is guaranteed to be sent.
 
       If the capture failed, the "failed" event is sent. This can happen 
anytime
       before the "ready" event.
@@ -96,14 +101,12 @@
     </description>
 
     <event name="buffer">
-      <description summary="buffer information">
-        Provides information about the frame's buffer. This event is sent once
-        as soon as the frame is created.
-
-        The client should then create a buffer with the provided attributes, 
and
-        send a "copy" request.
+      <description summary="wl_shm buffer information">
+        Provides information about wl_shm buffer parameters that need to be
+        used for this frame. This event is sent once after the frame is created
+        if wl_shm buffers are supported.
       </description>
-      <arg name="format" type="uint" summary="buffer format"/>
+      <arg name="format" type="uint" enum="wl_shm.format" summary="buffer 
format"/>
       <arg name="width" type="uint" summary="buffer width"/>
       <arg name="height" type="uint" summary="buffer height"/>
       <arg name="stride" type="uint" summary="buffer stride"/>
@@ -112,8 +115,9 @@
     <request name="copy">
       <description summary="copy the frame">
         Copy the frame to the supplied buffer. The buffer must have a the
-        correct size, see zwlr_screencopy_frame_v1.buffer. The buffer needs to
-        have a supported format.
+        correct size, see zwlr_screencopy_frame_v1.buffer and
+        zwlr_screencopy_frame_v1.linux_dmabuf. The buffer needs to have a
+        supported format.
 
         If the frame is successfully copied, a "flags" and a "ready" events are
         sent. Otherwise, a "failed" event is sent.
@@ -203,5 +207,26 @@
       <arg name="width" type="uint" summary="current width"/>
       <arg name="height" type="uint" summary="current height"/>
     </event>
+
+    <!-- Version 3 additions -->
+    <event name="linux_dmabuf" since="3">
+      <description summary="linux-dmabuf buffer information">
+        Provides information about linux-dmabuf buffer parameters that need to
+        be used for this frame. This event is sent once after the frame is
+        created if linux-dmabuf buffers are supported.
+      </description>
+      <arg name="format" type="uint" summary="fourcc pixel format"/>
+      <arg name="width" type="uint" summary="buffer width"/>
+      <arg name="height" type="uint" summary="buffer height"/>
+    </event>
+
+    <event name="buffer_done" since="3">
+      <description summary="all buffer types reported">
+        This event is sent once after all buffer events have been sent.
+
+        The client should proceed to create a buffer of one of the supported
+        types, and send a "copy" request.
+      </description>
+    </event>
   </interface>
 </protocol>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/wf-recorder-0.3.0+git19/src/averr.c 
new/wf-recorder-0.4.0+git0/src/averr.c
--- old/wf-recorder-0.3.0+git19/src/averr.c     2023-01-02 10:18:42.000000000 
+0100
+++ new/wf-recorder-0.4.0+git0/src/averr.c      2023-08-23 10:52:14.000000000 
+0200
@@ -2,5 +2,7 @@
 
 const char* averr(int err)
 {
-    return av_err2str(err);
+    static char buf[AV_ERROR_MAX_STRING_SIZE];
+    av_make_error_string(buf, sizeof(buf), err);
+    return buf;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/wf-recorder-0.3.0+git19/src/buffer-pool.hpp 
new/wf-recorder-0.4.0+git0/src/buffer-pool.hpp
--- old/wf-recorder-0.3.0+git19/src/buffer-pool.hpp     1970-01-01 
01:00:00.000000000 +0100
+++ new/wf-recorder-0.4.0+git0/src/buffer-pool.hpp      2023-08-23 
10:52:14.000000000 +0200
@@ -0,0 +1,107 @@
+#pragma once
+
+#include <array>
+#include <mutex>
+#include <atomic>
+#include <type_traits>
+
+class buffer_pool_buf
+{
+public:
+    bool ready_capture() const
+    {
+        return released;
+    }
+
+    bool ready_encode() const
+    {
+        return available;
+    }
+
+    std::atomic<bool> released{true}; // if the buffer can be used to store 
new pending frames
+    std::atomic<bool> available{false}; // if the buffer can be used to feed 
the encoder
+};
+
+template <class T, int N>
+class buffer_pool
+{
+public:
+    static_assert(std::is_base_of<buffer_pool_buf, T>::value, "T must be 
subclass of buffer_pool_buf");
+
+    buffer_pool()
+    {
+        for (size_t i = 0; i < bufs_size; ++i) {
+            bufs[i] = new T;
+        }
+    }
+
+    ~buffer_pool()
+    {
+        for (size_t i = 0; i < N; ++i) {
+            delete bufs[i];
+        }
+    }
+
+    size_t size() const
+    {
+        return N;
+    }
+
+    const T* at(size_t i) const
+    {
+        return bufs[i];
+    }
+
+    T& capture()
+    {
+        std::lock_guard<std::mutex> lock(mutex);
+        return *bufs[capture_idx];
+    }
+
+    T& encode()
+    {
+        std::lock_guard<std::mutex> lock(mutex);
+        return *bufs[encode_idx];
+    }
+
+    // Signal that the current capture buffer has been successfully obtained
+    // from the compositor and select the next buffer to capture in.
+    T& next_capture()
+    {
+        std::lock_guard<std::mutex> lock(mutex);
+        bufs[capture_idx]->released = false;
+        bufs[capture_idx]->available = true;
+        size_t next = (capture_idx + 1) % bufs_size;
+        if (!bufs[next]->ready_capture() && bufs_size < N) {
+            bufs_size++;
+            next = (capture_idx + 1) % bufs_size;
+            for (size_t i = N - 1; i > next; --i) {
+                bufs[i] = bufs[i - 1];
+                if (encode_idx == i - 1) {
+                    encode_idx = i;
+                }
+            }
+            bufs[next] = new T;
+        }
+        capture_idx = next;
+        return *bufs[capture_idx];
+    }
+
+    // Signal that the encode buffer has been submitted for encoding
+    // and select the next buffer for encoding.
+    T& next_encode()
+    {
+        std::lock_guard<std::mutex> lock(mutex);
+        bufs[encode_idx]->available = false;
+        bufs[encode_idx]->released = true;
+        encode_idx = (encode_idx + 1) % bufs_size;
+        return *bufs[encode_idx];
+    }
+
+private:
+    std::mutex mutex;
+    std::array<T*, N> bufs;
+    size_t bufs_size = 2;
+    size_t capture_idx = 0;
+    size_t encode_idx = 0;
+};
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/wf-recorder-0.3.0+git19/src/frame-writer.cpp 
new/wf-recorder-0.4.0+git0/src/frame-writer.cpp
--- old/wf-recorder-0.3.0+git19/src/frame-writer.cpp    2023-01-02 
10:18:42.000000000 +0100
+++ new/wf-recorder-0.4.0+git0/src/frame-writer.cpp     2023-08-23 
10:52:14.000000000 +0200
@@ -10,6 +10,7 @@
 #include <cstring>
 #include <sstream>
 #include "averr.h"
+#include <gbm.h>
 
 
 static const AVRational US_RATIONAL{1,1000000} ;
@@ -124,12 +125,48 @@
     case INPUT_FORMAT_X2BGR10:
         return AV_PIX_FMT_X2BGR10LE;
 #endif
+    case INPUT_FORMAT_RGBX64:
+        return AV_PIX_FMT_RGBA64LE;
+    case INPUT_FORMAT_BGRX64:
+        return AV_PIX_FMT_BGRA64LE;
+#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 33, 101)
+    case INPUT_FORMAT_RGBX64F:
+        return AV_PIX_FMT_RGBAF16LE;
+#endif
+    case INPUT_FORMAT_DMABUF:
+        return AV_PIX_FMT_VAAPI;
     default:
         std::cerr << "Unknown format: " << params.format << std::endl;
         std::exit(-1);
     }
 }
 
+static const struct {
+    int drm;
+    AVPixelFormat av;
+} drm_av_format_table [] = {
+    { GBM_FORMAT_ARGB8888, AV_PIX_FMT_BGRA },
+    { GBM_FORMAT_XRGB8888, AV_PIX_FMT_BGR0 },
+    { GBM_FORMAT_ABGR8888, AV_PIX_FMT_RGBA },
+    { GBM_FORMAT_XBGR8888, AV_PIX_FMT_RGB0 },
+    { GBM_FORMAT_RGBA8888, AV_PIX_FMT_ABGR },
+    { GBM_FORMAT_RGBX8888, AV_PIX_FMT_0BGR },
+    { GBM_FORMAT_BGRA8888, AV_PIX_FMT_ARGB },
+    { GBM_FORMAT_BGRX8888, AV_PIX_FMT_0RGB },
+    { GBM_FORMAT_XRGB2101010, AV_PIX_FMT_X2RGB10 },
+};
+
+static AVPixelFormat get_drm_av_format(int fmt)
+{
+    for (size_t i = 0; i < sizeof(drm_av_format_table) / 
sizeof(drm_av_format_table[0]); ++i) {
+        if (drm_av_format_table[i].drm == fmt) {
+            return drm_av_format_table[i].av;
+        }
+    }
+    std::cerr << "Failed to find AV format for" << fmt;
+    return AV_PIX_FMT_RGBA;
+}
+
 AVPixelFormat FrameWriter::lookup_pixel_format(std::string pix_fmt)
 {
     AVPixelFormat fmt = av_get_pix_fmt(pix_fmt.c_str());
@@ -159,24 +196,19 @@
     if (is_fmt_supported(in_fmt, codec->pix_fmts))
         return in_fmt;
 
-    /* Otherwise, try to use the already tested YUV420p */
-    if (is_fmt_supported(AV_PIX_FMT_YUV420P, codec->pix_fmts))
-        return AV_PIX_FMT_YUV420P;
-
-    /* Lastly, use the first supported format */
-    return codec->pix_fmts[0];
+    /* Choose the format supported by the codec which best approximates the
+     * input fmt. */
+    AVPixelFormat best_format = AV_PIX_FMT_NONE;
+    for (int i = 0; codec->pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
+        int loss = 0;
+        best_format = av_find_best_pix_fmt_of_2(best_format,
+            codec->pix_fmts[i], in_fmt, false, &loss);
+    }
+    return best_format;
 }
 
 void FrameWriter::init_video_filters(const AVCodec *codec)
 {
-    if (params.codec.find("vaapi") != std::string::npos) {
-        if (params.video_filter == "null") {
-            // Add `hwupload,scale_vaapi=format=nv12` by default
-            // It is necessary for conversion to a proper format.
-            params.video_filter = "hwupload,scale_vaapi=format=nv12";
-        }
-    }
-
     if (params.framerate != 0){
         if (params.video_filter != "null" && params.video_filter.find("fps") 
== std::string::npos) {
             params.video_filter += ",fps=" + std::to_string(params.framerate);
@@ -197,6 +229,32 @@
         exit(-1);
     }
 
+    if (this->hw_device_context) {
+        this->hw_frame_context = av_hwframe_ctx_alloc(this->hw_device_context);
+        AVHWFramesContext *hwfc = 
reinterpret_cast<AVHWFramesContext*>(this->hw_frame_context->data);
+        hwfc->format = AV_PIX_FMT_VAAPI;
+        hwfc->sw_format = AV_PIX_FMT_NV12;
+        hwfc->width = params.width;
+        hwfc->height = params.height;
+        int err = av_hwframe_ctx_init(this->hw_frame_context);
+        if (err < 0) {
+            std::cerr << "Cannot create hw frames context: " << averr(err) << 
std::endl;
+            exit(-1);
+        }
+
+        this->hw_frame_context_in = 
av_hwframe_ctx_alloc(this->hw_device_context);
+        hwfc = 
reinterpret_cast<AVHWFramesContext*>(this->hw_frame_context_in->data);
+        hwfc->format = AV_PIX_FMT_VAAPI;
+        hwfc->sw_format = get_drm_av_format(params.drm_format);
+        hwfc->width = params.width;
+        hwfc->height = params.height;
+        err = av_hwframe_ctx_init(this->hw_frame_context_in);
+        if (err < 0) {
+            std::cerr << "Cannot create hw frames context: " << averr(err) << 
std::endl;
+            exit(-1);
+        }
+    }
+
     // Build the configuration of the 'buffer' filter.
     // See: ffmpeg -h filter=buffer
     // See: https://ffmpeg.org/ffmpeg-filters.html#buffer
@@ -216,6 +274,17 @@
         exit(-1);
     }
 
+    AVBufferSrcParameters *p = av_buffersrc_parameters_alloc();
+    memset(p, 0, sizeof(*p));
+    p->format = AV_PIX_FMT_NONE;
+    p->hw_frames_ctx = this->hw_frame_context_in;
+    err = av_buffersrc_parameters_set(this->videoFilterSourceCtx, p);
+    av_free(p);
+    if (err < 0) {
+         std::cerr << "Cannot set hwcontext filter in: " << averr(err) << 
std::endl;;
+         exit(-1);
+    }
+
     err = avfilter_graph_create_filter(&this->videoFilterSinkCtx, sink, "Sink",
         NULL, NULL, this->videoFilterGraph);
     if (err < 0) {
@@ -312,8 +381,6 @@
     this->videoCodecCtx->framerate = filter_output->frame_rate; // can be 1/0 
if unknown
     this->videoCodecCtx->sample_aspect_ratio = 
filter_output->sample_aspect_ratio;
 
-    this->hw_frame_context = av_buffersink_get_hw_frames_ctx(
-        this->videoFilterSinkCtx);
     avfilter_inout_free(&inputs);
     avfilter_inout_free(&outputs);
 }
@@ -609,35 +676,8 @@
     }
 }
 
-AVFrame *FrameWriter::prepare_frame_data(const uint8_t* pixels, bool y_invert)
-{
-    /* Calculate data after y-inversion */
-    int stride[] = {int(params.stride)};
-    const uint8_t *formatted_pixels = pixels;
-    if (y_invert)
-    {
-        formatted_pixels += stride[0] * (params.height - 1);
-        stride[0] *= -1;
-    }
-
-    auto frame = av_frame_alloc();
-    if (!frame) {
-        std::cerr << "Failed to allocate frame!" << std::endl;
-        return NULL;
-    }
-
-    frame->data[0] = (uint8_t*)formatted_pixels;
-    frame->linesize[0] = stride[0];
-    frame->format = get_input_format();
-    frame->width = params.width;
-    frame->height = params.height;
-
-    return frame;
-}
-
-bool FrameWriter::add_frame(const uint8_t* pixels, int64_t usec, bool y_invert)
+bool FrameWriter::push_frame(AVFrame *frame, int64_t usec)
 {
-    auto frame = prepare_frame_data(pixels, y_invert);
     frame->pts = usec; // We use time_base = 1/US_RATE
 
     // Push the RGB frame into the filtergraph */
@@ -660,6 +700,7 @@
         if (err == AVERROR(EAGAIN)) {
             // Not an error. No frame available.
             // Try again later.
+            av_frame_free(&filtered_frame);
             break;
         } else if (err == AVERROR_EOF) {
             // There will be no more output frames on this sink.
@@ -687,6 +728,95 @@
     return true;
 }
 
+bool FrameWriter::add_frame(const uint8_t* pixels, int64_t usec, bool y_invert)
+{
+    /* Calculate data after y-inversion */
+    int stride[] = {int(params.stride)};
+    const uint8_t *formatted_pixels = pixels;
+    if (y_invert)
+    {
+        formatted_pixels += stride[0] * (params.height - 1);
+        stride[0] *= -1;
+    }
+
+    auto frame = av_frame_alloc();
+    if (!frame) {
+        std::cerr << "Failed to allocate frame!" << std::endl;
+        return false;
+    }
+
+    frame->data[0] = (uint8_t*)formatted_pixels;
+    frame->linesize[0] = stride[0];
+    frame->format = get_input_format();
+    frame->width = params.width;
+    frame->height = params.height;
+
+    return push_frame(frame, usec);
+}
+
+bool FrameWriter::add_frame(struct gbm_bo *bo, int64_t usec, bool y_invert)
+{
+    if (y_invert)
+    {
+        std::cerr << "Y_INVERT not supported with dmabuf" << std::endl;
+        return false;
+    }
+
+    auto frame = av_frame_alloc();
+    if (!frame)
+    {
+        std::cerr << "Failed to allocate frame!" << std::endl;
+        return false;
+    }
+
+    if (mapped_frames.find(bo) == mapped_frames.end()) {
+        auto vaapi_frame = av_frame_alloc();
+        if (!vaapi_frame) {
+            std::cerr << "Failed to allocate frame!" << std::endl;
+            return false;
+        }
+
+        AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor*) 
av_mallocz(sizeof(AVDRMFrameDescriptor));
+        desc->nb_layers = 1;
+        desc->nb_objects = 1;
+        desc->objects[0].fd = gbm_bo_get_fd(bo);
+        desc->objects[0].format_modifier = gbm_bo_get_modifier(bo);
+        desc->objects[0].size = gbm_bo_get_stride(bo) * gbm_bo_get_height(bo);
+        desc->layers[0].format = gbm_bo_get_format(bo);
+        desc->layers[0].nb_planes = gbm_bo_get_plane_count(bo);
+        for (int i = 0; i < gbm_bo_get_plane_count(bo); ++i) {
+            desc->layers[0].planes[i].object_index = 0;
+            desc->layers[0].planes[i].pitch = gbm_bo_get_stride_for_plane(bo, 
i);
+            desc->layers[0].planes[i].offset = gbm_bo_get_offset(bo, i);
+        }
+
+        frame->width = gbm_bo_get_width(bo);
+        frame->height = gbm_bo_get_height(bo);
+        frame->format = AV_PIX_FMT_DRM_PRIME;
+        frame->data[0] = reinterpret_cast<uint8_t*>(desc);
+        frame->buf[0] = av_buffer_create(frame->data[0], sizeof(*desc),
+            [](void *, uint8_t *data) {
+                av_free(data);
+        }, frame, 0);
+
+        vaapi_frame->format = AV_PIX_FMT_VAAPI;
+        vaapi_frame->hw_frames_ctx = av_buffer_ref(this->hw_frame_context_in);
+
+        int ret = av_hwframe_map(vaapi_frame, frame, AV_HWFRAME_MAP_READ);
+        av_frame_unref(frame);
+        if (ret < 0)
+        {
+            std::cerr << "Failed to map vaapi frame " << averr(ret) << 
std::endl;
+            return false;
+        }
+
+        mapped_frames[bo] = vaapi_frame;
+    }
+
+    av_frame_ref(frame, mapped_frames[bo]);
+    return push_frame(frame, usec);
+}
+
 #ifdef HAVE_PULSE
 #define SRC_RATE 1e6
 #define DST_RATE 1e3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/wf-recorder-0.3.0+git19/src/frame-writer.hpp 
new/wf-recorder-0.4.0+git0/src/frame-writer.hpp
--- old/wf-recorder-0.3.0+git19/src/frame-writer.hpp    2023-01-02 
10:18:42.000000000 +0100
+++ new/wf-recorder-0.4.0+git0/src/frame-writer.hpp     2023-08-23 
10:52:14.000000000 +0200
@@ -26,6 +26,7 @@
     #include <libavutil/pixdesc.h>
     #include <libavutil/hwcontext.h>
     #include <libavutil/opt.h>
+    #include <libavutil/hwcontext_drm.h>
 }
 
 #include "config.h"
@@ -39,6 +40,10 @@
     INPUT_FORMAT_BGR565,
     INPUT_FORMAT_X2RGB10,
     INPUT_FORMAT_X2BGR10,
+    INPUT_FORMAT_RGBX64,
+    INPUT_FORMAT_BGRX64,
+    INPUT_FORMAT_RGBX64F,
+    INPUT_FORMAT_DMABUF,
 };
 
 struct FrameWriterParams
@@ -49,6 +54,7 @@
     int stride;
 
     InputFormat format;
+    int drm_format;
 
     std::string video_filter = "null"; // dummy filter
 
@@ -92,6 +98,9 @@
 
     AVBufferRef *hw_device_context = NULL;
     AVBufferRef *hw_frame_context = NULL;
+    AVBufferRef *hw_frame_context_in = NULL;
+
+    std::map<struct gbm_bo*, AVFrame*> mapped_frames;
 
     AVPixelFormat lookup_pixel_format(std::string pix_fmt);
     AVPixelFormat handle_buffersink_pix_fmt(const AVCodec *codec);
@@ -112,15 +121,12 @@
     void send_audio_pkt(AVFrame *frame);
 #endif
     void finish_frame(AVCodecContext *enc_ctx, AVPacket& pkt);
-
-    /**
-     * Upload data to a frame.
-     */
-     AVFrame *prepare_frame_data(const uint8_t* pixels, bool y_invert);
+    bool push_frame(AVFrame *frame, int64_t usec);
 
   public:
     FrameWriter(const FrameWriterParams& params);
     bool add_frame(const uint8_t* pixels, int64_t usec, bool y_invert);
+    bool add_frame(struct gbm_bo *bo, int64_t usec, bool y_invert);
 
 #ifdef HAVE_PULSE
     /* Buffer must have size get_audio_buffer_size() */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/wf-recorder-0.3.0+git19/src/main.cpp 
new/wf-recorder-0.4.0+git0/src/main.cpp
--- old/wf-recorder-0.3.0+git19/src/main.cpp    2023-01-02 10:18:42.000000000 
+0100
+++ new/wf-recorder-0.4.0+git0/src/main.cpp     2023-08-23 10:52:14.000000000 
+0200
@@ -18,10 +18,15 @@
 #include <signal.h>
 #include <unistd.h>
 #include <wayland-client-protocol.h>
+#include <gbm.h>
+#include <fcntl.h>
 
 #include "frame-writer.hpp"
+#include "buffer-pool.hpp"
 #include "wlr-screencopy-unstable-v1-client-protocol.h"
 #include "xdg-output-unstable-v1-client-protocol.h"
+#include "linux-dmabuf-unstable-v1-client-protocol.h"
+#include "wl-drm-client-protocol.h"
 
 #include "config.h"
 
@@ -37,9 +42,15 @@
 std::mutex frame_writer_mutex, frame_writer_pending_mutex;
 std::unique_ptr<FrameWriter> frame_writer;
 
+static int drm_fd = -1;
+static struct gbm_device *gbm_device = NULL;
+static std::string drm_device_name;
+
 static struct wl_shm *shm = NULL;
 static struct zxdg_output_manager_v1 *xdg_output_manager = NULL;
 static struct zwlr_screencopy_manager_v1 *screencopy_manager = NULL;
+static struct zwp_linux_dmabuf_v1 *dmabuf = NULL;
+static struct wl_drm *drm = NULL;
 void request_next_frame();
 
 struct wf_recorder_output
@@ -109,26 +120,23 @@
     .description = handle_xdg_output_description
 };
 
-struct wf_buffer
+struct wf_buffer : public buffer_pool_buf
 {
-    struct wl_buffer *wl_buffer;
-    void *data;
+    struct gbm_bo *bo = nullptr;
+    struct wl_buffer *wl_buffer = nullptr;
+    void *data = nullptr;
     enum wl_shm_format format;
+    int drm_format;
     int width, height, stride;
     bool y_invert;
 
     timespec presented;
     uint64_t base_usec;
-
-    std::atomic<bool> released{true}; // if the buffer can be used to store 
new pending frames
-    std::atomic<bool> available{false}; // if the buffer can be used to feed 
the encoder
 };
 
 std::atomic<bool> exit_main_loop{false};
 
-#define MAX_BUFFERS 16
-wf_buffer buffers[MAX_BUFFERS];
-size_t active_buffer = 0;
+buffer_pool<wf_buffer, 16> buffers;
 
 bool buffer_copy_done = false;
 
@@ -182,11 +190,17 @@
 }
 
 static bool use_damage = true;
+static bool use_dmabuf = false;
+static bool use_hwupload = false;
 
 static void frame_handle_buffer(void *, struct zwlr_screencopy_frame_v1 
*frame, uint32_t format,
     uint32_t width, uint32_t height, uint32_t stride)
 {
-    auto& buffer = buffers[active_buffer];
+    if (use_dmabuf) {
+        return;
+    }
+
+    auto& buffer = buffers.capture();
 
     buffer.format = (wl_shm_format)format;
     buffer.width = width;
@@ -217,7 +231,7 @@
 }
 
 static void frame_handle_flags(void*, struct zwlr_screencopy_frame_v1 *, 
uint32_t flags) {
-    buffers[active_buffer].y_invert = flags & 
ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT;
+    buffers.capture().y_invert = flags & 
ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT;
 }
 
 int32_t frame_failed_cnt = 0;
@@ -225,7 +239,7 @@
 static void frame_handle_ready(void *, struct zwlr_screencopy_frame_v1 *,
     uint32_t tv_sec_hi, uint32_t tv_sec_low, uint32_t tv_nsec) {
 
-    auto& buffer = buffers[active_buffer];
+    auto& buffer = buffers.capture();
     buffer_copy_done = true;
     buffer.presented.tv_sec = ((1ll * tv_sec_hi) << 32ll) | tv_sec_low;
     buffer.presented.tv_nsec = tv_nsec;
@@ -235,6 +249,7 @@
 static void frame_handle_failed(void *, struct zwlr_screencopy_frame_v1 *) {
     std::cerr << "Failed to copy frame, retrying..." << std::endl;
     ++frame_failed_cnt;
+    request_next_frame();
     if (frame_failed_cnt > MAX_FRAME_FAILURES)
     {
         std::cerr << "Failed to copy frame too many times, exiting!" << 
std::endl;
@@ -247,12 +262,128 @@
 {
 }
 
+static void dmabuf_created(void *data, struct zwp_linux_buffer_params_v1 *,
+    struct wl_buffer *wl_buffer) {
+
+    auto& buffer = buffers.capture();
+    buffer.wl_buffer = wl_buffer;
+
+    zwlr_screencopy_frame_v1 *frame = (zwlr_screencopy_frame_v1*) data;
+
+    if (use_damage) {
+        zwlr_screencopy_frame_v1_copy_with_damage(frame, buffer.wl_buffer);
+    } else {
+        zwlr_screencopy_frame_v1_copy(frame, buffer.wl_buffer);
+    }
+}
+
+static void dmabuf_failed(void *, struct zwp_linux_buffer_params_v1 *) {
+    std::cerr << "Failed to create dmabuf" << std::endl;
+    exit_main_loop = true;
+}
+
+static const struct zwp_linux_buffer_params_v1_listener params_listener = {
+    .created = dmabuf_created,
+    .failed = dmabuf_failed,
+};
+
+static wl_shm_format drm_to_wl_shm_format(uint32_t format)
+{
+    if (format == GBM_FORMAT_ARGB8888) {
+        return WL_SHM_FORMAT_ARGB8888;
+    } else if (format == GBM_FORMAT_XRGB8888) {
+        return WL_SHM_FORMAT_XRGB8888;
+    } else {
+        return (wl_shm_format)format;
+    }
+}
+
+static void frame_handle_linux_dmabuf(void *, struct zwlr_screencopy_frame_v1 
*frame,
+    uint32_t format, uint32_t width, uint32_t height)
+{
+    if (!use_dmabuf) {
+        return;
+    }
+
+    auto& buffer = buffers.capture();
+
+    buffer.format = drm_to_wl_shm_format(format);
+    buffer.drm_format = format;
+    buffer.width = width;
+    buffer.height = height;
+
+    if (!buffer.wl_buffer) {
+        const uint64_t modifier = 0; // DRM_FORMAT_MOD_LINEAR
+        buffer.bo = gbm_bo_create_with_modifiers(gbm_device, buffer.width,
+            buffer.height, format, &modifier, 1);
+        if (buffer.bo == NULL)
+        {
+            buffer.bo = gbm_bo_create(gbm_device, buffer.width,
+                buffer.height, format, GBM_BO_USE_LINEAR | 
GBM_BO_USE_RENDERING);
+        }
+        if (buffer.bo == NULL)
+        {
+            std::cerr << "Failed to create gbm bo" << std::endl;
+            exit_main_loop = true;
+            return;
+        }
+
+        buffer.stride = gbm_bo_get_stride(buffer.bo);
+
+        struct zwp_linux_buffer_params_v1 *params =
+            zwp_linux_dmabuf_v1_create_params(dmabuf);
+
+        uint64_t mod = gbm_bo_get_modifier(buffer.bo);
+        zwp_linux_buffer_params_v1_add(params,
+            gbm_bo_get_fd(buffer.bo), 0,
+            gbm_bo_get_offset(buffer.bo, 0),
+            gbm_bo_get_stride(buffer.bo),
+            mod >> 32, mod & 0xffffffff);
+
+        zwp_linux_buffer_params_v1_add_listener(params, &params_listener, 
frame);
+
+        zwp_linux_buffer_params_v1_create(params, buffer.width,
+            buffer.height, format, 0);
+    } else {
+        if (use_damage) {
+            zwlr_screencopy_frame_v1_copy_with_damage(frame, buffer.wl_buffer);
+        } else {
+            zwlr_screencopy_frame_v1_copy(frame, buffer.wl_buffer);
+        }
+    }
+}
+
+static void frame_handle_buffer_done(void *, struct zwlr_screencopy_frame_v1 
*) {
+}
+
 static const struct zwlr_screencopy_frame_v1_listener frame_listener = {
     .buffer = frame_handle_buffer,
     .flags = frame_handle_flags,
     .ready = frame_handle_ready,
     .failed = frame_handle_failed,
     .damage = frame_handle_damage,
+    .linux_dmabuf = frame_handle_linux_dmabuf,
+    .buffer_done = frame_handle_buffer_done,
+};
+
+static void drm_handle_device(void *, struct wl_drm *, const char *name) {
+    drm_device_name = name;
+}
+
+static void drm_handle_format(void *, struct wl_drm *, uint32_t) {
+}
+
+static void drm_handle_authenticated(void *, struct wl_drm *) {
+}
+
+static void drm_handle_capabilities(void *, struct wl_drm *, uint32_t) {
+}
+
+static const struct wl_drm_listener drm_listener = {
+    .device = drm_handle_device,
+    .format = drm_handle_format,
+    .authenticated = drm_handle_authenticated,
+    .capabilities = drm_handle_capabilities,
 };
 
 static void handle_global(void*, struct wl_registry *registry,
@@ -272,13 +403,23 @@
     else if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0)
     {
         screencopy_manager = (zwlr_screencopy_manager_v1*) 
wl_registry_bind(registry, name,
-            &zwlr_screencopy_manager_v1_interface, 2);
+            &zwlr_screencopy_manager_v1_interface, 3);
     }
     else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0)
     {
         xdg_output_manager = (zxdg_output_manager_v1*) 
wl_registry_bind(registry, name,
             &zxdg_output_manager_v1_interface, 2); // version 2 for name & 
description, if available
     }
+    else if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0)
+    {
+        dmabuf = (zwp_linux_dmabuf_v1*) wl_registry_bind(registry, name,
+            &zwp_linux_dmabuf_v1_interface, 3);
+    }
+    else if (strcmp(interface, wl_drm_interface.name) == 0)
+    {
+        drm = (wl_drm*) wl_registry_bind(registry, name, &wl_drm_interface, 1);
+        wl_drm_add_listener(drm, &drm_listener, NULL);
+    }
 }
 
 static void handle_global_remove(void*, struct wl_registry *, uint32_t) {
@@ -295,13 +436,11 @@
     return ts.tv_sec * 1000000ll + 1ll * ts.tv_nsec / 1000ll;
 }
 
-static int next_frame(int frame)
-{
-    return (frame + 1) % MAX_BUFFERS;
-}
-
 static InputFormat get_input_format(wf_buffer& buffer)
 {
+    if (use_dmabuf && !use_hwupload) {
+        return INPUT_FORMAT_DMABUF;
+    }
     switch (buffer.format) {
     case WL_SHM_FORMAT_ARGB8888:
     case WL_SHM_FORMAT_XRGB8888:
@@ -321,6 +460,15 @@
     case WL_SHM_FORMAT_ABGR2101010:
     case WL_SHM_FORMAT_XBGR2101010:
         return INPUT_FORMAT_X2BGR10;
+    case WL_SHM_FORMAT_ABGR16161616:
+    case WL_SHM_FORMAT_XBGR16161616:
+        return INPUT_FORMAT_RGBX64;
+    case WL_SHM_FORMAT_ARGB16161616:
+    case WL_SHM_FORMAT_XRGB16161616:
+        return INPUT_FORMAT_BGRX64;
+    case WL_SHM_FORMAT_ABGR16161616F:
+    case WL_SHM_FORMAT_XBGR16161616F:
+        return INPUT_FORMAT_RGBX64F;
     default:
         fprintf(stderr, "Unsupported buffer format %d, exiting.", 
buffer.format);
         std::exit(0);
@@ -338,7 +486,6 @@
     }
     pthread_sigmask(SIG_BLOCK, &sigset, NULL);
 
-    int last_encoded_frame = 0;
 #ifdef HAVE_PULSE
     std::unique_ptr<PulseReader> pr;
 #endif
@@ -346,10 +493,14 @@
     while(!exit_main_loop)
     {
         // wait for frame to become available
-        while(buffers[last_encoded_frame].available != true && 
!exit_main_loop) {
+        while(buffers.encode().ready_encode() != true && !exit_main_loop) {
             std::this_thread::sleep_for(std::chrono::microseconds(1000));
         }
-        auto& buffer = buffers[last_encoded_frame];
+        if (exit_main_loop) {
+            break;
+        }
+
+        auto& buffer = buffers.encode();
 
         frame_writer_pending_mutex.lock();
         frame_writer_mutex.lock();
@@ -359,6 +510,7 @@
         {
             /* This is the first time buffer attributes are available */
             params.format = get_input_format(buffer);
+            params.drm_format = buffer.drm_format;
             params.width = buffer.width;
             params.height = buffer.height;
             params.stride = buffer.stride;
@@ -375,18 +527,37 @@
 #endif
         }
 
-        bool do_cont = frame_writer->add_frame((unsigned char*)buffer.data,
-            buffer.base_usec, buffer.y_invert);
-        if (!do_cont) {
-            break;
+        bool do_cont = false;
+
+        if (use_dmabuf) {
+            if (use_hwupload) {
+                uint32_t stride = 0;
+                void *map_data = NULL;
+                void *data = gbm_bo_map(buffer.bo, 0, 0, buffer.width, 
buffer.height,
+                    GBM_BO_TRANSFER_READ, &stride, &map_data);
+                if (!data) {
+                    std::cerr << "Failed to map bo" << std::endl;
+                    break;
+                }
+                do_cont = frame_writer->add_frame((unsigned char*)data,
+                    buffer.base_usec, buffer.y_invert);
+                gbm_bo_unmap(buffer.bo, map_data);
+            } else {
+                do_cont = frame_writer->add_frame(buffer.bo,
+                    buffer.base_usec, buffer.y_invert);
+            }
+        } else {
+            do_cont = frame_writer->add_frame((unsigned char*)buffer.data,
+                buffer.base_usec, buffer.y_invert);
         }
 
         frame_writer_mutex.unlock();
 
-        buffer.available = false;
-        buffer.released = true;
+        if (!do_cont) {
+            break;
+        }
 
-        last_encoded_frame = next_frame(last_encoded_frame);
+        buffers.next_encode();
     }
 
     std::lock_guard<std::mutex> lock(frame_writer_mutex);
@@ -406,7 +577,7 @@
 static bool user_specified_overwrite(std::string filename)
 {
     struct stat buffer;   
-    if (stat (filename.c_str(), &buffer) == 0)
+    if (stat (filename.c_str(), &buffer) == 0 && !S_ISCHR(buffer.st_mode))
     {
         std::string input;
         std::cout << "Output file \"" << filename << "\" exists. Overwrite? 
Y/n: ";
@@ -438,6 +609,11 @@
         exit(EXIT_FAILURE);
     }
 
+    if (use_dmabuf && dmabuf == NULL) {
+        fprintf(stderr, "compositor doesn't support 
linux-dmabuf-unstable-v1\n");
+        exit(EXIT_FAILURE);
+    }
+
     if (available_outputs.empty())
     {
         fprintf(stderr, "no outputs available\n");
@@ -572,6 +748,10 @@
                             Some drivers report support for rgb0 data for 
vaapi input but
                             really only support yuv.
 
+  --no-dmabuf               By default, wf-recorder will try to use only GPU 
buffers and copies if
+                            using a GPU encoder. However, this can cause 
issues on some systems. In such
+                            cases, this option will disable the GPU copy and 
force a CPU one.
+
   -D, --no-damage           By default, wf-recorder will request a new frame 
from the compositor
                             only when the screen updates. This results in a 
much smaller output
                             file, which however has a variable refresh rate. 
When this option is
@@ -605,7 +785,7 @@
                             -p <option_name>=<option_value>
 
   -F, --filter              Specify the ffmpeg filter string to use. For 
example,
-                            -F hwupload,scale_vaapi=format=nv12 is used for 
VAAPI.
+                            -F scale_vaapi=format=nv12 is used for VAAPI.
 
   -b, --bframes             This option is used to set the maximum number of 
b-frames to be used.
                             If b-frames are not supported by your hardware, 
set this to 0.
@@ -634,7 +814,7 @@
     printf(R"(
 
   - wf-recorder                         Records the video. Use Ctrl+C to stop 
recording.
-                                        The video file will be stored as 
recording.webm in the
+                                        The video file will be stored as 
recording.mp4 in the
                                         current working directory.
 
   - wf-recorder -f <filename>.ext       Records the video. Use Ctrl+C to stop 
recording.
@@ -646,7 +826,7 @@
   Video and Audio:
 
   - wf-recorder -a                      Records the video and audio. Use 
Ctrl+C to stop recording.
-                                        The video file will be stored as 
recording.webm in the
+                                        The video file will be stored as 
recording.mp4 in the
                                         current working directory.
 
   - wf-recorder -a -f <filename>.ext    Records the video and audio. Use 
Ctrl+C to stop recording.
@@ -715,6 +895,7 @@
 
     constexpr const char* default_cmdline_output = "interactive";
     std::string cmdline_output = default_cmdline_output;
+    bool force_no_dmabuf = false;
 
     struct option opts[] = {
         { "output",            required_argument, NULL, 'o' },
@@ -730,6 +911,7 @@
         { "sample-rate",       required_argument, NULL, 'R' },
         { "sample-format",     required_argument, NULL, 'X' },
         { "device",            required_argument, NULL, 'd' },
+        { "no-dmabuf",         no_argument,       NULL, '&' },
         { "filter",            required_argument, NULL, 'F' },
         { "log",               no_argument,       NULL, 'l' },
         { "audio",             optional_argument, NULL, 'a' },
@@ -836,6 +1018,10 @@
                 parse_codec_opts(params.audio_codec_options, optarg);
                 break;
 
+            case '&':
+                force_no_dmabuf = true;
+                break;
+
             default:
                 printf("Unsupported command line argument %s\n", optarg);
         }
@@ -857,6 +1043,64 @@
     wl_registry_add_listener(registry, &registry_listener, NULL);
     sync_wayland();
 
+    if (params.codec.find("vaapi") != std::string::npos)
+    {
+        std::cout << "using VA-API, trying to enable DMA-BUF capture..." << 
std::endl;
+
+        // try compositor device if not explicitly set
+        if (params.hw_device.empty())
+        {
+            params.hw_device = drm_device_name;
+        }
+
+        // check we use same device as compositor
+        if (!params.hw_device.empty() && params.hw_device == drm_device_name 
&& !force_no_dmabuf)
+        {
+            use_dmabuf = true;
+        } else if (force_no_dmabuf) {
+            std::cout << "Disabling DMA-BUF as requested on command line" << 
std::endl;
+        } else {
+            std::cout << "compositor running on different device, disabling 
DMA-BUF" << std::endl;
+        }
+
+        // region with dmabuf not implemented in wlroots
+        if (selected_region.is_selected())
+        {
+            use_dmabuf = false;
+            std::cout << "region capture not supported with DMA-BUF" << 
std::endl;
+        }
+
+        if (params.video_filter == "null")
+        {
+            params.video_filter = "scale_vaapi=format=nv12:out_range=full";
+            if (!use_dmabuf)
+            {
+                params.video_filter.insert(0, "hwupload,");
+            }
+        }
+
+        if (use_dmabuf)
+        {
+            std::cout << "enabled DMA-BUF capture, device " << 
params.hw_device.c_str() << std::endl;
+
+            drm_fd = open(params.hw_device.c_str(), O_RDWR);
+            if (drm_fd < 0)
+            {
+                fprintf(stderr, "failed to open drm device: %m\n");
+                return EXIT_FAILURE;
+            }
+
+            gbm_device = gbm_create_device(drm_fd);
+            if (gbm_device == NULL)
+            {
+                fprintf(stderr, "failed to create gbm device: %m\n");
+                return EXIT_FAILURE;
+            }
+
+            use_hwupload = params.video_filter.find("hwupload") != 
std::string::npos;
+        }
+    }
+
     check_has_protos();
     load_output_info();
 
@@ -921,14 +1165,6 @@
     timespec first_frame;
     first_frame.tv_sec = -1;
 
-    active_buffer = 0;
-    for (auto& buffer : buffers)
-    {
-        buffer.wl_buffer = NULL;
-        buffer.available = false;
-        buffer.released = true;
-    }
-
     bool spawned_thread = false;
     std::thread writer_thread;
 
@@ -940,7 +1176,7 @@
     while(!exit_main_loop)
     {
         // wait for a free buffer
-        while(buffers[active_buffer].released != true) {
+        while(buffers.capture().ready_capture() != true) {
             std::this_thread::sleep_for(std::chrono::microseconds(500));
         }
 
@@ -955,7 +1191,7 @@
             break;
         }
 
-        auto& buffer = buffers[active_buffer];
+        auto& buffer = buffers.capture();
         //std::cout << "first buffer at " << timespec_to_usec(get_ct()) / 
1.0e6<< std::endl;
 
         if (!spawned_thread)
@@ -973,18 +1209,24 @@
         buffer.base_usec = timespec_to_usec(buffer.presented)
             - timespec_to_usec(first_frame);
 
-        buffer.released = false;
-        buffer.available = true;
-
-        active_buffer = next_frame(active_buffer);
+        buffers.next_capture();
     }
 
-    writer_thread.join();
+    if (writer_thread.joinable())
+    {
+        writer_thread.join();
+    }
 
-    for (auto& buffer : buffers)
+    for (size_t i = 0; i < buffers.size(); ++i)
     {
-        if (buffer.wl_buffer)
-            wl_buffer_destroy(buffer.wl_buffer);
+        auto buffer = buffers.at(i);
+        if (buffer && buffer->wl_buffer)
+            wl_buffer_destroy(buffer->wl_buffer);
+    }
+
+    if (gbm_device) {
+        gbm_device_destroy(gbm_device);
+        close(drm_fd);
     }
 
     return EXIT_SUCCESS;

++++++ wf-recorder.obsinfo ++++++
--- /var/tmp/diff_new_pack.IJJEGe/_old  2023-08-23 14:59:53.082233690 +0200
+++ /var/tmp/diff_new_pack.IJJEGe/_new  2023-08-23 14:59:53.086233697 +0200
@@ -1,5 +1,5 @@
 name: wf-recorder
-version: 0.3.0+git19
-mtime: 1672651122
-commit: 460d454b1efd380a3f732f6fd70c7a5e265381f6
+version: 0.4.0+git0
+mtime: 1692780734
+commit: 7e42d6c4dcb650808cc9ec3b7c2375764e5a2662
 

Reply via email to