video call

This commit is contained in:
Gaelle Braud 2023-12-22 16:19:05 +01:00
parent 449fc17f78
commit 4782bd2990
5 changed files with 234 additions and 104 deletions

View file

@ -49,6 +49,7 @@ void CallModel::accept(bool withVideo) {
auto core = CoreModel::getInstance()->getCore();
auto params = core->createCallParams(mMonitor);
params->enableVideo(withVideo);
mMonitor->enableCamera(withVideo);
// Answer with local call address.
auto localAddress = mMonitor->getCallLog()->getLocalAddress();
for (auto account : core->getAccountList()) {
@ -109,6 +110,9 @@ void CallModel::setSpeakerMuted(bool isMuted) {
void CallModel::setCameraEnabled(bool enabled) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
mMonitor->enableCamera(enabled);
auto core = CoreModel::getInstance()->getCore();
auto params = core->createCallParams(mMonitor);
params->enableVideo(enabled);
emit cameraEnabledChanged(enabled);
}

View file

@ -15,15 +15,17 @@ Window {
property CallGui call
Connections {
target: call.core
onRemoteVideoEnabledChanged: console.log("remote video enabled", call.core.remoteVideoEnabled)
}
onCallChanged: {
waitingTime.seconds = 0
waitingTimer.restart()
console.log("call changed", call, waitingTime.seconds)
}
property var peerName: UtilsCpp.getDisplayName(call.core.peerAddress)
property string peerNameText: peerName ? peerName.value : ""
property var callState: call.core.state
onCallStateChanged: {
console.log("State:", callState)
@ -296,7 +298,6 @@ Window {
}
RowLayout {
Control.Control {
id: centerItem
Layout.fillWidth: true
Layout.preferredWidth: 1059 * DefaultStyle.dp
Layout.fillHeight: true
@ -309,23 +310,26 @@ Window {
radius: 15 * DefaultStyle.dp
}
contentItem: Item {
id: centerItem
anchors.fill: parent
StackLayout {
id: centerLayout
currentIndex: 0
anchors.fill: parent
Connections {
target: mainWindow
onCallStateChanged: {
if (mainWindow.call.core.state === LinphoneEnums.CallState.Error) {
centerLayout.currentIndex = 2
centerLayout.currentIndex = 1
}
}
}
Item {
id: audioCallItem
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: parent.width
Layout.preferredHeight: parent.height
Sticker {
visible: false
call: mainWindow.call
Layout.fillWidth: true
Layout.fillHeight: true
Timer {
id: waitingTimer
interval: 1000
@ -361,44 +365,6 @@ Window {
}
}
}
ColumnLayout {
anchors.centerIn: parent
spacing: 2 * DefaultStyle.dp
Avatar {
Layout.alignment: Qt.AlignCenter
// TODO : remove username when friend list ready
call: mainWindow.call
// address: mainWindow.peerNameText
Layout.preferredWidth: 120 * DefaultStyle.dp
Layout.preferredHeight: 120 * DefaultStyle.dp
}
Text {
Layout.alignment: Qt.AlignCenter
Layout.topMargin: 15 * DefaultStyle.dp
visible: mainWindow.peerNameText.length > 0
text: mainWindow.peerNameText
color: DefaultStyle.grey_0
font {
pixelSize: 22 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
capitalization: Font.Capitalize
}
}
Text {
Layout.alignment: Qt.AlignCenter
text: mainWindow.call.core.peerAddress
color: DefaultStyle.grey_0
font {
pixelSize: 14 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
}
}
}
}
Image {
id: videoCallItem
Layout.preferredWidth: parent.width
Layout.preferredHeight: parent.height
}
ColumnLayout {
id: userNotFoundLayout
@ -413,17 +379,36 @@ Window {
}
}
}
Text {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.leftMargin: 10 * DefaultStyle.dp
Sticker {
id: preview
height: 180 * DefaultStyle.dp
width: 300 * DefaultStyle.dp
anchors.right: centerItem.right
anchors.bottom: centerItem.bottom
anchors.rightMargin: 10 * DefaultStyle.dp
anchors.bottomMargin: 10 * DefaultStyle.dp
text: mainWindow.peerNameText
color: DefaultStyle.grey_0
font {
pixelSize: 14 * DefaultStyle.dp
weight: 500 * DefaultStyle.dp
AccountProxy{
id: accounts
}
account: accounts.defaultAccount
enablePersonalCamera: mainWindow.call.core.cameraEnabled
MovableMouseArea{
anchors.fill: parent
// visible: mainItem.participantCount <= 2
function resetPosition(){
preview.anchors.right = centerItem.right
preview.anchors.bottom = centerItem.bottom
}
onVisibleChanged: if(!visible){
resetPosition()
}
drag.target: preview
onDraggingChanged: if(dragging){
preview.anchors.right = undefined
preview.anchors.bottom = undefined
}
onRequestResetPosition: resetPosition()
}
}
}
@ -552,7 +537,8 @@ Window {
}
Text {
id: delegateName
text: UtilsCpp.getDisplayName(modelData.core.peerAddress).value
property var remoteAddress: UtilsCpp.getDisplayName(modelData.core.peerAddress)
text: remoteAddress ? remoteAddress.value : ""
Connections {
target: modelData.core
}
@ -863,15 +849,4 @@ Window {
}
}
}
Sticker{
height: 100 * DefaultStyle.dp
width: 100 * DefaultStyle.dp
anchors.right: parent.right
anchors.bottom: parent.bottom
visible: mainWindow.call.core.cameraEnabled
AccountProxy{
id: accounts
}
account: accounts.defaultAccount
}
}

View file

@ -30,6 +30,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Item/DigitInput.qml
view/Item/EffectImage.qml
view/Item/ErrorText.qml
view/Item/MovableMouseArea.qml
view/Item/NumericPad.qml
view/Item/PhoneNumberComboBox.qml
view/Item/PhoneNumberInput.qml

View file

@ -1,4 +1,5 @@
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import QtQuick.Controls as Control
import Linphone
@ -9,20 +10,56 @@ import UtilsCpp 1.0
// The loader restart in case of resetting the renderer. This allow to display the avatar while loading.
// TODO: sizes, colors, decorations
Rectangle{
Item {
id: mainItem
height: 300
width: 200
property CallGui call: null
property AccountGui account: null
color: 'gray'
Avatar{
property bool enablePersonalCamera: false
onEnablePersonalCameraChanged: console.log ("enable camera", enablePersonalCamera)
property color color: DefaultStyle.grey_600
property int radius: 15 * DefaultStyle.dp
property var peerAddress: call ? UtilsCpp.getDisplayName(call.core.peerAddress) : null
property var identityAddress: account ? UtilsCpp.getDisplayName(account.core.identityAddress) : null
Rectangle {
id: background
color: mainItem.color
radius: mainItem.radius
anchors.fill: parent
ColumnLayout {
anchors.centerIn: parent
visible: !cameraLoader.active || cameraLoader.status != Loader.Ready || !cameraLoader.item.isReady
Avatar{
Layout.alignment: Qt.AlignHCenter
height: 100
width: height
account: mainItem.account
call: mainItem.call
visible: !cameraLoader.active || cameraLoader.status != Loader.Ready || !cameraLoader.item.isReady
}
Text {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 15 * DefaultStyle.dp
visible: mainItem.call && mainItem.call != undefined
text: mainItem.peerAddress ? mainItem.peerAddress.value : ""
color: DefaultStyle.grey_0
font {
pixelSize: 22 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
capitalization: Font.Capitalize
}
}
Text {
Layout.alignment: Qt.AlignHCenter
visible: mainItem.call && mainItem.call != undefined
text: mainItem.call && mainItem.call.core.peerAddress
color: DefaultStyle.grey_0
font {
pixelSize: 14 * DefaultStyle.dp
weight: 300 * DefaultStyle.dp
}
}
}
Loader{
id: cameraLoader
@ -32,7 +69,8 @@ Rectangle{
interval: 1
onTriggered: {cameraLoader.active=false; cameraLoader.active=true;}
}
active: mainItem.visible && (!call || call.core.remoteVideoEnabled)
active: mainItem.visible && call ? call.core.remoteVideoEnabled : mainItem.enablePersonalCamera
onActiveChanged: console.log("camera active", active)
sourceComponent: cameraComponent
}
Component{
@ -54,4 +92,36 @@ Rectangle{
}
}
}
Text {
id: bottomAddress
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.leftMargin: 10 * DefaultStyle.dp
anchors.bottomMargin: 10 * DefaultStyle.dp
width: txtMeter.width
text: mainItem.call && mainItem.peerAddress
? mainItem.peerAddress.value
: mainItem.account && mainItem.identityAddress
? mainItem.identityAddress.value
: ""
color: DefaultStyle.grey_0
font {
pixelSize: 14 * DefaultStyle.dp
weight: 500 * DefaultStyle.dp
}
}
TextMetrics {
id: txtMeter
text: bottomAddress.text
}
}
MultiEffect {
id: shadow
source: background
anchors.fill: background
shadowEnabled: true
shadowColor: DefaultStyle.grey_1000
shadowBlur: 1
shadowOpacity: 0.4
}
}

View file

@ -0,0 +1,80 @@
import QtQuick 2.7
MouseArea{
id: mainItem
property var movableArea: mainItem.Window.contentItem
signal requestResetPosition()
property bool dragging: drag.active
onDraggingChanged: {
if(dragging){
xClicked = mouseX
yClicked = mouseY
}
}
// Position buffer
property int xClicked : 0
property int yClicked : 0
// Scaling buffer
property int heightOrigin
property int widthOrigin
property double startTime: 0 // For acceleration computation to avoid excessive wheel scrolling
property double mScale: 1.0 // Using scale reduce quality. Apply our factor.
property bool scaled : false // Zoom state : -for storing origin state ; -for resetting scale on right click. In this case, second click lead to emit reset signal instead of first..
acceptedButtons: Qt.LeftButton | Qt.RightButton // Left is for Dragging. Right is for resetting. Wheel will scale.
cursorShape: dragging ? Qt.DragMoveCursor : undefined
preventStealing: true
propagateComposedEvents: true
hoverEnabled: true
function updateScale(){// Avoid scaling if leading outside movableArea.
drag.target.height = Math.max(0, Math.min(movableArea.height, heightOrigin * mScale))
drag.target.width = Math.max(0, Math.min(movableArea.width, widthOrigin * mScale))
updatePosition(0,0)
}
function updatePosition(x, y){// Avoid moving outside movableArea.
var parentTLBounds = drag.target.parent.mapFromItem(movableArea, 0, 0);
var parentBRBounds = drag.target.parent.mapFromItem(movableArea, movableArea.width, movableArea.height);
drag.target.x = Math.max(parentTLBounds.x, Math.min(parentBRBounds.x - drag.target.width, drag.target.x + x))
drag.target.y = Math.max(parentTLBounds.y, Math.min(parentBRBounds.y - drag.target.height, drag.target.y + y))
}
onMScaleChanged: updateScale()
onPositionChanged: {
if(dragging){
updatePosition(mouse.x - xClicked, mouse.y - yClicked)
}
mouse.accepted = false
}
onWheel: {
if(!scaled){
scaled = true
heightOrigin = drag.target.height
widthOrigin = drag.target.width
}
var acceleration = 0.01; // Try to make smoother the scaling from wheel
if(startTime == 0){
startTime = new Date().getTime();
}else{
var delay = new Date().getTime() - startTime;
if(delay > 0)
acceleration = Math.max(0.01, Math.min(1, 4/delay));
else
acceleration = 1
}
mScale = Math.max(0.5 , mScale * ( 1 + acceleration*(wheel.angleDelta.y >0 ? 1 : -1) ));
startTime = new Date().getTime();
}
onClicked: {
if(mouse.button == Qt.RightButton){
if(scaled) {
scaled = false
mScale = 1.0
}else
requestResetPosition()
}
mouse.accepted = false
}
}