mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-09-09 19:43:31 +02:00
call layout: add option to mirror local camera videos
In conference, this patch depends on using local sinks ids to avoid mirroring video from sharing resources. GitLab: https://git.jami.net/savoirfairelinux/jami-client-gnome/-/issues/1284 Change-Id: I0c74f0780ebf17c68ffcffdca62eb33784189741
This commit is contained in:
parent
8fd2c36bfd
commit
378161ebe5
15 changed files with 59 additions and 5 deletions
2
daemon
2
daemon
|
@ -1 +1 @@
|
||||||
Subproject commit bbf8cca24f6ca8f164a1282d7b8fe93de7e953d4
|
Subproject commit 977ee99c115f8435b0aa0521aa5b54ea7a2d4895
|
1
resources/icons/flip_24dp.svg
Normal file
1
resources/icons/flip_24dp.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 15 15"><g transform="translate(-74.997 -75.001)"><path class="a" d="M581.25,75.269V89.734a.268.268,0,0,0,.536,0V75.269a.268.268,0,1,0-.536,0Z" transform="translate(-499.02)"/><path class="a" d="M75.412,330.394a.8.8,0,0,0-.415.7v6.335a.8.8,0,0,0,1.248.67l4.913-3.271a.8.8,0,0,0-.019-1.339l-4.913-3.078a.8.8,0,0,0-.814-.016Z" transform="translate(0 -251.642)"/><path class="a" d="M675.856,330.788a.8.8,0,0,0-.8.021l-4.915,3.062a.8.8,0,0,0-.019,1.339l4.9,3.281a.8.8,0,0,0,1.248-.67v-6.335a.8.8,0,0,0-.415-.7Zm-3.731,5.127-1.666-1.109h5.277v1.071h-3.482a.269.269,0,0,0-.129.037Zm.1-2.716h3.506v1.071H670.51Zm3.1-1.934a.265.265,0,0,1,.413.22v1.179h-2.657Zm.268,6.8a.268.268,0,0,1-.268,0l-2.456-1.65h2.866v1.409a.268.268,0,0,1-.142.236Z" transform="translate(-586.274 -252.037)"/></g></svg>
|
After Width: | Height: | Size: 861 B |
|
@ -57,7 +57,8 @@ extern const QString defaultDownloadPath;
|
||||||
X(EnableExperimentalSwarm, false) \
|
X(EnableExperimentalSwarm, false) \
|
||||||
X(LANG, "SYSTEM") \
|
X(LANG, "SYSTEM") \
|
||||||
X(PositionShareDuration, 15) \
|
X(PositionShareDuration, 15) \
|
||||||
X(PositionShareLimit, true)
|
X(PositionShareLimit, true) \
|
||||||
|
X(FlipSelf, false)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A class to expose settings keys in both c++ and QML.
|
* A class to expose settings keys in both c++ and QML.
|
||||||
|
|
|
@ -94,6 +94,9 @@ CallParticipantsModel::data(const QModelIndex& index, int role) const
|
||||||
return QVariant(item.value(VOICEACTIVITY).toBool());
|
return QVariant(item.value(VOICEACTIVITY).toBool());
|
||||||
case Role::IsRecording:
|
case Role::IsRecording:
|
||||||
return QVariant(item.value(ISRECORDING).toBool());
|
return QVariant(item.value(ISRECORDING).toBool());
|
||||||
|
case Role::IsSharing:
|
||||||
|
// this only works when using local sinks in conference
|
||||||
|
return QVariant(item.value(STREAMID).toString().startsWith("file://") || item.value(STREAMID).toString().startsWith("display://"));
|
||||||
}
|
}
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,8 @@
|
||||||
X(IsContact) \
|
X(IsContact) \
|
||||||
X(VoiceActivity) \
|
X(VoiceActivity) \
|
||||||
X(IsRecording) \
|
X(IsRecording) \
|
||||||
X(HandRaised)
|
X(HandRaised) \
|
||||||
|
X(IsSharing)
|
||||||
|
|
||||||
namespace CallParticipant {
|
namespace CallParticipant {
|
||||||
Q_NAMESPACE
|
Q_NAMESPACE
|
||||||
|
|
|
@ -31,6 +31,7 @@ Item {
|
||||||
/ videoOutput.sourceRect.width) ||
|
/ videoOutput.sourceRect.width) ||
|
||||||
0.5625 // 16:9 default
|
0.5625 // 16:9 default
|
||||||
property bool crop: false
|
property bool crop: false
|
||||||
|
property bool flip: false
|
||||||
|
|
||||||
// This rect describes the actual rendered content rectangle
|
// This rect describes the actual rendered content rectangle
|
||||||
// as the VideoOutput component may use PreserveAspectFit
|
// as the VideoOutput component may use PreserveAspectFit
|
||||||
|
@ -72,6 +73,11 @@ Item {
|
||||||
anchors.fill: root
|
anchors.fill: root
|
||||||
radius: (1. - opacity) * 100
|
radius: (1. - opacity) * 100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transform: Scale {
|
||||||
|
origin.x: videoOutput.width / 2
|
||||||
|
xScale: root.flip ? -1 : 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
|
|
@ -197,6 +197,7 @@ Item {
|
||||||
property string fps: qsTr("Frames per second")
|
property string fps: qsTr("Frames per second")
|
||||||
property string selectFPS: qsTr("Select video frame rate (frames per second)")
|
property string selectFPS: qsTr("Select video frame rate (frames per second)")
|
||||||
property string enableHWAccel: qsTr("Enable hardware acceleration")
|
property string enableHWAccel: qsTr("Enable hardware acceleration")
|
||||||
|
property string mirrorLocalVideo: qsTr("Mirror local video")
|
||||||
property string previewUnavailable: qsTr("Preview unavailable")
|
property string previewUnavailable: qsTr("Preview unavailable")
|
||||||
property string screenSharing: qsTr("Screen Sharing")
|
property string screenSharing: qsTr("Screen Sharing")
|
||||||
property string selectScreenSharingFPS: qsTr("Select screen sharing frame rate (frames per second)")
|
property string selectScreenSharingFPS: qsTr("Select screen sharing frame rate (frames per second)")
|
||||||
|
|
|
@ -51,6 +51,8 @@ class CurrentCall final : public QObject
|
||||||
QML_PROPERTY(bool, hideSpectators)
|
QML_PROPERTY(bool, hideSpectators)
|
||||||
QML_RO_PROPERTY(bool, isOutgoing)
|
QML_RO_PROPERTY(bool, isOutgoing)
|
||||||
|
|
||||||
|
QML_PROPERTY(bool, flipSelf)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CurrentCall(LRCInstance* lrcInstance, QObject* parent = nullptr);
|
explicit CurrentCall(LRCInstance* lrcInstance, QObject* parent = nullptr);
|
||||||
~CurrentCall() = default;
|
~CurrentCall() = default;
|
||||||
|
|
|
@ -188,6 +188,10 @@ Control {
|
||||||
UtilsAdapter.setAppValue(Settings.HideSpectators, !layoutModel.get(index).ActiveSetting)
|
UtilsAdapter.setAppValue(Settings.HideSpectators, !layoutModel.get(index).ActiveSetting)
|
||||||
CurrentCall.hideSpectators = UtilsAdapter.getAppValue(Settings.HideSpectators)
|
CurrentCall.hideSpectators = UtilsAdapter.getAppValue(Settings.HideSpectators)
|
||||||
break
|
break
|
||||||
|
case JamiStrings.mirrorLocalVideo:
|
||||||
|
UtilsAdapter.setAppValue(Settings.FlipSelf, !layoutModel.get(index).ActiveSetting)
|
||||||
|
CurrentCall.flipSelf = UtilsAdapter.getAppValue(Settings.FlipSelf)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
@ -218,9 +222,17 @@ Control {
|
||||||
"IconSource": JamiResources.hidemyself_black_24dp_svg,
|
"IconSource": JamiResources.hidemyself_black_24dp_svg,
|
||||||
"ActiveSetting": UtilsAdapter.getAppValue(Settings.HideSelf),
|
"ActiveSetting": UtilsAdapter.getAppValue(Settings.HideSelf),
|
||||||
"TopMargin": true,
|
"TopMargin": true,
|
||||||
"BottomMargin": true,
|
"BottomMargin": false,
|
||||||
"SectionEnd": true})
|
"SectionEnd": false})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
layoutModel.append({"Name": JamiStrings.mirrorLocalVideo,
|
||||||
|
"IconSource": JamiResources.flip_24dp_svg,
|
||||||
|
"ActiveSetting": UtilsAdapter.getAppValue(Settings.FlipSelf),
|
||||||
|
"TopMargin": !CurrentCall.isConference,
|
||||||
|
"BottomMargin": true,
|
||||||
|
"SectionEnd": true})
|
||||||
|
|
||||||
layoutModel.append({"Name": JamiStrings.viewFullScreen,
|
layoutModel.append({"Name": JamiStrings.viewFullScreen,
|
||||||
"IconSource": JamiResources.open_in_full_24dp_svg,
|
"IconSource": JamiResources.open_in_full_24dp_svg,
|
||||||
"ActiveSetting": layoutManager.isCallFullscreen,
|
"ActiveSetting": layoutManager.isCallFullscreen,
|
||||||
|
|
|
@ -209,6 +209,7 @@ Rectangle {
|
||||||
width: Math.max(callPageMainRect.width / 5, JamiTheme.minimumPreviewWidth)
|
width: Math.max(callPageMainRect.width / 5, JamiTheme.minimumPreviewWidth)
|
||||||
x: callPageMainRect.width - previewRenderer.width - previewMargin
|
x: callPageMainRect.width - previewRenderer.width - previewMargin
|
||||||
y: previewMarginYTop
|
y: previewMarginYTop
|
||||||
|
flip: CurrentCall.flipSelf && !CurrentCall.isSharing
|
||||||
|
|
||||||
// HACK: this is a workaround to the preview video starting
|
// HACK: this is a workaround to the preview video starting
|
||||||
// and stopping a few times. The root cause should be investigated ASAP.
|
// and stopping a few times. The root cause should be investigated ASAP.
|
||||||
|
|
|
@ -59,6 +59,7 @@ Item {
|
||||||
property bool voiceActive: false
|
property bool voiceActive: false
|
||||||
property bool isLocalMuted: true
|
property bool isLocalMuted: true
|
||||||
property bool isRecording: false
|
property bool isRecording: false
|
||||||
|
property bool isSharing: false
|
||||||
|
|
||||||
property bool meHost: CallAdapter.isCurrentHost()
|
property bool meHost: CallAdapter.isCurrentHost()
|
||||||
property bool meModerator: CallAdapter.isModerator()
|
property bool meModerator: CallAdapter.isModerator()
|
||||||
|
@ -123,6 +124,8 @@ Item {
|
||||||
anchors.margins: 2
|
anchors.margins: 2
|
||||||
rendererId: root.sinkId
|
rendererId: root.sinkId
|
||||||
crop: !participantIsActive
|
crop: !participantIsActive
|
||||||
|
flip: isMe && !isSharing && CurrentCall.flipSelf
|
||||||
|
|
||||||
underlayItems: Avatar {
|
underlayItems: Avatar {
|
||||||
property real componentSize: Math.min(mediaDistRender.contentRect.width / 2, mediaDistRender.contentRect.height / 2)
|
property real componentSize: Math.min(mediaDistRender.contentRect.width / 2, mediaDistRender.contentRect.height / 2)
|
||||||
height: componentSize
|
height: componentSize
|
||||||
|
|
|
@ -45,6 +45,7 @@ Item {
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
CurrentCall.hideSelf = UtilsAdapter.getAppValue(Settings.HideSelf)
|
CurrentCall.hideSelf = UtilsAdapter.getAppValue(Settings.HideSelf)
|
||||||
CurrentCall.hideSpectators = UtilsAdapter.getAppValue(Settings.HideSpectators)
|
CurrentCall.hideSpectators = UtilsAdapter.getAppValue(Settings.HideSpectators)
|
||||||
|
CurrentCall.flipSelf = UtilsAdapter.getAppValue(Settings.FlipSelf)
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
|
@ -76,6 +77,7 @@ Item {
|
||||||
isRecording: isRecording_
|
isRecording: isRecording_
|
||||||
participantIsModeratorMuted: audioModeratorMuted_
|
participantIsModeratorMuted: audioModeratorMuted_
|
||||||
participantHandIsRaised: isHandRaised_
|
participantHandIsRaised: isHandRaised_
|
||||||
|
isSharing: isSharing_
|
||||||
|
|
||||||
onParticipantHoveredChanged: {
|
onParticipantHoveredChanged: {
|
||||||
if (participantHovered) {
|
if (participantHovered) {
|
||||||
|
|
|
@ -122,6 +122,7 @@ SplitView {
|
||||||
property bool isHandRaised_: HandRaised
|
property bool isHandRaised_: HandRaised
|
||||||
property bool voiceActive_: VoiceActivity
|
property bool voiceActive_: VoiceActivity
|
||||||
property bool isRecording_: IsRecording
|
property bool isRecording_: IsRecording
|
||||||
|
property bool isSharing_: IsSharing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,6 +312,7 @@ SplitView {
|
||||||
property bool isHandRaised_: HandRaised
|
property bool isHandRaised_: HandRaised
|
||||||
property bool voiceActive_: VoiceActivity
|
property bool voiceActive_: VoiceActivity
|
||||||
property bool isRecording_: IsRecording
|
property bool isRecording_: IsRecording
|
||||||
|
property bool isSharing_: IsSharing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -217,6 +217,7 @@ SplitView {
|
||||||
property bool isHandRaised_: HandRaised
|
property bool isHandRaised_: HandRaised
|
||||||
property bool voiceActive_: VoiceActivity
|
property bool voiceActive_: VoiceActivity
|
||||||
property bool isRecording_: IsRecording
|
property bool isRecording_: IsRecording
|
||||||
|
property bool isSharing_: IsSharing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,6 +293,7 @@ SplitView {
|
||||||
property bool isHandRaised_: HandRaised
|
property bool isHandRaised_: HandRaised
|
||||||
property bool voiceActive_: VoiceActivity
|
property bool voiceActive_: VoiceActivity
|
||||||
property bool isRecording_: IsRecording
|
property bool isRecording_: IsRecording
|
||||||
|
property bool isSharing_: IsSharing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
|
flipControl.checked = UtilsAdapter.getAppValue(Settings.FlipSelf)
|
||||||
if (visible) {
|
if (visible) {
|
||||||
hardwareAccelControl.checked = AvAdapter.getHardwareAcceleration()
|
hardwareAccelControl.checked = AvAdapter.getHardwareAcceleration()
|
||||||
if (previewWidget.visible)
|
if (previewWidget.visible)
|
||||||
|
@ -230,6 +231,21 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ToggleSwitch {
|
||||||
|
id: flipControl
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: JamiTheme.preferredMarginSize
|
||||||
|
|
||||||
|
labelText: JamiStrings.mirrorLocalVideo
|
||||||
|
fontPointSize: JamiTheme.settingsFontSize
|
||||||
|
|
||||||
|
onSwitchToggled: {
|
||||||
|
UtilsAdapter.setAppValue(Settings.FlipSelf, checked)
|
||||||
|
CurrentCall.flipSelf = UtilsAdapter.getAppValue(Settings.FlipSelf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// video Preview
|
// video Preview
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: VideoDevices.listSize !== 0
|
visible: VideoDevices.listSize !== 0
|
||||||
|
@ -248,6 +264,7 @@ ColumnLayout {
|
||||||
id: previewWidget
|
id: previewWidget
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
flip: flipControl.checked
|
||||||
|
|
||||||
underlayItems: Text {
|
underlayItems: Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
Loading…
Add table
Reference in a new issue