mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-02-07 15:08:24 +00:00
- Add Fullscreen mode for video conference.
- Factorization of camera window ID and fix crash from removing GUI while stopping streams. - Fix layout changing and Active Speaker crash.
This commit is contained in:
parent
d8b33de489
commit
d344701e99
11 changed files with 617 additions and 65 deletions
|
|
@ -400,6 +400,7 @@
|
|||
<file>ui/views/App/Calls/Conference.qml</file>
|
||||
<file>ui/views/App/Calls/VideoConference.qml</file>
|
||||
<file>ui/views/App/Calls/VideoConferenceActiveSpeaker.qml</file>
|
||||
<file>ui/views/App/Calls/VideoConferenceFullscreen.qml</file>
|
||||
<file>ui/views/App/Calls/VideoConferenceGrid.qml</file>
|
||||
<file>ui/views/App/Calls/VideoConferenceMenu.qml</file>
|
||||
<file>ui/views/App/Calls/Dialogs/CallSipAddress.qml</file>
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ int Camera::mPreviewCounter;
|
|||
|
||||
// =============================================================================
|
||||
Camera::Camera (QQuickItem *parent) : QQuickFramebufferObject(parent) {
|
||||
updateWindowIdLocation();
|
||||
setTextureFollowsItemSize(true);
|
||||
// The fbo content must be y-mirrored because the ms rendering is y-inverted.
|
||||
setMirrorVertically(true);
|
||||
|
|
@ -60,72 +61,109 @@ Camera::Camera (QQuickItem *parent) : QQuickFramebufferObject(parent) {
|
|||
}
|
||||
|
||||
Camera::~Camera(){
|
||||
qWarning() << "Camera destructor" << this;
|
||||
if(mIsPreview)
|
||||
deactivatePreview();
|
||||
// else
|
||||
// resetWindowId();
|
||||
setWindowIdLocation(None);
|
||||
}
|
||||
|
||||
void Camera::resetWindowId() {
|
||||
if(mIsPreview)
|
||||
CoreManager::getInstance()->getCore()->setNativePreviewWindowId(NULL);
|
||||
else if( mCallModel && mCallModel->getCall())
|
||||
mCallModel->getCall()->setNativeVideoWindowId(NULL);
|
||||
else if(mParticipantDeviceModel){
|
||||
if(mParticipantDeviceModel->getDevice())
|
||||
mParticipantDeviceModel->getDevice()->setNativeVideoWindowId(NULL);
|
||||
}else
|
||||
CoreManager::getInstance()->getCore()->setNativeVideoWindowId(NULL);
|
||||
void Camera::resetWindowId() const{
|
||||
if(mIsWindowIdSet){
|
||||
QQuickFramebufferObject::Renderer * oldRenderer = NULL;
|
||||
if(mWindowIdLocation == CorePreview){
|
||||
oldRenderer = (QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->getNativePreviewWindowId();
|
||||
if(oldRenderer)
|
||||
CoreManager::getInstance()->getCore()->setNativePreviewWindowId(NULL);
|
||||
}else if( mWindowIdLocation == Call){
|
||||
if(mCallModel){
|
||||
auto call = mCallModel->getCall();
|
||||
if( call ){
|
||||
oldRenderer = (QQuickFramebufferObject::Renderer *) call->getNativeVideoWindowId();
|
||||
if(oldRenderer)
|
||||
call->setNativeVideoWindowId(NULL);
|
||||
}
|
||||
}
|
||||
}else if(mWindowIdLocation == Device){
|
||||
if(mParticipantDeviceModel){
|
||||
auto device = mParticipantDeviceModel->getDevice();
|
||||
if( device ){
|
||||
oldRenderer = (QQuickFramebufferObject::Renderer *)device->getNativeVideoWindowId();
|
||||
if(oldRenderer)
|
||||
mParticipantDeviceModel->getDevice()->setNativeVideoWindowId(NULL);
|
||||
}
|
||||
}
|
||||
}else if( mWindowIdLocation == Core){
|
||||
oldRenderer = (QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->getNativeVideoWindowId();
|
||||
if(oldRenderer)
|
||||
CoreManager::getInstance()->getCore()->setNativeVideoWindowId(NULL);
|
||||
}
|
||||
qWarning() << "Removed " << oldRenderer << " at " << mWindowIdLocation << " for " << this;
|
||||
mIsWindowIdSet = false;
|
||||
}
|
||||
}
|
||||
|
||||
QQuickFramebufferObject::Renderer *Camera::createRenderer () const {
|
||||
QQuickFramebufferObject::Renderer * renderer = NULL;
|
||||
void Camera::setWindowIdLocation(const WindowIdLocation& location){
|
||||
if( mWindowIdLocation != location){
|
||||
resetWindowId();// Location change: Reset old window ID.
|
||||
mWindowIdLocation = location;
|
||||
}
|
||||
}
|
||||
void Camera::updateWindowIdLocation(){
|
||||
bool useDefaultWindow = true;
|
||||
if(mIsPreview){
|
||||
qWarning() << "Setting Camera to Preview";
|
||||
renderer = (QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->getNativePreviewWindowId();
|
||||
if(renderer)
|
||||
CoreManager::getInstance()->getCore()->setNativePreviewWindowId(NULL);// Reset
|
||||
renderer=(QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->createNativePreviewWindowId();
|
||||
if(renderer)
|
||||
CoreManager::getInstance()->getCore()->setNativePreviewWindowId(renderer);
|
||||
}else{
|
||||
if(mIsPreview)
|
||||
setWindowIdLocation( WindowIdLocation::CorePreview);
|
||||
else{
|
||||
if(mCallModel){
|
||||
auto call = mCallModel->getCall();
|
||||
if(call){
|
||||
qWarning() << "Setting Camera to CallModel";
|
||||
renderer = (QQuickFramebufferObject::Renderer *) call->getNativeVideoWindowId();
|
||||
if(renderer)
|
||||
call->setNativeVideoWindowId(NULL);// Reset
|
||||
renderer = (QQuickFramebufferObject::Renderer *) call->createNativeVideoWindowId();
|
||||
if(renderer)
|
||||
call->setNativeVideoWindowId(renderer);
|
||||
setWindowIdLocation( WindowIdLocation::Call);
|
||||
useDefaultWindow = false;
|
||||
}
|
||||
}else if( mParticipantDeviceModel){
|
||||
auto participantDevice = mParticipantDeviceModel->getDevice();
|
||||
if(participantDevice){
|
||||
qWarning() << "Setting Camera to Participant Device";
|
||||
renderer = (QQuickFramebufferObject::Renderer *)participantDevice->getNativeVideoWindowId();
|
||||
if(renderer)
|
||||
participantDevice->setNativeVideoWindowId(NULL);// Reset
|
||||
qWarning() << "Trying to create new window ID for " << participantDevice->getName().c_str() << ", addr=" << participantDevice->getAddress()->asString().c_str();
|
||||
renderer = (QQuickFramebufferObject::Renderer *) participantDevice->createNativeVideoWindowId();
|
||||
if(renderer)
|
||||
participantDevice->setNativeVideoWindowId(renderer);
|
||||
setWindowIdLocation(WindowIdLocation::Device);
|
||||
useDefaultWindow = false;
|
||||
}
|
||||
}
|
||||
if(useDefaultWindow){
|
||||
qWarning() << "Setting Camera to Defaul tWindow";
|
||||
renderer = (QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->getNativeVideoWindowId();
|
||||
if(renderer)
|
||||
CoreManager::getInstance()->getCore()->setNativeVideoWindowId(NULL);
|
||||
renderer = (QQuickFramebufferObject::Renderer *) CoreManager::getInstance()->getCore()->createNativeVideoWindowId();
|
||||
if(renderer)
|
||||
CoreManager::getInstance()->getCore()->setNativeVideoWindowId(renderer);
|
||||
setWindowIdLocation(WindowIdLocation::Core);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQuickFramebufferObject::Renderer *Camera::createRenderer () const {
|
||||
resetWindowId();
|
||||
|
||||
QQuickFramebufferObject::Renderer * renderer = NULL;
|
||||
if(mWindowIdLocation == CorePreview){
|
||||
qWarning() << "Setting Camera to Preview";
|
||||
renderer=(QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->createNativePreviewWindowId();
|
||||
if(renderer)
|
||||
CoreManager::getInstance()->getCore()->setNativePreviewWindowId(renderer);
|
||||
}else if(mWindowIdLocation == Call){
|
||||
auto call = mCallModel->getCall();
|
||||
if(call){
|
||||
qWarning() << "Setting Camera to CallModel";
|
||||
renderer = (QQuickFramebufferObject::Renderer *) call->createNativeVideoWindowId();
|
||||
if(renderer)
|
||||
call->setNativeVideoWindowId(renderer);
|
||||
}
|
||||
}else if( mWindowIdLocation == Device) {
|
||||
auto participantDevice = mParticipantDeviceModel->getDevice();
|
||||
if(participantDevice){
|
||||
qWarning() << "Setting Camera to Participant Device";
|
||||
qWarning() << "Trying to create new window ID for " << participantDevice->getName().c_str() << ", addr=" << participantDevice->getAddress()->asString().c_str();
|
||||
renderer = (QQuickFramebufferObject::Renderer *) participantDevice->createNativeVideoWindowId();
|
||||
if(renderer)
|
||||
participantDevice->setNativeVideoWindowId(renderer);
|
||||
}
|
||||
}else if( mWindowIdLocation == Core){
|
||||
qWarning() << "Setting Camera to Default Window";
|
||||
renderer = (QQuickFramebufferObject::Renderer *) CoreManager::getInstance()->getCore()->createNativeVideoWindowId();
|
||||
if(renderer)
|
||||
CoreManager::getInstance()->getCore()->setNativeVideoWindowId(renderer);
|
||||
}
|
||||
if( !renderer){
|
||||
QTimer::singleShot(1, this, &Camera::isNotReady);// Workaround for const createRenderer
|
||||
qWarning() << "Camera stream couldn't start for Rendering. Retrying in 1s";
|
||||
|
|
@ -133,6 +171,8 @@ QQuickFramebufferObject::Renderer *Camera::createRenderer () const {
|
|||
QTimer::singleShot(1000, this, &Camera::requestNewRenderer);
|
||||
|
||||
}else{
|
||||
mIsWindowIdSet = true;
|
||||
qWarning() << "Added " << renderer << " at " << mWindowIdLocation << " for " << this;
|
||||
QTimer::singleShot(1, this, &Camera::isReady);// Workaround for const createRenderer
|
||||
}
|
||||
return renderer;
|
||||
|
|
@ -159,6 +199,7 @@ ParticipantDeviceModel * Camera::getParticipantDeviceModel() const{
|
|||
void Camera::setCallModel (CallModel *callModel) {
|
||||
if (mCallModel != callModel) {
|
||||
mCallModel = callModel;
|
||||
updateWindowIdLocation();
|
||||
update();
|
||||
|
||||
emit callChanged(mCallModel);
|
||||
|
|
@ -172,6 +213,7 @@ void Camera::setIsPreview (bool status) {
|
|||
activatePreview();
|
||||
else
|
||||
deactivatePreview();
|
||||
updateWindowIdLocation();
|
||||
update();
|
||||
|
||||
emit isPreviewChanged(status);
|
||||
|
|
@ -188,6 +230,7 @@ void Camera::setIsReady(bool status) {
|
|||
void Camera::setParticipantDeviceModel(ParticipantDeviceModel * participantDeviceModel){
|
||||
if (mParticipantDeviceModel != participantDeviceModel) {
|
||||
mParticipantDeviceModel = participantDeviceModel;
|
||||
updateWindowIdLocation();
|
||||
update();
|
||||
emit participantDeviceModelChanged(mParticipantDeviceModel);
|
||||
}
|
||||
|
|
@ -214,6 +257,5 @@ void Camera::deactivatePreview(){
|
|||
if (--mPreviewCounter == 0)
|
||||
core->enableVideoPreview(false);
|
||||
mPreviewCounterMutex.unlock();
|
||||
core->setNativePreviewWindowId(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,14 @@ class Camera : public QQuickFramebufferObject {
|
|||
Q_PROPERTY(ParticipantDeviceModel * participantDeviceModel READ getParticipantDeviceModel WRITE setParticipantDeviceModel NOTIFY participantDeviceModelChanged)
|
||||
Q_PROPERTY(bool isPreview READ getIsPreview WRITE setIsPreview NOTIFY isPreviewChanged);
|
||||
Q_PROPERTY(bool isReady READ getIsReady WRITE setIsReady NOTIFY isReadyChanged);
|
||||
|
||||
typedef enum{
|
||||
None = -1,
|
||||
CorePreview = 0,
|
||||
Call,
|
||||
Device,
|
||||
Core
|
||||
}WindowIdLocation;
|
||||
|
||||
public:
|
||||
Camera (QQuickItem *parent = Q_NULLPTR);
|
||||
|
|
@ -51,7 +59,7 @@ public:
|
|||
|
||||
QQuickFramebufferObject::Renderer *createRenderer () const override;
|
||||
|
||||
Q_INVOKABLE void resetWindowId();
|
||||
Q_INVOKABLE void resetWindowId() const; // const to be used from createRenderer()
|
||||
|
||||
static QMutex mPreviewCounterMutex;
|
||||
static int mPreviewCounter;
|
||||
|
|
@ -76,14 +84,19 @@ private:
|
|||
void setIsPreview (bool status);
|
||||
void setIsReady(bool status);
|
||||
void setParticipantDeviceModel(ParticipantDeviceModel * participantDeviceModel);
|
||||
void setWindowIdLocation(const WindowIdLocation& location);
|
||||
|
||||
void activatePreview();
|
||||
void deactivatePreview();
|
||||
void updateWindowIdLocation();
|
||||
|
||||
bool mIsPreview = false;
|
||||
bool mIsReady = false;
|
||||
CallModel *mCallModel = nullptr;
|
||||
ParticipantDeviceModel *mParticipantDeviceModel = nullptr;
|
||||
|
||||
WindowIdLocation mWindowIdLocation = None;
|
||||
mutable bool mIsWindowIdSet = false;
|
||||
|
||||
QTimer *mRefreshTimer = nullptr;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ Item {
|
|||
isPreview: container.isPreview
|
||||
|
||||
onRequestNewRenderer: {resetTimer.resetActive()}
|
||||
Component.onDestruction: {resetWindowId(); console.log("Destroyed Camera [" + isPreview + "] : " + camera)}
|
||||
Component.onDestruction: {/*resetWindowId();*/ console.log("Destroyed Camera [" + isPreview + "] : " + camera)}
|
||||
Component.onCompleted: console.log("Completed Camera [" + isPreview + "] : " + camera)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,27 +123,28 @@ function openMediaParameters (window, incall) {
|
|||
call: incall.call
|
||||
})
|
||||
}
|
||||
|
||||
function showFullscreen (position) {
|
||||
incall.isFullScreen = true
|
||||
if (incall._fullscreen) {
|
||||
incall._fullscreen.raise()
|
||||
// callerId = incall, qmlFile = 'IncallFullscreenWindow.qml'
|
||||
// callerId need to have : _fullscreen and isFullScreen
|
||||
function showFullscreen (window, callerId, qmlFile, position) {
|
||||
callerId.isFullScreen = true
|
||||
if (callerId._fullscreen) {
|
||||
callerId._fullscreen.raise()
|
||||
return
|
||||
}
|
||||
DesktopTools.DesktopTools.screenSaverStatus = false
|
||||
var parameters = {
|
||||
caller: incall,
|
||||
caller: callerId,
|
||||
x:position.x,
|
||||
y:position.y,
|
||||
width:window.width,
|
||||
height:window.height,
|
||||
window:window
|
||||
}
|
||||
incall._fullscreen = Utils.openWindow(Qt.resolvedUrl('IncallFullscreenWindow.qml'), parameters.window, {
|
||||
callerId._fullscreen = Utils.openWindow(Qt.resolvedUrl(qmlFile), parameters.window, {
|
||||
properties: parameters
|
||||
}, true)
|
||||
if(incall._fullscreen) {
|
||||
incall._fullscreen.cameraIsReady = Qt.binding(function(){ return !incall.cameraIsReady})
|
||||
incall._fullscreen.previewIsReady = Qt.binding(function(){ return !incall.previewIsReady})
|
||||
if(callerId._fullscreen) {
|
||||
callerId._fullscreen.cameraIsReady = Qt.binding(function(){ return !callerId.cameraIsReady})
|
||||
callerId._fullscreen.previewIsReady = Qt.binding(function(){ return !callerId.previewIsReady})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ Rectangle {
|
|||
colorSet: CallStyle.buttons.fullscreen
|
||||
visible: incall.call.videoEnabled
|
||||
|
||||
onClicked: Logic.showFullscreen(contactDescription.mapToGlobal(0,0))
|
||||
onClicked: Logic.showFullscreen(window, incall, 'IncallFullscreenWindow.qml', contactDescription.mapToGlobal(0,0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,12 @@ Rectangle {
|
|||
|
||||
property CallModel callModel
|
||||
property ConferenceModel conferenceModel: callModel && callModel.getConferenceModel()
|
||||
property bool cameraIsReady : false
|
||||
property bool previewIsReady : false
|
||||
property bool isFullScreen: false // Use this variable to test if we are in fullscreen. Do not test _fullscreen : we need to clean memory before having the window (see .js file)
|
||||
property var _fullscreen: null
|
||||
on_FullscreenChanged: if( !_fullscreen) isFullScreen = false
|
||||
|
||||
property bool listCallsOpened: true
|
||||
|
||||
signal openListCallsRequest()
|
||||
|
|
@ -115,6 +120,7 @@ Rectangle {
|
|||
}
|
||||
// Title
|
||||
Text{
|
||||
id: title
|
||||
Timer{
|
||||
id: elapsedTimeRefresher
|
||||
running: true
|
||||
|
|
@ -152,7 +158,8 @@ Rectangle {
|
|||
isCustom: true
|
||||
backgroundRadius: width/2
|
||||
colorSet: VideoConferenceStyle.buttons.fullscreen
|
||||
visible: false //TODO
|
||||
visible: conference.callModel.videoEnabled
|
||||
onClicked: Logic.showFullscreen(window, conference, 'VideoConferenceFullscreen.qml', title.mapToGlobal(0,0))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -184,6 +191,7 @@ Rectangle {
|
|||
anchors.leftMargin: 70
|
||||
anchors.rightMargin: rightMenu.visible ? 15 : 70
|
||||
callModel: conference.callModel
|
||||
isFullScreen: conference.isFullScreen
|
||||
}
|
||||
}
|
||||
Component{
|
||||
|
|
@ -193,6 +201,7 @@ Rectangle {
|
|||
callModel: conference.callModel
|
||||
isRightReducedLayout: rightMenu.visible
|
||||
isLeftReducedLayout: conference.listCallsOpened
|
||||
isFullScreen: conference.isFullScreen
|
||||
}
|
||||
}
|
||||
RowLayout{
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ Item {
|
|||
property alias callModel: allDevices.callModel
|
||||
property bool isRightReducedLayout: false
|
||||
property bool isLeftReducedLayout: false
|
||||
property bool isFullScreen: false
|
||||
property alias showMe : allDevices.showMe
|
||||
property int participantCount: allDevices.count
|
||||
|
||||
|
|
@ -39,11 +40,13 @@ Item {
|
|||
CameraView{
|
||||
id: cameraView
|
||||
callModel: mainItem.callModel
|
||||
enabled: !mainItem.isFullScreen
|
||||
isCameraFromDevice: false
|
||||
isPreview: false
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: isRightReducedLayout || isLeftReducedLayout? 30 : 140
|
||||
anchors.rightMargin: isRightReducedLayout ? 10 : 140
|
||||
isPaused: callModel.pausedByUser || currentDevice && currentDevice.isPaused //callModel.pausedByUser
|
||||
isPaused: (callModel && callModel.pausedByUser) || (currentDevice && currentDevice.isPaused) //callModel.pausedByUser
|
||||
showCloseButton: false
|
||||
color: 'black'
|
||||
}
|
||||
|
|
@ -71,7 +74,7 @@ Item {
|
|||
anchors.centerIn: parent
|
||||
height: miniViews.cellHeight - 6
|
||||
width: miniViews.width - 6
|
||||
enabled: index >=0
|
||||
enabled: index >=0 && !mainItem.isFullScreen
|
||||
currentDevice: modelData
|
||||
callModel: mainItem.callModel
|
||||
isCameraFromDevice: true
|
||||
|
|
|
|||
482
linphone-app/ui/views/App/Calls/VideoConferenceFullscreen.qml
Normal file
482
linphone-app/ui/views/App/Calls/VideoConferenceFullscreen.qml
Normal file
|
|
@ -0,0 +1,482 @@
|
|||
import QtQuick 2.7
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQml.Models 2.12
|
||||
import QtGraphicalEffects 1.12
|
||||
|
||||
import Common 1.0
|
||||
import Common.Styles 1.0
|
||||
import Linphone 1.0
|
||||
import LinphoneUtils 1.0
|
||||
|
||||
import DesktopTools 1.0
|
||||
import LinphoneEnums 1.0
|
||||
import UtilsCpp 1.0
|
||||
|
||||
|
||||
import App.Styles 1.0
|
||||
|
||||
|
||||
// Temp
|
||||
import 'Incall.js' as Logic
|
||||
import 'qrc:/ui/scripts/Utils/utils.js' as Utils
|
||||
|
||||
Window {
|
||||
id: window
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
property alias callModel: conference.callModel
|
||||
property var caller
|
||||
property bool hideButtons: !hideButtonsTimer.running
|
||||
property bool cameraIsReady : false
|
||||
property bool previewIsReady : false
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function exit (cb) {
|
||||
DesktopTools.screenSaverStatus = true
|
||||
// `exit` is called by `Incall.qml`.
|
||||
// The `window` id can be null if the window was closed in this view.
|
||||
if (!window) {
|
||||
return
|
||||
}
|
||||
if(!window.close() && parent)
|
||||
parent.close()
|
||||
if (cb) {
|
||||
cb()
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
onCallModelChanged: if(!callModel) window.exit()
|
||||
Component.onCompleted: {
|
||||
window.callModel = caller.callModel
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Close
|
||||
onActivated: window.exit()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// =============================================================================
|
||||
|
||||
Rectangle {
|
||||
id: conference
|
||||
|
||||
property CallModel callModel
|
||||
property ConferenceModel conferenceModel: callModel && callModel.getConferenceModel()
|
||||
property var _fullscreen: null
|
||||
property bool listCallsOpened: true
|
||||
|
||||
signal openListCallsRequest()
|
||||
// ---------------------------------------------------------------------------
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
|
||||
Keys.onEscapePressed: window.exit()
|
||||
color: VideoConferenceStyle.backgroundColor
|
||||
|
||||
Connections {
|
||||
target: callModel
|
||||
|
||||
onCameraFirstFrameReceived: Logic.handleCameraFirstFrameReceived(width, height)
|
||||
onStatusChanged: Logic.handleStatusChanged (status)
|
||||
onVideoRequested: Logic.handleVideoRequested(callModel)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
Rectangle{
|
||||
anchors.fill: parent
|
||||
visible: callModel.pausedByUser
|
||||
color: VideoConferenceStyle.pauseArea.backgroundColor
|
||||
z: 1
|
||||
ColumnLayout{
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
Item{
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
ActionButton{
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
isCustom: true
|
||||
colorSet: VideoConferenceStyle.pauseArea.play
|
||||
backgroundRadius: width/2
|
||||
onClicked: callModel.pausedByUser = !callModel.pausedByUser
|
||||
}
|
||||
Text{
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
text: 'Vous êtes actuellement en dehors de la conférence.'
|
||||
font.pointSize: VideoConferenceStyle.pauseArea.title.pointSize
|
||||
font.weight: VideoConferenceStyle.pauseArea.title.weight
|
||||
color: VideoConferenceStyle.pauseArea.title.color
|
||||
}
|
||||
Text{
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
text: 'Cliquez sur le bouton "play" pour la rejoindre.'
|
||||
font.pointSize: VideoConferenceStyle.pauseArea.description.pointSize
|
||||
font.weight: VideoConferenceStyle.pauseArea.description.weight
|
||||
color: VideoConferenceStyle.pauseArea.description.color
|
||||
}
|
||||
Item{
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 140
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Conference info.
|
||||
// -------------------------------------------------------------------------
|
||||
RowLayout{
|
||||
id: featuresRow
|
||||
// Aux features
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.topMargin: 10
|
||||
anchors.leftMargin: 25
|
||||
anchors.rightMargin: 25
|
||||
spacing: 10
|
||||
|
||||
visible: !window.hideButtons
|
||||
/*
|
||||
ActionButton{
|
||||
isCustom: true
|
||||
backgroundRadius: width/2
|
||||
colorSet: VideoConferenceStyle.buttons.callsList
|
||||
visible: !listCallsOpened && !window.hideButtons
|
||||
onClicked: openListCallsRequest()
|
||||
}*/
|
||||
ActionButton{
|
||||
id: keypadButton
|
||||
isCustom: true
|
||||
backgroundRadius: width/2
|
||||
colorSet: VideoConferenceStyle.buttons.dialpad
|
||||
onClicked: telKeypad.visible = !telKeypad.visible
|
||||
|
||||
visible: !window.hideButtons
|
||||
}
|
||||
// Title
|
||||
Text{
|
||||
Timer{
|
||||
id: elapsedTimeRefresher
|
||||
running: true
|
||||
interval: 1000
|
||||
repeat: true
|
||||
onTriggered: if(conference.conferenceModel) parent.elaspedTime = ' - ' +Utils.formatElapsedTime(conference.conferenceModel.getElapsedSeconds())
|
||||
}
|
||||
property string elaspedTime
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
text: conference.conferenceModel ? conference.conferenceModel.subject+ elaspedTime : ''
|
||||
color: VideoConferenceStyle.title.color
|
||||
font.pointSize: VideoConferenceStyle.title.pointSize
|
||||
}
|
||||
// Mode buttons
|
||||
ActionButton{
|
||||
isCustom: true
|
||||
backgroundRadius: width/2
|
||||
colorSet: VideoConferenceStyle.buttons.screenSharing
|
||||
visible: false //TODO
|
||||
}
|
||||
ActionButton{
|
||||
isCustom: true
|
||||
backgroundRadius: width/2
|
||||
colorSet: VideoConferenceStyle.buttons.recordOff
|
||||
visible: false //TODO
|
||||
}
|
||||
ActionButton{
|
||||
isCustom: true
|
||||
backgroundRadius: width/2
|
||||
colorSet: VideoConferenceStyle.buttons.screenshot
|
||||
visible: false //TODO
|
||||
}
|
||||
ActionButton{
|
||||
isCustom: true
|
||||
backgroundRadius: width/2
|
||||
colorSet: VideoConferenceStyle.buttons.fullscreen
|
||||
onClicked: window.exit()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Contacts visual.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
MouseArea{
|
||||
id: mainGrid
|
||||
anchors.top: featuresRow.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: actionsButtons.top
|
||||
|
||||
anchors.topMargin: 15
|
||||
anchors.bottomMargin: 20
|
||||
onClicked: {
|
||||
if(!conference.callModel)
|
||||
grid.add({color: '#'+ Math.floor(Math.random()*255).toString(16)
|
||||
+Math.floor(Math.random()*255).toString(16)
|
||||
+Math.floor(Math.random()*255).toString(16)})
|
||||
}
|
||||
|
||||
Component{
|
||||
id: gridComponent
|
||||
VideoConferenceGrid{
|
||||
id: grid
|
||||
anchors.leftMargin: 70
|
||||
anchors.rightMargin: rightMenu.visible ? 15 : 70
|
||||
callModel: conference.callModel
|
||||
}
|
||||
}
|
||||
Component{
|
||||
id: activeSpeakerComponent
|
||||
VideoConferenceActiveSpeaker{
|
||||
id: activeSpeaker
|
||||
callModel: conference.callModel
|
||||
isRightReducedLayout: rightMenu.visible
|
||||
isLeftReducedLayout: conference.listCallsOpened
|
||||
}
|
||||
}
|
||||
RowLayout{
|
||||
anchors.fill: parent
|
||||
Loader{
|
||||
id: conferenceLayout
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
sourceComponent: conference.callModel.conferenceVideoLayout == LinphoneEnums.ConferenceLayoutGrid || !conference.callModel.videoEnabled? gridComponent : activeSpeakerComponent
|
||||
onSourceComponentChanged: console.log(conference.callModel.conferenceVideoLayout)
|
||||
active: conference.callModel
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
visible: !conference.callModel || !conferenceLayout.item || conferenceLayout.item.participantCount == 0
|
||||
BusyIndicator{
|
||||
Layout.preferredHeight: 50
|
||||
Layout.preferredWidth: 50
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
running: parent.visible
|
||||
color: VideoConferenceStyle.buzyColor
|
||||
}
|
||||
Text{
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
text: "Video conference is not ready. Please Wait..."
|
||||
color: VideoConferenceStyle.buzyColor
|
||||
}
|
||||
}
|
||||
}
|
||||
VideoConferenceMenu{
|
||||
id: rightMenu
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: 400
|
||||
Layout.rightMargin: 30
|
||||
callModel: conference.callModel
|
||||
visible: false
|
||||
onClose: rightMenu.visible = !rightMenu.visible
|
||||
}
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------
|
||||
// Action Buttons.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// Security
|
||||
ActionButton{
|
||||
visible: false // TODO
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 30
|
||||
anchors.leftMargin: 25
|
||||
height: VideoConferenceStyle.buttons.secure.buttonSize
|
||||
width: height
|
||||
isCustom: true
|
||||
iconIsCustom: false
|
||||
backgroundRadius: width/2
|
||||
colorSet: VideoConferenceStyle.buttons.secure
|
||||
|
||||
icon: 'secure_level_1'
|
||||
}
|
||||
// Action buttons
|
||||
RowLayout{
|
||||
id: actionsButtons
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 30
|
||||
height: 60
|
||||
spacing: 30
|
||||
z: 2
|
||||
visible: !window.hideButtons
|
||||
RowLayout{
|
||||
spacing: 10
|
||||
Row {
|
||||
spacing: 2
|
||||
visible: SettingsModel.muteMicrophoneEnabled
|
||||
property bool microMuted: callModel.microMuted
|
||||
|
||||
VuMeter {
|
||||
enabled: !parent.microMuted
|
||||
Timer {
|
||||
interval: 50
|
||||
repeat: true
|
||||
running: parent.enabled
|
||||
|
||||
onTriggered: parent.value = callModel.microVu
|
||||
}
|
||||
}
|
||||
ActionSwitch {
|
||||
id: micro
|
||||
isCustom: true
|
||||
backgroundRadius: 90
|
||||
colorSet: parent.microMuted ? VideoConferenceStyle.buttons.microOff : VideoConferenceStyle.buttons.microOn
|
||||
onClicked: callModel.microMuted = !parent.microMuted
|
||||
}
|
||||
}
|
||||
Row {
|
||||
spacing: 2
|
||||
property bool speakerMuted: callModel.speakerMuted
|
||||
VuMeter {
|
||||
enabled: !parent.speakerMuted
|
||||
Timer {
|
||||
interval: 50
|
||||
repeat: true
|
||||
running: parent.enabled
|
||||
onTriggered: parent.value = callModel.speakerVu
|
||||
}
|
||||
}
|
||||
ActionSwitch {
|
||||
id: speaker
|
||||
isCustom: true
|
||||
backgroundRadius: 90
|
||||
colorSet: parent.speakerMuted ? VideoConferenceStyle.buttons.speakerOff : VideoConferenceStyle.buttons.speakerOn
|
||||
onClicked: callModel.speakerMuted = !parent.speakerMuted
|
||||
}
|
||||
}
|
||||
ActionSwitch {
|
||||
id: camera
|
||||
isCustom: true
|
||||
backgroundRadius: 90
|
||||
colorSet: callModel && callModel.cameraEnabled ? VideoConferenceStyle.buttons.cameraOn : VideoConferenceStyle.buttons.cameraOff
|
||||
updating: callModel.videoEnabled && callModel.updating
|
||||
enabled: callModel.videoEnabled
|
||||
onClicked: if(callModel) callModel.cameraEnabled = !callModel.cameraEnabled
|
||||
}
|
||||
}
|
||||
RowLayout{
|
||||
spacing: 10
|
||||
ActionButton{
|
||||
isCustom: true
|
||||
backgroundRadius: width/2
|
||||
visible: SettingsModel.callPauseEnabled
|
||||
updating: callModel.updating
|
||||
colorSet: callModel.pausedByUser ? VideoConferenceStyle.buttons.play : VideoConferenceStyle.buttons.pause
|
||||
onClicked: callModel.pausedByUser = !callModel.pausedByUser
|
||||
}
|
||||
ActionButton{
|
||||
isCustom: true
|
||||
backgroundRadius: width/2
|
||||
colorSet: VideoConferenceStyle.buttons.hangup
|
||||
|
||||
onClicked: callModel.terminate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Panel buttons
|
||||
RowLayout{
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 30
|
||||
anchors.rightMargin: 25
|
||||
height: 60
|
||||
visible: !window.hideButtons
|
||||
ActionButton{
|
||||
isCustom: true
|
||||
backgroundRadius: width/2
|
||||
colorSet: VideoConferenceStyle.buttons.chat
|
||||
visible: false // TODO for next version
|
||||
}
|
||||
ActionButton{
|
||||
isCustom: true
|
||||
backgroundRadius: width/2
|
||||
colorSet: VideoConferenceStyle.buttons.participants
|
||||
visible: false // TODO
|
||||
}
|
||||
ActionButton {
|
||||
id: callQuality
|
||||
|
||||
isCustom: true
|
||||
backgroundRadius: 4
|
||||
colorSet: VideoConferenceStyle.buttons.callQuality
|
||||
percentageDisplayed: 0
|
||||
|
||||
onClicked: {Logic.openCallStatistics();}
|
||||
Timer {
|
||||
interval: 500
|
||||
repeat: true
|
||||
running: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
// Note: `quality` is in the [0, 5] interval and -1.
|
||||
var quality = callModel.quality
|
||||
if(quality >= 0)
|
||||
callQuality.percentageDisplayed = quality * 100 / 5
|
||||
else
|
||||
callQuality.percentageDisplayed = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
ActionButton{
|
||||
isCustom: true
|
||||
backgroundRadius: width/2
|
||||
colorSet: VideoConferenceStyle.buttons.options
|
||||
onClicked: rightMenu.visible = !rightMenu.visible
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// TelKeypad.
|
||||
// ---------------------------------------------------------------------------
|
||||
CallStatistics {
|
||||
id: callStatistics
|
||||
|
||||
call: conference.callModel
|
||||
width: conference.width - 20
|
||||
height: conference.height * 2/3
|
||||
relativeTo: conference
|
||||
relativeY: CallStyle.header.stats.relativeY
|
||||
relativeX: 10
|
||||
onClosed: Logic.handleCallStatisticsClosed()
|
||||
}
|
||||
}
|
||||
TelKeypad {
|
||||
id: telKeypad
|
||||
|
||||
call: callModel
|
||||
visible: SettingsModel.showTelKeypadAutomatically
|
||||
}
|
||||
MouseArea{
|
||||
Timer {
|
||||
id: hideButtonsTimer
|
||||
|
||||
interval: 5000
|
||||
running: true
|
||||
|
||||
onTriggered: {console.log("hideButtons");}
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
cursorShape: Qt.ArrowCursor
|
||||
|
||||
onEntered: hideButtonsTimer.start()
|
||||
onExited: hideButtonsTimer.stop()
|
||||
|
||||
onPositionChanged: {
|
||||
hideButtonsTimer.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ import 'qrc:/ui/scripts/Utils/utils.js' as Utils
|
|||
Mosaic {
|
||||
id: grid
|
||||
property alias callModel: participantDevices.callModel
|
||||
property bool isFullScreen: false
|
||||
property int participantCount: gridModel.count
|
||||
anchors.fill: parent
|
||||
squaredDisplay: true
|
||||
|
|
@ -70,7 +71,7 @@ Mosaic {
|
|||
|
||||
CameraView{
|
||||
id: cameraView
|
||||
enabled: index >=0
|
||||
enabled: index >=0 && !grid.isFullScreen
|
||||
anchors.fill: parent
|
||||
currentDevice: avatarCell.currentDevice
|
||||
callModel: participantDevices.callModel
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit dbc795c83ef288e5fd27eb4a2a8a1c9da127eb95
|
||||
Subproject commit c8d936196b84855415e90d41346e7b3a25374077
|
||||
Loading…
Add table
Reference in a new issue