From 92e822d580c6663f91bf1e32d2fecb3f93e02a5e Mon Sep 17 00:00:00 2001 From: graysky Date: Sun, 26 Nov 2023 09:11:56 -0500 Subject: [PATCH] alarm/vlc-rpi to 3.0.20-4 Swtich patch from dev/3.0.19/port_1 to test/3.0.20/rpi_1 and reenable chromecast but keep --enable-aribcam out as it is an unrecognized option. Still does not build against lua so retain lua5.2 --- ....20.patch => 0003-test-3.0.20-rpi_1.patch} | 5357 ++++++++++------- alarm/vlc-rpi/PKGBUILD | 12 +- 2 files changed, 3148 insertions(+), 2221 deletions(-) rename alarm/vlc-rpi/{0003-dev-3.0.19-port_1_to_3.0.20.patch => 0003-test-3.0.20-rpi_1.patch} (92%) diff --git a/alarm/vlc-rpi/0003-dev-3.0.19-port_1_to_3.0.20.patch b/alarm/vlc-rpi/0003-test-3.0.20-rpi_1.patch similarity index 92% rename from alarm/vlc-rpi/0003-dev-3.0.19-port_1_to_3.0.20.patch rename to alarm/vlc-rpi/0003-test-3.0.20-rpi_1.patch index d2ce211ed..9339833fd 100644 --- a/alarm/vlc-rpi/0003-dev-3.0.19-port_1_to_3.0.20.patch +++ b/alarm/vlc-rpi/0003-test-3.0.20-rpi_1.patch @@ -1,38 +1,3 @@ -diff --git a/NEWS b/NEWS -index 2cbc3e00b5..7690f54a53 100644 ---- a/NEWS -+++ b/NEWS -@@ -1,21 +1,3 @@ --Changes between 3.0.19 and 3.0.20: ------------------------------------ -- --Video Output: -- - Fix green line in fullscreen in D3D11 video output -- - Fix crash with some AMD drivers old versions -- - Fix events propagation issue when double-clicking with mouse wheel -- --Decoders: -- - Fix crash when AV1 hardware decoder fails -- --Interface: -- - Fix annoying disappearance of the Windows fullscreen controller -- --Demuxers: -- - Fix potential security issue (OOB Write) on MMS:// by checking user size bounds -- -- - Changes between 3.0.18 and 3.0.19: - ---------------------------------- - -@@ -73,7 +55,7 @@ Misc: - * Fix 5.1/7.1 output on macOS and tvOS - * Fix several crashes and bugs in the macOS preferences panel - * Improvements on the threading of the MMDevice audio output on Windows -- * Fix a potential security issue on the uninstaller DLLs (CVE-2023-46814) -+ * Fix a potential security issue on the uninstaller DLLs - * Fix memory leaks when using the media_list_player libVLC APIs - - Translations: diff --git a/bin/vlc.c b/bin/vlc.c index 72e0eee428..6d92b95990 100644 --- a/bin/vlc.c @@ -50,22 +15,9 @@ index 72e0eee428..6d92b95990 100644 /***************************************************************************** diff --git a/configure.ac b/configure.ac -index 69beb77ac0..2933715dc6 100644 +index 69beb77ac0..4791877325 100644 --- a/configure.ac +++ b/configure.ac -@@ -2,10 +2,10 @@ dnl Autoconf settings for vlc - - AC_COPYRIGHT([Copyright 1999-2023 VLC authors and VideoLAN]) - --AC_INIT(vlc, 3.0.20) -+AC_INIT(vlc, 3.0.19) - VERSION_MAJOR=3 - VERSION_MINOR=0 --VERSION_REVISION=20 -+VERSION_REVISION=19 - VERSION_EXTRA=0 - VERSION_DEV= - @@ -3083,6 +3083,21 @@ AS_IF([test "${have_gl}" = "yes"], [ dnl OpenGL ES 2: depends on EGL 1.1 PKG_ENABLE_MODULES_VLC([GLES2], [], [glesv2], [OpenGL ES v2 support], [disabled]) @@ -88,7 +40,37 @@ index 69beb77ac0..2933715dc6 100644 dnl dnl Xlib dnl -@@ -3447,20 +3462,24 @@ AM_CONDITIONAL([HAVE_KVA], [test "${have_kva}" = "yes"]) +@@ -3182,6 +3197,7 @@ AC_ARG_ENABLE(wayland, + [AS_HELP_STRING([--enable-wayland], [Incomplete Wayland support (default disabled)])]) + have_wayland="no" + have_wayland_egl="no" ++have_wayland_single_pixel_buffer="no" + + AS_IF([test "${enable_wayland}" = "yes"], [ + PKG_CHECK_MODULES([WAYLAND_CLIENT], [wayland-client >= 1.5.91], [ +@@ -3189,6 +3205,13 @@ AS_IF([test "${enable_wayland}" = "yes"], [ + PKG_CHECK_EXISTS([wayland-protocols >= 1.4], [ + WAYLAND_PROTOCOLS="$(${PKG_CONFIG} wayland-protocols --variable pkgdatadir)" + AC_MSG_RESULT([${WAYLAND_PROTOCOLS}]) ++ AC_MSG_CHECKING([for Wayland protocols single pixel buffer]) ++ PKG_CHECK_EXISTS([wayland-protocols >= 1.26], [ ++ AC_MSG_RESULT([yes]) ++ have_wayland_single_pixel_buffer="yes" ++ ], [ ++ AC_MSG_RESULT([no]) ++ ]) + ], [ + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([$(${PKG_CONFIG} --print-errors 'wayland-protocols >= 1.4')]) +@@ -3222,6 +3245,7 @@ AC_SUBST([WAYLAND_PROTOCOLS]) + AC_SUBST([WAYLAND_SCANNER]) + AM_CONDITIONAL([HAVE_WAYLAND], [test "${have_wayland}" = "yes"]) + AM_CONDITIONAL([HAVE_WAYLAND_EGL], [test "${have_wayland_egl}" = "yes"]) ++AM_CONDITIONAL([HAVE_WAYLAND_SINGLE_PIXEL_BUFFER], [test "${have_wayland_single_pixel_buffer}" = "yes"]) + + + dnl +@@ -3447,20 +3471,24 @@ AM_CONDITIONAL([HAVE_KVA], [test "${have_kva}" = "yes"]) dnl dnl MMAL plugin dnl @@ -116,7 +98,7 @@ index 69beb77ac0..2933715dc6 100644 AS_IF([test "${enable_mmal}" = "yes"], [ AC_MSG_ERROR([Cannot find bcm library...]) ], [ AC_MSG_WARN([Cannot find bcm library...]) ]) -@@ -3472,6 +3491,7 @@ if test "${enable_mmal}" != "no"; then +@@ -3472,6 +3500,7 @@ if test "${enable_mmal}" != "no"; then VLC_RESTORE_FLAGS fi AM_CONDITIONAL([HAVE_MMAL], [test "${have_mmal}" = "yes"]) @@ -124,44 +106,8 @@ index 69beb77ac0..2933715dc6 100644 dnl dnl evas plugin -diff --git a/contrib/src/dav1d/SHA512SUMS b/contrib/src/dav1d/SHA512SUMS -index 737a98e57e..ae01773dff 100644 ---- a/contrib/src/dav1d/SHA512SUMS -+++ b/contrib/src/dav1d/SHA512SUMS -@@ -1 +1 @@ --a91d217b8656c452dd80f5597e988f0e7a2ea32b12092c2695e7dc388033db7b193f7a8c98413bec31d1c38af376561d39f7618b935c4feeb6498c5d2b43f6a4 dav1d-1.3.0.tar.xz -+f172eebc0a5f6c51d31fc9e9758c2dd0de51d8a5d0e00c93a5f2b1b16b7b4a37b365f9c56dea95d400e66b63af5fa4c63d9e720719ac38852777fc8c6066e4a7 dav1d-1.2.1.tar.xz -diff --git a/contrib/src/dav1d/rules.mak b/contrib/src/dav1d/rules.mak -index 1a692e41f5..dd009c8dad 100644 ---- a/contrib/src/dav1d/rules.mak -+++ b/contrib/src/dav1d/rules.mak -@@ -1,6 +1,6 @@ - # libdav1d - --DAV1D_VERSION := 1.3.0 -+DAV1D_VERSION := 1.2.1 - DAV1D_URL := $(VIDEOLAN)/dav1d/$(DAV1D_VERSION)/dav1d-$(DAV1D_VERSION).tar.xz - - PKGS += dav1d -diff --git a/include/vlc_fixups.h b/include/vlc_fixups.h -index 37f7889337..2e8a1456bb 100644 ---- a/include/vlc_fixups.h -+++ b/include/vlc_fixups.h -@@ -589,12 +589,6 @@ struct addrinfo - struct addrinfo *ai_next; - }; - --# ifdef __LIBCN__ --/* OS/2 LIBCn has inet_pton(). Because of this, socklen_t is not defined above. -- * And OS/2 LIBCn has socklen_t. So include sys/socket.h here for socklen_t. */ --# include --# endif -- - const char *gai_strerror (int); - - int getaddrinfo (const char *node, const char *service, diff --git a/include/vlc_fourcc.h b/include/vlc_fourcc.h -index 97827bd4c1..76ad846f2f 100644 +index 97827bd4c1..6d4f62ab93 100644 --- a/include/vlc_fourcc.h +++ b/include/vlc_fourcc.h @@ -374,6 +374,11 @@ @@ -176,7 +122,7 @@ index 97827bd4c1..76ad846f2f 100644 /* DXVA2 opaque video surface for use with D3D9 */ #define VLC_CODEC_D3D9_OPAQUE VLC_FOURCC('D','X','A','9') /* 4:2:0 8 bpc */ -@@ -383,6 +388,12 @@ +@@ -383,6 +388,13 @@ #define VLC_CODEC_D3D11_OPAQUE VLC_FOURCC('D','X','1','1') /* 4:2:0 8 bpc */ #define VLC_CODEC_D3D11_OPAQUE_10B VLC_FOURCC('D','X','1','0') /* 4:2:0 10 bpc */ @@ -185,6 +131,7 @@ index 97827bd4c1..76ad846f2f 100644 +#define VLC_CODEC_DRM_PRIME_NV12 VLC_FOURCC('D','P','N','1') +#define VLC_CODEC_DRM_PRIME_SAND8 VLC_FOURCC('D','P','S','8') +#define VLC_CODEC_DRM_PRIME_SAND30 VLC_FOURCC('D','P','S','3') ++#define VLC_CODEC_DRM_PRIME_RGB32 VLC_FOURCC('D','P','3','2') + /* CVPixelBuffer opaque buffer type */ #define VLC_CODEC_CVPX_NV12 VLC_FOURCC('C','V','P','N') @@ -203,6 +150,25 @@ index fdebfeb2e1..42671f85db 100644 } egl; /* if ext == VLC_GL_EXT_WGL */ struct +diff --git a/include/vlc_vout_window.h b/include/vlc_vout_window.h +index 3a613d4e47..90f70abb01 100644 +--- a/include/vlc_vout_window.h ++++ b/include/vlc_vout_window.h +@@ -197,6 +197,14 @@ struct vout_window_t { + vout_window_sys_t *sys; + + vout_window_owner_t owner; ++ ++ /* Wayland handle sync objects - potentially other windowing systems ++ * too if the handle might change ++ * handle seq incremented if there is a possibility that the handle has ++ * changed - 0 => handle is fixed ++ */ ++ vlc_mutex_t handle_lock; ++ unsigned int handle_seq; + }; + + /** diff --git a/modules/Makefile.am b/modules/Makefile.am index 53b90a2713..6a29b126e9 100644 --- a/modules/Makefile.am @@ -225,39 +191,6 @@ index 53b90a2713..6a29b126e9 100644 include hw/vaapi/Makefile.am include hw/vdpau/Makefile.am include keystore/Makefile.am -diff --git a/modules/access/mms/mmsh.c b/modules/access/mms/mmsh.c -index ff598d775f..b032b4c5bb 100644 ---- a/modules/access/mms/mmsh.c -+++ b/modules/access/mms/mmsh.c -@@ -927,15 +927,7 @@ static int GetPacket( stream_t * p_access, chunk_t *p_ck ) - if( restsize < 8 ) - p_ck->i_size2 = 8; - else -- { - p_ck->i_size2 = GetWLE( p_sys->buffer + 10); -- if (p_ck->i_size2 < 8 /* Prevent underflow when set to i_data */ -- || p_ck->i_size2 - 8 > BUFFER_SIZE - 12 /* Prevent Out Of Bound Write */) -- { -- msg_Err(p_access, "invalid size2: %" PRIu16, p_ck->i_size2); -- return VLC_EGENERIC; -- } -- } - - p_ck->p_data = p_sys->buffer + 12; - p_ck->i_data = p_ck->i_size2 - 8; -diff --git a/modules/access/vcd/cdrom.c b/modules/access/vcd/cdrom.c -index 83ef4388c5..8b719ec532 100644 ---- a/modules/access/vcd/cdrom.c -+++ b/modules/access/vcd/cdrom.c -@@ -856,7 +856,7 @@ static int OpenVCDImage( vlc_object_t * p_this, const char *psz_dev, - p_vcddev->i_vcdimage_handle = vlc_open( psz_vcdfile, - O_RDONLY | O_NONBLOCK | O_BINARY ); - -- while( !b_found && fgets( line, 1024, cuefile ) ) -+ while( fgets( line, 1024, cuefile ) && !b_found ) - { - /* We have a cue file, but no valid vcd file yet */ - char filename[1024]; diff --git a/modules/audio_filter/converter/tospdif.c b/modules/audio_filter/converter/tospdif.c index 0c725d99e7..8d962d6e81 100644 --- a/modules/audio_filter/converter/tospdif.c @@ -946,10 +879,10 @@ index 0000000000..a15f512c23 +int drm_prime_attach_buf_to_pic(struct picture_t *pic, struct AVFrame *frame); + diff --git a/modules/codec/avcodec/va.c b/modules/codec/avcodec/va.c -index 0feb03b974..aa3a80f929 100644 +index 0feb03b974..70956eb885 100644 --- a/modules/codec/avcodec/va.c +++ b/modules/codec/avcodec/va.c -@@ -57,6 +57,22 @@ vlc_fourcc_t vlc_va_GetChroma(enum PixelFormat hwfmt, enum PixelFormat swfmt) +@@ -57,6 +57,24 @@ vlc_fourcc_t vlc_va_GetChroma(enum PixelFormat hwfmt, enum PixelFormat swfmt) return VLC_CODEC_D3D9_OPAQUE; } break; @@ -965,6 +898,8 @@ index 0feb03b974..aa3a80f929 100644 + case AV_PIX_FMT_RPI4_10: + case AV_PIX_FMT_YUV420P10LE: // When probing this is the swfmt given + return VLC_CODEC_DRM_PRIME_SAND30; ++ case AV_PIX_FMT_BGR0: ++ return VLC_CODEC_DRM_PRIME_RGB32; + default: + return 0; + } @@ -973,7 +908,7 @@ index 0feb03b974..aa3a80f929 100644 #if LIBAVUTIL_VERSION_CHECK(54, 13, 1, 24, 100) case AV_PIX_FMT_D3D11VA_VLD: diff --git a/modules/codec/avcodec/video.c b/modules/codec/avcodec/video.c -index 8c892dd3f4..c299fe2a38 100644 +index 8c892dd3f4..eb8a8956db 100644 --- a/modules/codec/avcodec/video.c +++ b/modules/codec/avcodec/video.c @@ -29,6 +29,8 @@ @@ -1162,24 +1097,7 @@ index 8c892dd3f4..c299fe2a38 100644 #endif AV_PIX_FMT_NONE, }; -@@ -788,7 +866,7 @@ int InitVideoHwDec( vlc_object_t *obj ) - - int res = InitVideoDecCommon( p_dec ); - if (res != VLC_SUCCESS) -- return res; -+ goto not_usable; - - const AVPixFmtDescriptor *src_desc = av_pix_fmt_desc_get(p_context->sw_pix_fmt); - -@@ -799,6 +877,7 @@ int InitVideoHwDec( vlc_object_t *obj ) - return VLC_SUCCESS; - } - -+not_usable: - EndVideoDec(obj); - return VLC_EGENERIC; - failed: -@@ -813,11 +892,11 @@ failed: +@@ -813,11 +891,11 @@ failed: * the ffmpeg codec will be opened, some memory allocated. The vout is not yet * opened (done after the first decoded frame). *****************************************************************************/ @@ -1193,7 +1111,7 @@ index 8c892dd3f4..c299fe2a38 100644 if( p_context == NULL ) return VLC_EGENERIC; -@@ -839,6 +918,27 @@ int InitVideoDec( vlc_object_t *obj ) +@@ -839,6 +917,27 @@ int InitVideoDec( vlc_object_t *obj ) return InitVideoDecCommon( p_dec ); } @@ -1221,7 +1139,7 @@ index 8c892dd3f4..c299fe2a38 100644 /***************************************************************************** * Flush: *****************************************************************************/ -@@ -848,6 +948,8 @@ static void Flush( decoder_t *p_dec ) +@@ -848,6 +947,8 @@ static void Flush( decoder_t *p_dec ) AVCodecContext *p_context = p_sys->p_context; date_Set(&p_sys->pts, VLC_TICK_INVALID); /* To make sure we recover properly */ @@ -1230,7 +1148,7 @@ index 8c892dd3f4..c299fe2a38 100644 p_sys->i_late_frames = 0; p_sys->b_draining = false; cc_Flush( &p_sys->cc ); -@@ -875,6 +977,8 @@ static bool check_block_validity( decoder_sys_t *p_sys, block_t *block ) +@@ -875,6 +976,8 @@ static bool check_block_validity( decoder_sys_t *p_sys, block_t *block ) if( block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) { date_Set( &p_sys->pts, VLC_TICK_INVALID ); /* To make sure we recover properly */ @@ -1239,7 +1157,7 @@ index 8c892dd3f4..c299fe2a38 100644 cc_Flush( &p_sys->cc ); p_sys->i_late_frames = 0; -@@ -1215,6 +1319,10 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error +@@ -1215,6 +1318,10 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error } if( b_has_data ) { @@ -1250,7 +1168,7 @@ index 8c892dd3f4..c299fe2a38 100644 pkt->data = p_block->p_buffer; pkt->size = p_block->i_buffer; pkt->pts = p_block->i_pts > VLC_TICK_INVALID ? p_block->i_pts : AV_NOPTS_VALUE; -@@ -1321,6 +1429,17 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error +@@ -1321,6 +1428,17 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error if( i_pts == AV_NOPTS_VALUE ) i_pts = date_Get( &p_sys->pts ); @@ -1268,7 +1186,7 @@ index 8c892dd3f4..c299fe2a38 100644 /* Interpolate the next PTS */ if( i_pts > VLC_TICK_INVALID ) date_Set( &p_sys->pts, i_pts ); -@@ -1373,9 +1492,10 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error +@@ -1373,9 +1491,10 @@ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error { /* When direct rendering is not used, get_format() and get_buffer() * might not be called. The output video format must be set here * then picture buffer can be allocated. */ @@ -1281,7 +1199,7 @@ index 8c892dd3f4..c299fe2a38 100644 p_pic = decoder_NewPicture(p_dec); if( !p_pic ) -@@ -1768,6 +1888,7 @@ static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context, +@@ -1768,6 +1887,7 @@ static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context, } swfmt = defaultfmt; } @@ -1289,38 +1207,87 @@ index 8c892dd3f4..c299fe2a38 100644 if (p_sys->pix_fmt == AV_PIX_FMT_NONE) goto no_reuse; +@@ -1821,7 +1941,10 @@ no_reuse: + p_sys->level = p_context->level; + + if (!can_hwaccel) ++ { ++ msg_Dbg(p_dec, "No hwaccel - using %s", av_get_pix_fmt_name(swfmt)); + return swfmt; ++ } + + #if (LIBAVCODEC_VERSION_MICRO >= 100) \ + && (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 83, 101)) diff --git a/modules/gui/qt/components/controller.cpp b/modules/gui/qt/components/controller.cpp -index 3adfe67c0d..382be37dcc 100644 +index 3adfe67c0d..b93a83af6b 100644 --- a/modules/gui/qt/components/controller.cpp +++ b/modules/gui/qt/components/controller.cpp -@@ -1089,7 +1089,7 @@ bool FullscreenControllerWidget::eventFilter( QObject *watched, QEvent *event ) - if ( watched == wParent && event->type() == QEvent::ActivationChange ) +@@ -812,6 +812,17 @@ FullscreenControllerWidget::FullscreenControllerWidget( intf_thread_t *_p_i, QWi + + vout.clear(); + ++#ifdef QT5_HAS_WAYLAND ++ if( b_hasWayland ) ++ { ++ // Popup is less than perfect in that it seems impossible to make it non-modal ++ // and you can't get it to fade but at least it goes where it is asked to and ++ // does less confusing things with other popups ++ setWindowFlags( Qt::Popup | Qt::FramelessWindowHint); ++ setWindowModality( Qt::NonModal ); ++ } ++ else ++#endif + setWindowFlags( Qt::Tool | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint ); + setAttribute( Qt::WA_ShowWithoutActivating ); + setMinimumWidth( FSC_WIDTH ); +@@ -879,8 +890,11 @@ FullscreenControllerWidget::~FullscreenControllerWidget() + + void FullscreenControllerWidget::restoreFSC() + { ++ msg_Info(p_intf, "%s", __func__); + if( !isWideFSC ) { - /* Hide if not active */ -- if ( !wParent->isActiveWindow() && !isActiveWindow() ) -+ if ( !wParent->isActiveWindow() ) - hideFSC(); ++ msg_Info(p_intf, "%s: 1", __func__); ++ + /* Restore half-bar and re-centre if needed */ + setMinimumWidth( FSC_WIDTH ); + adjustSize(); +@@ -903,6 +917,7 @@ void FullscreenControllerWidget::restoreFSC() + if( currentRes == screenRes && + currentRes.contains( previousPosition, true ) ) + { ++ msg_Info(p_intf, "%s: prev: %d, %d", __func__, previousPosition.x(), previousPosition.y()); + /* Restore to the last known position */ + move( previousPosition ); + } +@@ -917,6 +932,7 @@ void FullscreenControllerWidget::restoreFSC() } - -@@ -1307,7 +1307,7 @@ void FullscreenControllerWidget::mouseChanged( vout_thread_t *, int i_mousex, in - Q_ASSERT( wParent ); - - /* Ignore mouse events if not active */ -- if ( !wParent->isActiveWindow() && !isActiveWindow() ) return; -+ if ( !wParent->isActiveWindow() ) return; - - bool b_toShow; + else + { ++ msg_Info(p_intf, "%s: 1", __func__); + /* Dock at the bottom of the screen */ + updateFullwidthGeometry( targetScreen() ); + } +@@ -929,6 +945,7 @@ void FullscreenControllerWidget::centerFSC( int number ) + /* screen has changed, calculate new position */ + QPoint pos = QPoint( currentRes.x() + (currentRes.width() / 2) - (width() / 2), + currentRes.y() + currentRes.height() - height()); ++ msg_Info(p_intf, "%s: move to %d, %d", __func__, pos.x(), pos.y()); + move( pos ); + } diff --git a/modules/gui/qt/components/interface_widgets.cpp b/modules/gui/qt/components/interface_widgets.cpp -index 4a0a0dae12..17946c58d6 100644 +index 4a0a0dae12..9ee73a3eaa 100644 --- a/modules/gui/qt/components/interface_widgets.cpp +++ b/modules/gui/qt/components/interface_widgets.cpp -@@ -107,6 +107,36 @@ void VideoWidget::sync( void ) - #ifdef QT5_HAS_X11 - if( QX11Info::isPlatformX11() ) - XSync( QX11Info::display(), False ); -+#endif -+ refreshHandles(); +@@ -110,6 +110,44 @@ void VideoWidget::sync( void ) + #endif + } + ++static inline void incnz(unsigned int * px) ++{ ++ if ( ++*px == 0 ) ++ *px = 1; +} + +/** @@ -1328,15 +1295,12 @@ index 4a0a0dae12..17946c58d6 100644 + * seems to happen sometimes on resize + * In Qt it looks like this happens if the window is hidden + **/ -+void VideoWidget::refreshHandles() ++void VideoWidget::refreshHandle() +{ +#ifdef QT5_HAS_WAYLAND + if (!p_window || p_window->type != VOUT_WINDOW_TYPE_WAYLAND) + return; + -+ /* Ensure only the video widget is native (needed for Wayland) */ -+ stable->setAttribute( Qt::WA_DontCreateNativeAncestors, true); -+ + QWindow *window = stable->windowHandle(); + assert(window != NULL); + window->create(); @@ -1344,46 +1308,86 @@ index 4a0a0dae12..17946c58d6 100644 + QPlatformNativeInterface *qni = qApp->platformNativeInterface(); + assert(qni != NULL); + -+ p_window->handle.wl = static_cast( ++ struct wl_surface * const new_surface = static_cast( + qni->nativeResourceForWindow(QByteArrayLiteral("surface"), + window)); -+ p_window->display.wl = static_cast( -+ qni->nativeResourceForIntegration(QByteArrayLiteral("wl_display"))); - #endif - } - -@@ -165,21 +195,7 @@ bool VideoWidget::request( struct vout_window_t *p_wnd ) - #ifdef QT5_HAS_WAYLAND - case VOUT_WINDOW_TYPE_WAYLAND: - { -- /* Ensure only the video widget is native (needed for Wayland) */ -- stable->setAttribute( Qt::WA_DontCreateNativeAncestors, true); -- -- QWindow *window = stable->windowHandle(); -- assert(window != NULL); -- window->create(); -- -- QPlatformNativeInterface *qni = qApp->platformNativeInterface(); -- assert(qni != NULL); -- -- p_wnd->handle.wl = static_cast( -- qni->nativeResourceForWindow(QByteArrayLiteral("surface"), -- window)); -- p_wnd->display.wl = static_cast( -- qni->nativeResourceForIntegration(QByteArrayLiteral("wl_display"))); -+ refreshHandles(); ++ ++ if ( p_window->handle.wl != new_surface ) ++ { ++ vlc_mutex_lock(&p_window->handle_lock); ++ p_window->handle.wl = new_surface; ++ incnz(&p_window->handle_seq); ++ vlc_mutex_unlock(&p_window->handle_lock); ++ } ++#endif ++} ++ + /** + * Request the video to avoid the conflicts + **/ +@@ -180,6 +218,7 @@ bool VideoWidget::request( struct vout_window_t *p_wnd ) + window)); + p_wnd->display.wl = static_cast( + qni->nativeResourceForIntegration(QByteArrayLiteral("wl_display"))); ++ p_wnd->handle_seq = 1; break; } #endif +@@ -238,6 +277,8 @@ void VideoWidget::reportSize() + if( !p_window ) + return; + ++ refreshHandle(); ++ + QSize size = physicalSize(); + WindowResized(p_window, size); + } +@@ -293,6 +334,27 @@ bool VideoWidget::nativeEvent( const QByteArray& eventType, void* message, long* + return false; + } + ++void VideoWidget::showEvent(QShowEvent *event) ++{ ++ QFrame::showEvent(event); ++ ++ if (p_window && p_window->type == VOUT_WINDOW_TYPE_WAYLAND && p_window->handle.wl == NULL) ++ refreshHandle(); ++} ++ ++void VideoWidget::hideEvent(QHideEvent *event) ++{ ++ if (p_window && p_window->type == VOUT_WINDOW_TYPE_WAYLAND && p_window->handle.wl != NULL) ++ { ++ vlc_mutex_lock(&p_window->handle_lock); ++ p_window->handle.wl = NULL; ++ incnz(&p_window->handle_seq); ++ vlc_mutex_unlock(&p_window->handle_lock); ++ } ++ ++ QFrame::hideEvent(event); ++} ++ + void VideoWidget::resizeEvent( QResizeEvent *event ) + { + QWidget::resizeEvent( event ); diff --git a/modules/gui/qt/components/interface_widgets.hpp b/modules/gui/qt/components/interface_widgets.hpp -index 583f2d1f50..9dfda5b1de 100644 +index 583f2d1f50..e949e65b1a 100644 --- a/modules/gui/qt/components/interface_widgets.hpp +++ b/modules/gui/qt/components/interface_widgets.hpp -@@ -88,6 +88,7 @@ private: +@@ -69,6 +69,8 @@ protected: + return NULL; + } + ++ void hideEvent(QHideEvent *event) Q_DECL_OVERRIDE; ++ void showEvent(QShowEvent *event) Q_DECL_OVERRIDE; + bool nativeEvent(const QByteArray &eventType, void *message, long *result) Q_DECL_OVERRIDE; + virtual void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; + void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE; +@@ -88,6 +90,7 @@ private: bool enable_mouse_events; void reportSize(); -+ void refreshHandles(); ++ void refreshHandle(); signals: void sizeChanged( int, int ); @@ -16569,69 +16573,8 @@ index 2709425255..5ac384e633 100644 i_ret = CreateChain( p_filter, &fmt_mid ); es_format_Clean( &fmt_mid ); if( i_ret == VLC_SUCCESS ) -diff --git a/modules/video_chroma/d3d11_fmt.c b/modules/video_chroma/d3d11_fmt.c -index 1ca26e8e20..0875af6ea2 100644 ---- a/modules/video_chroma/d3d11_fmt.c -+++ b/modules/video_chroma/d3d11_fmt.c -@@ -146,32 +146,21 @@ int D3D11_AllocateShaderView(vlc_object_t *obj, ID3D11Device *d3ddevice, - #if !VLC_WINSTORE_APP - static HKEY GetAdapterRegistry(vlc_object_t *obj, DXGI_ADAPTER_DESC *adapterDesc) - { -- HKEY hDisplayKey, hKey; -+ HKEY hKey; - CHAR key[128]; - CHAR szData[256], lookup[256]; - DWORD len = 256; - LSTATUS ret; - -- ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}", 0, KEY_READ, &hDisplayKey); -- if ( ret != ERROR_SUCCESS ) -- { -- msg_Warn(obj, "failed to read the Display Adapter registry key (%ld)", ret); -- return NULL; -- } -- -- char DisplayNum[16]; - _snprintf(lookup, 256, "pci\\ven_%04x&dev_%04x", adapterDesc->VendorId, adapterDesc->DeviceId); -- for (DWORD k=0;; k++) -+ for (int i=0;;i++) - { -- ret = RegEnumKeyA(hDisplayKey, k, DisplayNum, ARRAY_SIZE(DisplayNum)); -- if (ret != ERROR_SUCCESS) -- break; -- -- ret = RegOpenKeyExA(hDisplayKey, DisplayNum, 0, KEY_READ, &hKey); -+ _snprintf(key, 128, "SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}\\%04d", i); -+ ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hKey); - if ( ret != ERROR_SUCCESS ) - { -- msg_Warn(obj, "failed to read the %s Display Adapter registry key (%ld)", DisplayNum, ret); -- continue; -+ msg_Warn(obj, "failed to read the %d Display Adapter registry key (%ld)", i, ret); -+ return NULL; - } - - len = sizeof(szData); -@@ -179,15 +168,13 @@ static HKEY GetAdapterRegistry(vlc_object_t *obj, DXGI_ADAPTER_DESC *adapterDesc - if ( ret == ERROR_SUCCESS ) { - if (_strnicmp(lookup, szData, strlen(lookup)) == 0) - return hKey; -- msg_Dbg(obj, "different %s device %s vs %s", DisplayNum, lookup, szData); -+ msg_Dbg(obj, "different %d device %s vs %s", i, lookup, szData); - } - else -- msg_Warn(obj, "failed to get the %s MatchingDeviceId (%ld)", DisplayNum, ret); -+ msg_Warn(obj, "failed to get the %d MatchingDeviceId (%ld)", i, ret); - - RegCloseKey(hKey); - } -- RegCloseKey(hDisplayKey); -- - return NULL; - } - diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am -index ae48c8e062..270f1a63b8 100644 +index ae48c8e062..a099f4d6b6 100644 --- a/modules/video_output/Makefile.am +++ b/modules/video_output/Makefile.am @@ -187,6 +187,29 @@ vout_LTLIBRARIES += libglx_plugin.la @@ -16664,7 +16607,7 @@ index ae48c8e062..270f1a63b8 100644 ### Wayland ### libwl_shm_plugin_la_SOURCES = video_output/wayland/shm.c -@@ -205,7 +228,38 @@ video_output/wayland/viewporter-client-protocol.h: \ +@@ -205,7 +228,52 @@ video_output/wayland/viewporter-client-protocol.h: \ video_output/wayland/viewporter-protocol.c: \ $(WAYLAND_PROTOCOLS)/stable/viewporter/viewporter.xml @@ -16679,7 +16622,11 @@ index ae48c8e062..270f1a63b8 100644 + $(WAYLAND_PROTOCOLS)/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml + $(AM_V_GEN)$(WAYLAND_SCANNER) private-code $< $@ + -+libwl_dmabuf_plugin_la_SOURCES = video_output/wayland/dmabuf.c\ ++video_output/wayland/single-pixel-buffer-v1-client-protocol.h: \ ++ $(WAYLAND_PROTOCOLS)/staging/single-pixel-buffer/single-pixel-buffer-v1.xml ++ $(AM_V_GEN)$(WAYLAND_SCANNER) client-header $< $@ ++ ++libwl_dmabuf_plugin_la_SOURCES = video_output/wayland/wl_dmabuf.c\ + video_output/wayland/picpool.c video_output/wayland/picpool.h\ + video_output/wayland/dmabuf_alloc.c video_output/wayland/dmabuf_alloc.h\ + video_output/wayland/rgba_premul.c video_output/wayland/rgba_premul.h\ @@ -16690,6 +16637,7 @@ index ae48c8e062..270f1a63b8 100644 + video_output/wayland/viewporter-protocol.c \ + video_output/wayland/linux-dmabuf-unstable-v1-client-protocol.h \ + video_output/wayland/linux-dmabuf-unstable-v1-protocol.c ++ +libwl_dmabuf_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) \ + -I$(builddir)/video_output/wayland -pthread +libwl_dmabuf_plugin_la_CFLAGS = $(WAYLAND_CLIENT_CFLAGS) -pthread @@ -16700,11 +16648,20 @@ index ae48c8e062..270f1a63b8 100644 + video_output/wayland/rgba_premul_aarch64.h +libwl_dmabuf_plugin_la_CFLAGS += -DHAVE_AARCH64_ASM=1 +endif ++if HAVE_WAYLAND_SINGLE_PIXEL_BUFFER ++video_output/wayland/single-pixel-buffer-v1-protocol.c: \ ++ $(WAYLAND_PROTOCOLS)/staging/single-pixel-buffer/single-pixel-buffer-v1.xml ++ $(AM_V_GEN)$(WAYLAND_SCANNER) private-code $< $@ ++nodist_libwl_dmabuf_plugin_la_SOURCES +=\ ++ video_output/wayland/single-pixel-buffer-v1-client-protocol.h \ ++ video_output/wayland/single-pixel-buffer-v1-protocol.c ++libwl_dmabuf_plugin_la_CFLAGS += -DHAVE_WAYLAND_SINGLE_PIXEL_BUFFER=1 ++endif +CLEANFILES += $(nodist_libwl_dmabuf_plugin_la_SOURCES) libwl_shell_plugin_la_SOURCES = video_output/wayland/shell.c libwl_shell_plugin_la_CFLAGS = $(WAYLAND_CLIENT_CFLAGS) -@@ -244,6 +298,8 @@ libegl_wl_plugin_la_LIBADD = $(EGL_LIBS) $(WAYLAND_EGL_LIBS) +@@ -244,6 +312,8 @@ libegl_wl_plugin_la_LIBADD = $(EGL_LIBS) $(WAYLAND_EGL_LIBS) if HAVE_WAYLAND BUILT_SOURCES += $(nodist_libwl_shm_plugin_la_SOURCES) vout_LTLIBRARIES += libwl_shm_plugin.la @@ -16732,10 +16689,10 @@ index 3440401082..9297823267 100644 #endif diff --git a/modules/video_output/drmu/drm_vout.c b/modules/video_output/drmu/drm_vout.c new file mode 100644 -index 0000000000..728cc0744a +index 0000000000..fc3395e4b4 --- /dev/null +++ b/modules/video_output/drmu/drm_vout.c -@@ -0,0 +1,1060 @@ +@@ -0,0 +1,1058 @@ +/***************************************************************************** + * drm_vout.c: DRM based output device + ***************************************************************************** @@ -16867,11 +16824,12 @@ index 0000000000..728cc0744a +static drmu_fb_t * +copy_pic_to_fb(vout_display_t *vd, drmu_pool_t * const pool, picture_t * const src) +{ -+ const uint32_t drm_fmt = drmu_format_vlc_to_drm(&src->format); ++ uint64_t mod; ++ const uint32_t drm_fmt = drmu_format_vlc_to_drm(&src->format, &mod); + drmu_fb_t * fb; + int i; + -+ if (drm_fmt == 0) { ++ if (drm_fmt == 0 || mod != DRM_FORMAT_MOD_LINEAR) { + msg_Warn(vd, "Failed vlc->drm format for copy_pic: %s", drmu_log_fourcc(src->format.i_chroma)); + return NULL; + } @@ -17239,13 +17197,13 @@ index 0000000000..728cc0744a +#endif + +#if HAS_ZC_CMA -+ if (drmu_format_vlc_to_drm_cma(pic->format.i_chroma) != 0) { ++ if (drmu_format_vlc_to_drm_cma(&pic->format, NULL) != 0) { + dfb = drmu_fb_vlc_new_pic_cma_attach(sys->du, pic); + } + else +#endif +#if HAS_DRMPRIME -+ if (drmu_format_vlc_to_drm_prime(pic->format.i_chroma, NULL) != 0) { ++ if (drmu_format_vlc_to_drm_prime(&pic->format, NULL) != 0) { + dfb = drmu_fb_vlc_new_pic_attach(sys->du, pic); + } + else @@ -17373,20 +17331,16 @@ index 0000000000..728cc0744a +static void +set_format(vout_display_t * const vd, vout_display_sys_t * const sys, const video_format_t *const fmtp) +{ -+#if HAS_DRMPRIME -+ uint32_t drm_fmt; -+ uint64_t drm_mod; ++ const drmu_vlc_fmt_info_t * const fi = drmu_vlc_fmt_info_find_vlc(fmtp); ++ const uint64_t drm_mod = drmu_vlc_fmt_info_drm_modifier(fi); ++ const uint32_t drm_fmt = drmu_vlc_fmt_info_drm_pixelformat(fi); ++ ++ msg_Dbg(vd, "%s: %s -> %s (%#"PRIx64"): prime: %d", __func__, ++ drmu_log_fourcc(fmtp->i_chroma), drmu_log_fourcc(drm_fmt), drm_mod, ++ drmu_vlc_fmt_info_is_drmprime(fi)); + -+ // We think we can deal with the source format so set requested -+ // input format to source + vd->fmt = *fmtp; + -+ if ((drm_fmt = drmu_format_vlc_to_drm_prime(fmtp->i_chroma, &drm_mod)) != 0 && -+ drmu_plane_format_check(sys->dp, drm_fmt, drm_mod)) { -+ // Hurrah! -+ } -+ else -+#endif +#if HAS_ZC_CMA + if (fmtp->i_chroma == VLC_CODEC_MMAL_OPAQUE) { + // Can't deal directly with opaque - but we can always convert it to ZC I420 @@ -17394,15 +17348,15 @@ index 0000000000..728cc0744a + } + else +#endif -+ if ((drm_fmt = drmu_format_vlc_to_drm(fmtp)) != 0 && -+ drmu_plane_format_check(sys->dp, drm_fmt, 0)) { -+ // It is a format where simple byte copying works ++ if (drmu_plane_format_check(sys->dp, drm_fmt, drm_mod)) { ++ // DRMP or it is a format where simple byte copying works + } + else { + const vlc_fourcc_t *fallback = vlc_fourcc_IsYUV(fmtp->i_chroma) ? + vlc_fourcc_GetYUVFallback(fmtp->i_chroma) : + vlc_fourcc_GetRGBFallback(fmtp->i_chroma); + ++ // *** How should we check RGB fallbacks given we need masks too? + for (; *fallback; ++fallback) { + if (drmu_plane_format_check(sys->dp, drmu_format_vlc_chroma_to_drm(*fallback), 0)) + break; @@ -17518,7 +17472,7 @@ index 0000000000..728cc0744a + return NULL; + + for (unsigned int j = 0; j != n; ++j) { -+ if ((*p = drmu_format_vlc_to_vlc(drm_chromas[j])) != 0) ++ if ((*p = drmu_vlc_fmt_info_vlc_chroma(drmu_vlc_fmt_info_find_drm(drm_chromas[j], 0))) != 0) + ++p; + } + @@ -17712,6 +17666,7 @@ index 0000000000..728cc0744a + unsigned int w, h, hz; + if (*drmu_util_parse_mode(modestr, &w, &h, &hz) != 0) { + msg_Err(vd, "Bad mode string: '%s'", modestr); ++ free(mode_name); + ret = VLC_EGENERIC; + goto fail; + } @@ -17781,7 +17736,7 @@ index 0000000000..728cc0744a + set_shortname(N_("DRM vout")) + set_description(N_("DRM vout plugin")) + set_capability("vout display", 16) // 1 point better than ASCII art -+ add_shortcut("drm_vout") ++ add_shortcut("drm-vout") + set_category(CAT_VIDEO) + set_subcategory(SUBCAT_VIDEO_VOUT) + @@ -21728,10 +21683,10 @@ index 0000000000..e090feec5b + diff --git a/modules/video_output/drmu/drmu.h b/modules/video_output/drmu/drmu.h new file mode 100644 -index 0000000000..42782594d9 +index 0000000000..ab0796ada9 --- /dev/null +++ b/modules/video_output/drmu/drmu.h -@@ -0,0 +1,591 @@ +@@ -0,0 +1,576 @@ +#ifndef _DRMU_DRMU_H +#define _DRMU_DRMU_H + @@ -21783,6 +21738,9 @@ index 0000000000..42782594d9 +struct drmu_env_s; +typedef struct drmu_env_s drmu_env_t; + ++struct drm_log_env_s; ++typedef struct drmu_log_env_s drmu_log_env_t; ++ +typedef struct drmu_rect_s { + int32_t x, y; + uint32_t w, h; @@ -22244,24 +22202,6 @@ index 0000000000..42782594d9 + +// Logging + -+enum drmu_log_level_e { -+ DRMU_LOG_LEVEL_NONE = -1, // Max level specifier for nothing (not a real level) -+ DRMU_LOG_LEVEL_MESSAGE = 0, // (Nearly) always printed info -+ DRMU_LOG_LEVEL_ERROR, // Error -+ DRMU_LOG_LEVEL_WARNING, -+ DRMU_LOG_LEVEL_INFO, // Interesting but not critical info -+ DRMU_LOG_LEVEL_DEBUG, // Info only useful for debug -+ DRMU_LOG_LEVEL_ALL, // Max level specifier for everything (not a real level) -+}; -+ -+typedef void drmu_log_fn(void * v, enum drmu_log_level_e level, const char * fmt, va_list vl); -+ -+typedef struct drmu_log_env_s { -+ drmu_log_fn * fn; -+ void * v; -+ enum drmu_log_level_e max_level; -+} drmu_log_env_t; -+ +extern const struct drmu_log_env_s drmu_log_env_none; // pre-built do-nothing log structure + +// drmu_atomic @@ -23507,10 +23447,10 @@ index 0000000000..a2400357e4 + diff --git a/modules/video_output/drmu/drmu_log.h b/modules/video_output/drmu/drmu_log.h new file mode 100644 -index 0000000000..00fa9c3d12 +index 0000000000..cc6ba5cc91 --- /dev/null +++ b/modules/video_output/drmu/drmu_log.h -@@ -0,0 +1,53 @@ +@@ -0,0 +1,71 @@ +#ifndef _DRMU_DRMU_LOG_H +#define _DRMU_DRMU_LOG_H + @@ -23518,6 +23458,24 @@ index 0000000000..00fa9c3d12 + +struct drmu_env_s; + ++enum drmu_log_level_e { ++ DRMU_LOG_LEVEL_NONE = -1, // Max level specifier for nothing (not a real level) ++ DRMU_LOG_LEVEL_MESSAGE = 0, // (Nearly) always printed info ++ DRMU_LOG_LEVEL_ERROR, // Error ++ DRMU_LOG_LEVEL_WARNING, ++ DRMU_LOG_LEVEL_INFO, // Interesting but not critical info ++ DRMU_LOG_LEVEL_DEBUG, // Info only useful for debug ++ DRMU_LOG_LEVEL_ALL, // Max level specifier for everything (not a real level) ++}; ++ ++typedef void drmu_log_fn(void * v, enum drmu_log_level_e level, const char * fmt, va_list vl); ++ ++typedef struct drmu_log_env_s { ++ drmu_log_fn * fn; ++ void * v; ++ enum drmu_log_level_e max_level; ++} drmu_log_env_t; ++ +void drmu_log_generic(const struct drmu_log_env_s * const log, const enum drmu_log_level_e level, + const char * const fmt, ...); + @@ -24442,10 +24400,10 @@ index 0000000000..9c4ea46e4c + diff --git a/modules/video_output/drmu/drmu_vlc.c b/modules/video_output/drmu/drmu_vlc.c new file mode 100644 -index 0000000000..f2efd30c21 +index 0000000000..e5930f59fc --- /dev/null +++ b/modules/video_output/drmu/drmu_vlc.c -@@ -0,0 +1,351 @@ +@@ -0,0 +1,352 @@ +#include "drmu_vlc.h" +#include "drmu_fmts.h" +#include "drmu_log.h" @@ -24688,7 +24646,8 @@ index 0000000000..f2efd30c21 + int i; + drmu_fb_t * const dfb = drmu_fb_int_alloc(du); + fb_aux_pic_t * aux = NULL; -+ uint32_t fmt = drmu_format_vlc_to_drm_cma(pic->format.i_chroma); ++ uint64_t mod; ++ uint32_t fmt = drmu_format_vlc_to_drm_cma(&pic->format, &mod); + const bool is_sand = (pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND8 || + pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND30); + cma_buf_t * const cb = cma_buf_pic_get(pic); @@ -24737,7 +24696,7 @@ index 0000000000..f2efd30c21 + drmu_fb_int_layer_mod_set(dfb, i, 0, pic->format.i_width, pic->p[i].p_pixels - base_addr, + DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(pic->p[i].i_pitch)); + else -+ drmu_fb_int_layer_mod_set(dfb, i, 0, pic->p[i].i_pitch, pic->p[i].p_pixels - base_addr, 0); ++ drmu_fb_int_layer_mod_set(dfb, i, 0, pic->p[i].i_pitch, pic->p[i].p_pixels - base_addr, mod); + } + } + @@ -24886,65 +24845,235 @@ index 0000000000..5c19141930 + diff --git a/modules/video_output/drmu/drmu_vlc_fmts.c b/modules/video_output/drmu/drmu_vlc_fmts.c new file mode 100644 -index 0000000000..335a78e5ac +index 0000000000..862b89eef5 --- /dev/null +++ b/modules/video_output/drmu/drmu_vlc_fmts.c -@@ -0,0 +1,229 @@ +@@ -0,0 +1,237 @@ +#include "drmu_vlc_fmts.h" + ++#include +#include + +#ifndef DRM_FORMAT_P030 +#define DRM_FORMAT_P030 fourcc_code('P', '0', '3', '0') +#endif + ++#define DRMU_VLC_FMTS_FLAG_DRMP 1 ++#define DRMU_VLC_FMTS_FLAG_ZC 2 ++#define DRMU_VLC_FMTS_FLAG_FULL_RANGE 4 ++ ++struct drmu_vlc_fmt_info_ss { ++ vlc_fourcc_t vlc_chroma; ++ uint32_t drm_pixelformat; ++ uint32_t rmask; ++ uint32_t gmask; ++ uint32_t bmask; ++ uint64_t drm_modifier; ++ unsigned int flags; ++}; ++ +// N.B. DRM seems to order its format descriptor names the opposite way round to VLC +// DRM is hi->lo within a little-endian word, VLC is byte order + ++#define I2(vlc, drm) {(vlc), (drm), 0, 0, 0, DRM_FORMAT_MOD_LINEAR, 0 } ++#define RM(vlc, drm, r, g, b) {(vlc), (drm), (r), (g), (b), DRM_FORMAT_MOD_LINEAR, 0 } ++#define R2(vlc, drm) RM((vlc), (drm), 0, 0, 0) ++ ++static const drmu_vlc_fmt_info_t fmt_table[] = { ++ R2(VLC_CODEC_RGBA, DRM_FORMAT_ABGR8888), ++ R2(VLC_CODEC_BGRA, DRM_FORMAT_ARGB8888), ++ R2(VLC_CODEC_ARGB, DRM_FORMAT_BGRA8888), ++ // VLC_CODEC_ABGR does not exist in VLC ++ // AYUV appears to be the only DRM YUVA-like format ++ I2(VLC_CODEC_VUYA, DRM_FORMAT_AYUV), ++ I2(VLC_CODEC_VYUY, DRM_FORMAT_YUYV), ++ I2(VLC_CODEC_UYVY, DRM_FORMAT_YVYU), ++ I2(VLC_CODEC_YUYV, DRM_FORMAT_VYUY), ++ I2(VLC_CODEC_YVYU, DRM_FORMAT_UYVY), ++ I2(VLC_CODEC_NV12, DRM_FORMAT_NV12), ++ I2(VLC_CODEC_NV21, DRM_FORMAT_NV21), ++ I2(VLC_CODEC_NV16, DRM_FORMAT_NV16), ++ I2(VLC_CODEC_NV61, DRM_FORMAT_NV61), ++ I2(VLC_CODEC_NV24, DRM_FORMAT_NV24), ++ I2(VLC_CODEC_NV42, DRM_FORMAT_NV42), ++ I2(VLC_CODEC_P010, DRM_FORMAT_P010), ++ I2(VLC_CODEC_I420, DRM_FORMAT_YUV420), ++ { VLC_CODEC_J420, DRM_FORMAT_YUV420, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_FULL_RANGE }, ++ I2(VLC_CODEC_YV12, DRM_FORMAT_YVU420), ++ I2(VLC_CODEC_I422, DRM_FORMAT_YUV422), ++ { VLC_CODEC_J422, DRM_FORMAT_YUV422, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_FULL_RANGE }, ++ I2(VLC_CODEC_I444, DRM_FORMAT_YUV444), ++ { VLC_CODEC_J444, DRM_FORMAT_YUV444, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_FULL_RANGE }, ++#if HAS_DRMPRIME ++ { VLC_CODEC_DRM_PRIME_I420, DRM_FORMAT_YUV420, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_DRMP }, ++ { VLC_CODEC_DRM_PRIME_NV12, DRM_FORMAT_NV12, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_DRMP }, ++ { VLC_CODEC_DRM_PRIME_SAND8, DRM_FORMAT_NV12, 0, 0, 0, DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0), DRMU_VLC_FMTS_FLAG_DRMP }, ++ { VLC_CODEC_DRM_PRIME_SAND30, DRM_FORMAT_P030, 0, 0, 0, DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0), DRMU_VLC_FMTS_FLAG_DRMP }, ++ { VLC_CODEC_DRM_PRIME_RGB32, DRM_FORMAT_XRGB8888, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_DRMP }, ++#endif ++#if HAS_ZC_CMA ++ { VLC_CODEC_MMAL_ZC_I420, DRM_FORMAT_YUV420, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_ZC }, ++ { VLC_CODEC_MMAL_ZC_SAND8, DRM_FORMAT_NV12, 0, 0, 0, DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0), DRMU_VLC_FMTS_FLAG_ZC }, ++ { VLC_CODEC_MMAL_ZC_SAND30, DRM_FORMAT_P030, 0, 0, 0, DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0), DRMU_VLC_FMTS_FLAG_ZC }, ++ { VLC_CODEC_MMAL_ZC_RGB32, DRM_FORMAT_RGBX8888, 0, 0, 0, DRM_FORMAT_MOD_LINEAR, DRMU_VLC_FMTS_FLAG_ZC }, ++#endif ++ ++ RM(VLC_CODEC_RGB32, DRM_FORMAT_XRGB8888, 0xff0000, 0xff00, 0xff), ++ RM(VLC_CODEC_RGB32, DRM_FORMAT_XBGR8888, 0xff, 0xff00, 0xff0000), ++ RM(VLC_CODEC_RGB32, DRM_FORMAT_RGBX8888, 0xff000000, 0xff0000, 0xff00), ++ RM(VLC_CODEC_RGB32, DRM_FORMAT_BGRX8888, 0xff00, 0xff0000, 0xff000000), ++ RM(VLC_CODEC_RGB24, DRM_FORMAT_RGB888, 0xff0000, 0xff00, 0xff), ++ RM(VLC_CODEC_RGB24, DRM_FORMAT_BGR888, 0xff, 0xff00, 0xff0000), ++ RM(VLC_CODEC_RGB16, DRM_FORMAT_RGB565, 0xf800, 0x7e0, 0x1f), ++ RM(VLC_CODEC_RGB16, DRM_FORMAT_BGR565, 0x1f, 0x7e0, 0xf800), ++ ++ I2(0, 0) ++}; ++#undef I2 ++#undef RM ++#undef R2 ++ ++// *** Sorted lookups? ++ ++const drmu_vlc_fmt_info_t * ++drmu_vlc_fmt_info_find_vlc_next(const video_frame_format_t * const vf_vlc, const drmu_vlc_fmt_info_t * f) ++{ ++ f = (f == NULL) ? fmt_table : f + 1; ++ ++ for (; f->vlc_chroma != 0; ++f) ++ { ++ if (f->vlc_chroma != vf_vlc->i_chroma) ++ continue; ++ if (f->rmask != 0 && vf_vlc->i_rmask != 0 && ++ (f->rmask != vf_vlc->i_rmask || f->gmask != vf_vlc->i_gmask || f->bmask != vf_vlc->i_bmask)) ++ continue; ++ return f; ++ } ++ return NULL; ++} ++ ++const drmu_vlc_fmt_info_t * ++drmu_vlc_fmt_info_find_vlc(const video_frame_format_t * const vf_vlc) ++{ ++ return drmu_vlc_fmt_info_find_vlc_next(vf_vlc, NULL); ++} ++ ++const drmu_vlc_fmt_info_t * ++drmu_vlc_fmt_info_find_drm_next(const uint32_t pixelformat, const uint64_t modifier, const drmu_vlc_fmt_info_t * f) ++{ ++ f = (f == NULL) ? fmt_table : f + 1; ++ ++ for (; f->vlc_chroma != 0; ++f) ++ { ++ if (f->drm_pixelformat != pixelformat || f->drm_modifier != modifier) ++ continue; ++ // Only return the "base" version ++ if (f->flags != 0) ++ continue; ++ return f; ++ } ++ return NULL; ++} ++ ++const drmu_vlc_fmt_info_t * ++drmu_vlc_fmt_info_find_drm(const uint32_t pixelformat, const uint64_t modifier) ++{ ++ return drmu_vlc_fmt_info_find_drm_next(pixelformat, modifier, NULL); ++} ++ ++vlc_fourcc_t ++drmu_vlc_fmt_info_vlc_chroma(const drmu_vlc_fmt_info_t * const f) ++{ ++ return f == NULL ? 0 : f->vlc_chroma; ++} ++ ++void ++drmu_vlc_fmt_info_vlc_rgb_masks(const drmu_vlc_fmt_info_t * const f, uint32_t * r, uint32_t * g, uint32_t * b) ++{ ++ if (f == NULL) ++ { ++ *r = 0; ++ *g = 0; ++ *b = 0; ++ return; ++ } ++ *r = f->rmask; ++ *g = f->gmask; ++ *b = f->bmask; ++} ++ ++uint32_t ++drmu_vlc_fmt_info_drm_pixelformat(const drmu_vlc_fmt_info_t * const f) ++{ ++ return f == NULL ? 0 : f->drm_pixelformat; ++} ++ ++uint64_t ++drmu_vlc_fmt_info_drm_modifier(const drmu_vlc_fmt_info_t * const f) ++{ ++ return f == NULL ? DRM_FORMAT_MOD_INVALID : f->drm_modifier; ++} ++ ++bool ++drmu_vlc_fmt_info_is_drmprime(const drmu_vlc_fmt_info_t * const f) ++{ ++#if HAS_DRMPRIME ++ return f != NULL && (f->flags & DRMU_VLC_FMTS_FLAG_DRMP); ++#else ++ (void)f; ++ return false; ++#endif ++} ++ ++bool ++drmu_vlc_fmt_info_is_zc_cma(const drmu_vlc_fmt_info_t * const f) ++{ ++#if HAS_ZC_CMA ++ return f != NULL && (f->flags & DRMU_VLC_FMTS_FLAG_ZC); ++#else ++ (void)f; ++ return false; ++#endif ++} ++ ++ ++uint32_t ++drmu_format_vlc_to_drm(const video_frame_format_t * const vf_vlc, uint64_t * const pMod) ++{ ++ const drmu_vlc_fmt_info_t * const f = drmu_vlc_fmt_info_find_vlc(vf_vlc); ++ ++ if (pMod) ++ *pMod = drmu_vlc_fmt_info_drm_modifier(f); ++ return drmu_vlc_fmt_info_drm_pixelformat(f); ++} ++ +#if HAS_ZC_CMA +uint32_t -+drmu_format_vlc_to_drm_cma(const vlc_fourcc_t chroma_in) ++drmu_format_vlc_to_drm_cma(const video_frame_format_t * const vf_vlc, uint64_t * const pMod) +{ -+ switch (chroma_in) { -+ case VLC_CODEC_MMAL_ZC_I420: -+ return DRM_FORMAT_YUV420; -+ case VLC_CODEC_MMAL_ZC_SAND8: -+ return DRM_FORMAT_NV12; -+ case VLC_CODEC_MMAL_ZC_SAND30: -+ return DRM_FORMAT_P030; -+ case VLC_CODEC_MMAL_ZC_RGB32: -+ return DRM_FORMAT_RGBX8888; -+ } -+ return 0; ++ const drmu_vlc_fmt_info_t * f = drmu_vlc_fmt_info_find_vlc(vf_vlc); ++ ++ if (!drmu_vlc_fmt_info_is_zc_cma(f)) ++ f = NULL; ++ ++ if (pMod) ++ *pMod = drmu_vlc_fmt_info_drm_modifier(f); ++ return drmu_vlc_fmt_info_drm_pixelformat(f); +} +#endif + +#if HAS_DRMPRIME +uint32_t -+drmu_format_vlc_to_drm_prime(const vlc_fourcc_t chroma_in, uint64_t * const pmod) ++drmu_format_vlc_to_drm_prime(const video_frame_format_t * const vf_vlc, uint64_t * const pMod) +{ -+ uint32_t fmt = 0; -+ uint64_t mod = DRM_FORMAT_MOD_LINEAR; ++ const drmu_vlc_fmt_info_t * f = drmu_vlc_fmt_info_find_vlc(vf_vlc); + -+ switch (chroma_in) { -+ case VLC_CODEC_DRM_PRIME_I420: -+ fmt = DRM_FORMAT_YUV420; -+ break; -+ case VLC_CODEC_DRM_PRIME_NV12: -+ fmt = DRM_FORMAT_NV12; -+ break; -+ case VLC_CODEC_DRM_PRIME_SAND8: -+ fmt = DRM_FORMAT_NV12; -+ mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0); -+ break; -+ case VLC_CODEC_DRM_PRIME_SAND30: -+ fmt = DRM_FORMAT_P030; -+ mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0); -+ break; -+ } -+ if (pmod) -+ *pmod = !fmt ? DRM_FORMAT_MOD_INVALID : mod; -+ return fmt; ++ if (!drmu_vlc_fmt_info_is_drmprime(f)) ++ f = NULL; ++ ++ if (pMod) ++ *pMod = drmu_vlc_fmt_info_drm_modifier(f); ++ return drmu_vlc_fmt_info_drm_pixelformat(f); +} +#endif + @@ -24953,178 +25082,16 @@ index 0000000000..335a78e5ac +uint32_t +drmu_format_vlc_chroma_to_drm(const vlc_fourcc_t chroma) +{ -+ switch (chroma) { -+ case VLC_CODEC_RGBA: -+ return DRM_FORMAT_ABGR8888; -+ case VLC_CODEC_BGRA: -+ return DRM_FORMAT_ARGB8888; -+ case VLC_CODEC_ARGB: -+ return DRM_FORMAT_BGRA8888; -+ // VLC_CODEC_ABGR does not exist in VLC -+ case VLC_CODEC_VUYA: -+ return DRM_FORMAT_AYUV; -+ // AYUV appears to be the only DRM YUVA-like format -+ case VLC_CODEC_VYUY: -+ return DRM_FORMAT_YUYV; -+ case VLC_CODEC_UYVY: -+ return DRM_FORMAT_YVYU; -+ case VLC_CODEC_YUYV: -+ return DRM_FORMAT_VYUY; -+ case VLC_CODEC_YVYU: -+ return DRM_FORMAT_UYVY; -+ case VLC_CODEC_NV12: -+ return DRM_FORMAT_NV12; -+ case VLC_CODEC_NV21: -+ return DRM_FORMAT_NV21; -+ case VLC_CODEC_NV16: -+ return DRM_FORMAT_NV16; -+ case VLC_CODEC_NV61: -+ return DRM_FORMAT_NV61; -+ case VLC_CODEC_NV24: -+ return DRM_FORMAT_NV24; -+ case VLC_CODEC_NV42: -+ return DRM_FORMAT_NV42; -+ case VLC_CODEC_P010: -+ return DRM_FORMAT_P010; -+ case VLC_CODEC_J420: -+ case VLC_CODEC_I420: -+ return DRM_FORMAT_YUV420; -+ case VLC_CODEC_YV12: -+ return DRM_FORMAT_YVU420; -+ case VLC_CODEC_J422: -+ case VLC_CODEC_I422: -+ return DRM_FORMAT_YUV422; -+ case VLC_CODEC_J444: -+ case VLC_CODEC_I444: -+ return DRM_FORMAT_YUV444; -+ default: -+ break; -+ } -+ -+#if HAS_ZC_CMA -+ return drmu_format_vlc_to_drm_cma(chroma); -+#else -+ return 0; -+#endif -+} -+ -+uint32_t -+drmu_format_vlc_to_drm(const video_frame_format_t * const vf_vlc) -+{ -+ switch (vf_vlc->i_chroma) { -+ case VLC_CODEC_RGB32: -+ { -+ // VLC RGB32 aka RV32 means we have to look at the mask values -+ const uint32_t r = vf_vlc->i_rmask; -+ const uint32_t g = vf_vlc->i_gmask; -+ const uint32_t b = vf_vlc->i_bmask; -+ if (r == 0xff0000 && g == 0xff00 && b == 0xff) -+ return DRM_FORMAT_XRGB8888; -+ if (r == 0xff && g == 0xff00 && b == 0xff0000) -+ return DRM_FORMAT_XBGR8888; -+ if (r == 0xff000000 && g == 0xff0000 && b == 0xff00) -+ return DRM_FORMAT_RGBX8888; -+ if (r == 0xff00 && g == 0xff0000 && b == 0xff000000) -+ return DRM_FORMAT_BGRX8888; -+ break; -+ } -+ case VLC_CODEC_RGB24: -+ { -+ // VLC RGB24 aka RV24 means we have to look at the mask values -+ const uint32_t r = vf_vlc->i_rmask; -+ const uint32_t g = vf_vlc->i_gmask; -+ const uint32_t b = vf_vlc->i_bmask; -+ if (r == 0xff0000 && g == 0xff00 && b == 0xff) -+ return DRM_FORMAT_RGB888; -+ if (r == 0xff && g == 0xff00 && b == 0xff0000) -+ return DRM_FORMAT_BGR888; -+ break; -+ } -+ case VLC_CODEC_RGB16: -+ { -+ // VLC RGB16 aka RV16 means we have to look at the mask values -+ const uint32_t r = vf_vlc->i_rmask; -+ const uint32_t g = vf_vlc->i_gmask; -+ const uint32_t b = vf_vlc->i_bmask; -+ if (r == 0xf800 && g == 0x7e0 && b == 0x1f) -+ return DRM_FORMAT_RGB565; -+ if (r == 0x1f && g == 0x7e0 && b == 0xf800) -+ return DRM_FORMAT_BGR565; -+ break; -+ } -+ default: -+ break; -+ } -+ -+ return drmu_format_vlc_chroma_to_drm(vf_vlc->i_chroma); -+} -+ -+vlc_fourcc_t -+drmu_format_vlc_to_vlc(const uint32_t vf_drm) -+{ -+ switch (vf_drm) { -+ case DRM_FORMAT_XRGB8888: -+ case DRM_FORMAT_XBGR8888: -+ case DRM_FORMAT_RGBX8888: -+ case DRM_FORMAT_BGRX8888: -+ return VLC_CODEC_RGB32; -+ case DRM_FORMAT_RGB888: -+ case DRM_FORMAT_BGR888: -+ return VLC_CODEC_RGB24; -+ case DRM_FORMAT_BGR565: -+ case DRM_FORMAT_RGB565: -+ return VLC_CODEC_RGB16; -+ case DRM_FORMAT_ABGR8888: -+ return VLC_CODEC_RGBA; -+ case DRM_FORMAT_ARGB8888: -+ return VLC_CODEC_BGRA; -+ case DRM_FORMAT_BGRA8888: -+ return VLC_CODEC_ARGB; -+ // VLC_CODEC_ABGR does not exist in VLC -+ case DRM_FORMAT_AYUV: -+ return VLC_CODEC_VUYA; -+ case DRM_FORMAT_YUYV: -+ return VLC_CODEC_VYUY; -+ case DRM_FORMAT_YVYU: -+ return VLC_CODEC_UYVY; -+ case DRM_FORMAT_VYUY: -+ return VLC_CODEC_YUYV; -+ case DRM_FORMAT_UYVY: -+ return VLC_CODEC_YVYU; -+ case DRM_FORMAT_NV12: -+ return VLC_CODEC_NV12; -+ case DRM_FORMAT_NV21: -+ return VLC_CODEC_NV21; -+ case DRM_FORMAT_NV16: -+ return VLC_CODEC_NV16; -+ case DRM_FORMAT_NV61: -+ return VLC_CODEC_NV61; -+ case DRM_FORMAT_NV24: -+ return VLC_CODEC_NV24; -+ case DRM_FORMAT_NV42: -+ return VLC_CODEC_NV42; -+ case DRM_FORMAT_P010: -+ return VLC_CODEC_P010; -+ case DRM_FORMAT_YUV420: -+ return VLC_CODEC_I420; -+ case DRM_FORMAT_YVU420: -+ return VLC_CODEC_YV12; -+ case DRM_FORMAT_YUV422: -+ return VLC_CODEC_I422; -+ case DRM_FORMAT_YUV444: -+ return VLC_CODEC_I444; -+ default: -+ break; -+ } -+ return 0; ++ const drmu_vlc_fmt_info_t * f = drmu_vlc_fmt_info_find_vlc(&(const video_frame_format_t){.i_chroma = chroma}); ++ return drmu_vlc_fmt_info_drm_pixelformat(f); +} + diff --git a/modules/video_output/drmu/drmu_vlc_fmts.h b/modules/video_output/drmu/drmu_vlc_fmts.h new file mode 100644 -index 0000000000..ebdb0c8499 +index 0000000000..1e7fa4711c --- /dev/null +++ b/modules/video_output/drmu/drmu_vlc_fmts.h -@@ -0,0 +1,38 @@ +@@ -0,0 +1,57 @@ +#ifndef _DRMU_DRMU_VLC_FMTS_H +#define _DRMU_DRMU_VLC_FMTS_H + @@ -25135,28 +25102,47 @@ index 0000000000..ebdb0c8499 +#endif +#define HAS_DRMPRIME 1 + ++#include +#include + +#include -+#include + +#ifdef __cplusplus +extern "C" { +#endif + ++struct drmu_vlc_fmt_info_ss; ++typedef struct drmu_vlc_fmt_info_ss drmu_vlc_fmt_info_t; ++ ++const drmu_vlc_fmt_info_t * drmu_vlc_fmt_info_find_vlc(const video_frame_format_t * const vf_vlc); ++// f == NULL => start at the beginning ++const drmu_vlc_fmt_info_t * drmu_vlc_fmt_info_find_vlc_next(const video_frame_format_t * const vf_vlc, const drmu_vlc_fmt_info_t * f); ++const drmu_vlc_fmt_info_t * drmu_vlc_fmt_info_find_drm(const uint32_t pixelformat, const uint64_t modifier); ++// f == NULL => start at the beginning ++const drmu_vlc_fmt_info_t * drmu_vlc_fmt_info_find_drm_next(const uint32_t pixelformat, const uint64_t modifier, const drmu_vlc_fmt_info_t * f); ++ ++vlc_fourcc_t drmu_vlc_fmt_info_vlc_chroma(const drmu_vlc_fmt_info_t * const f); ++void drmu_vlc_fmt_info_vlc_rgb_masks(const drmu_vlc_fmt_info_t * const f, uint32_t * r, uint32_t * g, uint32_t * b); ++uint32_t drmu_vlc_fmt_info_drm_pixelformat(const drmu_vlc_fmt_info_t * const f); ++uint64_t drmu_vlc_fmt_info_drm_modifier(const drmu_vlc_fmt_info_t * const f); ++ ++bool drmu_vlc_fmt_info_is_zc_cma(const drmu_vlc_fmt_info_t * const f); ++bool drmu_vlc_fmt_info_is_drmprime(const drmu_vlc_fmt_info_t * const f); ++ ++ +// Convert chroma to drm - can't cope with RGB32 or RGB16 as they require +// more info. returns 0 if unknown. +uint32_t drmu_format_vlc_chroma_to_drm(const vlc_fourcc_t chroma); +// Convert format to drm fourcc - does cope with RGB32 & RGB16 -+uint32_t drmu_format_vlc_to_drm(const video_frame_format_t * const vf_vlc); -+vlc_fourcc_t drmu_format_vlc_to_vlc(const uint32_t vf_drm); ++// pMod receives modifier - may be null ++uint32_t drmu_format_vlc_to_drm(const video_frame_format_t * const vf_vlc, uint64_t * const pMod); + +#if HAS_DRMPRIME +// pmod may be NULL -+uint32_t drmu_format_vlc_to_drm_prime(const vlc_fourcc_t chroma_in, uint64_t * const pmod); ++uint32_t drmu_format_vlc_to_drm_prime(const video_frame_format_t * const vf_vlc, uint64_t * const pmod); +#endif +#if HAS_ZC_CMA -+uint32_t drmu_format_vlc_to_drm_cma(const vlc_fourcc_t chroma_in); ++uint32_t drmu_format_vlc_to_drm_cma(const video_frame_format_t * const vf_vlc, uint64_t * const pMod); +#endif + +#ifdef __cplusplus @@ -25314,10 +25300,10 @@ index 0000000000..9bf2d7b25b + diff --git a/modules/video_output/drmu/pollqueue.c b/modules/video_output/drmu/pollqueue.c new file mode 100644 -index 0000000000..912afec9a7 +index 0000000000..6dc76bd431 --- /dev/null +++ b/modules/video_output/drmu/pollqueue.c -@@ -0,0 +1,417 @@ +@@ -0,0 +1,502 @@ +#include "pollqueue.h" + +#include @@ -25348,6 +25334,8 @@ index 0000000000..912afec9a7 + POLLTASK_RUN_KILL, +}; + ++#define POLLTASK_FLAG_ONCE 1 ++ +struct polltask { + struct polltask *next; + struct polltask *prev; @@ -25356,6 +25344,7 @@ index 0000000000..912afec9a7 + + int fd; + short events; ++ unsigned short flags; + + void (*fn)(void *v, short revents); + void * v; @@ -25371,17 +25360,29 @@ index 0000000000..912afec9a7 + struct polltask *head; + struct polltask *tail; + ++ struct prepost_ss { ++ void (*pre)(void *v, struct pollfd *pfd); ++ void (*post)(void *v, short revents); ++ void *v; ++ } prepost; ++ + bool kill; + bool no_prod; ++ ++ bool sig_seq; // Signal cond when seq incremented ++ uint32_t seq; ++ + int prod_fd; + struct polltask *prod_pt; + pthread_t worker; +}; + -+struct polltask *polltask_new(struct pollqueue *const pq, -+ const int fd, const short events, -+ void (*const fn)(void *v, short revents), -+ void *const v) ++static struct polltask * ++polltask_new2(struct pollqueue *const pq, ++ const int fd, const short events, ++ void (*const fn)(void *v, short revents), ++ void *const v, ++ const unsigned short flags) +{ + struct polltask *pt; + @@ -25398,6 +25399,7 @@ index 0000000000..912afec9a7 + .q = pollqueue_ref(pq), + .fd = fd, + .events = events, ++ .flags = flags, + .fn = fn, + .v = v + }; @@ -25405,6 +25407,15 @@ index 0000000000..912afec9a7 + return pt; +} + ++struct polltask * ++polltask_new(struct pollqueue *const pq, ++ const int fd, const short events, ++ void (*const fn)(void *v, short revents), ++ void *const v) ++{ ++ return polltask_new2(pq, fd, events, fn, v, 0); ++} ++ +struct polltask *polltask_new_timer(struct pollqueue *const pq, + void (*const fn)(void *v, short revents), + void *const v) @@ -25412,6 +25423,18 @@ index 0000000000..912afec9a7 + return polltask_new(pq, -1, 0, fn, v); +} + ++int ++pollqueue_callback_once(struct pollqueue *const pq, ++ void (*const fn)(void *v, short revents), ++ void *const v) ++{ ++ struct polltask * const pt = polltask_new2(pq, -1, 0, fn, v, POLLTASK_FLAG_ONCE); ++ if (pt == NULL) ++ return -EINVAL; ++ pollqueue_add_task(pt, 0); ++ return 0; ++} ++ +static void pollqueue_rem_task(struct pollqueue *const pq, struct polltask *const pt) +{ + if (pt->prev) @@ -25555,6 +25578,7 @@ index 0000000000..912afec9a7 + unsigned int npoll = 0; + struct polltask *pt; + struct polltask *pt_next; ++ struct prepost_ss prepost; + uint64_t now = pollqueue_now(0); + int timeout = -1; + int rv; @@ -25576,7 +25600,7 @@ index 0000000000..912afec9a7 + } + + if (pt->fd != -1) { -+ assert(npoll < POLLQUEUE_MAX_QUEUE); ++ assert(npoll < POLLQUEUE_MAX_QUEUE - 1); // Allow for pre/post + a[npoll++] = (struct pollfd){ + .fd = pt->fd, + .events = pt->events @@ -25589,13 +25613,25 @@ index 0000000000..912afec9a7 + timeout = (t < 0) ? 0 : (int)t; + ++nall; + } ++ prepost = pq->prepost; + pthread_mutex_unlock(&pq->lock); + -+ if ((rv = poll(a, npoll, timeout)) == -1) { -+ if (errno != EINTR) { -+ request_log("Poll error: %s\n", strerror(errno)); -+ goto fail_unlocked; -+ } ++ a[npoll] = (struct pollfd){.fd=-1, .events=0, .revents=0}; ++ if (prepost.pre) ++ prepost.pre(prepost.v, a + npoll); ++ ++ while ((rv = poll(a, npoll + (a[npoll].fd != -1), timeout)) == -1) ++ { ++ if (errno != EINTR) ++ break; ++ } ++ ++ if (prepost.post) ++ prepost.post(prepost.v, a[npoll].revents); ++ ++ if (rv == -1) { ++ request_log("Poll error: %s\n", strerror(errno)); ++ goto fail_unlocked; + } + + now = pollqueue_now(0); @@ -25605,6 +25641,14 @@ index 0000000000..912afec9a7 + * infinite looping + */ + pq->no_prod = true; ++ ++ // Sync for prepost changes ++ ++pq->seq; ++ if (pq->sig_seq) { ++ pq->sig_seq = false; ++ pthread_cond_broadcast(&pq->cond); ++ } ++ + for (i = 0, j = 0, pt = pq->head; i < nall; ++i, pt = pt_next) { + const short r = pt->fd == -1 ? 0 : a[j++].revents; + pt_next = pt->next; @@ -25625,10 +25669,13 @@ index 0000000000..912afec9a7 + pt->fn(pt->v, r); + + pthread_mutex_lock(&pq->lock); -+ if (pt->state == POLLTASK_RUNNING) -+ pt->state = POLLTASK_UNQUEUED; -+ if (pt->state == POLLTASK_RUN_KILL) ++ if (pt->state == POLLTASK_Q_KILL) ++ polltask_dead(pt); ++ else if (pt->state == POLLTASK_RUN_KILL || ++ (pt->flags & POLLTASK_FLAG_ONCE) != 0) + polltask_kill(pt); ++ else if (pt->state == POLLTASK_RUNNING) ++ pt->state = POLLTASK_UNQUEUED; + } + } + pq->no_prod = false; @@ -25733,14 +25780,38 @@ index 0000000000..912afec9a7 + pollqueue_free(pq); +} + ++void pollqueue_set_pre_post(struct pollqueue *const pq, ++ void (*fn_pre)(void *v, struct pollfd *pfd), ++ void (*fn_post)(void *v, short revents), ++ void *v) ++{ ++ bool no_prod; + ++ pthread_mutex_lock(&pq->lock); ++ pq->prepost.pre = fn_pre; ++ pq->prepost.post = fn_post; ++ pq->prepost.v = v; ++ no_prod = pq->no_prod; ++ ++ if (!no_prod) { ++ const uint32_t seq = pq->seq; ++ int rv = 0; ++ ++ pollqueue_prod(pq); ++ ++ pq->sig_seq = true; ++ while (rv == 0 && pq->seq == seq) ++ rv = pthread_cond_wait(&pq->cond, &pq->lock); ++ } ++ pthread_mutex_unlock(&pq->lock); ++} + diff --git a/modules/video_output/drmu/pollqueue.h b/modules/video_output/drmu/pollqueue.h new file mode 100644 -index 0000000000..922b6e3d0b +index 0000000000..b4f9348986 --- /dev/null +++ b/modules/video_output/drmu/pollqueue.h -@@ -0,0 +1,61 @@ +@@ -0,0 +1,78 @@ +#ifndef POLLQUEUE_H_ +#define POLLQUEUE_H_ + @@ -25787,6 +25858,11 @@ index 0000000000..922b6e3d0b +// May only be added once (currently) +void pollqueue_add_task(struct polltask *const pt, const int timeout); + ++// Run a callback once on the poll thread ++int pollqueue_callback_once(struct pollqueue *const pq, ++ void (*const fn)(void *v, short revents), ++ void *const v); ++ +// Create a pollqueue +// Generates a new thread to do the polltask callbacks +struct pollqueue * pollqueue_new(void); @@ -25801,6 +25877,18 @@ index 0000000000..922b6e3d0b +// Add a reference to a pollqueue +struct pollqueue * pollqueue_ref(struct pollqueue *const pq); + ++// Set pre & post poll functions ++// Both set s.t. there is always matched pre - poll - post even when ++// changing the fns. ++// pollfd will be added to polls if *pfd set (leave unset if not wanted) ++// One or both of pre/post may be 0 (uncalled) ++// Will wait if needed until the poll thread is not in the old pre/poll/post ++// sequence ++void pollqueue_set_pre_post(struct pollqueue *const pq, ++ void (*fn_pre)(void *v, struct pollfd *pfd), ++ void (*fn_post)(void *v, short revents), ++ void *v); ++ +#endif /* POLLQUEUE_H_ */ diff --git a/modules/video_output/opengl/display.c b/modules/video_output/opengl/display.c index ec671109bc..46f693a13a 100644 @@ -25886,1572 +25974,13 @@ index ab20fb4c9c..dbff2e5efe 100644 set_callbacks (OpenGLES2, Close) add_shortcut ("egl") -diff --git a/modules/video_output/wayland/dmabuf.c b/modules/video_output/wayland/dmabuf.c -new file mode 100644 -index 0000000000..03b71f7d7f ---- /dev/null -+++ b/modules/video_output/wayland/dmabuf.c -@@ -0,0 +1,1554 @@ -+/** -+ * @file shm.c -+ * @brief Wayland shared memory video output module for VLC media player -+ */ -+/***************************************************************************** -+ * Copyright © 2014, 2017 Rémi Denis-Courmont -+ * -+ * This program 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. -+ * -+ * This program 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 this program; if not, write to the Free Software Foundation, -+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. -+ *****************************************************************************/ -+ -+#ifdef HAVE_CONFIG_H -+# include -+#endif -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#include -+#include "viewporter-client-protocol.h" -+#include "linux-dmabuf-unstable-v1-client-protocol.h" -+ -+#include -+#include -+#include -+#include -+#include -+ -+// *** Avoid this include if possible -+#include -+ -+#include "dmabuf_alloc.h" -+#include "picpool.h" -+#include "rgba_premul.h" -+#include "../drmu/drmu_vlc_fmts.h" -+#include "../drmu/pollqueue.h" -+#include "../../codec/avcodec/drm_pic.h" -+#include -+ -+#define TRACE_ALL 0 -+ -+#define MAX_PICTURES 4 -+#define MAX_SUBPICS 6 -+ -+#define VIDEO_ON_SUBSURFACE 1 -+ -+typedef struct fmt_ent_s { -+ uint32_t fmt; -+ int32_t pri; -+ uint64_t mod; -+} fmt_ent_t; -+ -+typedef struct fmt_list_s { -+ fmt_ent_t * fmts; -+ unsigned int size; -+ unsigned int len; -+} fmt_list_t; -+ -+typedef struct subplane_s { -+ struct wl_surface * surface; -+ struct wl_subsurface * subsurface; -+ struct wp_viewport * viewport; -+} subplane_t; -+ -+typedef struct subpic_ent_s { -+ struct wl_buffer * wb; -+ struct dmabuf_h * dh; -+ picture_t * pic; -+ int alpha; -+ vout_display_place_t dst_rect; -+ vout_display_place_t src_rect; -+ bool update; -+} subpic_ent_t; -+ -+typedef struct video_dmabuf_release_env_ss -+{ -+ struct picture_context_t * ctx; -+ unsigned int rel_count; -+ unsigned int pt_count; -+ struct polltask * pt[AV_DRM_MAX_PLANES]; -+} video_dmabuf_release_env_t; -+ -+struct vout_display_sys_t -+{ -+ vout_window_t *embed; /* VLC window */ -+ struct wp_viewporter *viewporter; -+ struct wp_viewport *viewport; -+ struct zwp_linux_dmabuf_v1 * linux_dmabuf_v1; -+ struct wl_compositor *compositor; -+ struct wl_subcompositor *subcompositor; -+ -+ picture_pool_t *vlc_pic_pool; /* picture pool */ -+ -+ struct wl_surface * last_embed_surface; -+ -+ int x; -+ int y; -+ bool video_attached; -+ bool viewport_set; -+ -+ vout_display_place_t spu_rect; // Window that subpic coords orignate from -+ vout_display_place_t dst_rect; // Window in the display size that holds the video -+ -+ video_format_t curr_aspect; -+ -+#if VIDEO_ON_SUBSURFACE -+ struct wl_surface * video_surface; -+ struct wl_subsurface * video_subsurface; -+ -+ struct wp_viewport * bkg_viewport; -+ unsigned int bkg_w; -+ unsigned int bkg_h; -+#endif -+ -+ struct pollqueue * pollq; -+ -+ picpool_ctl_t * subpic_pool; -+ subplane_t subplanes[MAX_SUBPICS]; -+ subpic_ent_t subpics[MAX_SUBPICS]; -+ subpic_ent_t piccpy; -+ video_dmabuf_release_env_t * pic_vdre; -+ vlc_fourcc_t * subpic_chromas; -+ -+ fmt_list_t dmabuf_fmts; -+}; -+ -+static inline struct wl_display * -+video_display(const vout_display_sys_t * const sys) -+{ -+ return sys->embed->display.wl; -+} -+ -+static inline struct wl_surface * -+video_surface(const vout_display_sys_t * const sys) -+{ -+#if VIDEO_ON_SUBSURFACE -+ return sys->video_surface; -+#else -+ return sys->embed->handle.wl; -+#endif -+} -+ -+static inline struct wl_compositor * -+video_compositor(const vout_display_sys_t * const sys) -+{ -+ return sys->compositor; -+} -+ -+#if VIDEO_ON_SUBSURFACE -+static inline struct wl_surface * -+bkg_surface(const vout_display_sys_t * const sys) -+{ -+ return sys->embed->handle.wl; -+} -+#endif -+ -+static int -+check_embed(vout_display_t * const vd, vout_display_sys_t * const sys, const char * const func) -+{ -+ if (!sys->embed) { -+ msg_Err(vd, "%s: Embed NULL", func); -+ return -1; -+ } -+ if (sys->embed->handle.wl != sys->last_embed_surface) { -+ msg_Warn(vd, "%s: Embed surface changed %p->%p", func, sys->last_embed_surface, sys->embed->handle.wl); -+ sys->last_embed_surface = sys->embed->handle.wl; -+ return 1; -+ } -+ return 0; -+} -+ -+static inline void -+roundtrip(const vout_display_sys_t * const sys) -+{ -+ wl_display_roundtrip(video_display(sys)); -+} -+ -+static void -+buffer_destroy(struct wl_buffer ** ppbuffer) -+{ -+ struct wl_buffer * const buffer = *ppbuffer; -+ if (buffer == NULL) -+ return; -+ *ppbuffer = NULL; -+ wl_buffer_destroy(buffer); -+} -+ -+static void -+subsurface_destroy(struct wl_subsurface ** const ppsubsurface) -+{ -+ if (*ppsubsurface == NULL) -+ return; -+ wl_subsurface_destroy(*ppsubsurface); -+ *ppsubsurface = NULL; -+} -+ -+static void -+surface_destroy(struct wl_surface ** const ppsurface) -+{ -+ if (*ppsurface == NULL) -+ return; -+ wl_surface_destroy(*ppsurface); -+ *ppsurface = NULL; -+} -+ -+static void -+viewport_destroy(struct wp_viewport ** const ppviewport) -+{ -+ if (*ppviewport == NULL) -+ return; -+ wp_viewport_destroy(*ppviewport); -+ *ppviewport = NULL; -+} -+ -+static inline int_fast32_t -+place_rescale_1s(int_fast32_t x, uint_fast32_t mul, uint_fast32_t div) -+{ -+ const int_fast64_t m = x * (int_fast64_t)mul; -+ const uint_fast32_t d2 = div/2; -+ return div == 0 ? (int_fast32_t)m : -+ m >= 0 ? (int_fast32_t)(((uint_fast64_t)m + d2) / div) : -+ -(int_fast32_t)(((uint_fast64_t)(-m) + d2) / div); -+} -+ -+static inline uint_fast32_t -+place_rescale_1u(uint_fast32_t x, uint_fast32_t mul, uint_fast32_t div) -+{ -+ const uint_fast64_t m = x * (uint_fast64_t)mul; -+ return (uint_fast32_t)(div == 0 ? m : (m + div/2) / div); -+} -+ -+static inline vout_display_place_t -+place_rescale(const vout_display_place_t s, const vout_display_place_t mul, const vout_display_place_t div) -+{ -+ return (vout_display_place_t){ -+ .x = place_rescale_1s(s.x - div.x, mul.width, div.width) + mul.x, -+ .y = place_rescale_1s(s.y - div.y, mul.height, div.height) + mul.y, -+ .width = place_rescale_1u(s.width, mul.width, div.width), -+ .height = place_rescale_1u(s.height, mul.height, div.height) -+ }; -+} -+ -+ -+// MMAL headers comment these (getting 2 a bit wrong) but do not give -+// defines -+#define VXF_H_SHIFT 0 // Hflip -+#define VXF_V_SHIFT 1 // Vflip -+#define VXF_T_SHIFT 2 // Transpose -+#define VXF_H_BIT (1 << VXF_H_SHIFT) -+#define VXF_V_BIT (1 << VXF_V_SHIFT) -+#define VXF_T_BIT (1 << VXF_T_SHIFT) -+ -+static inline bool -+is_vxf_transpose(const video_transform_t t) -+{ -+ return ((unsigned int)t & VXF_T_BIT) != 0; -+} -+ -+static inline bool -+is_vxf_hflip(const video_transform_t t) -+{ -+ return ((unsigned int)t & VXF_H_BIT) != 0; -+} -+ -+static inline bool -+is_vxf_vflip(const video_transform_t t) -+{ -+ return ((unsigned int)t & VXF_V_BIT) != 0; -+} -+ -+static inline video_transform_t -+swap_vxf_hv(const video_transform_t x) -+{ -+ return (((x >> VXF_H_SHIFT) & 1) << VXF_V_SHIFT) | -+ (((x >> VXF_V_SHIFT) & 1) << VXF_H_SHIFT) | -+ (x & VXF_T_BIT); -+} -+ -+static inline video_transform_t -+vxf_inverse(const video_transform_t x) -+{ -+ return is_vxf_transpose(x) ? swap_vxf_hv(x) : x; -+} -+ -+// Transform generated by A then B -+// All ops are self inverse so can simply be XORed on their own -+// H & V flips after a transpose need to be swapped -+static inline video_transform_t -+combine_vxf(const video_transform_t a, const video_transform_t b) -+{ -+ return a ^ (is_vxf_transpose(a) ? swap_vxf_hv(b) : b); -+} -+ -+static inline vout_display_place_t -+vplace_transpose(const vout_display_place_t s) -+{ -+ return (vout_display_place_t){ -+ .x = s.y, -+ .y = s.x, -+ .width = s.height, -+ .height = s.width -+ }; -+} -+ -+// hflip s in c -+static inline vout_display_place_t vplace_hflip(const vout_display_place_t s, const vout_display_place_t c) -+{ -+ return (vout_display_place_t){ -+ .x = c.x + (c.x + c.width) - (s.x + s.width), -+ .y = s.y, -+ .width = s.width, -+ .height = s.height -+ }; -+} -+ -+// vflip s in c -+static inline vout_display_place_t vplace_vflip(const vout_display_place_t s, const vout_display_place_t c) -+{ -+ return (vout_display_place_t){ -+ .x = s.x, -+ .y = (c.y + c.height) - (s.y - c.y) - s.height, -+ .width = s.width, -+ .height = s.height -+ }; -+} -+ -+static vout_display_place_t -+place_out(const vout_display_cfg_t * cfg, -+ const video_format_t * fmt, -+ const vout_display_place_t r) -+{ -+ video_format_t tfmt; -+ vout_display_cfg_t tcfg; -+ vout_display_place_t place; -+ -+ // Fix SAR if unknown -+ if (fmt->i_sar_den == 0 || fmt->i_sar_num == 0) { -+ tfmt = *fmt; -+ tfmt.i_sar_den = 1; -+ tfmt.i_sar_num = 1; -+ fmt = &tfmt; -+ } -+ -+ // Override what VLC thinks might be going on with display size -+ // if we know better -+ if (r.width != 0 && r.height != 0) -+ { -+ tcfg = *cfg; -+ tcfg.display.width = r.width; -+ tcfg.display.height = r.height; -+ cfg = &tcfg; -+ } -+ -+ vout_display_PlacePicture(&place, fmt, cfg, false); -+ -+ place.x += r.x; -+ place.y += r.y; -+ -+ return place; -+} -+ -+#if 0 -+static vout_display_place_t -+rect_transform(vout_display_place_t s, const vout_display_place_t c, const video_transform_t t) -+{ -+ if (is_vxf_transpose(t)) -+ s = vplace_transpose(s); -+ if (is_vxf_hflip(t)) -+ s = vplace_hflip(s, c); -+ if (is_vxf_vflip(t) != 0) -+ s = vplace_vflip(s, c); -+ return s; -+} -+ -+static void -+place_dest_rect(vout_display_t * const vd, -+ const vout_display_cfg_t * const cfg, -+ const video_format_t * fmt) -+{ -+ vout_display_sys_t * const sys = vd->sys; -+ sys->dest_rect = rect_transform(place_out(cfg, fmt, sys->win_rect), -+ sys->display_rect, sys->dest_transform); -+} -+#endif -+ -+static void -+place_spu_rect(vout_display_t * const vd, -+ const vout_display_cfg_t * const cfg, -+ const video_format_t * fmt) -+{ -+ vout_display_sys_t * const sys = vd->sys; -+ static const vout_display_place_t r0 = {0}; -+ -+ sys->spu_rect = place_out(cfg, fmt, r0); -+ sys->spu_rect.x = 0; -+ sys->spu_rect.y = 0; -+ -+ // Copy place override logic for spu pos from video_output.c -+ // This info doesn't appear to reside anywhere natively -+ -+ if (fmt->i_width * fmt->i_height >= (unsigned int)(sys->spu_rect.width * sys->spu_rect.height)) { -+ sys->spu_rect.width = fmt->i_visible_width; -+ sys->spu_rect.height = fmt->i_visible_height; -+ } -+ -+ if (ORIENT_IS_SWAP(fmt->orientation)) -+ sys->spu_rect = vplace_transpose(sys->spu_rect); -+} -+ -+ -+static int -+fmt_list_add(fmt_list_t * const fl, uint32_t fmt, uint64_t mod, int32_t pri) -+{ -+ if (fl->len >= fl->size) -+ { -+ unsigned int n = fl->len == 0 ? 64 : fl->len * 2; -+ fmt_ent_t * t = realloc(fl->fmts, n * sizeof(*t)); -+ if (t == NULL) -+ return VLC_ENOMEM; -+ fl->fmts = t; -+ fl->size = n; -+ } -+ fl->fmts[fl->len++] = (fmt_ent_t){ -+ .fmt = fmt, -+ .pri = pri, -+ .mod = mod -+ }; -+ return 0; -+} -+ -+static int -+fmt_sort_cb(const void * va, const void * vb) -+{ -+ const fmt_ent_t * const a = va; -+ const fmt_ent_t * const b = vb; -+ return a->fmt < b->fmt ? -1 : a->fmt != b->fmt ? 1 : -+ a->mod < b->mod ? -1 : a->mod != b->mod ? 1 : 0; -+} -+ -+static void -+fmt_list_sort(fmt_list_t * const fl) -+{ -+ unsigned int n = 0; -+ if (fl->len <= 1) -+ return; -+ qsort(fl->fmts, fl->len, sizeof(*fl->fmts), fmt_sort_cb); -+ // Dedup - in case we have multiple working callbacks -+ for (unsigned int i = 1; i != fl->len; ++i) -+ { -+ if (fl->fmts[i].fmt != fl->fmts[n].fmt || fl->fmts[i].mod != fl->fmts[n].mod) -+ fl->fmts[n++] = fl->fmts[i]; -+ } -+ fl->len = n + 1; -+} -+ -+static int -+fmt_list_find(const fmt_list_t * const fl, uint32_t fmt, uint64_t mod) -+{ -+ const fmt_ent_t x = { -+ .fmt = fmt, -+ .mod = mod -+ }; -+ const fmt_ent_t * const fe = (fl->len == 0) ? NULL : -+ bsearch(&x, fl->fmts, fl->len, sizeof(x), fmt_sort_cb); -+ return fe == NULL ? -1 : fe->pri; -+} -+ -+static void -+fmt_list_uninit(fmt_list_t * const fl) -+{ -+ free(fl->fmts); -+ fl->fmts = NULL; -+ fl->size = 0; -+ fl->len = 0; -+} -+ -+static int -+fmt_list_init(fmt_list_t * const fl, const size_t initial_size) -+{ -+ fl->size = 0; -+ fl->len = 0; -+ if ((fl->fmts = malloc(initial_size * sizeof(*fl->fmts))) == NULL) -+ return VLC_ENOMEM; -+ fl->size = initial_size; -+ return VLC_SUCCESS; -+} -+ -+static void -+chequerboard(uint32_t *const data, unsigned int stride, const unsigned int width, const unsigned int height) -+{ -+ stride /= sizeof(uint32_t); -+ -+ /* Draw checkerboxed background */ -+ for (unsigned int y = 0; y < height; ++y) { -+ for (unsigned int x = 0; x < width; ++x) { -+ if ((x + y / 8 * 8) % 16 < 8) -+ data[y * stride + x] = 0xFF666666; -+ else -+ data[y * stride + x] = 0xFFEEEEEE; -+ } -+ } -+} -+ -+/* Sent by the compositor when it's no longer using this buffer */ -+static void -+subpic_buffer_release(void *data, struct wl_buffer *wl_buffer) -+{ -+ struct dmabuf_h * dh = data; -+ -+ buffer_destroy(&wl_buffer); -+ dmabuf_unref(&dh); -+} -+ -+static const struct wl_buffer_listener subpic_buffer_listener = { -+ .release = subpic_buffer_release, -+}; -+ -+static inline size_t cpypic_plane_alloc_size(const plane_t * const p) -+{ -+ return p->i_pitch * p->i_lines; -+} -+ -+static int -+copy_subpic_to_w_buffer(vout_display_t *vd, vout_display_sys_t * const sys, picture_t * const src, -+ int alpha, -+ struct dmabuf_h ** pDmabuf_h, struct wl_buffer ** pW_buffer) -+{ -+ unsigned int w = src->format.i_width; -+ unsigned int h = src->format.i_height; -+ struct zwp_linux_buffer_params_v1 *params = NULL; -+ const uint32_t drm_fmt = drmu_format_vlc_to_drm(&src->format); -+ size_t total_size = 0; -+ size_t offset = 0; -+ struct dmabuf_h * dh = NULL; -+ int i; -+ -+ for (i = 0; i != src->i_planes; ++i) -+ total_size += cpypic_plane_alloc_size(src->p + i); -+ -+ *pW_buffer = NULL; -+ *pDmabuf_h = NULL; -+ -+ if ((dh = picpool_get(sys->subpic_pool, total_size)) == NULL) -+ { -+ msg_Warn(vd, "Failed to alloc dmabuf for subpic"); -+ goto error; -+ } -+ *pDmabuf_h = dh; -+ -+ if ((params = zwp_linux_dmabuf_v1_create_params(sys->linux_dmabuf_v1)) == NULL) -+ { -+ msg_Err(vd, "zwp_linux_dmabuf_v1_create_params FAILED"); -+ goto error; -+ } -+ -+ dmabuf_write_start(dh); -+ for (i = 0; i != src->i_planes; ++i) -+ { -+ const size_t stride = src->p[i].i_pitch; -+ const size_t size = cpypic_plane_alloc_size(src->p + i); -+ -+ if (src->format.i_chroma == VLC_CODEC_RGBA || -+ src->format.i_chroma == VLC_CODEC_BGRA) -+ copy_frame_xxxa_with_premul(dmabuf_map(dh), stride, src->p[i].p_pixels, src->p[i].i_pitch, w, h, alpha); -+ else -+ memcpy((char *)dmabuf_map(dh) + offset, src->p[i].p_pixels, size); -+ -+ zwp_linux_buffer_params_v1_add(params, dmabuf_fd(dh), i, offset, stride, 0, 0); -+ -+ offset += size; -+ } -+ dmabuf_write_end(dh); -+ -+ if ((*pW_buffer = zwp_linux_buffer_params_v1_create_immed(params, w, h, drm_fmt, 0)) == NULL) -+ { -+ msg_Err(vd, "zwp_linux_buffer_params_v1_create_immed FAILED"); -+ goto error; -+ } -+ -+ zwp_linux_buffer_params_v1_destroy(params); -+ wl_buffer_add_listener(*pW_buffer, &subpic_buffer_listener, dh); -+ -+ return VLC_SUCCESS; -+ -+error: -+ if (params) -+ zwp_linux_buffer_params_v1_destroy(params); -+ dmabuf_unref(pDmabuf_h); -+ return VLC_EGENERIC; -+} -+ -+ -+static void kill_pool(vout_display_sys_t * const sys) -+{ -+ if (sys->vlc_pic_pool != NULL) -+ { -+ picture_pool_Release(sys->vlc_pic_pool); -+ sys->vlc_pic_pool = NULL; -+ } -+} -+ -+// Actual picture pool for dmabufs is just a set of trivial containers -+static picture_pool_t *vd_dmabuf_pool(vout_display_t * const vd, unsigned count) -+{ -+ vout_display_sys_t * const sys = vd->sys; -+ -+#if TRACE_ALL -+ msg_Dbg(vd, "%s: fmt:%dx%d,sar:%d/%d; source:%dx%d", __func__, -+ vd->fmt.i_width, vd->fmt.i_height, vd->fmt.i_sar_num, vd->fmt.i_sar_den, vd->source.i_width, vd->source.i_height); -+#endif -+ -+ if (sys->vlc_pic_pool == NULL) -+ sys->vlc_pic_pool = picture_pool_NewFromFormat(&vd->fmt, count); -+ return sys->vlc_pic_pool; -+} -+ -+static video_dmabuf_release_env_t * -+vdre_new(struct picture_context_t * ctx) -+{ -+ video_dmabuf_release_env_t * const vdre = calloc(1, sizeof(*vdre)); -+ if ((vdre->ctx = ctx->copy(ctx)) == NULL) -+ { -+ free(vdre); -+ return NULL; -+ } -+ return vdre; -+} -+ -+static void -+vdre_free(video_dmabuf_release_env_t * const vdre) -+{ -+ unsigned int i; -+ vdre->ctx->destroy(vdre->ctx); -+ for (i = 0; i != vdre->pt_count; ++i) -+ polltask_delete(vdre->pt + i); -+ free(vdre); -+} -+ -+static void -+vdre_delete(video_dmabuf_release_env_t ** const ppvdre) -+{ -+ video_dmabuf_release_env_t * const vdre = *ppvdre; -+ if (vdre == NULL) -+ return; -+ *ppvdre = NULL; -+ vdre_free(vdre); -+} -+ -+static void -+w_ctx_release(void * v, short revents) -+{ -+ video_dmabuf_release_env_t * const vdre = v; -+ VLC_UNUSED(revents); -+ // Wait for all callbacks to come back before releasing buffer -+ if (++vdre->rel_count >= vdre->pt_count) -+ vdre_free(vdre); -+} -+ -+static void -+vdre_add_pt(video_dmabuf_release_env_t * const vdre, struct pollqueue * pq, int fd) -+{ -+ assert(vdre->pt_count < AV_DRM_MAX_PLANES); -+ vdre->pt[vdre->pt_count++] = polltask_new(pq, fd, POLLOUT, w_ctx_release, vdre); -+} -+ -+// Avoid use of vd here as there's a possibility this will be called after -+// it has gone -+static void -+w_buffer_release(void *data, struct wl_buffer *wl_buffer) -+{ -+ video_dmabuf_release_env_t * const vdre = data; -+ unsigned int i = vdre->pt_count; -+ -+ /* Sent by the compositor when it's no longer using this buffer */ -+ buffer_destroy(&wl_buffer); -+ -+ // Whilst we can happily destroy the buffer that doesn't mean we can reuse -+ // the dmabufs yet - we have to wait for them to be free of fences. -+ // We don't want to wait in this callback so do the waiting in pollqueue -+ while (i-- != 0) -+ pollqueue_add_task(vdre->pt[i], 1000); -+} -+ -+static const struct wl_buffer_listener w_buffer_listener = { -+ .release = w_buffer_release, -+}; -+ -+static int -+do_display_dmabuf(vout_display_t * const vd, vout_display_sys_t * const sys, picture_t * const pic, -+ video_dmabuf_release_env_t ** const pVdre, struct wl_buffer ** const pWbuffer) -+{ -+ struct zwp_linux_buffer_params_v1 *params; -+ const AVDRMFrameDescriptor * const desc = drm_prime_get_desc(pic); -+ const uint32_t format = desc->layers[0].format; -+ const unsigned int width = pic->format.i_visible_width; -+ const unsigned int height = pic->format.i_visible_height; -+ unsigned int n = 0; -+ unsigned int flags = 0; -+ int i; -+ struct wl_buffer * w_buffer; -+ video_dmabuf_release_env_t * const vdre = vdre_new(pic->context); -+ -+ assert(*pWbuffer == NULL); -+ assert(*pVdre == NULL); -+ -+ if (vdre == NULL) { -+ msg_Err(vd, "Failed to create vdre"); -+ return VLC_ENOMEM; -+ } -+ -+ for (i = 0; i != desc->nb_objects; ++i) -+ vdre_add_pt(vdre, sys->pollq, desc->objects[i].fd); -+ -+ /* Creation and configuration of planes */ -+ params = zwp_linux_dmabuf_v1_create_params(sys->linux_dmabuf_v1); -+ if (!params) -+ { -+ msg_Err(vd, "zwp_linux_dmabuf_v1_create_params FAILED"); -+ goto error; -+ } -+ -+ for (i = 0; i < desc->nb_layers; ++i) -+ { -+ int j; -+ for (j = 0; j < desc->layers[i].nb_planes; ++j) -+ { -+ const AVDRMPlaneDescriptor *const p = desc->layers[i].planes + j; -+ const AVDRMObjectDescriptor *const obj = desc->objects + p->object_index; -+ -+ zwp_linux_buffer_params_v1_add(params, obj->fd, n++, p->offset, p->pitch, -+ (unsigned int)(obj->format_modifier >> 32), -+ (unsigned int)(obj->format_modifier & 0xFFFFFFFF)); -+ } -+ } -+ -+ if (!pic->b_progressive) -+ { -+ flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED; -+ if (!pic->b_top_field_first) -+ flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST; -+ } -+ -+ /* Request buffer creation */ -+ if ((w_buffer = zwp_linux_buffer_params_v1_create_immed(params, width, height, format, flags)) == NULL) -+ { -+ msg_Err(vd, "zwp_linux_buffer_params_v1_create_immed FAILED"); -+ goto error; -+ } -+ -+ zwp_linux_buffer_params_v1_destroy(params); -+ -+ wl_buffer_add_listener(w_buffer, &w_buffer_listener, vdre); -+ -+ *pVdre = vdre; -+ *pWbuffer = w_buffer; -+ return VLC_SUCCESS; -+ -+error: -+ vdre_free(vdre); -+ return VLC_EGENERIC; -+} -+ -+static void -+subpic_ent_flush(subpic_ent_t * const spe) -+{ -+ if (spe->pic != NULL) { -+ picture_Release(spe->pic); -+ spe->pic = NULL; -+ } -+ buffer_destroy(&spe->wb); -+ dmabuf_unref(&spe->dh); -+} -+ -+static void -+subpic_ent_attach(struct wl_surface * const surface, subpic_ent_t * const spe) -+{ -+ wl_surface_attach(surface, spe->wb, 0, 0); -+ spe->dh = NULL; -+ spe->wb = NULL; -+} -+ -+static void -+mark_all_surface_opaque(struct wl_compositor * compositor, struct wl_surface * surface) -+{ -+ struct wl_region * region_all = wl_compositor_create_region(compositor); -+ wl_region_add(region_all, 0, 0, INT32_MAX, INT32_MAX); -+ wl_surface_set_opaque_region(surface, region_all); -+ wl_region_destroy(region_all); -+} -+ -+static int -+make_video_surface(vout_display_t * const vd, vout_display_sys_t * const sys) -+{ -+ VLC_UNUSED(vd); -+ -+ if (sys->viewport) -+ return VLC_SUCCESS; -+ -+#if VIDEO_ON_SUBSURFACE -+ // Make a new subsurface to use for video -+ sys->video_surface = wl_compositor_create_surface(video_compositor(sys)); -+ sys->video_subsurface = wl_subcompositor_get_subsurface(sys->subcompositor, sys->video_surface, bkg_surface(sys)); -+ wl_subsurface_place_above(sys->video_subsurface, bkg_surface(sys)); -+ wl_subsurface_set_desync(sys->video_subsurface); // Video update can be desync from main window -+#endif -+ -+ struct wl_surface * const surface = video_surface(sys); -+ -+ // Video is opaque -+ mark_all_surface_opaque(video_compositor(sys), surface); -+ -+ sys->viewport = wp_viewporter_get_viewport(sys->viewporter, surface); -+ -+ /* Determine our pixel format */ -+ static const enum wl_output_transform transforms[8] = { -+ [ORIENT_TOP_LEFT] = WL_OUTPUT_TRANSFORM_NORMAL, -+ [ORIENT_TOP_RIGHT] = WL_OUTPUT_TRANSFORM_FLIPPED, -+ [ORIENT_BOTTOM_LEFT] = WL_OUTPUT_TRANSFORM_FLIPPED_180, -+ [ORIENT_BOTTOM_RIGHT] = WL_OUTPUT_TRANSFORM_180, -+ [ORIENT_LEFT_TOP] = WL_OUTPUT_TRANSFORM_FLIPPED_270, -+ [ORIENT_LEFT_BOTTOM] = WL_OUTPUT_TRANSFORM_90, -+ [ORIENT_RIGHT_TOP] = WL_OUTPUT_TRANSFORM_270, -+ [ORIENT_RIGHT_BOTTOM] = WL_OUTPUT_TRANSFORM_FLIPPED_90, -+ }; -+ -+ wl_surface_set_buffer_transform(surface, transforms[vd->fmt.orientation]); -+ return VLC_SUCCESS; -+} -+ -+static int -+make_subpic_surfaces(vout_display_t * const vd, vout_display_sys_t * const sys) -+{ -+ unsigned int i; -+ struct wl_surface * const surface = video_surface(sys); -+ struct wl_surface * below = surface; -+ VLC_UNUSED(vd); -+ -+ if (sys->subplanes[0].surface) -+ return VLC_SUCCESS; -+ -+ for (i = 0; i != MAX_SUBPICS; ++i) -+ { -+ subplane_t *plane = sys->subplanes + i; -+ plane->surface = wl_compositor_create_surface(video_compositor(sys)); -+ plane->subsurface = wl_subcompositor_get_subsurface(sys->subcompositor, plane->surface, surface); -+ wl_subsurface_place_above(plane->subsurface, below); -+ below = plane->surface; -+ wl_subsurface_set_sync(plane->subsurface); -+ plane->viewport = wp_viewporter_get_viewport(sys->viewporter, plane->surface); -+ } -+ return VLC_SUCCESS; -+} -+ -+static int -+make_background(vout_display_t * const vd, vout_display_sys_t * const sys) -+{ -+#if !VIDEO_ON_SUBSURFACE -+ VLC_UNUSED(vd); -+ VLC_UNUSED(sys); -+ return VLC_SUCCESS; -+#else -+ // Build a background -+ // This would be a perfect use of the single_pixel_surface extension -+ // However we don't seem to support it -+ struct dmabuf_h * dh = NULL; -+ -+ if (!sys->bkg_viewport) -+ { -+ unsigned int width = 640; -+ unsigned int height = 480; -+ unsigned int stride = 640 * 4; -+ struct zwp_linux_buffer_params_v1 *params; -+ struct wl_buffer * w_buffer; -+ -+ if ((dh = picpool_get(sys->subpic_pool, stride * height)) == NULL) { -+ msg_Err(vd, "Failed to get DmaBuf for background"); -+ goto error; -+ } -+ -+ dmabuf_write_start(dh); -+ chequerboard(dmabuf_map(dh), stride, width, height); -+ dmabuf_write_end(dh); -+ -+ params = zwp_linux_dmabuf_v1_create_params(sys->linux_dmabuf_v1); -+ if (!params) { -+ msg_Err(vd, "zwp_linux_dmabuf_v1_create_params FAILED"); -+ goto error; -+ } -+ zwp_linux_buffer_params_v1_add(params, dmabuf_fd(dh), 0, 0, stride, 0, 0); -+ w_buffer = zwp_linux_buffer_params_v1_create_immed(params, width, height, DRM_FORMAT_XRGB8888, 0); -+ zwp_linux_buffer_params_v1_destroy(params); -+ if (!w_buffer) { -+ msg_Err(vd, "Failed to create background buffer"); -+ goto error; -+ } -+ -+ sys->bkg_viewport = wp_viewporter_get_viewport(sys->viewporter, bkg_surface(sys)); -+ -+ wl_buffer_add_listener(w_buffer, &subpic_buffer_listener, dh); -+ wl_surface_attach(bkg_surface(sys), w_buffer, 0, 0); -+ dh = NULL; -+ -+ wp_viewport_set_destination(sys->bkg_viewport, sys->bkg_w, sys->bkg_h); -+ mark_all_surface_opaque(video_compositor(sys), bkg_surface(sys)); -+ -+ wl_surface_commit(bkg_surface(sys)); -+ } -+ return VLC_SUCCESS; -+ -+error: -+ dmabuf_unref(&dh); -+ return VLC_ENOMEM; -+#endif -+} -+ -+static void -+set_video_viewport(vout_display_t * const vd, vout_display_sys_t * const sys) -+{ -+ video_format_t fmt; -+ -+ if (!sys->video_attached || sys->viewport_set) -+ return; -+ -+ sys->viewport_set = true; -+ -+ video_format_ApplyRotation(&fmt, &vd->source); -+ wp_viewport_set_source(sys->viewport, -+ wl_fixed_from_int(fmt.i_x_offset), -+ wl_fixed_from_int(fmt.i_y_offset), -+ wl_fixed_from_int(fmt.i_visible_width), -+ wl_fixed_from_int(fmt.i_visible_height)); -+ wp_viewport_set_destination(sys->viewport, -+ sys->dst_rect.width, sys->dst_rect.height); -+ wl_subsurface_set_position(sys->video_subsurface, sys->dst_rect.x, sys->dst_rect.y); -+} -+ -+static void Prepare(vout_display_t *vd, picture_t *pic, subpicture_t *subpic) -+{ -+ vout_display_sys_t * const sys = vd->sys; -+ unsigned int n = 0; -+ -+#if TRACE_ALL -+ msg_Dbg(vd, "<<< %s: Surface: %p", __func__, sys->embed->handle.wl); -+#endif -+ check_embed(vd, sys, __func__); -+ -+ if (drmu_format_vlc_to_drm_prime(pic->format.i_chroma, NULL) == 0) { -+ copy_subpic_to_w_buffer(vd, sys, pic, 0xff, &sys->piccpy.dh, &sys->piccpy.wb); -+ } -+ else { -+ do_display_dmabuf(vd, sys, pic, &sys->pic_vdre, &sys->piccpy.wb); -+ } -+ -+ // Attempt to import the subpics -+ for (subpicture_t * spic = subpic; spic != NULL; spic = spic->p_next) -+ { -+ for (subpicture_region_t *sreg = spic->p_region; sreg != NULL; sreg = sreg->p_next) { -+ picture_t * const src = sreg->p_picture; -+ subpic_ent_t * const dst = sys->subpics + n; -+ -+ // If the same picture then assume the same contents -+ // We keep a ref to the previous pic to ensure that the same picture -+ // structure doesn't get reused and confuse us. -+ if (src != dst->pic || sreg->i_alpha != dst->alpha) { -+ subpic_ent_flush(dst); -+ -+ if (copy_subpic_to_w_buffer(vd, sys, src, sreg->i_alpha, &dst->dh, &dst->wb) != 0) -+ continue; -+ -+ dst->pic = picture_Hold(src); -+ dst->alpha = sreg->i_alpha; -+ dst->update = true; -+ } -+ -+ dst->src_rect = (vout_display_place_t) { -+ .x = sreg->fmt.i_x_offset, -+ .y = sreg->fmt.i_y_offset, -+ .width = sreg->fmt.i_visible_width, -+ .height = sreg->fmt.i_visible_height, -+ }; -+ dst->dst_rect = place_rescale( -+ (vout_display_place_t) { -+ .x = sreg->i_x, -+ .y = sreg->i_y, -+ .width = sreg->fmt.i_visible_width, -+ .height = sreg->fmt.i_visible_height, -+ }, -+ (vout_display_place_t) { -+ .x = 0, -+ .y = 0, -+ .width = sys->dst_rect.width, -+ .height = sys->dst_rect.height, -+ }, -+ sys->spu_rect); -+ -+ if (++n == MAX_SUBPICS) -+ goto subpics_done; -+ } -+ } -+subpics_done: -+ -+ // Clear any other entries -+ for (; n != MAX_SUBPICS; ++n) { -+ subpic_ent_t * const dst = sys->subpics + n; -+ -+ if (dst->dh != NULL) -+ dst->update = true; -+ subpic_ent_flush(dst); -+ } -+ -+ (void)pic; -+ -+#if TRACE_ALL -+ msg_Dbg(vd, ">>> %s: Surface: %p", __func__, sys->embed->handle.wl); -+#endif -+} -+ -+static void Display(vout_display_t *vd, picture_t *pic, subpicture_t *subpic) -+{ -+ vout_display_sys_t * const sys = vd->sys; -+ -+#if TRACE_ALL -+ msg_Dbg(vd, "<<< %s: Surface: %p", __func__, sys->embed->handle.wl); -+#endif -+ -+ check_embed(vd, sys, __func__); -+ -+ make_video_surface(vd, sys); -+ make_subpic_surfaces(vd, sys); -+ make_background(vd, sys); -+ -+ for (unsigned int i = 0; i != MAX_SUBPICS; ++i) -+ { -+ subpic_ent_t * const spe = sys->subpics + i; -+ -+ if (!spe->update) -+ continue; -+ -+ msg_Dbg(vd, "%s: Update subpic %i: wb=%p alpha=%d", __func__, i, spe->wb, spe->alpha); -+ subpic_ent_attach(sys->subplanes[i].surface, spe); -+ -+ wl_subsurface_set_position(sys->subplanes[i].subsurface, spe->dst_rect.x, spe->dst_rect.y); -+ wp_viewport_set_source(sys->subplanes[i].viewport, -+ wl_fixed_from_int(spe->src_rect.x), wl_fixed_from_int(spe->src_rect.y), -+ wl_fixed_from_int(spe->src_rect.width), wl_fixed_from_int(spe->src_rect.height)); -+ wp_viewport_set_destination(sys->subplanes[i].viewport, spe->dst_rect.width, spe->dst_rect.height); -+ wl_surface_damage(sys->subplanes[i].surface, 0, 0, INT32_MAX, INT32_MAX); -+ -+ wl_surface_commit(sys->subplanes[i].surface); -+ spe->update = false; -+ } -+ -+ if (!sys->piccpy.wb) { -+ msg_Warn(vd, "Display called but prepared pic buffer"); -+ } -+ else { -+ subpic_ent_attach(video_surface(sys), &sys->piccpy); -+ sys->video_attached = true; -+ // Now up to the buffer callback to free stuff -+ sys->pic_vdre = NULL; -+ } -+ -+ set_video_viewport(vd, sys); -+ -+ wl_surface_damage(video_surface(sys), 0, 0, INT32_MAX, INT32_MAX); -+ wl_surface_commit(video_surface(sys)); -+ -+ // With VIDEO_ON_SUBSURFACE we need a commit on the background here -+ // too if the video surface isn't desync. Desync is set, but wayland -+ // can force sync if the bkg surface is a sync subsurface. -+ // Try adding a bkg surface commit here if things freeze with -+ // VIDEO_ON_SUBSURFACE set but don't with it unset -+ -+ wl_display_flush(video_display(sys)); -+// roundtrip(sys); -+ -+ if (subpic) -+ subpicture_Delete(subpic); -+ picture_Release(pic); -+ -+#if TRACE_ALL -+ msg_Dbg(vd, ">>> %s: Surface: %p", __func__, sys->embed->handle.wl); -+#endif -+} -+ -+static void ResetPictures(vout_display_t *vd) -+{ -+ vout_display_sys_t *sys = vd->sys; -+ -+#if TRACE_ALL -+ msg_Dbg(vd, "<<< %s", __func__); -+#endif -+ check_embed(vd, sys, __func__); -+ -+ kill_pool(sys); -+ -+#if TRACE_ALL -+ msg_Dbg(vd, ">>> %s", __func__); -+#endif -+} -+ -+static int Control(vout_display_t *vd, int query, va_list ap) -+{ -+ vout_display_sys_t * const sys = vd->sys; -+ -+#if TRACE_ALL -+ msg_Dbg(vd, "<<< %s: Query=%d", __func__, query); -+#endif -+ check_embed(vd, sys, __func__); -+ -+ switch (query) -+ { -+ case VOUT_DISPLAY_RESET_PICTURES: -+ { -+ vout_display_place_t place; -+ video_format_t src; -+ -+ assert(sys->viewport == NULL); -+ -+ vout_display_PlacePicture(&place, &vd->source, vd->cfg, false); -+ video_format_ApplyRotation(&src, &vd->source); -+ -+ vd->fmt.i_width = src.i_width * place.width -+ / src.i_visible_width; -+ vd->fmt.i_height = src.i_height * place.height -+ / src.i_visible_height; -+ vd->fmt.i_visible_width = place.width; -+ vd->fmt.i_visible_height = place.height; -+ vd->fmt.i_x_offset = src.i_x_offset * place.width -+ / src.i_visible_width; -+ vd->fmt.i_y_offset = src.i_y_offset * place.height -+ / src.i_visible_height; -+ ResetPictures(vd); -+ sys->curr_aspect = vd->source; -+ break; -+ } -+ -+ case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: -+ case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED: -+ case VOUT_DISPLAY_CHANGE_ZOOM: -+ case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: -+ case VOUT_DISPLAY_CHANGE_SOURCE_CROP: -+ { -+ const vout_display_cfg_t *cfg; -+ -+ if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT -+ || query == VOUT_DISPLAY_CHANGE_SOURCE_CROP) -+ { -+ cfg = vd->cfg; -+ } -+ else -+ { -+ cfg = va_arg(ap, const vout_display_cfg_t *); -+ } -+ -+#if !VIDEO_ON_SUBSURFACE -+ vout_display_place_t place; -+ -+ vout_display_PlacePicture(&place, &sys->curr_aspect, vd->cfg, false); -+ sys->x += place.width / 2; -+ sys->y += place.height / 2; -+ -+ vout_display_PlacePicture(&sys->dst_rect, &vd->source, cfg, false); -+ sys->x -= sys->dst_rect.width / 2; -+ sys->y -= sys->dst_rect.height / 2; -+#else -+ vout_display_PlacePicture(&sys->dst_rect, &vd->source, cfg, true); -+#endif -+ -+ place_spu_rect(vd, cfg, &vd->fmt); -+ sys->viewport_set = false; -+ -+ if (sys->viewport) -+ set_video_viewport(vd, sys); -+ -+#if VIDEO_ON_SUBSURFACE -+ if (sys->bkg_viewport != NULL && (cfg->display.width != sys->bkg_w || cfg->display.height != sys->bkg_h)) -+ { -+ sys->bkg_w = cfg->display.width; -+ sys->bkg_h = cfg->display.height; -+ -+ msg_Dbg(vd, "Resize background: %dx%d; surface=%p", cfg->display.width, cfg->display.height, bkg_surface(sys)); -+ wp_viewport_set_destination(sys->bkg_viewport, cfg->display.width, cfg->display.height); -+ wl_surface_commit(bkg_surface(sys)); -+ } -+#endif -+ -+ sys->curr_aspect = vd->source; -+ break; -+ } -+ default: -+ msg_Err(vd, "unknown request %d", query); -+ return VLC_EGENERIC; -+ } -+ -+#if TRACE_ALL -+ msg_Dbg(vd, ">>> %s: Surface: %p", __func__, sys->embed->handle.wl); -+#endif -+ return VLC_SUCCESS; -+} -+ -+static void linux_dmabuf_v1_listener_format(void *data, -+ struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, -+ uint32_t format) -+{ -+ // Superceeded by _modifier -+ vout_display_t * const vd = data; -+ vout_display_sys_t * const sys = vd->sys; -+ (void)zwp_linux_dmabuf_v1; -+ -+ msg_Dbg(vd, "%s[%p], %.4s", __func__, (void*)vd, (const char *)&format); -+ -+ fmt_list_add(&sys->dmabuf_fmts, format, DRM_FORMAT_MOD_LINEAR, 0); -+} -+ -+static void -+linux_dmabuf_v1_listener_modifier(void *data, -+ struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, -+ uint32_t format, -+ uint32_t modifier_hi, -+ uint32_t modifier_lo) -+{ -+ vout_display_t * const vd = data; -+ vout_display_sys_t * const sys = vd->sys; -+ (void)zwp_linux_dmabuf_v1; -+ -+ msg_Dbg(vd, "%s[%p], %.4s %08x%08x", __func__, (void*)vd, (const char *)&format, modifier_hi, modifier_lo); -+ -+ fmt_list_add(&sys->dmabuf_fmts, format, modifier_lo | ((uint64_t)modifier_hi << 32), 0); -+} -+ -+static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_v1_listener = { -+ .format = linux_dmabuf_v1_listener_format, -+ .modifier = linux_dmabuf_v1_listener_modifier, -+}; -+ -+ -+static void registry_global_cb(void *data, struct wl_registry *registry, -+ uint32_t name, const char *iface, uint32_t vers) -+{ -+ vout_display_t * const vd = data; -+ vout_display_sys_t * const sys = vd->sys; -+ -+ msg_Dbg(vd, "global %3"PRIu32": %s version %"PRIu32, name, iface, vers); -+ -+ if (strcmp(iface, wl_subcompositor_interface.name) == 0) -+ sys->subcompositor = wl_registry_bind(registry, name, &wl_subcompositor_interface, 1); -+ else -+ if (!strcmp(iface, "wp_viewporter")) -+ sys->viewporter = wl_registry_bind(registry, name, &wp_viewporter_interface, 1); -+ else -+ if (!strcmp(iface, "wl_compositor")) -+ { -+ if (vers >= 4) -+ sys->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4); -+ else -+ msg_Warn(vd, "Interface %s wanted v 4 got v %d", wl_compositor_interface.name, vers); -+ } -+ else -+ if (strcmp(iface, zwp_linux_dmabuf_v1_interface.name) == 0) -+ { -+ if (vers >= 3) -+ sys->linux_dmabuf_v1 = wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, 3); -+ else -+ msg_Warn(vd, "Interface %s wanted v 3 got v %d", zwp_linux_dmabuf_v1_interface.name, vers); -+ } -+ -+} -+ -+static void registry_global_remove_cb(void *data, struct wl_registry *registry, -+ uint32_t name) -+{ -+ vout_display_t *vd = data; -+ -+ msg_Dbg(vd, "global remove %3"PRIu32, name); -+ (void) registry; -+} -+ -+static const struct wl_registry_listener registry_cbs = -+{ -+ registry_global_cb, -+ registry_global_remove_cb, -+}; -+ -+ -+static void -+clear_surface_buffer(struct wl_surface * surface) -+{ -+ if (surface == NULL) -+ return; -+ wl_surface_attach(surface, NULL, 0, 0); -+ wl_surface_commit(surface); -+} -+ -+static void -+clear_all_buffers(vout_display_sys_t *sys) -+{ -+ for (unsigned int i = 0; i != MAX_SUBPICS; ++i) -+ { -+ subpic_ent_t * const spe = sys->subpics + i; -+ subpic_ent_flush(spe); -+ -+ clear_surface_buffer(sys->subplanes[i].surface); -+ } -+ -+ clear_surface_buffer(video_surface(sys)); -+ sys->video_attached = false; -+ -+#if VIDEO_ON_SUBSURFACE -+ clear_surface_buffer(bkg_surface(sys)); -+#endif -+ -+ subpic_ent_flush(&sys->piccpy); -+ vdre_delete(&sys->pic_vdre); -+} -+ -+ -+static void Close(vlc_object_t *obj) -+{ -+ vout_display_t * const vd = (vout_display_t *)obj; -+ vout_display_sys_t * const sys = vd->sys; -+ -+#if TRACE_ALL -+ msg_Dbg(vd, "<<< %s", __func__); -+#endif -+ -+ if (sys == NULL) -+ return; -+ -+ if (sys->embed == NULL) -+ goto no_window; -+ -+ check_embed(vd, sys, __func__); -+ -+ clear_all_buffers(sys); -+ -+ // Free subpic resources -+ for (unsigned int i = 0; i != MAX_SUBPICS; ++i) -+ { -+ subplane_t * const spl = sys->subplanes + i; -+ -+ viewport_destroy(&spl->viewport); -+ subsurface_destroy(&spl->subsurface); -+ surface_destroy(&spl->surface); -+ } -+ -+ viewport_destroy(&sys->viewport); -+ -+#if VIDEO_ON_SUBSURFACE -+ subsurface_destroy(&sys->video_subsurface); -+ surface_destroy(&sys->video_surface); -+ -+ viewport_destroy(&sys->bkg_viewport); -+#endif -+ -+ wl_display_flush(video_display(sys)); -+ -+ if (sys->viewporter != NULL) -+ wp_viewporter_destroy(sys->viewporter); -+ if (sys->linux_dmabuf_v1 != NULL) -+ zwp_linux_dmabuf_v1_destroy(sys->linux_dmabuf_v1); -+ if (sys->subcompositor != NULL) -+ wl_subcompositor_destroy(sys->subcompositor); -+ if (sys->compositor != NULL) -+ wl_compositor_destroy(sys->compositor); -+ -+ wl_display_flush(video_display(sys)); -+ -+ vout_display_DeleteWindow(vd, sys->embed); -+ sys->embed = NULL; -+ -+ kill_pool(sys); -+ picpool_unref(&sys->subpic_pool); -+ pollqueue_unref(&sys->pollq); -+ -+ free(sys->subpic_chromas); -+ -+no_window: -+ fmt_list_uninit(&sys->dmabuf_fmts); -+ free(sys); -+ -+#if TRACE_ALL -+ msg_Dbg(vd, ">>> %s", __func__); -+#endif -+} -+ -+static int Open(vlc_object_t *obj) -+{ -+ vout_display_t * const vd = (vout_display_t *)obj; -+ vout_display_sys_t *sys; -+ uint32_t pic_drm_fmt = 0; -+ uint64_t pic_drm_mod = DRM_FORMAT_MOD_LINEAR; -+ -+ msg_Info(vd, "<<< %s: %.4s %dx%d, cfg.display: %dx%d", __func__, -+ (const char*)&vd->fmt.i_chroma, vd->fmt.i_width, vd->fmt.i_height, -+ vd->cfg->display.width, vd->cfg->display.height); -+ -+ if (!(pic_drm_fmt = drmu_format_vlc_to_drm(&vd->fmt)) && -+ !(pic_drm_fmt = drmu_format_vlc_to_drm_prime(vd->fmt.i_chroma, &pic_drm_mod))) -+ return VLC_EGENERIC; -+ -+ sys = calloc(1, sizeof(*sys)); -+ if (unlikely(sys == NULL)) -+ return VLC_ENOMEM; -+ -+ vd->sys = sys; -+ -+ if (fmt_list_init(&sys->dmabuf_fmts, 128)) { -+ msg_Err(vd, "Failed to allocate format list!"); -+ goto error; -+ } -+ -+ /* Get window */ -+ sys->embed = vout_display_NewWindow(vd, VOUT_WINDOW_TYPE_WAYLAND); -+ if (sys->embed == NULL) -+ goto error; -+ sys->last_embed_surface = sys->embed->handle.wl; -+ -+ { -+ struct wl_registry * const registry = wl_display_get_registry(video_display(sys)); -+ if (registry == NULL) { -+ msg_Err(vd, "Cannot get registry for display"); -+ goto error; -+ } -+ -+ wl_registry_add_listener(registry, ®istry_cbs, vd); -+ roundtrip(sys); -+ wl_registry_destroy(registry); -+ } -+ -+ if (sys->compositor == NULL) { -+ msg_Warn(vd, "Interface %s missing", wl_compositor_interface.name); -+ goto error; -+ } -+ if (sys->subcompositor == NULL) { -+ msg_Warn(vd, "Interface %s missing", wl_subcompositor_interface.name); -+ goto error; -+ } -+ if (sys->viewporter == NULL) { -+ msg_Warn(vd, "Interface %s missing", wp_viewporter_interface.name); -+ goto error; -+ } -+ if (sys->linux_dmabuf_v1 == NULL) { -+ msg_Warn(vd, "Interface %s missing", zwp_linux_dmabuf_v1_interface.name); -+ goto error; -+ } -+ -+ // And again for registering formats -+ zwp_linux_dmabuf_v1_add_listener(sys->linux_dmabuf_v1, &linux_dmabuf_v1_listener, vd); -+ -+ roundtrip(sys); -+ -+ fmt_list_sort(&sys->dmabuf_fmts); -+ -+ // Check PIC DRM format here -+ if (fmt_list_find(&sys->dmabuf_fmts, pic_drm_fmt, pic_drm_mod) < 0) { -+ msg_Warn(vd, "Could not find %.4s mod %#"PRIx64" in supported formats", (char*)&pic_drm_fmt, pic_drm_mod); -+ goto error; -+ } -+ -+ { -+ static vlc_fourcc_t const tryfmts[] = { -+ VLC_CODEC_RGBA, -+ VLC_CODEC_BGRA, -+ }; -+ unsigned int n = 0; -+ -+ if ((sys->subpic_chromas = calloc(ARRAY_SIZE(tryfmts) + 1, sizeof(vlc_fourcc_t))) == NULL) -+ goto error; -+ for (unsigned int i = 0; i != ARRAY_SIZE(tryfmts); ++i) -+ { -+ uint32_t drmfmt = drmu_format_vlc_chroma_to_drm(tryfmts[i]); -+ msg_Dbg(vd, "Look for %.4s", (char*)&drmfmt); -+ if (fmt_list_find(&sys->dmabuf_fmts, drmfmt, DRM_FORMAT_MOD_LINEAR) >= 0) -+ sys->subpic_chromas[n++] = tryfmts[i]; -+ } -+ -+ if (n == 0) -+ msg_Warn(vd, "No compatible subpic formats found"); -+ } -+ -+ { -+ struct dmabufs_ctl * dbsc = dmabufs_ctl_new(); -+ if (dbsc == NULL) -+ { -+ msg_Err(vd, "Failed to create dmabuf ctl"); -+ goto error; -+ } -+ sys->subpic_pool = picpool_new(dbsc); -+ dmabufs_ctl_unref(&dbsc); -+ if (sys->subpic_pool == NULL) -+ { -+ msg_Err(vd, "Failed to create picpool"); -+ goto error; -+ } -+ } -+ -+ if ((sys->pollq = pollqueue_new()) == NULL) -+ { -+ msg_Err(vd, "Failed to create pollqueue"); -+ goto error; -+ } -+ -+ sys->curr_aspect = vd->source; -+ sys->bkg_w = vd->cfg->display.width; -+ sys->bkg_h = vd->cfg->display.height; -+ -+ vd->info.has_pictures_invalid = sys->viewport == NULL; -+ vd->info.subpicture_chromas = sys->subpic_chromas; -+ -+ vd->pool = vd_dmabuf_pool; -+ vd->prepare = Prepare; -+ vd->display = Display; -+ vd->control = Control; -+ -+#if TRACE_ALL -+ msg_Dbg(vd, ">>> %s: OK", __func__); -+#endif -+ return VLC_SUCCESS; -+ -+error: -+ Close(obj); -+#if TRACE_ALL -+ msg_Dbg(vd, ">>> %s: ERROR", __func__); -+#endif -+ return VLC_EGENERIC; -+} -+ -+vlc_module_begin() -+ set_shortname(N_("WL DMABUF")) -+ set_description(N_("Wayland dmabuf video output")) -+ set_category(CAT_VIDEO) -+ set_subcategory(SUBCAT_VIDEO_VOUT) -+ set_capability("vout display", 0) -+ set_callbacks(Open, Close) -+ add_shortcut("wl-dmabuf") -+vlc_module_end() diff --git a/modules/video_output/wayland/dmabuf_alloc.c b/modules/video_output/wayland/dmabuf_alloc.c new file mode 100644 -index 0000000000..31578c3098 +index 0000000000..b8442c08c7 --- /dev/null +++ b/modules/video_output/wayland/dmabuf_alloc.c -@@ -0,0 +1,417 @@ +@@ -0,0 +1,516 @@ ++#define _GNU_SOURCE 1 +#include +#include +#include @@ -27462,7 +25991,6 @@ index 0000000000..31578c3098 +#include +#include +#include -+#include +#include +#include + @@ -27494,6 +26022,8 @@ index 0000000000..31578c3098 + const struct dmabuf_fns * fns; +}; + ++#define DH_FLAG_FAKE 1 ++ +struct dmabuf_h { + atomic_int ref_count; + int fd; @@ -27502,6 +26032,7 @@ index 0000000000..31578c3098 + void * mapptr; + void * v; + const struct dmabuf_fns * fns; ++ unsigned int flags; + + void * predel_v; + int (* predel_fn)(struct dmabuf_h * dh, void * v); @@ -27526,7 +26057,8 @@ index 0000000000..31578c3098 + *dh = (struct dmabuf_h) { + .fd = -1, + .size = size, -+ .mapptr = mapptr ++ .mapptr = mapptr, ++ .flags = DH_FLAG_FAKE, + }; + + return dh; @@ -27662,7 +26194,7 @@ index 0000000000..31578c3098 + struct dma_buf_sync sync = { + .flags = flags + }; -+ if (dh->fd == -1) ++ if ((dh->flags & DH_FLAG_FAKE) != 0) + return 0; + while (ioctl(dh->fd, DMA_BUF_IOCTL_SYNC, &sync) == -1) { + const int err = errno; @@ -27711,6 +26243,7 @@ index 0000000000..31578c3098 + request_log("%s: Map failed\n", __func__); + return NULL; + } ++// fprintf(stderr, "map to %p\n", dh->mapptr); + return dh->mapptr; +} + @@ -27740,6 +26273,11 @@ index 0000000000..31578c3098 + dh->len = len; +} + ++bool dmabuf_is_fake(const struct dmabuf_h * const dh) ++{ ++ return (dh->flags & DH_FLAG_FAKE) != 0; ++} ++ +static struct dmabufs_ctl * dmabufs_ctl_new2(const struct dmabuf_fns * const fns) +{ + struct dmabufs_ctl * dbsc = calloc(1, sizeof(*dbsc)); @@ -27749,7 +26287,12 @@ index 0000000000..31578c3098 + + dbsc->fd = -1; + dbsc->fns = fns; ++ + dbsc->page_size = (size_t)sysconf(_SC_PAGE_SIZE); ++ // Check page size for plausability & power of 2 - set to 4k if not ++ if (dbsc->page_size < 0x1000 || dbsc->page_size > 0x1000000 || ++ (dbsc->page_size & (dbsc->page_size - 1)) != 0) ++ dbsc->page_size = 0x1000; + + if (fns->ctl_new(dbsc) != 0) + goto fail; @@ -27794,23 +26337,30 @@ index 0000000000..31578c3098 +// +// Alloc dmabuf via CMA + -+static int ctl_cma_new(struct dmabufs_ctl * dbsc) ++static int ctl_cma_new2(struct dmabufs_ctl * dbsc, const char * const * names) +{ -+ while ((dbsc->fd = open(DMABUF_NAME1, O_RDWR)) == -1 && -+ errno == EINTR) -+ /* Loop */; -+ -+ if (dbsc->fd == -1) { -+ while ((dbsc->fd = open(DMABUF_NAME2, O_RDWR)) == -1 && ++ for (; *names != NULL; ++names) ++ { ++ while ((dbsc->fd = open(*names, O_RDWR | __O_CLOEXEC)) == -1 && + errno == EINTR) + /* Loop */; -+ if (dbsc->fd == -1) { -+ request_log("Unable to open either %s or %s\n", -+ DMABUF_NAME1, DMABUF_NAME2); -+ return -1; -+ } ++ if (dbsc->fd != -1) ++ return 0; + } -+ return 0; ++ request_log("Unable to open any dma_heap device\n"); ++ return -1; ++} ++ ++static int ctl_cma_new(struct dmabufs_ctl * dbsc) ++{ ++ static const char * const names[] = { ++ "/dev/dma_heap/vidbuf_cached", ++ "/dev/dma_heap/linux,cma", ++ "/dev/dma_heap/reserved", ++ NULL ++ }; ++ ++ return ctl_cma_new2(dbsc, names); +} + +static void ctl_cma_free(struct dmabufs_ctl * dbsc) @@ -27869,15 +26419,93 @@ index 0000000000..31578c3098 + request_debug(NULL, "Dmabufs using CMA\n");; + return dmabufs_ctl_new2(&dmabuf_cma_fns); +} ++ ++//----------------------------------------------------------------------------- ++// ++// Alloc "dmabuf" via shm (one file per alloc) ++ ++static int ctl_shm_new(struct dmabufs_ctl * dbsc) ++{ ++ (void)dbsc; ++ return 0; ++} ++ ++static void ctl_shm_free(struct dmabufs_ctl * dbsc) ++{ ++ (void)dbsc; ++} ++ ++static int buf_shm_alloc(struct dmabufs_ctl * const dbsc, struct dmabuf_h * dh, size_t size) ++{ ++ int fd; ++ ++#if 0 ++ const char * const tmpdir = "/tmp"; ++ fd = open(tmpdir, __O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR); ++ if (fd == -1) { ++ const int err = errno; ++ request_log("Failed to open tmp file in %s: %s\n", tmpdir, strerror(err)); ++ return -err; ++ } ++#else ++ fd = memfd_create("vlc/shm_buf", 0); ++ if (fd == -1) { ++ const int err = errno; ++ request_log("Failed to create memfd: %s\n", strerror(err)); ++ return -err; ++ } ++#endif ++ ++ // Round up to page size ++ size = (size + dbsc->page_size - 1) & ~(dbsc->page_size - 1); ++ ++ if (ftruncate(fd, (off_t)size) != 0) ++ { ++ const int err = errno; ++ request_log("Failed to extend tmp file to %zd: %s\n", size, strerror(err)); ++ return -err; ++ } ++ ++ dh->fd = fd; ++ dh->size = size; ++ dh->flags = DH_FLAG_FAKE; ++ ++// fprintf(stderr, "%s: size=%#zx, ftell=%#zx\n", __func__, ++// dh->size, (size_t)lseek(dh->fd, 0, SEEK_END)); ++ ++ return 0; ++} ++ ++static void buf_shm_free(struct dmabuf_h * dh) ++{ ++ (void)dh; ++ // Nothing needed ++} ++ ++static const struct dmabuf_fns dmabuf_shm_fns = { ++ .buf_alloc = buf_shm_alloc, ++ .buf_free = buf_shm_free, ++ .ctl_new = ctl_shm_new, ++ .ctl_free = ctl_shm_free, ++}; ++ ++struct dmabufs_ctl * dmabufs_shm_new() ++{ ++ request_debug(NULL, "Dmabufs using SHM\n");; ++ return dmabufs_ctl_new2(&dmabuf_shm_fns); ++} ++ ++ diff --git a/modules/video_output/wayland/dmabuf_alloc.h b/modules/video_output/wayland/dmabuf_alloc.h new file mode 100644 -index 0000000000..4ca02e0567 +index 0000000000..9757886d0e --- /dev/null +++ b/modules/video_output/wayland/dmabuf_alloc.h -@@ -0,0 +1,52 @@ +@@ -0,0 +1,59 @@ +#ifndef _WAYLAND_DMABUF_ALLOC_H +#define _WAYLAND_DMABUF_ALLOC_H + ++#include +#include + +struct dmabufs_ctl; @@ -27887,6 +26515,10 @@ index 0000000000..4ca02e0567 +void dmabufs_ctl_unref(struct dmabufs_ctl ** const pdbsc); +struct dmabufs_ctl * dmabufs_ctl_ref(struct dmabufs_ctl * const dbsc); + ++// Build a "dmabuf" struct that uses ordinary shared memory ++struct dmabufs_ctl * dmabufs_shm_new(void); ++ ++ +// Need not preserve old contents +// On NULL return old buffer is freed +struct dmabuf_h * dmabuf_realloc(struct dmabufs_ctl * dbsc, struct dmabuf_h *, size_t size); @@ -27916,6 +26548,8 @@ index 0000000000..4ca02e0567 +size_t dmabuf_len(const struct dmabuf_h * const dh); +/* Set bytes in use */ +void dmabuf_len_set(struct dmabuf_h * const dh, const size_t len); ++/* Are these real dmabufs (false) or is this just something else mmapable (true) */ ++bool dmabuf_is_fake(const struct dmabuf_h * const dh); + +void dmabuf_predel_cb_set(struct dmabuf_h * const dh, + int (* const predel_fn)(struct dmabuf_h * dh, void * v), void * const predel_v); @@ -27929,10 +26563,10 @@ index 0000000000..4ca02e0567 +#endif diff --git a/modules/video_output/wayland/picpool.c b/modules/video_output/wayland/picpool.c new file mode 100644 -index 0000000000..fb3b8e78c1 +index 0000000000..78d753759a --- /dev/null +++ b/modules/video_output/wayland/picpool.c -@@ -0,0 +1,368 @@ +@@ -0,0 +1,369 @@ +#include +#include +#include @@ -28298,6 +26932,7 @@ index 0000000000..fb3b8e78c1 + pc->max_n = 8; + pc->dbsc = dmabufs_ctl_ref(dbsc); + atomic_store(&pc->ref_count, 1); ++ vlc_mutex_init(&pc->lock); + + return pc; +} @@ -28808,83 +27443,2342 @@ index 08ad5022f9..b1feb622d8 100644 set_callbacks(Open, Close) add_shortcut("wl") vlc_module_end() -diff --git a/modules/video_output/win32/direct3d11.c b/modules/video_output/win32/direct3d11.c -index 9fe576ebad..06aeddeff8 100644 ---- a/modules/video_output/win32/direct3d11.c -+++ b/modules/video_output/win32/direct3d11.c -@@ -122,7 +122,6 @@ struct vout_display_sys_t - d3d11_handle_t hd3d; - IDXGISwapChain1 *dxgiswapChain; /* DXGI 1.2 swap chain */ - IDXGISwapChain4 *dxgiswapChain4; /* DXGI 1.5 for HDR */ -- DXGI_HDR_METADATA_HDR10 hdr10; - d3d11_device_t d3d_dev; - d3d_quad_t picQuad; - video_format_t quad_fmt; -@@ -1104,11 +1103,7 @@ static void Prepare(vout_display_t *vd, picture_t *picture, subpicture_t *subpic - hdr10.MaxMasteringLuminance = picture->format.mastering.max_luminance; - hdr10.MaxContentLightLevel = picture->format.lighting.MaxCLL; - hdr10.MaxFrameAverageLightLevel = picture->format.lighting.MaxFALL; -- if (memcmp(&sys->hdr10, &hdr10, sizeof(hdr10))) -- { -- memcpy(&sys->hdr10, &hdr10, sizeof(hdr10)); -- IDXGISwapChain4_SetHDRMetaData(sys->dxgiswapChain4, DXGI_HDR_METADATA_TYPE_HDR10, sizeof(hdr10), &hdr10); -- } -+ IDXGISwapChain4_SetHDRMetaData(sys->dxgiswapChain4, DXGI_HDR_METADATA_TYPE_HDR10, sizeof(hdr10), &hdr10); - } - } - -@@ -1575,6 +1570,16 @@ static int Direct3D11Open(vout_display_t *vd, bool external_device) - - video_format_Copy(&sys->pool_fmt, &fmt); - -+ sys->legacy_shader = sys->d3d_dev.feature_level < D3D_FEATURE_LEVEL_10_0 || -+ (sys->scaleProc == NULL && !CanUseTextureArray(vd)) || -+ BogusZeroCopy(vd); +diff --git a/modules/video_output/wayland/wl_dmabuf.c b/modules/video_output/wayland/wl_dmabuf.c +new file mode 100644 +index 0000000000..14d06d147f +--- /dev/null ++++ b/modules/video_output/wayland/wl_dmabuf.c +@@ -0,0 +1,2330 @@ ++/** ++ * @file shm.c ++ * @brief Wayland shared memory video output module for VLC media player ++ */ ++/***************************************************************************** ++ * Copyright © 2014, 2017 Rémi Denis-Courmont ++ * ++ * This program 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. ++ * ++ * This program 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 this program; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. ++ *****************************************************************************/ + -+ if (!sys->legacy_shader && is_d3d11_opaque(sys->pool_fmt.i_chroma)) ++#ifdef HAVE_CONFIG_H ++# include ++#endif ++#ifndef HAVE_WAYLAND_SINGLE_PIXEL_BUFFER ++#define HAVE_WAYLAND_SINGLE_PIXEL_BUFFER 0 ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#if HAVE_WAYLAND_SINGLE_PIXEL_BUFFER ++#include "single-pixel-buffer-v1-client-protocol.h" ++#endif ++#include "viewporter-client-protocol.h" ++#include "linux-dmabuf-unstable-v1-client-protocol.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++// *** Avoid this include if possible ++#include ++ ++#include "dmabuf_alloc.h" ++#include "picpool.h" ++#include "rgba_premul.h" ++#include "../drmu/drmu_log.h" ++#include "../drmu/drmu_vlc_fmts.h" ++#include "../drmu/pollqueue.h" ++#include "../../codec/avcodec/drm_pic.h" ++#include ++ ++#define TRACE_ALL 0 ++#define CHECK_VDRE_COUNTS 0 ++ ++#define MAX_PICTURES 4 ++#define MAX_SUBPICS 6 ++ ++#define WL_DMABUF_DISABLE_NAME "wl-dmabuf-disable" ++#define WL_DMABUF_DISABLE_TEXT N_("Disable wl-dmabuf") ++#define WL_DMABUF_DISABLE_LONGTEXT N_("Disable wl-dmabuf - useful if auto selection is wanted but not wl-dmabuf") ++ ++#define WL_DMABUF_USE_SHM_NAME "wl-dmabuf-use-shm" ++#define WL_DMABUF_USE_SHM_TEXT N_("Attempt to map via shm") ++#define WL_DMABUF_USE_SHM_LONGTEXT N_("Attempt to map via shm rather than linux_dmabuf") ++ ++#define WL_DMABUF_CHEQUERBOARD_NAME "wl-dmabuf-chequerboard" ++#define WL_DMABUF_CHEQUERBOARD_TEXT N_("Chequerboard background") ++#define WL_DMABUF_CHEQUERBOARD_LONGTEXT N_("Fill unused window area with chequerboard rather than black") ++ ++typedef struct fmt_ent_s { ++ uint32_t fmt; ++ int32_t pri; ++ uint64_t mod; ++} fmt_ent_t; ++ ++typedef struct fmt_list_s { ++ fmt_ent_t * fmts; ++ unsigned int size; ++ unsigned int len; ++} fmt_list_t; ++ ++typedef struct eq_env_ss { ++ atomic_int eq_count; ++ sem_t sem; ++ ++ struct wl_display *display; ++ struct pollqueue *pq; ++ struct wl_event_queue *q; ++ struct wl_display *wrapped_display; ++} eq_env_t; ++ ++typedef struct video_dmabuf_release_env_ss ++{ ++ void (* dma_rel_fn)(void *); ++ void * dma_rel_v; ++ eq_env_t * eq; ++ unsigned int rel_count; ++ unsigned int pt_count; ++ struct polltask * pt[AV_DRM_MAX_PLANES]; ++#if CHECK_VDRE_COUNTS ++ atomic_int * vdre_check; ++#endif ++} video_dmabuf_release_env_t; ++ ++typedef struct subpic_ent_s { ++ struct wl_buffer * wb; ++ struct dmabuf_h * dh; ++ video_dmabuf_release_env_t * vdre; ++ picture_t * pic; ++ int alpha; ++ enum wl_output_transform trans; ++ vout_display_place_t src_rect; ++ vout_display_place_t dst_rect; ++ ++ atomic_int ready; ++ ++ struct polltask * pt; ++ vout_display_t * vd; ++ vout_display_sys_t * sys; ++} subpic_ent_t; ++ ++typedef struct subplane_s { ++ struct wl_surface * surface; ++ struct wl_subsurface * subsurface; ++ struct wp_viewport * viewport; ++ ++ enum wl_output_transform trans; ++ vout_display_place_t src_rect; ++ vout_display_place_t dst_rect; ++ ++ subpic_ent_t * spe_cur; ++ subpic_ent_t * spe_next; ++} subplane_t; ++ ++typedef struct w_bound_ss ++{ ++ struct wp_viewporter *viewporter; ++ struct zwp_linux_dmabuf_v1 * linux_dmabuf_v1; ++ struct wl_compositor *compositor; ++ struct wl_subcompositor *subcompositor; ++ struct wl_shm *shm; ++#if HAVE_WAYLAND_SINGLE_PIXEL_BUFFER ++ struct wp_single_pixel_buffer_manager_v1 *single_pixel_buffer_manager_v1; ++#endif ++} w_bound_t; ++ ++#define COMMIT_BKG 0 ++#define COMMIT_VID 1 ++#define COMMIT_SUB 2 ++ ++struct vout_display_sys_t ++{ ++ vout_window_t *embed; /* VLC window */ ++ ++ w_bound_t bound; ++ ++ picture_pool_t *vlc_pic_pool; /* picture pool */ ++ ++ struct wl_surface * last_embed_surface; ++ unsigned int last_embed_seq; ++ ++ int x; ++ int y; ++ bool video_attached; ++ bool use_shm; ++ bool chequerboard; ++ ++ struct wp_viewport * bkg_viewport; ++ // Current size of background viewport if we have one ++ // If not created yet then the size that the viewport should be created ++ unsigned int bkg_w; ++ unsigned int bkg_h; ++ ++#if CHECK_VDRE_COUNTS ++ atomic_int vdre_check_bkg; ++ atomic_int vdre_check_fg; ++#endif ++ ++ eq_env_t * eq; ++ ++ struct pollqueue * pollq; ++ struct pollqueue * speq; ++ ++ picpool_ctl_t * subpic_pool; ++ subplane_t video_plane[1]; ++ subplane_t subplanes[MAX_SUBPICS]; ++ bool commit_req[MAX_SUBPICS + 2]; ++ subpic_ent_t video_spe; ++ vlc_fourcc_t * subpic_chromas; ++ ++ struct wl_region * region_none; ++ struct wl_region * region_all; ++ ++ fmt_list_t dmabuf_fmts; ++ fmt_list_t shm_fmts; ++}; ++ ++ ++static struct wl_surface * bkg_surface_get_lock(vout_display_t * const vd, vout_display_sys_t * const sys); ++static void bkg_surface_unlock(vout_display_t * const vd, vout_display_sys_t * const sys); ++ ++ ++static inline struct wl_display * ++video_display(const vout_display_sys_t * const sys) ++{ ++ return sys->embed->display.wl; ++} ++ ++static inline struct wl_surface * ++video_surface(const vout_display_sys_t * const sys) ++{ ++ return sys->video_plane->surface; ++} ++ ++static inline struct wl_compositor * ++video_compositor(const vout_display_sys_t * const sys) ++{ ++ return sys->bound.compositor; ++} ++ ++static void ++buffer_destroy(struct wl_buffer ** ppbuffer) ++{ ++ struct wl_buffer * const buffer = *ppbuffer; ++ if (buffer == NULL) ++ return; ++ *ppbuffer = NULL; ++ wl_buffer_destroy(buffer); ++} ++ ++static void ++region_destroy(struct wl_region ** const ppregion) ++{ ++ if (*ppregion == NULL) ++ return; ++ wl_region_destroy(*ppregion); ++ *ppregion = NULL; ++} ++ ++static void ++subsurface_destroy(struct wl_subsurface ** const ppsubsurface) ++{ ++ if (*ppsubsurface == NULL) ++ return; ++ wl_subsurface_destroy(*ppsubsurface); ++ *ppsubsurface = NULL; ++} ++ ++static void ++surface_destroy(struct wl_surface ** const ppsurface) ++{ ++ if (*ppsurface == NULL) ++ return; ++ wl_surface_destroy(*ppsurface); ++ *ppsurface = NULL; ++} ++ ++static void ++viewport_destroy(struct wp_viewport ** const ppviewport) ++{ ++ if (*ppviewport == NULL) ++ return; ++ wp_viewport_destroy(*ppviewport); ++ *ppviewport = NULL; ++} ++ ++static inline int_fast32_t ++place_rescale_1s(int_fast32_t x, uint_fast32_t mul, uint_fast32_t div) ++{ ++ const int_fast64_t m = x * (int_fast64_t)mul; ++ const uint_fast32_t d2 = div/2; ++ return div == 0 ? (int_fast32_t)m : ++ m >= 0 ? (int_fast32_t)(((uint_fast64_t)m + d2) / div) : ++ -(int_fast32_t)(((uint_fast64_t)(-m) + d2) / div); ++} ++ ++static inline uint_fast32_t ++place_rescale_1u(uint_fast32_t x, uint_fast32_t mul, uint_fast32_t div) ++{ ++ const uint_fast64_t m = x * (uint_fast64_t)mul; ++ return (uint_fast32_t)(div == 0 ? m : (m + div/2) / div); ++} ++ ++static inline vout_display_place_t ++place_rescale(const vout_display_place_t s, const vout_display_place_t mul, const vout_display_place_t div) ++{ ++ return (vout_display_place_t){ ++ .x = place_rescale_1s(s.x - div.x, mul.width, div.width) + mul.x, ++ .y = place_rescale_1s(s.y - div.y, mul.height, div.height) + mul.y, ++ .width = place_rescale_1u(s.width, mul.width, div.width), ++ .height = place_rescale_1u(s.height, mul.height, div.height) ++ }; ++} ++ ++static inline bool ++place_xy_eq(const vout_display_place_t a, const vout_display_place_t b) ++{ ++ return a.x == b.x && a.y == b.y; ++} ++ ++static inline bool ++place_wh_eq(const vout_display_place_t a, const vout_display_place_t b) ++{ ++ return a.width == b.width && a.height == b.height; ++} ++ ++static inline bool ++place_eq(const vout_display_place_t a, const vout_display_place_t b) ++{ ++ return place_xy_eq(a, b) && place_wh_eq(a, b); ++} ++ ++ ++// MMAL headers comment these (getting 2 a bit wrong) but do not give ++// defines ++#define VXF_H_SHIFT 0 // Hflip ++#define VXF_V_SHIFT 1 // Vflip ++#define VXF_T_SHIFT 2 // Transpose ++#define VXF_H_BIT (1 << VXF_H_SHIFT) ++#define VXF_V_BIT (1 << VXF_V_SHIFT) ++#define VXF_T_BIT (1 << VXF_T_SHIFT) ++ ++static inline bool ++is_vxf_transpose(const video_transform_t t) ++{ ++ return ((unsigned int)t & VXF_T_BIT) != 0; ++} ++ ++static inline bool ++is_vxf_hflip(const video_transform_t t) ++{ ++ return ((unsigned int)t & VXF_H_BIT) != 0; ++} ++ ++static inline bool ++is_vxf_vflip(const video_transform_t t) ++{ ++ return ((unsigned int)t & VXF_V_BIT) != 0; ++} ++ ++static inline video_transform_t ++swap_vxf_hv(const video_transform_t x) ++{ ++ return (((x >> VXF_H_SHIFT) & 1) << VXF_V_SHIFT) | ++ (((x >> VXF_V_SHIFT) & 1) << VXF_H_SHIFT) | ++ (x & VXF_T_BIT); ++} ++ ++static inline video_transform_t ++vxf_inverse(const video_transform_t x) ++{ ++ return is_vxf_transpose(x) ? swap_vxf_hv(x) : x; ++} ++ ++// Transform generated by A then B ++// All ops are self inverse so can simply be XORed on their own ++// H & V flips after a transpose need to be swapped ++static inline video_transform_t ++combine_vxf(const video_transform_t a, const video_transform_t b) ++{ ++ return a ^ (is_vxf_transpose(a) ? swap_vxf_hv(b) : b); ++} ++ ++static inline vout_display_place_t ++vplace_transpose(const vout_display_place_t s) ++{ ++ return (vout_display_place_t){ ++ .x = s.y, ++ .y = s.x, ++ .width = s.height, ++ .height = s.width ++ }; ++} ++ ++// hflip s in c ++static inline vout_display_place_t vplace_hflip(const vout_display_place_t s, const vout_display_place_t c) ++{ ++ return (vout_display_place_t){ ++ .x = c.x + (c.x + c.width) - (s.x + s.width), ++ .y = s.y, ++ .width = s.width, ++ .height = s.height ++ }; ++} ++ ++// vflip s in c ++static inline vout_display_place_t vplace_vflip(const vout_display_place_t s, const vout_display_place_t c) ++{ ++ return (vout_display_place_t){ ++ .x = s.x, ++ .y = (c.y + c.height) - (s.y - c.y) - s.height, ++ .width = s.width, ++ .height = s.height ++ }; ++} ++ ++#if 0 ++static vout_display_place_t ++rect_transform(vout_display_place_t s, const vout_display_place_t c, const video_transform_t t) ++{ ++ if (is_vxf_transpose(t)) ++ s = vplace_transpose(s); ++ if (is_vxf_hflip(t)) ++ s = vplace_hflip(s, c); ++ if (is_vxf_vflip(t) != 0) ++ s = vplace_vflip(s, c); ++ return s; ++} ++ ++static void ++place_dest_rect(vout_display_t * const vd, ++ const vout_display_cfg_t * const cfg, ++ const video_format_t * fmt) ++{ ++ vout_display_sys_t * const sys = vd->sys; ++ sys->dest_rect = rect_transform(place_out(cfg, fmt, sys->win_rect), ++ sys->display_rect, sys->dest_transform); ++} ++#endif ++ ++static int ++fmt_list_add(fmt_list_t * const fl, uint32_t fmt, uint64_t mod, int32_t pri) ++{ ++ if (fl->len >= fl->size) + { -+ sys->pool_fmt.i_width = (sys->pool_fmt.i_width + 0x7F) & ~0x7F; -+ sys->pool_fmt.i_height = (sys->pool_fmt.i_height + 0x7F) & ~0x7F; ++ unsigned int n = fl->len == 0 ? 64 : fl->len * 2; ++ fmt_ent_t * t = realloc(fl->fmts, n * sizeof(*t)); ++ if (t == NULL) ++ return VLC_ENOMEM; ++ fl->fmts = t; ++ fl->size = n; ++ } ++ fl->fmts[fl->len++] = (fmt_ent_t){ ++ .fmt = fmt, ++ .pri = pri, ++ .mod = mod ++ }; ++ return 0; ++} ++ ++static int ++fmt_sort_cb(const void * va, const void * vb) ++{ ++ const fmt_ent_t * const a = va; ++ const fmt_ent_t * const b = vb; ++ return a->fmt < b->fmt ? -1 : a->fmt != b->fmt ? 1 : ++ a->mod < b->mod ? -1 : a->mod != b->mod ? 1 : 0; ++} ++ ++static void ++fmt_list_sort(fmt_list_t * const fl) ++{ ++ unsigned int n = 0; ++ if (fl->len <= 1) ++ return; ++ qsort(fl->fmts, fl->len, sizeof(*fl->fmts), fmt_sort_cb); ++ // Dedup - in case we have multiple working callbacks ++ for (unsigned int i = 1; i != fl->len; ++i) ++ { ++ if (fl->fmts[i].fmt != fl->fmts[n].fmt || fl->fmts[i].mod != fl->fmts[n].mod) ++ fl->fmts[n++] = fl->fmts[i]; ++ } ++ fl->len = n + 1; ++} ++ ++static int ++fmt_list_find(const fmt_list_t * const fl, const drmu_vlc_fmt_info_t * const fmti) ++{ ++ if (fmti == NULL || fl->len == 0) ++ { ++ return -1; + } + else - if ( sys->picQuad.formatInfo->formatTexture != DXGI_FORMAT_R8G8B8A8_UNORM && - sys->picQuad.formatInfo->formatTexture != DXGI_FORMAT_B5G6R5_UNORM ) - { -@@ -1684,10 +1689,6 @@ static int SetupOutputFormat(vout_display_t *vd, video_format_t *fmt) - - InitScaleProcessor(vd); - -- sys->legacy_shader = sys->d3d_dev.feature_level < D3D_FEATURE_LEVEL_10_0 || -- (sys->scaleProc == NULL && !CanUseTextureArray(vd)) || -- BogusZeroCopy(vd); -- - if (Direct3D11CreateFormatResources(vd, fmt)) { - msg_Err(vd, "Failed to allocate format resources"); - return VLC_EGENERIC; -diff --git a/modules/video_output/win32/events.c b/modules/video_output/win32/events.c -index 474142f0fc..476a710529 100644 ---- a/modules/video_output/win32/events.c -+++ b/modules/video_output/win32/events.c -@@ -277,9 +277,6 @@ static void *EventThread( void *p_this ) - case WM_MBUTTONUP: - MouseReleased( p_event, MOUSE_BUTTON_CENTER ); - break; -- case WM_MBUTTONDBLCLK: -- MousePressed( p_event, msg.hwnd, MOUSE_BUTTON_CENTER ); -- break; - - case WM_RBUTTONDOWN: - MousePressed( p_event, msg.hwnd, MOUSE_BUTTON_RIGHT ); -@@ -287,9 +284,6 @@ static void *EventThread( void *p_this ) - case WM_RBUTTONUP: - MouseReleased( p_event, MOUSE_BUTTON_RIGHT ); - break; -- case WM_RBUTTONDBLCLK: -- MousePressed( p_event, msg.hwnd, MOUSE_BUTTON_RIGHT ); -- break; - - case WM_KEYDOWN: - case WM_SYSKEYDOWN: ++ { ++ const fmt_ent_t x = { ++ .fmt = drmu_vlc_fmt_info_drm_pixelformat(fmti), ++ .mod = drmu_vlc_fmt_info_drm_modifier(fmti), ++ }; ++ const fmt_ent_t * const fe = ++ bsearch(&x, fl->fmts, fl->len, sizeof(x), fmt_sort_cb); ++ return fe == NULL ? -1 : fe->pri; ++ } ++} ++ ++static void ++fmt_list_uninit(fmt_list_t * const fl) ++{ ++ free(fl->fmts); ++ fl->fmts = NULL; ++ fl->size = 0; ++ fl->len = 0; ++} ++ ++static int ++fmt_list_init(fmt_list_t * const fl, const size_t initial_size) ++{ ++ fl->size = 0; ++ fl->len = 0; ++ if ((fl->fmts = malloc(initial_size * sizeof(*fl->fmts))) == NULL) ++ return VLC_ENOMEM; ++ fl->size = initial_size; ++ return VLC_SUCCESS; ++} ++ ++// ---------------------------------------------------------------------------- ++ ++static struct wl_display * ++eq_wrapper(eq_env_t * const eq) ++{ ++ return eq->wrapped_display; ++} ++ ++static void ++eq_ref(eq_env_t * const eq) ++{ ++ const int n = atomic_fetch_add(&eq->eq_count, 1); ++ (void)n; ++// fprintf(stderr, "Ref: count=%d\n", n + 1); ++} ++ ++static void ++eq_unref(eq_env_t ** const ppeq) ++{ ++ eq_env_t * eq = *ppeq; ++ if (eq != NULL) ++ { ++ int n; ++ *ppeq = NULL; ++ n = atomic_fetch_sub(&eq->eq_count, 1); ++// fprintf(stderr, "Unref: Buffer count=%d\n", n); ++ if (n == 0) ++ { ++ pollqueue_set_pre_post(eq->pq, 0, 0, NULL); ++ pollqueue_unref(&eq->pq); ++ ++ wl_proxy_wrapper_destroy(eq->wrapped_display); ++ wl_event_queue_destroy(eq->q); ++ ++ sem_destroy(&eq->sem); ++ free(eq); ++// fprintf(stderr, "Eq closed\n"); ++ } ++ } ++} ++ ++static int ++eq_finish(eq_env_t ** const ppeq) ++{ ++ eq_env_t * const eq = *ppeq; ++ ++ if (eq == NULL) ++ return 0; ++ ++ eq_unref(ppeq); ++ return 0; ++} ++ ++static void ++pollq_pre_cb(void * v, struct pollfd * pfd) ++{ ++ eq_env_t * const eq = v; ++ struct wl_display *const display = eq->display; ++ int ferr; ++ int frv; ++ ++// fprintf(stderr, "Start Prepare\n"); ++ ++ while (wl_display_prepare_read_queue(display, eq->q) != 0) { ++ int n = wl_display_dispatch_queue_pending(display, eq->q); ++ (void)n; ++// fprintf(stderr, "Dispatch=%d\n", n); ++ } ++ if ((frv = wl_display_flush(display)) >= 0) { ++ pfd->events = POLLIN; ++ ferr = 0; ++ } ++ else { ++ ferr = errno; ++ pfd->events = POLLOUT | POLLIN; ++ } ++ pfd->fd = wl_display_get_fd(display); ++(void)ferr; ++// fprintf(stderr, "Done Prepare: fd=%d, evts=%#x, frv=%d, ferr=%s\n", pfd->fd, pfd->events, frv, ferr == 0 ? "ok" : strerror(ferr)); ++} ++ ++static void ++pollq_post_cb(void *v, short revents) ++{ ++ eq_env_t * const eq = v; ++ struct wl_display *const display = eq->display; ++ int n; ++ ++ if ((revents & POLLIN) == 0) { ++// fprintf(stderr, "Cancel read: Events=%#x: IN=%#x, OUT=%#x, ERR=%#x\n", (int)revents, POLLIN, POLLOUT, POLLERR); ++ wl_display_cancel_read(display); ++ } ++ else { ++// fprintf(stderr, "Read events: Events=%#x: IN=%#x, OUT=%#x, ERR=%#x\n", (int)revents, POLLIN, POLLOUT, POLLERR); ++ wl_display_read_events(display); ++ } ++ ++// fprintf(stderr, "Start Dispatch\n"); ++ n = wl_display_dispatch_queue_pending(display, eq->q); ++ (void)n; ++// fprintf(stderr, "Dispatch=%d\n", n); ++} ++ ++static eq_env_t * ++eq_new(struct wl_display * const display, struct pollqueue * const pq) ++{ ++ eq_env_t * eq = calloc(1, sizeof(*eq)); ++ ++ if (eq == NULL) ++ return NULL; ++ ++ atomic_init(&eq->eq_count, 0); ++ sem_init(&eq->sem, 0, 0); ++ ++ if ((eq->q = wl_display_create_queue(display)) == NULL) ++ goto err1; ++ if ((eq->wrapped_display = wl_proxy_create_wrapper(display)) == NULL) ++ goto err2; ++ wl_proxy_set_queue((struct wl_proxy *)eq->wrapped_display, eq->q); ++ ++ eq->display = display; ++ eq->pq = pollqueue_ref(pq); ++ ++ pollqueue_set_pre_post(eq->pq, pollq_pre_cb, pollq_post_cb, eq); ++ ++ return eq; ++ ++err2: ++ wl_event_queue_destroy(eq->q); ++err1: ++ free(eq); ++ return NULL; ++} ++ ++static void eventq_sync_cb(void * data, struct wl_callback * cb, unsigned int cb_data) ++{ ++ sem_t * const sem = data; ++ VLC_UNUSED(cb_data); ++ wl_callback_destroy(cb); ++ sem_post(sem); ++} ++ ++static const struct wl_callback_listener eq_sync_listener = {.done = eventq_sync_cb}; ++ ++struct eq_sync_env_ss { ++ eq_env_t * eq; ++ sem_t sem; ++}; ++ ++static void ++eq_sync_pq_cb(void * v, short revents) ++{ ++ struct eq_sync_env_ss * const eqs = v; ++ struct wl_callback * const cb = wl_display_sync(eq_wrapper(eqs->eq)); ++ VLC_UNUSED(revents); ++ wl_callback_add_listener(cb, &eq_sync_listener, &eqs->sem); ++ // No flush needed as that will occur as part of the pollqueue loop ++} ++ ++static int ++eventq_sync(eq_env_t * const eq) ++{ ++ struct eq_sync_env_ss eqs = {.eq = eq}; ++ int rv; ++ ++ if (!eq) ++ return -1; ++ ++ sem_init(&eqs.sem, 0, 0); ++ // Bounce execution to pollqueue to avoid race setting up listener ++ pollqueue_callback_once(eq->pq, eq_sync_pq_cb, &eqs); ++ while ((rv = sem_wait(&eqs.sem)) == -1 && errno == EINTR) ++ /* Loop */; ++ sem_destroy(&eqs.sem); ++ return rv; ++} ++ ++// ---------------------------------------------------------------------------- ++ ++static void ++chequerboard(uint32_t *const data, unsigned int stride, const unsigned int width, const unsigned int height) ++{ ++ stride /= sizeof(uint32_t); ++ ++ /* Draw checkerboxed background */ ++ for (unsigned int y = 0; y < height; ++y) { ++ for (unsigned int x = 0; x < width; ++x) { ++ if ((x + y / 8 * 8) % 16 < 8) ++ data[y * stride + x] = 0xFF666666; ++ else ++ data[y * stride + x] = 0xFFEEEEEE; ++ } ++ } ++} ++ ++static void ++fill_uniform(uint32_t *const data, unsigned int stride, const unsigned int width, const unsigned int height, const uint32_t val) ++{ ++ stride /= sizeof(uint32_t); ++ ++ /* Draw solid background */ ++ for (unsigned int y = 0; y < height; ++y) { ++ for (unsigned int x = 0; x < width; ++x) ++ data[y * stride + x] = val; ++ } ++} ++ ++// ---------------------------------------------------------------------------- ++ ++static void ++vdre_free(video_dmabuf_release_env_t * const vdre) ++{ ++ unsigned int i; ++ if (vdre->dma_rel_fn) ++ vdre->dma_rel_fn(vdre->dma_rel_v); ++ for (i = 0; i != vdre->pt_count; ++i) ++ polltask_delete(vdre->pt + i); ++ eq_unref(&vdre->eq); ++#if CHECK_VDRE_COUNTS ++ if (vdre->vdre_check != NULL) ++ atomic_fetch_sub(vdre->vdre_check, 1); ++#endif ++ free(vdre); ++} ++ ++static video_dmabuf_release_env_t * ++vdre_new_null(void) ++{ ++ video_dmabuf_release_env_t * const vdre = calloc(1, sizeof(*vdre)); ++ return vdre; ++} ++ ++static void ++vdre_dma_rel_cb(void * v) ++{ ++ struct picture_context_t * ctx = v; ++ ctx->destroy(ctx); ++} ++ ++static video_dmabuf_release_env_t * ++vdre_new_ctx(struct picture_context_t * ctx) ++{ ++ video_dmabuf_release_env_t * const vdre = vdre_new_null(); ++ if (vdre == NULL) ++ return NULL; ++ if ((vdre->dma_rel_v = ctx->copy(ctx)) == NULL) ++ { ++ free(vdre); ++ return NULL; ++ } ++ vdre->dma_rel_fn = vdre_dma_rel_cb; ++ return vdre; ++} ++ ++static void ++vdre_delete(video_dmabuf_release_env_t ** const ppvdre) ++{ ++ video_dmabuf_release_env_t * const vdre = *ppvdre; ++ if (vdre == NULL) ++ return; ++ *ppvdre = NULL; ++ vdre_free(vdre); ++} ++ ++static void ++vdre_polltask_cb(void * v, short revents) ++{ ++ video_dmabuf_release_env_t * const vdre = v; ++ VLC_UNUSED(revents); ++ // Wait for all callbacks to come back before releasing buffer ++ if (++vdre->rel_count >= vdre->pt_count) ++ vdre_free(vdre); ++} ++ ++static void ++vdre_eq_ref(video_dmabuf_release_env_t * const vdre, eq_env_t * const eq) ++{ ++ if (vdre == NULL) ++ return; ++ vdre->eq = eq; ++ eq_ref(vdre->eq); ++} ++ ++#if CHECK_VDRE_COUNTS ++static void ++vdre_add_check(video_dmabuf_release_env_t * const vdre, atomic_int * const pa) ++{ ++ vdre->vdre_check = pa; ++ atomic_fetch_add(pa, 1); ++} ++#endif ++ ++static void ++vdre_add_pt(video_dmabuf_release_env_t * const vdre, struct pollqueue * pq, int fd) ++{ ++ assert(vdre->pt_count < AV_DRM_MAX_PLANES); ++ vdre->pt[vdre->pt_count++] = polltask_new(pq, fd, POLLOUT, vdre_polltask_cb, vdre); ++} ++ ++static void ++vdre_dh_rel_cb(void * v) ++{ ++ struct dmabuf_h * dh = v; ++ dmabuf_unref(&dh); ++} ++ ++static video_dmabuf_release_env_t * ++vdre_new_dh(struct dmabuf_h *const dh, struct pollqueue *const pq) ++{ ++ video_dmabuf_release_env_t * const vdre = vdre_new_null(); ++ ++ vdre->dma_rel_fn = vdre_dh_rel_cb; ++ vdre->dma_rel_v = dh; ++ ++ if (!dmabuf_is_fake(dh)) ++ vdre_add_pt(vdre, pq, dmabuf_fd(dh)); ++ return vdre; ++} ++ ++// Avoid use of vd here as there's a possibility this will be called after ++// it has gone ++static void ++vdre_buffer_release_cb(void *data, struct wl_buffer *wl_buffer) ++{ ++ video_dmabuf_release_env_t * const vdre = data; ++ unsigned int i = vdre->pt_count; ++ ++ /* Sent by the compositor when it's no longer using this buffer */ ++ buffer_destroy(&wl_buffer); ++ ++ eq_unref(&vdre->eq); ++ ++ if (i == 0) ++ vdre_free(vdre); ++ else ++ { ++ // Whilst we can happily destroy the buffer that doesn't mean we can reuse ++ // the dmabufs yet - we have to wait for them to be free of fences. ++ // We don't want to wait in this callback so do the waiting in pollqueue ++ while (i-- != 0) ++ pollqueue_add_task(vdre->pt[i], 1000); ++ } ++} ++ ++static const struct wl_buffer_listener vdre_buffer_listener = { ++ .release = vdre_buffer_release_cb, ++}; ++ ++// ---------------------------------------------------------------------------- ++ ++static inline size_t cpypic_plane_alloc_size(const plane_t * const p) ++{ ++ return p->i_pitch * p->i_lines; ++} ++ ++static inline uint32_t ++drm_fmt_to_wl_shm(const uint32_t drm_fmt) ++{ ++ return (drm_fmt == DRM_FORMAT_ARGB8888) ? WL_SHM_FORMAT_ARGB8888 : ++ (drm_fmt == DRM_FORMAT_XRGB8888) ? WL_SHM_FORMAT_XRGB8888 : drm_fmt; ++} ++ ++static struct wl_buffer * ++dfd_make_buffer(vout_display_t * const vd, vout_display_sys_t * const sys, ++ const bool use_shm, ++ const AVDRMFrameDescriptor * const desc, ++ const unsigned int width, const unsigned int height, ++ const uint32_t flags) ++{ ++ struct wl_buffer * w_buffer; ++ ++ if (!sys->bound.linux_dmabuf_v1 || use_shm) ++ { ++ const AVDRMPlaneDescriptor *const p = desc->layers[0].planes + 0; ++ struct wl_shm_pool *pool = wl_shm_create_pool(sys->bound.shm, desc->objects[0].fd, desc->objects[0].size); ++ const uint32_t w_fmt = drm_fmt_to_wl_shm(desc->layers[0].format); ++ ++ if (pool == NULL) ++ { ++ msg_Err(vd, "Failed to create pool from dmabuf"); ++ return NULL; ++ } ++ w_buffer = wl_shm_pool_create_buffer(pool, p->offset, width, height, p->pitch, w_fmt); ++ wl_shm_pool_destroy(pool); ++ if (w_buffer == NULL) ++ msg_Err(vd, "Failed to create buffer from pool"); ++ } ++ else ++ { ++ /* Creation and configuration of planes */ ++ int i; ++ unsigned int n = 0; ++ struct zwp_linux_buffer_params_v1 * const params = zwp_linux_dmabuf_v1_create_params(sys->bound.linux_dmabuf_v1); ++ ++ if (!params) ++ { ++ msg_Err(vd, "zwp_linux_dmabuf_v1_create_params FAILED"); ++ return NULL; ++ } ++ ++ for (i = 0; i < desc->nb_layers; ++i) ++ { ++ int j; ++ for (j = 0; j < desc->layers[i].nb_planes; ++j) ++ { ++ const AVDRMPlaneDescriptor *const p = desc->layers[i].planes + j; ++ const AVDRMObjectDescriptor *const obj = desc->objects + p->object_index; ++ ++ zwp_linux_buffer_params_v1_add(params, obj->fd, n++, p->offset, p->pitch, ++ (unsigned int)(obj->format_modifier >> 32), ++ (unsigned int)(obj->format_modifier & 0xFFFFFFFF)); ++ } ++ } ++ ++ /* Request buffer creation */ ++ if ((w_buffer = zwp_linux_buffer_params_v1_create_immed(params, width, height, desc->layers[0].format, flags)) == NULL) ++ msg_Err(vd, "zwp_linux_buffer_params_v1_create_immed FAILED"); ++ zwp_linux_buffer_params_v1_destroy(params); ++ } ++ return w_buffer; ++} ++ ++static int ++copy_subpic_to_w_buffer(vout_display_t *vd, vout_display_sys_t * const sys, picture_t * const src, ++ int alpha, ++ video_dmabuf_release_env_t ** pVdre, struct wl_buffer ** pW_buffer) ++{ ++ unsigned int w = src->format.i_width; ++ unsigned int h = src->format.i_height; ++ struct zwp_linux_buffer_params_v1 *params = NULL; ++ uint64_t mod; ++ const uint32_t drm_fmt = drmu_format_vlc_to_drm(&src->format, &mod); ++ struct dmabuf_h * dh = NULL; ++ int i; ++ AVDRMFrameDescriptor dfd = { ++ .nb_objects = 1, ++ .objects = {{ ++ .fd = -1, ++ .size = 0, ++ .format_modifier = mod ++ }}, ++ .nb_layers = 1, ++ .layers = {{ ++ .format = drm_fmt, ++ .nb_planes = src->i_planes, ++ }} ++ }; ++ ++ for (i = 0; i != src->i_planes; ++i) ++ { ++ dfd.layers[0].planes[i].object_index = 0; ++ dfd.layers[0].planes[i].offset = dfd.objects[0].size; ++ dfd.layers[0].planes[i].pitch = src->p[i].i_pitch; ++ dfd.objects[0].size += cpypic_plane_alloc_size(src->p + i); ++ } ++ ++ *pW_buffer = NULL; ++ *pVdre = NULL; ++ ++ if ((dh = picpool_get(sys->subpic_pool, dfd.objects[0].size)) == NULL) ++ { ++ msg_Warn(vd, "Failed to alloc dmabuf for subpic"); ++ goto error; ++ } ++ dfd.objects[0].fd = dmabuf_fd(dh); ++ ++ if ((*pVdre = vdre_new_dh(dh, sys->pollq)) == NULL) ++ { ++ msg_Warn(vd, "Failed to alloc vdre for subpic"); ++ dmabuf_unref(&dh); ++ goto error; ++ } ++ ++ // Copy to dh ++ dmabuf_write_start(dh); ++ for (i = 0; i != dfd.layers[0].nb_planes; ++i) ++ { ++ const size_t stride = dfd.layers[0].planes[i].pitch; ++ const size_t offset = dfd.layers[0].planes[i].offset; ++ void * const dst_data = (char *)dmabuf_map(dh) + offset; ++ ++ if (src->format.i_chroma == VLC_CODEC_RGBA || ++ src->format.i_chroma == VLC_CODEC_BGRA) ++ copy_frame_xxxa_with_premul(dst_data, stride, src->p[i].p_pixels, stride, w, h, alpha); ++ else ++ memcpy(dst_data, src->p[i].p_pixels, cpypic_plane_alloc_size(src->p + i)); ++ } ++ dmabuf_write_end(dh); ++ ++ *pW_buffer = dfd_make_buffer(vd, sys, dmabuf_is_fake(dh), &dfd, w, h, 0); ++ if (*pW_buffer == NULL) ++ goto error; ++ ++#if CHECK_VDRE_COUNTS ++ vdre_add_check(*pVdre, &sys->vdre_check_fg); ++#endif ++ ++ return VLC_SUCCESS; ++ ++error: ++ if (params) ++ zwp_linux_buffer_params_v1_destroy(params); ++ vdre_delete(pVdre); ++ return VLC_EGENERIC; ++} ++ ++static void kill_pool(vout_display_sys_t * const sys) ++{ ++ if (sys->vlc_pic_pool != NULL) ++ { ++ picture_pool_Release(sys->vlc_pic_pool); ++ sys->vlc_pic_pool = NULL; ++ } ++} ++ ++// Actual picture pool for dmabufs is just a set of trivial containers ++static picture_pool_t *vd_dmabuf_pool(vout_display_t * const vd, unsigned count) ++{ ++ vout_display_sys_t * const sys = vd->sys; ++ ++#if TRACE_ALL ++ msg_Dbg(vd, "%s: fmt:%dx%d,sar:%d/%d; source:%dx%d", __func__, ++ vd->fmt.i_width, vd->fmt.i_height, vd->fmt.i_sar_num, vd->fmt.i_sar_den, vd->source.i_width, vd->source.i_height); ++#endif ++ ++ if (sys->vlc_pic_pool == NULL) ++ sys->vlc_pic_pool = picture_pool_NewFromFormat(&vd->fmt, count); ++ return sys->vlc_pic_pool; ++} ++ ++static int ++do_display_dmabuf(vout_display_t * const vd, vout_display_sys_t * const sys, picture_t * const pic, ++ video_dmabuf_release_env_t ** const pVdre, struct wl_buffer ** const pWbuffer) ++{ ++ const AVDRMFrameDescriptor * const desc = drm_prime_get_desc(pic); ++ const unsigned int width = pic->format.i_width; ++ const unsigned int height = pic->format.i_height; ++ unsigned int flags = 0; ++ int i; ++ video_dmabuf_release_env_t * const vdre = vdre_new_ctx(pic->context); ++ ++ assert(*pWbuffer == NULL); ++ assert(*pVdre == NULL); ++ ++ if (vdre == NULL) { ++ msg_Err(vd, "Failed to create vdre"); ++ return VLC_ENOMEM; ++ } ++ ++ for (i = 0; i != desc->nb_objects; ++i) ++ vdre_add_pt(vdre, sys->pollq, desc->objects[i].fd); ++ ++ if (!pic->b_progressive) ++ { ++ flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED; ++ if (!pic->b_top_field_first) ++ flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST; ++ } ++ ++ *pWbuffer = dfd_make_buffer(vd, sys, false, desc, width, height, flags); ++ if (*pWbuffer == NULL) ++ goto error; ++ ++ *pVdre = vdre; ++ return VLC_SUCCESS; ++ ++error: ++ vdre_free(vdre); ++ return VLC_EGENERIC; ++} ++ ++static void ++subpic_ent_flush(subpic_ent_t * const spe) ++{ ++ if (spe->pic != NULL) { ++ picture_Release(spe->pic); ++ spe->pic = NULL; ++ } ++ buffer_destroy(&spe->wb); ++ vdre_delete(&spe->vdre); ++ dmabuf_unref(&spe->dh); ++} ++ ++static void ++subpic_ent_attach(struct wl_surface * const surface, subpic_ent_t * const spe, eq_env_t * eq) ++{ ++ if (spe->wb == NULL) ++ { ++ vdre_delete(&spe->vdre); ++ wl_surface_attach(surface, NULL, 0, 0); ++ } ++ else ++ { ++ vdre_eq_ref(spe->vdre, eq); ++ wl_buffer_add_listener(spe->wb, &vdre_buffer_listener, spe->vdre); ++ wl_surface_attach(surface, spe->wb, 0, 0); ++ spe->vdre = NULL; ++ spe->wb = NULL; ++ wl_surface_damage(surface, 0, 0, INT32_MAX, INT32_MAX); ++ } ++} ++ ++static void ++spe_convert_cb(void * v, short revents) ++{ ++ subpic_ent_t * const spe = v; ++ VLC_UNUSED(revents); ++ ++ copy_subpic_to_w_buffer(spe->vd, spe->sys, spe->pic, spe->alpha, &spe->vdre, &spe->wb); ++ atomic_store(&spe->ready, 1); ++} ++ ++static inline bool ++spe_no_pic(const subpic_ent_t * const spe) ++{ ++ return spe == NULL || spe->pic == NULL; ++} ++ ++static bool ++spe_changed(const subpic_ent_t * const spe, const subpicture_region_t * const sreg) ++{ ++ const bool no_pic = (sreg == NULL || sreg->i_alpha == 0); ++ if (no_pic && spe_no_pic(spe)) ++ return false; ++ return no_pic || spe_no_pic(spe) || spe->pic != sreg->p_picture || spe->alpha != sreg->i_alpha; ++} ++ ++static void ++spe_update_rect(subpic_ent_t * const spe, vout_display_sys_t * const sys, ++ const subpicture_t * const spic, ++ const subpicture_region_t * const sreg) ++{ ++ spe->src_rect = (vout_display_place_t) { ++ .x = sreg->fmt.i_x_offset, ++ .y = sreg->fmt.i_y_offset, ++ .width = sreg->fmt.i_visible_width, ++ .height = sreg->fmt.i_visible_height, ++ }; ++ spe->dst_rect = place_rescale( ++ (vout_display_place_t) { ++ .x = sreg->i_x, ++ .y = sreg->i_y, ++ .width = sreg->fmt.i_visible_width, ++ .height = sreg->fmt.i_visible_height, ++ }, ++ (vout_display_place_t) { ++ .x = 0, ++ .y = 0, ++ .width = sys->video_spe.dst_rect.width, ++ .height = sys->video_spe.dst_rect.height, ++ }, ++ (vout_display_place_t) { ++ .x = 0, ++ .y = 0, ++ .width = spic->i_original_picture_width, ++ .height = spic->i_original_picture_height, ++ }); ++} ++ ++static subpic_ent_t * ++spe_new(vout_display_t * const vd, vout_display_sys_t * const sys, ++ const subpicture_t * const spic, ++ const subpicture_region_t * const sreg) ++{ ++ subpic_ent_t * const spe = calloc(1, sizeof(*spe)); ++ ++ if (spe == NULL) ++ return NULL; ++ ++ atomic_init(&spe->ready, 0); ++ spe->vd = vd; ++ spe->sys = sys; ++ ++ if (sreg == NULL || sreg->i_alpha == 0) ++ { ++ atomic_init(&spe->ready, 1); ++ return spe; ++ } ++ ++ spe->pic = picture_Hold(sreg->p_picture); ++ spe->alpha = sreg->i_alpha; ++ ++ spe_update_rect(spe, sys, spic, sreg); ++ ++ spe->pt = polltask_new_timer(sys->speq, spe_convert_cb, spe); ++ ++ return spe; ++} ++ ++static void ++spe_delete(subpic_ent_t ** const ppspe) ++{ ++ subpic_ent_t * const spe = *ppspe; ++ ++ if (spe == NULL) ++ return; ++ *ppspe = NULL; ++ ++ polltask_delete(&spe->pt); ++ subpic_ent_flush(spe); ++ free(spe); ++} ++ ++static int ++spe_convert(subpic_ent_t * const spe) ++{ ++ if (spe->pt != NULL) ++ pollqueue_add_task(spe->pt, 0); ++ return 0; ++} ++ ++static void ++commit_req(vout_display_sys_t * const sys, const unsigned int layer) ++{ ++ sys->commit_req[layer] = true; ++} ++ ++static void ++commit_do(vout_display_t * const vd, vout_display_sys_t * const sys) ++{ ++ int i; ++ bool flush_req = false; ++ ++ for (i = MAX_SUBPICS - 1; i >= 0; --i) ++ { ++ if (sys->commit_req[i + COMMIT_SUB]) ++ { ++ sys->commit_req[i + COMMIT_SUB] = false; ++ wl_surface_commit(sys->subplanes[i].surface); ++ flush_req = true; ++ } ++ } ++ if (sys->commit_req[COMMIT_VID]) ++ { ++ sys->commit_req[COMMIT_VID] = false; ++ wl_surface_commit(video_surface(sys)); ++ flush_req = true; ++ } ++ if (sys->commit_req[COMMIT_BKG]) ++ { ++ struct wl_surface * const bkg_surface = bkg_surface_get_lock(vd, sys); ++ if (bkg_surface != NULL) ++ { ++ wp_viewport_set_destination(sys->bkg_viewport, sys->bkg_w, sys->bkg_h); ++ wl_surface_commit(bkg_surface); ++ bkg_surface_unlock(vd, sys); ++ flush_req = true; ++ } ++ sys->commit_req[COMMIT_BKG] = false; ++ } ++ if (flush_req) ++ wl_display_flush(video_display(sys)); ++} ++ ++static void ++clear_surface_buffer(struct wl_surface * surface) ++{ ++ if (surface == NULL) ++ return; ++ wl_surface_attach(surface, NULL, 0, 0); ++ wl_surface_commit(surface); ++} ++ ++static void ++clear_all_buffers(vout_display_sys_t * const sys, const bool bkg_valid) ++{ ++ for (unsigned int i = 0; i != MAX_SUBPICS; ++i) ++ { ++ subplane_t *const plane = sys->subplanes + i; ++ spe_delete(&plane->spe_next); ++ spe_delete(&plane->spe_cur); ++ clear_surface_buffer(plane->surface); ++ } ++ ++ clear_surface_buffer(video_surface(sys)); ++ sys->video_attached = false; ++ ++ if (bkg_valid) ++ clear_surface_buffer(sys->last_embed_surface); ++ ++ subpic_ent_flush(&sys->video_spe); ++} ++ ++static void ++plane_destroy(subplane_t * const spl) ++{ ++ viewport_destroy(&spl->viewport); ++ subsurface_destroy(&spl->subsurface); ++ surface_destroy(&spl->surface); ++ // Zap all tracking vars ++ spl->trans = 0; ++ memset(&spl->src_rect, 0, sizeof(spl->src_rect)); ++ memset(&spl->dst_rect, 0, sizeof(spl->dst_rect)); ++} ++ ++static int ++plane_create(vout_display_sys_t * const sys, subplane_t * const plane, ++ struct wl_surface * const parent, ++ struct wl_surface * const above, ++ const bool sync) ++{ ++ if ((plane->surface = wl_compositor_create_surface(video_compositor(sys))) == NULL || ++ (plane->subsurface = wl_subcompositor_get_subsurface(sys->bound.subcompositor, plane->surface, parent)) == NULL || ++ (plane->viewport = wp_viewporter_get_viewport(sys->bound.viewporter, plane->surface)) == NULL) ++ return VLC_EGENERIC; ++ wl_subsurface_place_above(plane->subsurface, above); ++ if (sync) ++ wl_subsurface_set_sync(plane->subsurface); ++ else ++ wl_subsurface_set_desync(plane->subsurface); ++ wl_surface_set_input_region(plane->surface, sys->region_none); ++ return 0; ++} ++ ++static void ++unmap_all(vout_display_sys_t * const sys, const bool bkg_valid) ++{ ++ clear_all_buffers(sys, bkg_valid); ++ ++ // Free subpic resources ++ for (unsigned int i = 0; i != MAX_SUBPICS; ++i) ++ plane_destroy(sys->subplanes + i); ++ ++ plane_destroy(sys->video_plane); ++ ++ viewport_destroy(&sys->bkg_viewport); ++} ++ ++static struct wl_surface * ++bkg_surface_get_lock(vout_display_t * const vd, vout_display_sys_t * const sys) ++{ ++ if (!sys->embed) { ++ msg_Err(vd, "%s: Embed NULL", __func__); ++ return NULL; ++ } ++ ++ vlc_mutex_lock(&sys->embed->handle_lock); ++ ++ if (sys->embed->handle.wl != sys->last_embed_surface || sys->embed->handle_seq != sys->last_embed_seq) ++ { ++ msg_Warn(vd, "%s: Embed surface changed %p (%u)->%p (%u)", __func__, ++ sys->last_embed_surface, sys->last_embed_seq, ++ sys->embed->handle.wl, sys->embed->handle_seq); ++ ++ sys->last_embed_surface = sys->embed->handle.wl; ++ sys->last_embed_seq = sys->embed->handle_seq; ++ unmap_all(sys, false); ++ } ++ ++ if (sys->last_embed_surface == NULL) ++ vlc_mutex_unlock(&sys->embed->handle_lock); ++ ++ return sys->last_embed_surface; ++} ++ ++static void ++bkg_surface_unlock(vout_display_t * const vd, vout_display_sys_t * const sys) ++{ ++ VLC_UNUSED(vd); ++ vlc_mutex_unlock(&sys->embed->handle_lock); ++} ++ ++static int ++make_subpic_surfaces(vout_display_t * const vd, vout_display_sys_t * const sys) ++{ ++ unsigned int i; ++ struct wl_surface * const surface = video_surface(sys); ++ struct wl_surface * below = surface; ++ int rv; ++ ++ if (sys->subplanes[0].surface) ++ return VLC_SUCCESS; ++ ++ for (i = 0; i != MAX_SUBPICS; ++i) ++ { ++ subplane_t * const plane = sys->subplanes + i; ++ if ((rv = plane_create(sys, plane, surface, below, true)) != 0) ++ { ++ msg_Err(vd, "%s: Failed to create subpic plane %d", __func__, i); ++ return rv; ++ } ++ below = plane->surface; ++ } ++ return VLC_SUCCESS; ++} ++ ++static int ++make_background_and_video(vout_display_t * const vd, vout_display_sys_t * const sys) ++{ ++ // Build a background ++ // Use single_pixel_surface extension if we have it & want a simple ++ // single colour (black) patch ++ video_dmabuf_release_env_t * vdre = NULL; ++ struct wl_buffer * w_buffer = NULL; ++ struct wl_surface * bkg_surface = NULL; ++ ++ if (sys->bkg_viewport) ++ return VLC_SUCCESS; ++ ++#if HAVE_WAYLAND_SINGLE_PIXEL_BUFFER ++ if (sys->bound.single_pixel_buffer_manager_v1 && !sys->chequerboard) ++ { ++ w_buffer = wp_single_pixel_buffer_manager_v1_create_u32_rgba_buffer( ++ sys->bound.single_pixel_buffer_manager_v1, ++ 0, 0, 0, UINT32_MAX); // R, G, B, A ++ vdre = vdre_new_null(); ++ } ++ else ++#endif ++ { ++ // Buffer width & height - not display ++ const unsigned int width = sys->chequerboard ? 640 : 32; ++ const unsigned int height = sys->chequerboard ? 480 : 32; ++ const unsigned int stride = width * 4; ++ struct dmabuf_h * const dh = picpool_get(sys->subpic_pool, stride * height); ++ const AVDRMFrameDescriptor dfd = { ++ .nb_objects = 1, ++ .objects = {{ ++ .fd = dmabuf_fd(dh), ++ .size = dmabuf_size(dh)}}, ++ .nb_layers = 1, ++ .layers = {{ ++ .format = DRM_FORMAT_XRGB8888, ++ .nb_planes = 1, ++ .planes = {{.pitch = stride}} ++ }} ++ }; ++ ++ if (dh == NULL) ++ { ++ msg_Err(vd, "Failed to get DmaBuf for background"); ++ goto error; ++ } ++ ++ vdre = vdre_new_dh(dh, sys->pollq); ++ ++ dmabuf_write_start(dh); ++ if (sys->chequerboard) ++ chequerboard(dmabuf_map(dh), stride, width, height); ++ else ++ fill_uniform(dmabuf_map(dh), stride, width, height, 0xff000000); ++ dmabuf_write_end(dh); ++ ++ w_buffer = dfd_make_buffer(vd, sys, dmabuf_is_fake(dh), &dfd, width, height, 0); ++ } ++ if (!w_buffer || !vdre) ++ { ++ msg_Err(vd, "Failed to create background buffer"); ++ goto error; ++ } ++ ++ if ((bkg_surface = bkg_surface_get_lock(vd, sys)) == NULL) ++ goto error; ++ ++ sys->bkg_viewport = wp_viewporter_get_viewport(sys->bound.viewporter, bkg_surface); ++ if (sys->bkg_viewport == NULL) ++ { ++ msg_Err(vd, "Failed to create background viewport"); ++ goto err_unlock; ++ } ++ ++#if CHECK_VDRE_COUNTS ++ vdre_add_check(vdre, &sys->vdre_check_bkg); ++#endif ++ vdre_eq_ref(vdre, sys->eq); ++ wl_buffer_add_listener(w_buffer, &vdre_buffer_listener, vdre); ++ wl_surface_attach(bkg_surface, w_buffer, 0, 0); ++ vdre = NULL; ++ w_buffer = NULL; ++ ++ wp_viewport_set_destination(sys->bkg_viewport, sys->bkg_w, sys->bkg_h); ++ wl_surface_set_opaque_region(bkg_surface, sys->region_all); ++ ++ wl_surface_damage(bkg_surface, 0, 0, INT32_MAX, INT32_MAX); ++ ++ if (plane_create(sys, sys->video_plane, bkg_surface, bkg_surface, false) != 0) ++ { ++ msg_Err(vd, "Failed to create video plane"); ++ goto err_unlock; ++ } ++ ++ wl_surface_set_opaque_region(sys->video_plane->surface, sys->region_all); ++ ++ commit_req(sys, COMMIT_BKG); ++ ++ bkg_surface_unlock(vd, sys); ++ ++ return VLC_SUCCESS; ++ ++err_unlock: ++ bkg_surface_unlock(vd, sys); ++error: ++ buffer_destroy(&w_buffer); ++ vdre_delete(&vdre); ++ return VLC_ENOMEM; ++} ++ ++// Get tranform & adjusted source coords for orientation ++static enum wl_output_transform ++transform_from_fmt(const video_format_t * const fmt, vout_display_place_t * const s) ++{ ++ const int rx_offset = fmt->i_width - (fmt->i_visible_width + fmt->i_x_offset); ++ const int by_offset = fmt->i_height - (fmt->i_visible_height + fmt->i_y_offset); ++ ++ switch (fmt->orientation) ++ { ++ case ORIENT_ROTATED_90: // ORIENT_RIGHT_TOP, ++ *s = (vout_display_place_t){ ++ .x = by_offset, ++ .y = fmt->i_x_offset, ++ .width = fmt->i_visible_height, ++ .height = fmt->i_visible_width}; ++ return WL_OUTPUT_TRANSFORM_90; ++ ++ case ORIENT_ROTATED_180: // ORIENT_BOTTOM_RIGHT, ++ *s = (vout_display_place_t){ ++ .x = by_offset, ++ .y = rx_offset, ++ .width = fmt->i_visible_width, ++ .height = fmt->i_visible_height}; ++ return WL_OUTPUT_TRANSFORM_180; ++ ++ case ORIENT_ROTATED_270: // ORIENT_LEFT_BOTTOM, ++ *s = (vout_display_place_t){ ++ .x = fmt->i_y_offset, ++ .y = rx_offset, ++ .width = fmt->i_visible_height, ++ .height = fmt->i_visible_width}; ++ return WL_OUTPUT_TRANSFORM_270; ++ ++ case ORIENT_HFLIPPED: // ORIENT_TOP_RIGHT, ++ *s = (vout_display_place_t){ ++ .x = rx_offset, ++ .y = fmt->i_y_offset, ++ .width = fmt->i_visible_width, ++ .height = fmt->i_visible_height}; ++ return WL_OUTPUT_TRANSFORM_FLIPPED; ++ ++ case ORIENT_VFLIPPED: // ORIENT_BOTTOM_LEFT, ++ *s = (vout_display_place_t){ ++ .x = fmt->i_x_offset, ++ .y = by_offset, ++ .width = fmt->i_visible_width, ++ .height = fmt->i_visible_height}; ++ return WL_OUTPUT_TRANSFORM_FLIPPED_180; ++ ++ case ORIENT_TRANSPOSED: // ORIENT_LEFT_TOP, ++ *s = (vout_display_place_t){ ++ .x = fmt->i_y_offset, ++ .y = fmt->i_x_offset, ++ .width = fmt->i_visible_height, ++ .height = fmt->i_visible_width}; ++ return WL_OUTPUT_TRANSFORM_FLIPPED_90; ++ ++ case ORIENT_ANTI_TRANSPOSED: // ORIENT_RIGHT_BOTTOM, ++ *s = (vout_display_place_t){ ++ .x = rx_offset, ++ .y = by_offset, ++ .width = fmt->i_visible_height, ++ .height = fmt->i_visible_width}; ++ return WL_OUTPUT_TRANSFORM_FLIPPED_270; ++ ++ case ORIENT_NORMAL: // ORIENT_TOP_LEFT, ++ default: ++ *s = (vout_display_place_t){ ++ .x = fmt->i_x_offset, ++ .y = fmt->i_y_offset, ++ .width = fmt->i_visible_width, ++ .height = fmt->i_visible_height}; ++ return WL_OUTPUT_TRANSFORM_NORMAL; ++ } ++} ++ ++static void ++place_rects(vout_display_t * const vd, ++ const vout_display_cfg_t * const cfg) ++{ ++ vout_display_sys_t * const sys = vd->sys; ++ ++ vout_display_PlacePicture(&sys->video_spe.dst_rect, &vd->source, cfg, true); ++ sys->video_spe.trans = transform_from_fmt(&vd->fmt, &sys->video_spe.src_rect); ++} ++ ++static void ++plane_set_rect(vout_display_sys_t * const sys, subplane_t * const plane, const subpic_ent_t * const spe, ++ const unsigned int commit_this, const unsigned int commit_parent) ++{ ++ if (spe->trans != plane->trans) ++ { ++ wl_surface_set_buffer_transform(plane->surface, spe->trans); ++ commit_req(sys, commit_this); ++ } ++ if (!place_eq(spe->src_rect, plane->src_rect)) ++ { ++ wp_viewport_set_source(plane->viewport, ++ wl_fixed_from_int(spe->src_rect.x), wl_fixed_from_int(spe->src_rect.y), ++ wl_fixed_from_int(spe->src_rect.width), wl_fixed_from_int(spe->src_rect.height)); ++ commit_req(sys, commit_this); ++ } ++ if (!place_xy_eq(spe->dst_rect, plane->dst_rect)) ++ { ++ wl_subsurface_set_position(plane->subsurface, spe->dst_rect.x, spe->dst_rect.y); ++ commit_req(sys, commit_this); ++ } ++ if (!place_wh_eq(spe->dst_rect, plane->dst_rect)) ++ { ++ wp_viewport_set_destination(plane->viewport, spe->dst_rect.width, spe->dst_rect.height); ++ commit_req(sys, commit_parent); // Subsurface pos needs parent commit (video) ++ } ++ ++ plane->trans = spe->trans; ++ plane->src_rect = spe->src_rect; ++ plane->dst_rect = spe->dst_rect; ++} ++ ++static void ++set_video_viewport(vout_display_sys_t * const sys) ++{ ++ if (!sys->video_attached) ++ return; ++ ++ plane_set_rect(sys, sys->video_plane, &sys->video_spe, COMMIT_VID, COMMIT_BKG); ++} ++ ++static void Prepare(vout_display_t *vd, picture_t *pic, subpicture_t *subpic) ++{ ++ vout_display_sys_t * const sys = vd->sys; ++ unsigned int n = 0; ++ ++#if TRACE_ALL ++ msg_Dbg(vd, "<<< %s: Surface: %p", __func__, sys->embed->handle.wl); ++#endif ++ ++ subpic_ent_flush(&sys->video_spe); // If somehow we have a buffer here - avoid leaking ++ if (drmu_format_vlc_to_drm_prime(&pic->format, NULL) == 0) ++ copy_subpic_to_w_buffer(vd, sys, pic, 0xff, &sys->video_spe.vdre, &sys->video_spe.wb); ++ else ++ do_display_dmabuf(vd, sys, pic, &sys->video_spe.vdre, &sys->video_spe.wb); ++ wl_display_flush(video_display(sys)); // Kick off any work required by Wayland ++ ++ // Attempt to import the subpics ++ for (const subpicture_t * spic = subpic; spic != NULL; spic = spic->p_next) ++ { ++ for (const subpicture_region_t *sreg = spic->p_region; sreg != NULL; sreg = sreg->p_next) ++ { ++ subplane_t * const plane = sys->subplanes + n; ++ ++ if (plane->spe_next != NULL) ++ { ++ if (!spe_changed(plane->spe_next, sreg)) ++ spe_update_rect(plane->spe_next, sys, spic, sreg); ++ // else if changed ignore as we are already doing stuff ++ } ++ else ++ { ++ if (!spe_changed(plane->spe_cur, sreg)) ++ spe_update_rect(plane->spe_cur, sys, spic, sreg); ++ else ++ { ++ plane->spe_next = spe_new(vd, sys, spic, sreg); ++ spe_convert(plane->spe_next); ++ } ++ } ++ ++ if (++n == MAX_SUBPICS) ++ goto subpics_done; ++ } ++ } ++subpics_done: ++ ++ // Clear any other entries ++ for (; n != MAX_SUBPICS; ++n) { ++ subplane_t * const plane = sys->subplanes + n; ++ if (plane->spe_next == NULL && spe_changed(plane->spe_cur, NULL)) ++ plane->spe_next = spe_new(vd, sys, NULL, NULL); ++ } ++ ++#if TRACE_ALL ++ msg_Dbg(vd, ">>> %s: Surface: %p", __func__, sys->embed->handle.wl); ++#endif ++} ++ ++static void Display(vout_display_t *vd, picture_t *pic, subpicture_t *subpic) ++{ ++ vout_display_sys_t * const sys = vd->sys; ++ ++#if TRACE_ALL ++ msg_Dbg(vd, "<<< %s: Surface: %p", __func__, sys->embed->handle.wl); ++#endif ++ ++ // Check we have a surface to put the video on ++ if (bkg_surface_get_lock(vd, sys) == NULL) ++ { ++ msg_Warn(vd, "%s: No background surface", __func__); ++ goto done; ++ } ++ bkg_surface_unlock(vd, sys); ++ ++ if (make_background_and_video(vd, sys) != 0) ++ { ++ msg_Warn(vd, "%s: Make background fail", __func__); ++ goto done; ++ } ++ make_subpic_surfaces(vd, sys); ++ ++ for (unsigned int i = 0; i != MAX_SUBPICS; ++i) ++ { ++ subplane_t * const plane = sys->subplanes + i; ++ subpic_ent_t * spe = plane->spe_cur; ++ ++ if (plane->spe_next && atomic_load(&plane->spe_next->ready)) ++ { ++ spe_delete(&plane->spe_cur); ++ spe = plane->spe_cur = plane->spe_next; ++ plane->spe_next = NULL; ++ subpic_ent_attach(plane->surface, spe, sys->eq); ++ commit_req(sys, COMMIT_SUB + i); ++ } ++ ++ if (!spe_no_pic(spe)) ++ plane_set_rect(sys, plane, spe, COMMIT_SUB + i, COMMIT_VID); ++ } ++ ++ if (!sys->video_spe.wb) ++ { ++ msg_Warn(vd, "Display called but no prepared pic buffer"); ++ } ++ else ++ { ++ subpic_ent_attach(video_surface(sys), &sys->video_spe, sys->eq); ++ sys->video_attached = true; ++ commit_req(sys, COMMIT_VID); ++ } ++ ++ set_video_viewport(sys); ++ ++ commit_do(vd, sys); ++ ++done: ++ if (subpic) ++ subpicture_Delete(subpic); ++ picture_Release(pic); ++ ++#if TRACE_ALL ++ msg_Dbg(vd, ">>> %s: Surface: %p", __func__, sys->embed->handle.wl); ++#endif ++} ++ ++static void ResetPictures(vout_display_t *vd) ++{ ++ vout_display_sys_t *sys = vd->sys; ++ ++#if TRACE_ALL ++ msg_Dbg(vd, "<<< %s", __func__); ++#endif ++ ++ kill_pool(sys); ++ ++#if TRACE_ALL ++ msg_Dbg(vd, ">>> %s", __func__); ++#endif ++} ++ ++static int Control(vout_display_t *vd, int query, va_list ap) ++{ ++ vout_display_sys_t * const sys = vd->sys; ++ ++#if TRACE_ALL ++ msg_Dbg(vd, "<<< %s: Query=%d", __func__, query); ++#endif ++ ++ switch (query) ++ { ++ case VOUT_DISPLAY_RESET_PICTURES: ++ { ++ vout_display_place_t place; ++ video_format_t src; ++ ++ assert(sys->video_plane->viewport == NULL); ++ ++ vout_display_PlacePicture(&place, &vd->source, vd->cfg, false); ++ video_format_ApplyRotation(&src, &vd->source); ++ ++ vd->fmt.i_width = src.i_width * place.width ++ / src.i_visible_width; ++ vd->fmt.i_height = src.i_height * place.height ++ / src.i_visible_height; ++ vd->fmt.i_visible_width = place.width; ++ vd->fmt.i_visible_height = place.height; ++ vd->fmt.i_x_offset = src.i_x_offset * place.width ++ / src.i_visible_width; ++ vd->fmt.i_y_offset = src.i_y_offset * place.height ++ / src.i_visible_height; ++ ResetPictures(vd); ++ break; ++ } ++ ++ case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: ++ case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED: ++ case VOUT_DISPLAY_CHANGE_ZOOM: ++ case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: ++ case VOUT_DISPLAY_CHANGE_SOURCE_CROP: ++ { ++ const vout_display_cfg_t *cfg; ++ ++ if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT ++ || query == VOUT_DISPLAY_CHANGE_SOURCE_CROP) ++ { ++ cfg = vd->cfg; ++ } ++ else ++ { ++ cfg = va_arg(ap, const vout_display_cfg_t *); ++ } ++ ++ place_rects(vd, cfg); ++ ++ set_video_viewport(sys); ++ ++ if (sys->bkg_viewport != NULL && (cfg->display.width != sys->bkg_w || cfg->display.height != sys->bkg_h)) ++ { ++ msg_Dbg(vd, "Resize background: %dx%d", cfg->display.width, cfg->display.height); ++ commit_req(sys, COMMIT_BKG); ++ } ++ sys->bkg_w = cfg->display.width; ++ sys->bkg_h = cfg->display.height; ++ commit_do(vd, sys); ++ break; ++ } ++ default: ++ msg_Err(vd, "unknown request %d", query); ++ return VLC_EGENERIC; ++ } ++ ++#if TRACE_ALL ++ msg_Dbg(vd, ">>> %s: Surface: %p", __func__, sys->embed->handle.wl); ++#endif ++ return VLC_SUCCESS; ++} ++ ++static void linux_dmabuf_v1_listener_format(void *data, ++ struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, ++ uint32_t format) ++{ ++ // Superceeded by _modifier ++ vout_display_t * const vd = data; ++ vout_display_sys_t * const sys = vd->sys; ++ (void)zwp_linux_dmabuf_v1; ++#if TRACE_ALL ++ msg_Dbg(vd, "%s[%p], %.4s", __func__, (void*)vd, (const char *)&format); ++#endif ++ fmt_list_add(&sys->dmabuf_fmts, format, DRM_FORMAT_MOD_LINEAR, 0); ++} ++ ++static void ++linux_dmabuf_v1_listener_modifier(void *data, ++ struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, ++ uint32_t format, ++ uint32_t modifier_hi, ++ uint32_t modifier_lo) ++{ ++ vout_display_t * const vd = data; ++ vout_display_sys_t * const sys = vd->sys; ++ (void)zwp_linux_dmabuf_v1; ++#if TRACE_ALL ++ msg_Dbg(vd, "%s[%p], %.4s %08x%08x", __func__, (void*)vd, (const char *)&format, modifier_hi, modifier_lo); ++#endif ++ fmt_list_add(&sys->dmabuf_fmts, format, modifier_lo | ((uint64_t)modifier_hi << 32), 0); ++} ++ ++static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_v1_listener = { ++ .format = linux_dmabuf_v1_listener_format, ++ .modifier = linux_dmabuf_v1_listener_modifier, ++}; ++ ++static void shm_listener_format(void *data, ++ struct wl_shm *shm, ++ uint32_t format) ++{ ++ vout_display_t * const vd = data; ++ vout_display_sys_t * const sys = vd->sys; ++ (void)shm; ++ ++ if (format == 0) ++ format = DRM_FORMAT_ARGB8888; ++ else if (format == 1) ++ format = DRM_FORMAT_XRGB8888; ++ ++#if TRACE_ALL ++ msg_Dbg(vd, "%s[%p], %.4s", __func__, (void*)vd, (const char *)&format); ++#endif ++ fmt_list_add(&sys->shm_fmts, format, DRM_FORMAT_MOD_LINEAR, 0); ++} ++ ++static const struct wl_shm_listener shm_listener = { ++ .format = shm_listener_format, ++}; ++ ++ ++static void w_bound_add(vout_display_t * const vd, w_bound_t * const b, ++ struct wl_registry * const registry, ++ const uint32_t name, const char *const iface, const uint32_t vers) ++{ ++#if TRACE_ALL ++ msg_Dbg(vd, "global %3"PRIu32": %s version %"PRIu32, name, iface, vers); ++#endif ++ if (strcmp(iface, wl_subcompositor_interface.name) == 0) ++ b->subcompositor = wl_registry_bind(registry, name, &wl_subcompositor_interface, 1); ++ else ++ if (strcmp(iface, wl_shm_interface.name) == 0) ++ { ++ b->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); ++ wl_shm_add_listener(b->shm, &shm_listener, vd); ++ } ++ else ++ if (strcmp(iface, wp_viewporter_interface.name) == 0) ++ b->viewporter = wl_registry_bind(registry, name, &wp_viewporter_interface, 1); ++ else ++ if (!strcmp(iface, wl_compositor_interface.name)) ++ { ++ if (vers >= 4) ++ b->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4); ++ else ++ msg_Warn(vd, "Interface %s wanted v 4 got v %d", wl_compositor_interface.name, vers); ++ } ++ else ++ if (!vd->sys->use_shm && strcmp(iface, zwp_linux_dmabuf_v1_interface.name) == 0) ++ { ++ if (vers >= 3) ++ { ++ b->linux_dmabuf_v1 = wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, 3); ++ zwp_linux_dmabuf_v1_add_listener(b->linux_dmabuf_v1, &linux_dmabuf_v1_listener, vd); ++ } ++ else ++ msg_Warn(vd, "Interface %s wanted v 3 got v %d", zwp_linux_dmabuf_v1_interface.name, vers); ++ } ++#if HAVE_WAYLAND_SINGLE_PIXEL_BUFFER ++ else ++ if (strcmp(iface, wp_single_pixel_buffer_manager_v1_interface.name) == 0) ++ b->single_pixel_buffer_manager_v1 = wl_registry_bind(registry, name, &wp_single_pixel_buffer_manager_v1_interface, 1); ++#endif ++} ++ ++static void w_bound_destroy(w_bound_t * const b) ++{ ++ if (b->viewporter != NULL) ++ wp_viewporter_destroy(b->viewporter); ++ if (b->linux_dmabuf_v1 != NULL) ++ zwp_linux_dmabuf_v1_destroy(b->linux_dmabuf_v1); ++ if (b->subcompositor != NULL) ++ wl_subcompositor_destroy(b->subcompositor); ++ if (b->compositor != NULL) ++ wl_compositor_destroy(b->compositor); ++ if (b->shm != NULL) ++ wl_shm_destroy(b->shm); ++#if HAVE_WAYLAND_SINGLE_PIXEL_BUFFER ++ if (b->single_pixel_buffer_manager_v1) ++ wp_single_pixel_buffer_manager_v1_destroy(b->single_pixel_buffer_manager_v1); ++#endif ++ memset(b, 0, sizeof(*b)); ++} ++ ++static void registry_global_cb(void *data, struct wl_registry *registry, ++ uint32_t name, const char *iface, uint32_t vers) ++{ ++ vout_display_t * const vd = data; ++ vout_display_sys_t * const sys = vd->sys; ++ ++ w_bound_add(vd, &sys->bound, registry, name, iface, vers); ++} ++ ++static void registry_global_remove_cb(void *data, struct wl_registry *registry, ++ uint32_t name) ++{ ++ vout_display_t *vd = data; ++ ++ msg_Dbg(vd, "global remove %3"PRIu32, name); ++ (void) registry; ++} ++ ++static const struct wl_registry_listener registry_cbs = ++{ ++ registry_global_cb, ++ registry_global_remove_cb, ++}; ++ ++struct registry_scan_bounce_env { ++ struct wl_registry * registry; ++ eq_env_t * const eq; ++ vout_display_t * const vd; ++}; ++ ++// Only safe place to add a listener is on pollq thread ++static void ++registry_scan_bounce_cb(void * v, short revents) ++{ ++ struct registry_scan_bounce_env * rsbe = v; ++ (void)revents; ++ rsbe->registry = wl_display_get_registry(eq_wrapper(rsbe->eq)), ++ wl_registry_add_listener(rsbe->registry, ®istry_cbs, rsbe->vd); ++} ++ ++// N.B. Having got the registry with a wrapped display ++// by default everything we do with the newly bound interfaces will turn ++// up on the wrapped queue ++ ++static int ++registry_scan(vout_display_t * const vd, vout_display_sys_t * const sys) ++{ ++ struct registry_scan_bounce_env rsbe = { ++ .registry = NULL, ++ .eq = sys->eq, ++ .vd = vd ++ }; ++ ++ pollqueue_callback_once(rsbe.eq->pq, registry_scan_bounce_cb, &rsbe); ++ ++ eventq_sync(rsbe.eq); ++ // Registry callback provokes shm & fmt callbacks so another sync needed ++ eventq_sync(rsbe.eq); ++ ++ if (rsbe.registry == NULL) ++ return -1; ++ ++ wl_registry_destroy(rsbe.registry); ++ return 0; ++} ++ ++static const drmu_vlc_fmt_info_t * ++find_fmt_fallback(vout_display_t * const vd, const fmt_list_t * const flist, const vlc_fourcc_t * fallback) ++{ ++ const drmu_vlc_fmt_info_t * fmti_best = NULL; ++ int pri_best = INT_MAX; ++ ++ for (; *fallback != 0; ++fallback) ++ { ++ const video_frame_format_t vf = {.i_chroma = *fallback}; ++ const drmu_vlc_fmt_info_t * fmti = NULL; ++ ++ msg_Dbg(vd, "Try %s", drmu_log_fourcc(*fallback)); ++ ++ for (fmti = drmu_vlc_fmt_info_find_vlc(&vf); ++ fmti != NULL; ++ fmti = drmu_vlc_fmt_info_find_vlc_next(&vf, fmti)) ++ { ++ const int pri = fmt_list_find(flist, fmti); ++ msg_Dbg(vd, "Try %s -> %s %"PRIx64": %d", drmu_log_fourcc(*fallback), ++ drmu_log_fourcc(drmu_vlc_fmt_info_drm_pixelformat(fmti)), ++ drmu_vlc_fmt_info_drm_modifier(fmti), pri); ++ if (pri >= 0 && pri < pri_best) ++ { ++ fmti_best = fmti; ++ pri_best = pri; ++ ++ // If we've got pri 0 then might as well stop now ++ if (pri == 0) ++ return fmti_best; ++ } ++ } ++ } ++ ++ return fmti_best; ++} ++ ++static void Close(vlc_object_t *obj) ++{ ++ vout_display_t * const vd = (vout_display_t *)obj; ++ vout_display_sys_t * const sys = vd->sys; ++ ++ msg_Dbg(vd, "<<< %s", __func__); ++ ++ if (sys == NULL) ++ return; ++ ++ if (sys->embed == NULL) ++ goto no_window; ++ ++ if (bkg_surface_get_lock(vd, sys) != NULL) ++ { ++ unmap_all(sys, true); ++ bkg_surface_unlock(vd, sys); ++ } ++ ++ region_destroy(&sys->region_all); ++ region_destroy(&sys->region_none); ++ ++ pollqueue_unref(&sys->speq); ++ ++ w_bound_destroy(&sys->bound); ++ ++ eventq_sync(sys->eq); ++ ++ if (eq_finish(&sys->eq) != 0) ++ msg_Err(vd, "Failed to reclaim all buffers on close"); ++ ++ pollqueue_unref(&sys->pollq); ++ ++ vout_display_DeleteWindow(vd, sys->embed); ++ sys->embed = NULL; ++ ++ kill_pool(sys); ++ picpool_unref(&sys->subpic_pool); ++ ++ free(sys->subpic_chromas); ++ ++no_window: ++ fmt_list_uninit(&sys->dmabuf_fmts); ++ fmt_list_uninit(&sys->shm_fmts); ++ ++#if CHECK_VDRE_COUNTS ++ msg_Info(vd, "%s: vdre_check_bkg: %d", __func__, atomic_load(&sys->vdre_check_bkg)); ++ msg_Info(vd, "%s: vdre_check_fg: %d", __func__, atomic_load(&sys->vdre_check_fg)); ++#endif ++ ++ free(sys); ++ ++ msg_Dbg(vd, ">>> %s", __func__); ++} ++ ++static int Open(vlc_object_t *obj) ++{ ++ vout_display_t * const vd = (vout_display_t *)obj; ++ vout_display_sys_t *sys; ++ const drmu_vlc_fmt_info_t * pic_fmti; ++ fmt_list_t * flist = NULL; ++ ++ if (var_InheritBool(vd, WL_DMABUF_DISABLE_NAME)) ++ return VLC_EGENERIC; ++ ++ sys = calloc(1, sizeof(*sys)); ++ if (unlikely(sys == NULL)) ++ return VLC_ENOMEM; ++ ++ vd->sys = sys; ++ if (fmt_list_init(&sys->dmabuf_fmts, 128)) { ++ msg_Err(vd, "Failed to allocate dmabuf format list!"); ++ goto error; ++ } ++ if (fmt_list_init(&sys->shm_fmts, 32)) { ++ msg_Err(vd, "Failed to allocate shm format list!"); ++ goto error; ++ } ++ ++ sys->use_shm = var_InheritBool(vd, WL_DMABUF_USE_SHM_NAME); ++ sys->chequerboard = var_InheritBool(vd, WL_DMABUF_CHEQUERBOARD_NAME); ++ ++ /* Get window */ ++ sys->embed = vout_display_NewWindow(vd, VOUT_WINDOW_TYPE_WAYLAND); ++ if (sys->embed == NULL) { ++ msg_Dbg(vd, "Cannot create window - probably not using Wayland"); ++ goto error; ++ } ++ sys->last_embed_surface = sys->embed->handle.wl; ++ sys->last_embed_seq = sys->embed->handle_seq; ++ ++ msg_Info(vd, "<<< %s: %s %dx%d(%dx%d @ %d,%d %d/%d), cfg.display: %dx%d, source: %dx%d(%dx%d @ %d,%d %d/%d)", __func__, ++ drmu_log_fourcc(vd->fmt.i_chroma), vd->fmt.i_width, vd->fmt.i_height, ++ vd->fmt.i_visible_width, vd->fmt.i_visible_height, vd->fmt.i_x_offset, vd->fmt.i_y_offset, ++ vd->fmt.i_sar_num, vd->fmt.i_sar_den, ++ vd->cfg->display.width, vd->cfg->display.height, ++ vd->source.i_width, vd->source.i_height, ++ vd->source.i_visible_width, vd->source.i_visible_height, vd->source.i_x_offset, vd->source.i_y_offset, ++ vd->source.i_sar_num, vd->source.i_sar_den); ++ ++ if ((sys->pollq = pollqueue_new()) == NULL || ++ (sys->speq = pollqueue_new()) == NULL) ++ { ++ msg_Err(vd, "Failed to create pollqueues"); ++ goto error; ++ } ++ if ((sys->eq = eq_new(video_display(sys), sys->pollq)) == NULL) ++ { ++ msg_Err(vd, "Failed to create event Q"); ++ goto error; ++ } ++ ++ if (registry_scan(vd, sys) != 0) ++ { ++ msg_Err(vd, "Cannot get registry for display"); ++ goto error; ++ } ++ ++ if (sys->bound.compositor == NULL) { ++ msg_Warn(vd, "Interface %s missing", wl_compositor_interface.name); ++ goto error; ++ } ++ if (sys->bound.subcompositor == NULL) { ++ msg_Warn(vd, "Interface %s missing", wl_subcompositor_interface.name); ++ goto error; ++ } ++ if (sys->bound.viewporter == NULL) { ++ msg_Warn(vd, "Interface %s missing", wp_viewporter_interface.name); ++ goto error; ++ } ++ if (!sys->use_shm && sys->bound.linux_dmabuf_v1 == NULL) { ++ msg_Warn(vd, "Interface %s missing", zwp_linux_dmabuf_v1_interface.name); ++ goto error; ++ } ++ ++ fmt_list_sort(&sys->dmabuf_fmts); ++ fmt_list_sort(&sys->shm_fmts); ++ flist = sys->use_shm ? &sys->shm_fmts : &sys->dmabuf_fmts; ++ ++ // Check PIC DRM format here ++ if ((pic_fmti = drmu_vlc_fmt_info_find_vlc(&vd->fmt)) == NULL || ++ fmt_list_find(flist, pic_fmti) < 0) ++ { ++ static const vlc_fourcc_t fallback2[] = { ++ VLC_CODEC_I420, ++ VLC_CODEC_RGB32, ++ 0 ++ }; ++ ++ msg_Warn(vd, "Could not find %s mod %#"PRIx64" in supported formats", ++ drmu_log_fourcc(drmu_vlc_fmt_info_drm_pixelformat(pic_fmti)), ++ drmu_vlc_fmt_info_drm_modifier(pic_fmti)); ++ ++ if ((pic_fmti = find_fmt_fallback(vd, flist, ++ vlc_fourcc_IsYUV(vd->fmt.i_chroma) ? ++ vlc_fourcc_GetYUVFallback(vd->fmt.i_chroma) : ++ vlc_fourcc_GetRGBFallback(vd->fmt.i_chroma))) == NULL && ++ (pic_fmti = find_fmt_fallback(vd, flist, fallback2)) == NULL) ++ { ++ msg_Warn(vd, "Failed to find any usable fallback format"); ++ goto error; ++ } ++ } ++ ++ { ++ static vlc_fourcc_t const tryfmts[] = { ++ VLC_CODEC_RGBA, ++ VLC_CODEC_BGRA, ++ VLC_CODEC_ARGB, ++ VLC_CODEC_VUYA, ++ VLC_CODEC_YUVA, ++ }; ++ unsigned int n = 0; ++ ++ if ((sys->subpic_chromas = calloc(ARRAY_SIZE(tryfmts) + 1, sizeof(vlc_fourcc_t))) == NULL) ++ goto error; ++ for (unsigned int i = 0; i != ARRAY_SIZE(tryfmts); ++i) ++ { ++ const video_frame_format_t vf = {.i_chroma = tryfmts[i]}; ++ if (fmt_list_find(flist, drmu_vlc_fmt_info_find_vlc(&vf)) >= 0) ++ sys->subpic_chromas[n++] = tryfmts[i]; ++ } ++ ++ if (n == 0) ++ msg_Warn(vd, "No compatible subpic formats found"); ++ } ++ ++ { ++ struct dmabufs_ctl *dbsc = sys->use_shm ? dmabufs_shm_new() : dmabufs_ctl_new(); ++ if (dbsc == NULL) ++ { ++ msg_Err(vd, "Failed to create dmabuf ctl"); ++ goto error; ++ } ++ sys->subpic_pool = picpool_new(dbsc); ++ dmabufs_ctl_unref(&dbsc); ++ if (sys->subpic_pool == NULL) ++ { ++ msg_Err(vd, "Failed to create picpool"); ++ goto error; ++ } ++ } ++ ++ sys->bkg_w = vd->cfg->display.width; ++ sys->bkg_h = vd->cfg->display.height; ++ ++ sys->region_all = wl_compositor_create_region(video_compositor(sys)); ++ wl_region_add(sys->region_all, 0, 0, INT32_MAX, INT32_MAX); ++ sys->region_none = wl_compositor_create_region(video_compositor(sys)); ++ wl_region_add(sys->region_all, 0, 0, 0, 0); ++ ++ vd->fmt.i_chroma = drmu_vlc_fmt_info_vlc_chroma(pic_fmti); ++ drmu_vlc_fmt_info_vlc_rgb_masks(pic_fmti, &vd->fmt.i_rmask, &vd->fmt.i_gmask, &vd->fmt.i_bmask); ++ ++ place_rects(vd, vd->cfg); ++ ++ vd->info.has_pictures_invalid = false; ++ vd->info.subpicture_chromas = sys->subpic_chromas; ++ ++ vd->pool = vd_dmabuf_pool; ++ vd->prepare = Prepare; ++ vd->display = Display; ++ vd->control = Control; ++ ++ msg_Dbg(vd, ">>> %s: OK: %.4s (%#x/%#x/%#x)", __func__, ++ (char*)&vd->fmt.i_chroma, ++ vd->fmt.i_rmask, vd->fmt.i_gmask, vd->fmt.i_bmask); ++ return VLC_SUCCESS; ++ ++error: ++ Close(obj); ++ msg_Dbg(vd, ">>> %s: ERROR", __func__); ++ return VLC_EGENERIC; ++} ++ ++vlc_module_begin() ++ set_shortname(N_("WL DMABUF")) ++ set_description(N_("Wayland dmabuf video output")) ++ set_category(CAT_VIDEO) ++ set_subcategory(SUBCAT_VIDEO_VOUT) ++ set_capability("vout display", 310) ++ set_callbacks(Open, Close) ++ add_shortcut("wl-dmabuf") ++ add_bool(WL_DMABUF_DISABLE_NAME, false, WL_DMABUF_DISABLE_TEXT, WL_DMABUF_DISABLE_LONGTEXT, false) ++ add_bool(WL_DMABUF_USE_SHM_NAME, false, WL_DMABUF_USE_SHM_TEXT, WL_DMABUF_USE_SHM_LONGTEXT, false) ++ add_bool(WL_DMABUF_CHEQUERBOARD_NAME, false, WL_DMABUF_CHEQUERBOARD_TEXT, WL_DMABUF_CHEQUERBOARD_LONGTEXT, false) ++vlc_module_end() diff --git a/pi-util/README.txt b/pi-util/README.txt new file mode 100644 index 0000000000..59a8655955 @@ -29149,10 +30043,10 @@ index 0000000000..54674fa8d0 +fi diff --git a/pi-util/genpatch.sh b/pi-util/genpatch.sh new file mode 100755 -index 0000000000..13265447cd +index 0000000000..f7c17bc4e7 --- /dev/null +++ b/pi-util/genpatch.sh -@@ -0,0 +1,68 @@ +@@ -0,0 +1,70 @@ +set -e + +NOTAG= @@ -29211,11 +30105,13 @@ index 0000000000..13265447cd + src/audio_output \ + src/input \ + src/misc \ ++ src/video_output \ + > $DIFFBASE-001-rpi.patch +git diff $REFNAME -- modules/video_chroma/chain.c > $DIFFBASE-002-chain.patch +git diff $REFNAME -- bin/vlc.c > $DIFFBASE-003-vlc.patch +git diff $REFNAME -- modules/video_output/caca.c > $DIFFBASE-004-caca.patch +git diff $REFNAME -- modules/gui/qt/components/interface_widgets.* > $DIFFBASE-005-qt-wayland.patch ++git diff $REFNAME -- modules/gui/qt/components/controller.cpp > $DIFFBASE-006-qt-fullscreen.patch +cd $DSTDIR +zip -m $ZIPNAME $PATCHNAME-*.patch + @@ -29493,7 +30389,7 @@ index 69488a681c..57858a8020 100644 vlc_join( p_owner->thread, NULL ); diff --git a/src/misc/fourcc.c b/src/misc/fourcc.c -index ebb7707a34..cd202ed74a 100644 +index ebb7707a34..0bec4e75da 100644 --- a/src/misc/fourcc.c +++ b/src/misc/fourcc.c @@ -416,6 +416,10 @@ static const vlc_fourcc_t p_D3D11_OPAQUE_10B_fallback[] = { @@ -29531,7 +30427,7 @@ index ebb7707a34..cd202ed74a 100644 0, }; -@@ -762,11 +776,19 @@ static const struct +@@ -762,11 +776,20 @@ static const struct { { VLC_CODEC_VDPAU_VIDEO_420, VLC_CODEC_VDPAU_VIDEO_422, VLC_CODEC_VDPAU_VIDEO_444, VLC_CODEC_VDPAU_OUTPUT }, FAKE_FMT() }, @@ -29550,6 +30446,7 @@ index ebb7707a34..cd202ed74a 100644 + { { VLC_CODEC_DRM_PRIME_I420, VLC_CODEC_DRM_PRIME_NV12, + VLC_CODEC_DRM_PRIME_SAND8, VLC_CODEC_DRM_PRIME_SAND30 }, + FAKE_FMT() }, ++ { { VLC_CODEC_DRM_PRIME_RGB32, 0 }, FAKE_FMT() }, { { VLC_CODEC_CVPX_NV12, VLC_CODEC_CVPX_UYVY, VLC_CODEC_CVPX_I420, VLC_CODEC_CVPX_BGRA }, @@ -29590,16 +30487,46 @@ index 892d5b7dac..3c75b6450f 100644 assert( p_dst->context == NULL ); -diff --git a/src/video_output/video_output.c b/src/video_output/video_output.c -index 085067cb40..592c0474ca 100644 ---- a/src/video_output/video_output.c -+++ b/src/video_output/video_output.c -@@ -1427,8 +1427,6 @@ static void ThreadChangeWindowMouse(vout_thread_t *vout, - case VOUT_WINDOW_MOUSE_DOUBLE_CLICK: - if (mouse->button_mask == 0) - vout_display_SendEventMouseDoubleClick(vd); -- else -- vout_display_SendEventMousePressed(vd, mouse->button_mask); - break; - default: vlc_assert_unreachable(); - break; +diff --git a/src/video_output/window.c b/src/video_output/window.c +index 36aaa9d046..6e0b5dadf2 100644 +--- a/src/video_output/window.c ++++ b/src/video_output/window.c +@@ -51,6 +51,12 @@ static int vout_window_start(void *func, va_list ap) + return activate(wnd, cfg); + } + ++static void vout_window_destructor(vlc_object_t *obj) ++{ ++ vout_window_t *window = (vout_window_t *)obj; ++ vlc_mutex_destroy(&window->handle_lock); ++} ++ + vout_window_t *vout_window_New(vlc_object_t *obj, const char *module, + const vout_window_cfg_t *cfg, + const vout_window_owner_t *owner) +@@ -58,10 +64,7 @@ vout_window_t *vout_window_New(vlc_object_t *obj, const char *module, + window_t *w = vlc_custom_create(obj, sizeof(*w), "window"); + vout_window_t *window = &w->wnd; + +- memset(&window->handle, 0, sizeof(window->handle)); +- window->info.has_double_click = false; +- window->control = NULL; +- window->sys = NULL; ++ memset((char*)window + sizeof(window->obj), 0, sizeof(*window) - sizeof(window->obj)); + + if (owner != NULL) + window->owner = *owner; +@@ -87,6 +90,13 @@ vout_window_t *vout_window_New(vlc_object_t *obj, const char *module, + } + else + w->inhibit = NULL; ++ ++ if (window->type == VOUT_WINDOW_TYPE_WAYLAND) ++ { ++ vlc_mutex_init(&window->handle_lock); ++ vlc_object_set_destructor(window, vout_window_destructor); ++ } ++ + return window; + } + diff --git a/alarm/vlc-rpi/PKGBUILD b/alarm/vlc-rpi/PKGBUILD index 622b6501b..41269f8e7 100644 --- a/alarm/vlc-rpi/PKGBUILD +++ b/alarm/vlc-rpi/PKGBUILD @@ -10,8 +10,8 @@ _vlcver=3.0.20 # optional fixup version including hyphen _vlcfixupver= pkgver=${_vlcver}${_vlcfixupver//-/.r} -pkgrel=3 -pkgdesc='Multi-platform MPEG, VCD/DVD, and DivX player with hw accel for RPi 3/4/400' +pkgrel=4 +pkgdesc='Multi-platform MPEG, VCD/DVD, and DivX player with hw accel for RPi 3 and above' url='https://www.videolan.org/vlc/' arch=(aarch64) license=('LGPL2.1' 'GPL2') @@ -61,7 +61,7 @@ optdepends=('avahi: service discovery using bonjour protocol' 'libnfs: NFS access' 'mpg123: mpg123 codec' 'protobuf: chromecast streaming' - 'libmicrodns: mDNS services discovery e.g. chromecast etc' + 'libmicrodns: mDNS services discovery (chromecast etc)' 'lua52-socket: http interface' 'libdvdread: DVD input module' 'libdvdnav: DVD with navigation input module' @@ -113,7 +113,7 @@ source=(https://download.videolan.org/${_pkgname}/${_vlcver}/${_pkgname}-${_vlcv # credit to jc and RPi-Distro maintainers for this work, see: # https://github.com/jc-kynesim/vlc.git # https://github.com/RPi-Distro/vlc/tree/bullseye-rpt/debian/patches - 0003-dev-3.0.19-port_1_to_3.0.20.patch + 0003-test-3.0.20-rpi_1.patch update-vlc-plugin-cache.hook) sha256sums=('adc7285b4d2721cddf40eb5270cada2aaa10a334cb546fd55a06353447ba29b5' 'SKIP' @@ -121,7 +121,7 @@ sha256sums=('adc7285b4d2721cddf40eb5270cada2aaa10a334cb546fd55a06353447ba29b5' 'be970a020695fdc4d0f968021f057a1cb625eeb6ee62995560e532d61ffb52dc' '753517a8b88c5950d516f0fe57a3ef169e0665ba7817d4b8d9976c666829a291' 'c47ecb0e8e8c03f8c5451aa12fc2e38e380364c38c411a13aa38b7b41def6989' - 'ea721ef759c7fa4b9cc66ea57b4ba02b78e2944049ef39792c1ef2c6082e499f' + '833e29aa4e1c17ef95dfaafc64bf37fbf8fa54f38211605f3394275919b6b2f2' 'b98043683dd90d3f5a3f501212dfc629839b661100de5ac79fd30cb7b4a06f13') validpgpkeys=('65F7C6B4206BD057A7EB73787180713BE58D1ADC') # VideoLAN Release Signing Key @@ -229,7 +229,7 @@ build() { --enable-samplerate \ --enable-soxr \ --disable-chromaprint \ - --disable-chromecast \ + --enable-chromecast \ --enable-qt \ --enable-skins2 \ --enable-libtar \