[vlc-commits] [Git][videolan/vlc][master] 5 commits: qt: introduce `exclude()` to `Common.glsl`

Steve Lhomme (@robUx4) gitlab at videolan.org
Fri Sep 26 14:53:06 UTC 2025



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
d9ed33e7 by Fatih Uzunoglu at 2025-09-26T14:29:55+00:00
qt: introduce `exclude()` to `Common.glsl`

- - - - -
6535528e by Fatih Uzunoglu at 2025-09-26T14:29:55+00:00
qt: introduce fragment-shader-level postprocessing in `DualKawaseBlur.frag`

Currently supporting tinting, noise, and exclusion.
These are to be used by the frosted glass effect.

Co-authored-by: Pierre Lamot <pierre at videolabs.io>

- - - - -
046e9b82 by Fatih Uzunoglu at 2025-09-26T14:29:55+00:00
qml: expose postprocessing in `DualKawaseBlur.qml`

- - - - -
4b7e7760 by Fatih Uzunoglu at 2025-09-26T14:29:55+00:00
qml: make use of built-in postprocessing in `FrostedGlassEffect`

This also reverts the frosted glass look back to the time when
we used an additional layer to have exclusion and tint (988b7bb2).
Noise-wise, it should not have any changes.

Thanks to the custom blur effect, we can now have shader-level
adjustments as we prefer directly in the blur effect without
needing an additional layer, similar to Qt's `MultiEffect`.

This also eases the work of the scene graph (batch) renderer.

- - - - -
c666a8b0 by Fatih Uzunoglu at 2025-09-26T14:29:55+00:00
qml: make use of built-in blur postprocessing in `Player.qml`

Multiply/screen fits better, but tinting is also fine here
as we can choose pure black and white tint colors with strength
0.5, which would mathematically be the same as screen/multiply
with rgb(0.5, 0.5, 0.5).

If necessary, multiply/screen can also later be added to the
blur shader.

- - - - -


11 changed files:

- modules/gui/qt/Makefile.am
- modules/gui/qt/player/qml/Player.qml
- modules/gui/qt/shaders/Common.glsl
- modules/gui/qt/shaders/DualKawaseBlur.frag
- modules/gui/qt/shaders/DualKawaseBlur_downsample.frag
- modules/gui/qt/shaders/DualKawaseBlur_upsample.frag
- + modules/gui/qt/shaders/DualKawaseBlur_upsample_postprocess.frag
- modules/gui/qt/shaders/meson.build
- modules/gui/qt/shaders/shaders.qrc
- modules/gui/qt/widgets/qml/DualKawaseBlur.qml
- modules/gui/qt/widgets/qml/FrostedGlassEffect.qml


Changes:

=====================================
modules/gui/qt/Makefile.am
=====================================
@@ -1361,7 +1361,8 @@ libqt_plugin_la_SHADER := shaders/FadingEdge.frag \
                           shaders/RoundedRectangleShadow_hollow.frag \
                           shaders/VoronoiSnow.frag \
                           shaders/DualKawaseBlur_upsample.frag \
-                          shaders/DualKawaseBlur_downsample.frag
+                          shaders/DualKawaseBlur_downsample.frag \
+                          shaders/DualKawaseBlur_upsample_postprocess.frag
 if ENABLE_QT
 
 libqt_plugin_la_LIBADD += libqml_module_dialogs.a \


=====================================
modules/gui/qt/player/qml/Player.qml
=====================================
@@ -301,6 +301,10 @@ FocusScope {
 
                         source: textureProviderItem
 
+                        postprocess: true
+                        tint: bgtheme.palette.isDark ? "black" : "white"
+                        tintStrength: 0.5
+
                         Widgets.TextureProviderItem {
                             id: textureProviderItem
 
@@ -314,15 +318,6 @@ FocusScope {
                             source: cover
                         }
 
-                        Widgets.FastBlend {
-                            anchors.fill: parent
-
-                            color: Qt.rgba(0.5, 0.5, 0.5, 1.0)
-
-                            mode: bgtheme.palette.isDark ? Widgets.FastBlend.Mode.Multiply // multiply makes darker
-                                                         : Widgets.FastBlend.Mode.Screen // screen (inverse multiply) makes lighter
-                        }
-
                         Component.onCompleted: {
                             // Blur layers are effect-size dependent, so once the user starts resizing the window (hence the effect),
                             // we should either momentarily turn on live, or repeatedly call `scheduleUpdate()`. Due to the optimization,


=====================================
modules/gui/qt/shaders/Common.glsl
=====================================
@@ -61,6 +61,11 @@
 #define DITHERING_CUTOFF 0.01
 #define DITHERING_STRENGTH 0.01
 
+vec4 exclude(vec4 src, vec4 dst)
+{
+    return src + dst - 2.0 * src * dst;
+}
+
 vec4 fromPremult(vec4 color) {
     vec4 result = vec4(0.0);
     result.rgb = color.rgb / max(1.0/256.0, color.a);


=====================================
modules/gui/qt/shaders/DualKawaseBlur.frag
=====================================
@@ -32,6 +32,13 @@ layout(std140, binding = 0) uniform qt_buf {
     vec4 normalRect; // unused, but Qt needs it as it used in first-pass vertex shader for sub-texturing (Qt bug?)
     vec2 sourceTextureSize;
     int radius;
+
+#ifdef POSTPROCESS
+    vec4 tint;
+    float tintStrength;
+    float exclusionStrength;
+    float noiseStrength;
+#endif
 };
 
 layout(binding = 1) uniform sampler2D source;
@@ -53,7 +60,12 @@ vec4 upsample(vec2 uv, vec2 halfpixel)
     sum += fromPremult(texture(source, uv + vec2(halfpixel.x, -halfpixel.y))) * 2.0;
     sum += fromPremult(texture(source, uv + vec2(0.0, -halfpixel.y * 2.0)));
     sum += fromPremult(texture(source, uv + vec2(-halfpixel.x, -halfpixel.y))) * 2.0;
-    return toPremult(sum / 12.0);
+    sum = sum / 12.0;
+#ifdef POSTPROCESS
+    return sum;
+#else
+    return toPremult(sum);
+#endif
 }
 #endif
 
@@ -66,7 +78,12 @@ vec4 downsample(vec2 uv, vec2 halfpixel)
     sum += fromPremult(texture(source, uv + halfpixel.xy));
     sum += fromPremult(texture(source, uv + vec2(halfpixel.x, -halfpixel.y)));
     sum += fromPremult(texture(source, uv - vec2(halfpixel.x, -halfpixel.y)));
-    return toPremult(sum / 8.0);
+    sum = sum / 8.0;
+#ifdef POSTPROCESS
+    return sum;
+#else
+    return toPremult(sum);
+#endif
 }
 #endif
 
@@ -98,5 +115,23 @@ void main()
     //       really important with the blur effect, so maybe it makes sense).
     vec2 halfpixel = (radius - 0.5) / sourceTextureSize;
 
-    fragColor = SAMPLE(uv, halfpixel) * qt_Opacity; // premultiplied alpha
+    vec4 result = SAMPLE(uv, halfpixel);
+
+#ifdef POSTPROCESS
+    // Exclusion:
+    vec4 exclColor = vec4(exclusionStrength, exclusionStrength, exclusionStrength, 0.0);
+    result = exclude(result, exclColor);
+
+    // Tint:
+    result = mix(result, fromPremult(tint), tintStrength);
+
+    // Noise:
+    float r = rand(qt_TexCoord0) - 0.5;
+    vec4 noise = vec4(r,r,r,1.0) * noiseStrength;
+    result += noise;
+
+    result = toPremult(result);
+#endif
+
+    fragColor = result * qt_Opacity; // premultiplied alpha
 }


=====================================
modules/gui/qt/shaders/DualKawaseBlur_downsample.frag
=====================================
@@ -36,6 +36,13 @@ layout(std140, binding = 0) uniform qt_buf {
     vec4 normalRect; // unused, but Qt needs it as it used in first-pass vertex shader for sub-texturing (Qt bug?)
     vec2 sourceTextureSize;
     int radius;
+
+#ifdef POSTPROCESS
+    vec4 tint;
+    float tintStrength;
+    float exclusionStrength;
+    float noiseStrength;
+#endif
 };
 
 layout(binding = 1) uniform sampler2D source;
@@ -57,7 +64,12 @@ vec4 upsample(vec2 uv, vec2 halfpixel)
     sum += fromPremult(texture(source, uv + vec2(halfpixel.x, -halfpixel.y))) * 2.0;
     sum += fromPremult(texture(source, uv + vec2(0.0, -halfpixel.y * 2.0)));
     sum += fromPremult(texture(source, uv + vec2(-halfpixel.x, -halfpixel.y))) * 2.0;
-    return toPremult(sum / 12.0);
+    sum = sum / 12.0;
+#ifdef POSTPROCESS
+    return sum;
+#else
+    return toPremult(sum);
+#endif
 }
 #endif
 
@@ -70,7 +82,12 @@ vec4 downsample(vec2 uv, vec2 halfpixel)
     sum += fromPremult(texture(source, uv + halfpixel.xy));
     sum += fromPremult(texture(source, uv + vec2(halfpixel.x, -halfpixel.y)));
     sum += fromPremult(texture(source, uv - vec2(halfpixel.x, -halfpixel.y)));
-    return toPremult(sum / 8.0);
+    sum = sum / 8.0;
+#ifdef POSTPROCESS
+    return sum;
+#else
+    return toPremult(sum);
+#endif
 }
 #endif
 
@@ -102,5 +119,23 @@ void main()
     //       really important with the blur effect, so maybe it makes sense).
     vec2 halfpixel = (radius - 0.5) / sourceTextureSize;
 
-    fragColor = SAMPLE(uv, halfpixel) * qt_Opacity; // premultiplied alpha
+    vec4 result = SAMPLE(uv, halfpixel);
+
+#ifdef POSTPROCESS
+    // Exclusion:
+    vec4 exclColor = vec4(exclusionStrength, exclusionStrength, exclusionStrength, 0.0);
+    result = exclude(result, exclColor);
+
+    // Tint:
+    result = mix(result, fromPremult(tint), tintStrength);
+
+    // Noise:
+    float r = rand(qt_TexCoord0) - 0.5;
+    vec4 noise = vec4(r,r,r,1.0) * noiseStrength;
+    result += noise;
+
+    result = toPremult(result);
+#endif
+
+    fragColor = result * qt_Opacity; // premultiplied alpha
 }


=====================================
modules/gui/qt/shaders/DualKawaseBlur_upsample.frag
=====================================
@@ -36,6 +36,13 @@ layout(std140, binding = 0) uniform qt_buf {
     vec4 normalRect; // unused, but Qt needs it as it used in first-pass vertex shader for sub-texturing (Qt bug?)
     vec2 sourceTextureSize;
     int radius;
+
+#ifdef POSTPROCESS
+    vec4 tint;
+    float tintStrength;
+    float exclusionStrength;
+    float noiseStrength;
+#endif
 };
 
 layout(binding = 1) uniform sampler2D source;
@@ -57,7 +64,12 @@ vec4 upsample(vec2 uv, vec2 halfpixel)
     sum += fromPremult(texture(source, uv + vec2(halfpixel.x, -halfpixel.y))) * 2.0;
     sum += fromPremult(texture(source, uv + vec2(0.0, -halfpixel.y * 2.0)));
     sum += fromPremult(texture(source, uv + vec2(-halfpixel.x, -halfpixel.y))) * 2.0;
-    return toPremult(sum / 12.0);
+    sum = sum / 12.0;
+#ifdef POSTPROCESS
+    return sum;
+#else
+    return toPremult(sum);
+#endif
 }
 #endif
 
@@ -70,7 +82,12 @@ vec4 downsample(vec2 uv, vec2 halfpixel)
     sum += fromPremult(texture(source, uv + halfpixel.xy));
     sum += fromPremult(texture(source, uv + vec2(halfpixel.x, -halfpixel.y)));
     sum += fromPremult(texture(source, uv - vec2(halfpixel.x, -halfpixel.y)));
-    return toPremult(sum / 8.0);
+    sum = sum / 8.0;
+#ifdef POSTPROCESS
+    return sum;
+#else
+    return toPremult(sum);
+#endif
 }
 #endif
 
@@ -102,5 +119,23 @@ void main()
     //       really important with the blur effect, so maybe it makes sense).
     vec2 halfpixel = (radius - 0.5) / sourceTextureSize;
 
-    fragColor = SAMPLE(uv, halfpixel) * qt_Opacity; // premultiplied alpha
+    vec4 result = SAMPLE(uv, halfpixel);
+
+#ifdef POSTPROCESS
+    // Exclusion:
+    vec4 exclColor = vec4(exclusionStrength, exclusionStrength, exclusionStrength, 0.0);
+    result = exclude(result, exclColor);
+
+    // Tint:
+    result = mix(result, fromPremult(tint), tintStrength);
+
+    // Noise:
+    float r = rand(qt_TexCoord0) - 0.5;
+    vec4 noise = vec4(r,r,r,1.0) * noiseStrength;
+    result += noise;
+
+    result = toPremult(result);
+#endif
+
+    fragColor = result * qt_Opacity; // premultiplied alpha
 }


=====================================
modules/gui/qt/shaders/DualKawaseBlur_upsample_postprocess.frag
=====================================
@@ -0,0 +1,142 @@
+#version 440
+
+// WARNING: This file must be in sync with DualKawaseBlur.frag
+// TODO: Generate this shader at build time.
+#define UPSAMPLE
+#define POSTPROCESS
+
+/*****************************************************************************
+ * Copyright (C) 2025 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ *****************************************************************************/
+
+#extension GL_GOOGLE_include_directive : enable
+
+#include "Common.glsl"
+
+layout(location = 0) in vec2 qt_TexCoord0;
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform qt_buf {
+    mat4 qt_Matrix;
+    float qt_Opacity;
+
+    vec4 normalRect; // unused, but Qt needs it as it used in first-pass vertex shader for sub-texturing (Qt bug?)
+    vec2 sourceTextureSize;
+    int radius;
+
+#ifdef POSTPROCESS
+    vec4 tint;
+    float tintStrength;
+    float exclusionStrength;
+    float noiseStrength;
+#endif
+};
+
+layout(binding = 1) uniform sampler2D source;
+
+// "Dual Kawase"
+// SIGGRAPH 2015, "Bandwidth Efficient Rendering", Marius Bjorge (ARM)
+// https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+/// <dualkawaseblur>
+
+#ifdef UPSAMPLE
+#define SAMPLE upsample
+vec4 upsample(vec2 uv, vec2 halfpixel)
+{
+    vec4 sum = fromPremult(texture(source, uv + vec2(-halfpixel.x * 2.0, 0.0)));
+    sum += fromPremult(texture(source, uv + vec2(-halfpixel.x, halfpixel.y))) * 2.0;
+    sum += fromPremult(texture(source, uv + vec2(0.0, halfpixel.y * 2.0)));
+    sum += fromPremult(texture(source, uv + vec2(halfpixel.x, halfpixel.y))) * 2.0;
+    sum += fromPremult(texture(source, uv + vec2(halfpixel.x * 2.0, 0.0)));
+    sum += fromPremult(texture(source, uv + vec2(halfpixel.x, -halfpixel.y))) * 2.0;
+    sum += fromPremult(texture(source, uv + vec2(0.0, -halfpixel.y * 2.0)));
+    sum += fromPremult(texture(source, uv + vec2(-halfpixel.x, -halfpixel.y))) * 2.0;
+    sum = sum / 12.0;
+#ifdef POSTPROCESS
+    return sum;
+#else
+    return toPremult(sum);
+#endif
+}
+#endif
+
+#ifdef DOWNSAMPLE
+#define SAMPLE downsample
+vec4 downsample(vec2 uv, vec2 halfpixel)
+{
+    vec4 sum = fromPremult(texture(source, uv)) * 4.0;
+    sum += fromPremult(texture(source, uv - halfpixel.xy));
+    sum += fromPremult(texture(source, uv + halfpixel.xy));
+    sum += fromPremult(texture(source, uv + vec2(halfpixel.x, -halfpixel.y)));
+    sum += fromPremult(texture(source, uv - vec2(halfpixel.x, -halfpixel.y)));
+    sum = sum / 8.0;
+#ifdef POSTPROCESS
+    return sum;
+#else
+    return toPremult(sum);
+#endif
+}
+#endif
+
+/// </dualkawaseblur>
+
+void main()
+{
+    // When `supportsAtlasTextures` is set, proper `uv` is provided, even for arbitrary
+    // sub-textures, so we don't need to calculate here:
+    vec2 uv = qt_TexCoord0;
+
+    // We need to be careful to calculate the halfpixel properly for sub- and atlas textures:
+    // If sourceTextureSize is sourced from QML, such as `Image`'s implicit size which normally
+    // matches the texture size 1:1, if the texture is a sub-texture or atlas texture, the
+    // texture size would not reflect the actual texture size. For that, we need to divide
+    // `sourceTextureSize` by `qt_SubRect_source.zw` to get the actual texture size (which
+    // means the atlas size, for example). This was the case in the first iteration, however
+    // we started using a C++ utility class to get the texture size directly, so now we can
+    // use it as is. The disadvantage is that the size needs to hop through the QML engine,
+    // essentially SG -> QML -> SG (here), which may delay having the correct size here. If
+    // we use GLSL 1.30 feature `textureSize()` instead, this would not be an issue. Currently
+    // we can not do that because even though the shaders are written in GLSL 4.40, we target
+    // as low as GLSL 1.20/ESSL 1.0. But maybe this is not a big deal, because if the size
+    // (or texture altogether) changes, `QSGTextureProvider::textureChanged()` may need to
+    // be processed in QML anyway (so the new size comes at the same time as the texture updates).
+    // TODO: Ditch targeting GLSL 1.20/ESSL 1.0, and use `(radius - 0.5) / textureSize(source, 0)` instead.
+    // TODO: This may be done in the vertex shader. I have not done that as this is a very simple
+    //       calculation, and custom vertex shader in `ShaderEffect` breaks batching (which is not
+    //       really important with the blur effect, so maybe it makes sense).
+    vec2 halfpixel = (radius - 0.5) / sourceTextureSize;
+
+    vec4 result = SAMPLE(uv, halfpixel);
+
+#ifdef POSTPROCESS
+    // Exclusion:
+    vec4 exclColor = vec4(exclusionStrength, exclusionStrength, exclusionStrength, 0.0);
+    result = exclude(result, exclColor);
+
+    // Tint:
+    result = mix(result, fromPremult(tint), tintStrength);
+
+    // Noise:
+    float r = rand(qt_TexCoord0) - 0.5;
+    vec4 noise = vec4(r,r,r,1.0) * noiseStrength;
+    result += noise;
+
+    result = toPremult(result);
+#endif
+
+    fragColor = result * qt_Opacity; // premultiplied alpha
+}


=====================================
modules/gui/qt/shaders/meson.build
=====================================
@@ -25,6 +25,7 @@ shader_sources = [
     'VoronoiSnow.frag',
     'DualKawaseBlur_downsample.frag',
     'DualKawaseBlur_upsample.frag',
+    'DualKawaseBlur_upsample_postprocess.frag',
 ]
 
 shader_files = files(shader_sources)


=====================================
modules/gui/qt/shaders/shaders.qrc
=====================================
@@ -19,5 +19,6 @@
         <file alias="VoronoiSnow.frag.qsb">VoronoiSnow.frag.qsb</file>
         <file alias="DualKawaseBlur_upsample.frag.qsb">DualKawaseBlur_upsample.frag.qsb</file>
         <file alias="DualKawaseBlur_downsample.frag.qsb">DualKawaseBlur_downsample.frag.qsb</file>
+        <file alias="DualKawaseBlur_upsample_postprocess.frag.qsb">DualKawaseBlur_upsample_postprocess.frag.qsb</file>
     </qresource>
 </RCC>


=====================================
modules/gui/qt/widgets/qml/DualKawaseBlur.qml
=====================================
@@ -39,6 +39,16 @@ Item {
         TwoPass // 1 downsample + 1 upsample (1 layer/buffer)
     }
 
+    /// <postprocess>
+    // The following property must be set in order to make other properties respected:
+    property bool postprocess: false
+
+    property alias tint: us2.tint
+    property alias tintStrength: us2.tintStrength
+    property alias noiseStrength: us2.noiseStrength
+    property alias exclusionStrength: us2.exclusionStrength
+    /// </postprocess>
+
     property int configuration: DualKawaseBlur.Configuration.FourPass
 
     // NOTE: This property is also an optimization hint. When it is false, the
@@ -58,7 +68,8 @@ Item {
     //       used even if it is set false here. For that reason, it should not be
     //       necessary to check for opacity (well, accumulated opacity can not be
     //       checked directly in QML anyway).
-    property bool blending: (!sourceTextureIsValid || sourceTextureProviderObserver.hasAlphaChannel)
+    property bool blending: (!sourceTextureIsValid || sourceTextureProviderObserver.hasAlphaChannel ||
+                             (postprocess && (tintStrength > 0.0 && tint.a < 1.0)))
 
     // source must be a texture provider item. Some items such as `Image` and
     // `ShaderEffectSource` are inherently texture provider. Other items needs
@@ -420,7 +431,13 @@ Item {
 
         // cullMode: ShaderEffect.BackFaceCulling // QTBUG-136611 (Layering breaks culling with OpenGL)
 
-        fragmentShader: "qrc:///shaders/DualKawaseBlur_upsample.frag.qsb"
+        property color tint: "transparent"
+        property real tintStrength: 0.0
+        property real noiseStrength: 0.0
+        property real exclusionStrength: 0.0
+
+        fragmentShader: root.postprocess ? "qrc:///shaders/DualKawaseBlur_upsample_postprocess.frag.qsb"
+                                         : "qrc:///shaders/DualKawaseBlur_upsample.frag.qsb"
 
         supportsAtlasTextures: true
 


=====================================
modules/gui/qt/widgets/qml/FrostedGlassEffect.qml
=====================================
@@ -18,8 +18,6 @@
 
 import QtQuick
 
-import VLC.Style
-import VLC.Util
 import VLC.Widgets as Widgets
 
 // This item can be used as a layer effect.
@@ -29,45 +27,8 @@ Widgets.DualKawaseBlur {
 
     radius: 3
 
-    property color tint: "transparent"
-    property real tintStrength: Qt.colorEqual(tint, "transparent") ? 0.0 : 0.7
-    property real noiseStrength: 0.02
-
-    // Have a semi-opaque filter blended with the source.
-    // This is intended to act as both colorization (tint),
-    // and exclusion effects.
-    Rectangle {
-        id: filter
-
-        // Underlay for the blur effect:
-        parent: root.source?.sourceItem ?? root.source
-
-        // Since we don't use layering for the effect area anymore,
-        // we need to restrict the filter to correspond to the effect
-        // area instead of the whole source:
-        x: root.x
-        y: root.y
-        width: root.width
-        height: root.height
-
-        z: 999
-
-        visible: root.tintStrength > 0.0
-
-        color: Qt.alpha(root.tint, root.tintStrength)
-    }
-
-    ShaderEffect {
-        id: noise
-
-        // Overlay for the blur effect:
-        anchors.fill: parent
-        z: 999
-
-        visible: root.noiseStrength > 0.0
-
-        readonly property real strength: root.noiseStrength
-
-        fragmentShader: "qrc:///shaders/Noise.frag.qsb"
-    }
+    postprocess: true
+    tintStrength: Qt.colorEqual(tint, "transparent") ? 0.0 : 0.7
+    noiseStrength: 0.02
+    exclusionStrength: 0.09
 }



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/520ccf8912d862f7476d787de41ef362b6a1b6e5...c666a8b0acb26092520a1646d4c03d07e40e6711

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/520ccf8912d862f7476d787de41ef362b6a1b6e5...c666a8b0acb26092520a1646d4c03d07e40e6711
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list