1
0
Fork 0
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:
Aline Gondim Santos 2023-03-15 09:33:51 -03:00 committed by Sébastien Blin
parent 8fd2c36bfd
commit 378161ebe5
15 changed files with 59 additions and 5 deletions

2
daemon

@ -1 +1 @@
Subproject commit bbf8cca24f6ca8f164a1282d7b8fe93de7e953d4 Subproject commit 977ee99c115f8435b0aa0521aa5b54ea7a2d4895

View 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

View file

@ -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.

View file

@ -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();
} }

View file

@ -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

View file

@ -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 {

View file

@ -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)")

View file

@ -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;

View file

@ -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,

View file

@ -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.

View file

@ -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

View file

@ -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) {

View file

@ -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
} }
} }
} }

View file

@ -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
} }
} }
} }

View file

@ -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