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