[PATCH] Playback audio in video attachments
authorJoshua Goins <josh@redstrate.com>
Mon, 30 Jun 2025 23:38:05 +0000 (19:38 -0400)
committerAurélien COUDERC <coucouf@debian.org>
Thu, 24 Jul 2025 16:34:20 +0000 (18:34 +0200)
When porting to QtMultimedia fully, I forgot that we needed to manually
add a VideoOutput too.

CCBUG: 505303

TODO:
* Fix the volume control hiding when it shouldn't, and test on mobile

Gbp-Pq: Name upstream_fc886374_Playback-audio-in-video-attachments.patch

src/content/ui/Components/VideoPlayer.qml
src/content/ui/PostDelegate/VideoAttachment.qml

index f5e2a6a07cf0fd37dc7dbfe1a532799a4fe6ada9..c8dc704c82309aab7edd0373aa04f637d54dd6d6 100644 (file)
@@ -17,6 +17,7 @@ Item {
     property alias source: player.source
     property alias position: player.position
     property bool looping
+    property alias volume: audioOutput.volume
 
     signal errorOccurred(error: int, errorString: string)
 
@@ -35,6 +36,9 @@ Item {
     MediaPlayer {
         id: player
         videoOutput: videoOutput
+        audioOutput: AudioOutput {
+            id: audioOutput
+        }
         loops: root.looping ? MediaPlayer.Infinite : 0
 
         onMediaStatusChanged: {
index 91f78047cde15ff4bc34570c4811fa6e2d95a79e..58a19936b195cd2773a9d67c6d2344b2e593abe8 100644 (file)
@@ -21,6 +21,7 @@ MediaContainer {
     property alias showControls: mediaControls.visible
     property bool looping: false
     property alias loading: busyIndicator.visible
+    property real volume: 1.0
 
     signal errorOccurred(error: int, errorString: string)
 
@@ -56,6 +57,7 @@ MediaContainer {
             source: root.videoUrl
             looping: root.looping
             onErrorOccurred: (error, errorString) => root.errorOccurred(error, errorString)
+            volume: root.volume
         }
     }
 
@@ -78,6 +80,13 @@ MediaContainer {
 
     HoverHandler {
         id: hoverHandler
+
+        acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+        onHoveredChanged: {
+            if (hovered) {
+                mediaControlsHideTimer.restart();
+            }
+        }
     }
 
     QQC2.BusyIndicator {
@@ -132,13 +141,18 @@ MediaContainer {
 
         radius: Kirigami.Units.cornerRadius
         color: Kirigami.Theme.backgroundColor
-        opacity: hoverHandler.hovered && !root.isSensitive && !(player.item?.paused ?? true) && !(player.item?.stopped ?? true) ? 0.7 : 0.0
+        opacity: (mediaControlsHideTimer.running || volumePopupTimer || hoverHandler.hovered) && !root.isSensitive && !(player.item?.paused ?? true) && !(player.item?.stopped ?? true) ? 0.7 : 0.0
         Behavior on opacity {
             OpacityAnimator {
                 duration: Kirigami.Units.longDuration
             }
         }
 
+        Timer {
+            id: mediaControlsHideTimer
+            interval: 5000
+        }
+
         RowLayout {
             id: mediaControlsLayout
             anchors.fill: parent
@@ -173,6 +187,80 @@ MediaContainer {
 
                 onMoved: player.item?.setPosition(value)
             }
+
+            QQC2.ToolButton {
+                id: volumeButton
+
+                property var unmuteVolume: root.volume
+
+                icon.name: root.volume <= 0 ? "player-volume-muted-symbolic" : "player-volume-symbolic"
+
+                QQC2.ToolTip.visible: hovered
+                QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
+                QQC2.ToolTip.timeout: Kirigami.Units.toolTipDelay
+                QQC2.ToolTip.text: i18nc("@action:button", "Volume")
+
+                onClicked: {
+                    if (root.volume > 0) {
+                        root.volume = 0;
+                    } else  if (unmuteVolume === 0) {
+                        root.volume = 1;
+                    } else {
+                        root.volume = unmuteVolume;
+                    }
+                }
+
+                QQC2.Popup {
+                    id: volumePopup
+                    y: -height
+                    width: volumeButton.width
+                    visible: (volumeButton.hovered || volumePopupHoverHandler.hovered || volumeSlider.hovered || volumePopupTimer.running)
+
+                    focus: true
+                    padding: Kirigami.Units.smallSpacing
+                    closePolicy: QQC2.Popup.NoAutoClose
+
+                    QQC2.Slider {
+                        id: volumeSlider
+                        anchors.centerIn: parent
+                        implicitHeight: Kirigami.Units.gridUnit * 7
+                        orientation: Qt.Vertical
+                        padding: 0
+                        from: 0
+                        to: 1
+                        value: root.volume
+                        onMoved: {
+                            root.volume = value;
+                            volumeButton.unmuteVolume = value;
+                        }
+                    }
+                    Timer {
+                        id: volumePopupTimer
+                        interval: 1000
+                    }
+                    HoverHandler {
+                        id: volumePopupHoverHandler
+
+                        onHoveredChanged: {
+                            if (hovered) {
+                                volumePopupTimer.restart();
+                            }
+                        }
+                    }
+                    background: Kirigami.ShadowedRectangle {
+                        radius: Kirigami.Units.cornerRadius
+                        color: Kirigami.Theme.backgroundColor
+                        opacity: 0.8
+
+                        shadow {
+                            xOffset: 0
+                            yOffset: 4
+                            color: Qt.rgba(0, 0, 0, 0.3)
+                            size: Kirigami.Units.largeSpacing
+                       }
+                    }
+                }
+            }
         }
     }
 }