video call
This commit is contained in:
parent
449fc17f78
commit
4782bd2990
5 changed files with 234 additions and 104 deletions
|
|
@ -49,6 +49,7 @@ void CallModel::accept(bool withVideo) {
|
||||||
auto core = CoreModel::getInstance()->getCore();
|
auto core = CoreModel::getInstance()->getCore();
|
||||||
auto params = core->createCallParams(mMonitor);
|
auto params = core->createCallParams(mMonitor);
|
||||||
params->enableVideo(withVideo);
|
params->enableVideo(withVideo);
|
||||||
|
mMonitor->enableCamera(withVideo);
|
||||||
// Answer with local call address.
|
// Answer with local call address.
|
||||||
auto localAddress = mMonitor->getCallLog()->getLocalAddress();
|
auto localAddress = mMonitor->getCallLog()->getLocalAddress();
|
||||||
for (auto account : core->getAccountList()) {
|
for (auto account : core->getAccountList()) {
|
||||||
|
|
@ -109,6 +110,9 @@ void CallModel::setSpeakerMuted(bool isMuted) {
|
||||||
void CallModel::setCameraEnabled(bool enabled) {
|
void CallModel::setCameraEnabled(bool enabled) {
|
||||||
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
|
||||||
mMonitor->enableCamera(enabled);
|
mMonitor->enableCamera(enabled);
|
||||||
|
auto core = CoreModel::getInstance()->getCore();
|
||||||
|
auto params = core->createCallParams(mMonitor);
|
||||||
|
params->enableVideo(enabled);
|
||||||
emit cameraEnabledChanged(enabled);
|
emit cameraEnabledChanged(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,15 +15,17 @@ Window {
|
||||||
|
|
||||||
property CallGui call
|
property CallGui call
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: call.core
|
||||||
|
onRemoteVideoEnabledChanged: console.log("remote video enabled", call.core.remoteVideoEnabled)
|
||||||
|
}
|
||||||
|
|
||||||
onCallChanged: {
|
onCallChanged: {
|
||||||
waitingTime.seconds = 0
|
waitingTime.seconds = 0
|
||||||
waitingTimer.restart()
|
waitingTimer.restart()
|
||||||
console.log("call changed", call, waitingTime.seconds)
|
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
|
property var callState: call.core.state
|
||||||
onCallStateChanged: {
|
onCallStateChanged: {
|
||||||
console.log("State:", callState)
|
console.log("State:", callState)
|
||||||
|
|
@ -296,7 +298,6 @@ Window {
|
||||||
}
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Control.Control {
|
Control.Control {
|
||||||
id: centerItem
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredWidth: 1059 * DefaultStyle.dp
|
Layout.preferredWidth: 1059 * DefaultStyle.dp
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
@ -309,23 +310,26 @@ Window {
|
||||||
radius: 15 * DefaultStyle.dp
|
radius: 15 * DefaultStyle.dp
|
||||||
}
|
}
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
|
id: centerItem
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
StackLayout {
|
StackLayout {
|
||||||
id: centerLayout
|
id: centerLayout
|
||||||
|
currentIndex: 0
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
Connections {
|
Connections {
|
||||||
target: mainWindow
|
target: mainWindow
|
||||||
onCallStateChanged: {
|
onCallStateChanged: {
|
||||||
if (mainWindow.call.core.state === LinphoneEnums.CallState.Error) {
|
if (mainWindow.call.core.state === LinphoneEnums.CallState.Error) {
|
||||||
centerLayout.currentIndex = 2
|
centerLayout.currentIndex = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Item {
|
Sticker {
|
||||||
id: audioCallItem
|
visible: false
|
||||||
Layout.alignment: Qt.AlignHCenter
|
call: mainWindow.call
|
||||||
Layout.preferredWidth: parent.width
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: parent.height
|
Layout.fillHeight: true
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: waitingTimer
|
id: waitingTimer
|
||||||
interval: 1000
|
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 {
|
ColumnLayout {
|
||||||
id: userNotFoundLayout
|
id: userNotFoundLayout
|
||||||
|
|
@ -413,17 +379,36 @@ Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Text {
|
Sticker {
|
||||||
anchors.left: parent.left
|
id: preview
|
||||||
anchors.right: parent.right
|
height: 180 * DefaultStyle.dp
|
||||||
anchors.bottom: parent.bottom
|
width: 300 * DefaultStyle.dp
|
||||||
anchors.leftMargin: 10 * DefaultStyle.dp
|
anchors.right: centerItem.right
|
||||||
|
anchors.bottom: centerItem.bottom
|
||||||
|
anchors.rightMargin: 10 * DefaultStyle.dp
|
||||||
anchors.bottomMargin: 10 * DefaultStyle.dp
|
anchors.bottomMargin: 10 * DefaultStyle.dp
|
||||||
text: mainWindow.peerNameText
|
AccountProxy{
|
||||||
color: DefaultStyle.grey_0
|
id: accounts
|
||||||
font {
|
}
|
||||||
pixelSize: 14 * DefaultStyle.dp
|
account: accounts.defaultAccount
|
||||||
weight: 500 * DefaultStyle.dp
|
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 {
|
Text {
|
||||||
id: delegateName
|
id: delegateName
|
||||||
text: UtilsCpp.getDisplayName(modelData.core.peerAddress).value
|
property var remoteAddress: UtilsCpp.getDisplayName(modelData.core.peerAddress)
|
||||||
|
text: remoteAddress ? remoteAddress.value : ""
|
||||||
Connections {
|
Connections {
|
||||||
target: modelData.core
|
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
|
||||||
view/Item/DigitInput.qml
|
view/Item/DigitInput.qml
|
||||||
view/Item/EffectImage.qml
|
view/Item/EffectImage.qml
|
||||||
view/Item/ErrorText.qml
|
view/Item/ErrorText.qml
|
||||||
|
view/Item/MovableMouseArea.qml
|
||||||
view/Item/NumericPad.qml
|
view/Item/NumericPad.qml
|
||||||
view/Item/PhoneNumberComboBox.qml
|
view/Item/PhoneNumberComboBox.qml
|
||||||
view/Item/PhoneNumberInput.qml
|
view/Item/PhoneNumberInput.qml
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls as Control
|
import QtQuick.Controls as Control
|
||||||
import Linphone
|
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.
|
// The loader restart in case of resetting the renderer. This allow to display the avatar while loading.
|
||||||
|
|
||||||
// TODO: sizes, colors, decorations
|
// TODO: sizes, colors, decorations
|
||||||
Rectangle{
|
Item {
|
||||||
id: mainItem
|
id: mainItem
|
||||||
height: 300
|
height: 300
|
||||||
width: 200
|
width: 200
|
||||||
property CallGui call: null
|
property CallGui call: null
|
||||||
property AccountGui account: null
|
property AccountGui account: null
|
||||||
color: 'gray'
|
property bool enablePersonalCamera: false
|
||||||
Avatar{
|
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
|
anchors.centerIn: parent
|
||||||
|
visible: !cameraLoader.active || cameraLoader.status != Loader.Ready || !cameraLoader.item.isReady
|
||||||
|
Avatar{
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
height: 100
|
height: 100
|
||||||
width: height
|
width: height
|
||||||
account: mainItem.account
|
account: mainItem.account
|
||||||
call: mainItem.call
|
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{
|
Loader{
|
||||||
id: cameraLoader
|
id: cameraLoader
|
||||||
|
|
@ -32,12 +69,13 @@ Rectangle{
|
||||||
interval: 1
|
interval: 1
|
||||||
onTriggered: {cameraLoader.active=false; cameraLoader.active=true;}
|
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
|
sourceComponent: cameraComponent
|
||||||
}
|
}
|
||||||
Component{
|
Component{
|
||||||
id: cameraComponent
|
id: cameraComponent
|
||||||
Item{
|
Item {
|
||||||
height: cameraLoader.height
|
height: cameraLoader.height
|
||||||
width: cameraLoader.width
|
width: cameraLoader.width
|
||||||
property bool isReady: cameraItem.visible
|
property bool isReady: cameraItem.visible
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
80
Linphone/view/Item/MovableMouseArea.qml
Normal file
80
Linphone/view/Item/MovableMouseArea.qml
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue