Commit 677712e8 authored by Ronan Abhamon's avatar Ronan Abhamon

feat(ui/views/App/Calls/Incall): supports screenshots, video recording

parent 222f8810
<?xml version="1.0" encoding="UTF-8"?>
<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 41 (35326) - http://www.bohemiancoding.com/sketch -->
<title>record_over</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="record_over">
<path d="M20,40 C31.045695,40 40,31.045695 40,20 C40,8.954305 31.045695,0 20,0 C8.954305,0 0,8.954305 0,20 C0,31.045695 8.954305,40 20,40 Z" fill="#E8E8E8" opacity="0.85"></path>
<circle stroke="#FF5E00" cx="19.5" cy="15.5" r="5"></circle>
<circle fill="#FF5E00" cx="19.5" cy="15.5" r="3"></circle>
<text font-family="Arial-BoldMT, Arial" font-size="8" font-weight="bold" fill="#FF5E00">
<tspan x="11.5546875" y="29.5">REC</tspan>
</text>
</g>
</g>
</svg>
\ No newline at end of file
...@@ -359,10 +359,6 @@ Server url not configured.</translation> ...@@ -359,10 +359,6 @@ Server url not configured.</translation>
</context> </context>
<context> <context>
<name>Incall</name> <name>Incall</name>
<message>
<source>saveScreenshotTitle</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>acceptVideoDescription</source> <source>acceptVideoDescription</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
......
...@@ -347,10 +347,6 @@ Url du serveur non configurée.</translation> ...@@ -347,10 +347,6 @@ Url du serveur non configurée.</translation>
</context> </context>
<context> <context>
<name>Incall</name> <name>Incall</name>
<message>
<source>saveScreenshotTitle</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>acceptVideoDescription</source> <source>acceptVideoDescription</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
......
...@@ -127,9 +127,8 @@ ...@@ -127,9 +127,8 @@
<file>assets/images/pause_on_normal.svg</file> <file>assets/images/pause_on_normal.svg</file>
<file>assets/images/pause_on_pressed.svg</file> <file>assets/images/pause_on_pressed.svg</file>
<file>assets/images/pause_on_updating.svg</file> <file>assets/images/pause_on_updating.svg</file>
<file>assets/images/record_hovered.svg</file> <file>assets/images/record_off.svg</file>
<file>assets/images/record_normal.svg</file> <file>assets/images/record_on.svg</file>
<file>assets/images/record_pressed.svg</file>
<file>assets/images/screenshot_hovered.svg</file> <file>assets/images/screenshot_hovered.svg</file>
<file>assets/images/screenshot_normal.svg</file> <file>assets/images/screenshot_normal.svg</file>
<file>assets/images/screenshot_pressed.svg</file> <file>assets/images/screenshot_pressed.svg</file>
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#endif // ifdef _WIN32 #endif // ifdef _WIN32
#define PATH_AVATARS (LINPHONE_FOLDER "avatars/") #define PATH_AVATARS (LINPHONE_FOLDER "avatars/")
#define PATH_CAPTURES (LINPHONE_FOLDER "captures/")
#define PATH_LOGS (LINPHONE_FOLDER "logs/") #define PATH_LOGS (LINPHONE_FOLDER "logs/")
#define PATH_THUMBNAILS (LINPHONE_FOLDER "thumbnails/") #define PATH_THUMBNAILS (LINPHONE_FOLDER "thumbnails/")
...@@ -92,3 +93,7 @@ string Paths::getMessageHistoryFilepath () { ...@@ -92,3 +93,7 @@ string Paths::getMessageHistoryFilepath () {
string Paths::getThumbnailsDirPath () { string Paths::getThumbnailsDirPath () {
return getDirectoryPath(MAIN_PATH + PATH_THUMBNAILS); return getDirectoryPath(MAIN_PATH + PATH_THUMBNAILS);
} }
string Paths::getCapturesDirPath () {
return getDirectoryPath(MAIN_PATH + PATH_CAPTURES);
}
...@@ -10,6 +10,7 @@ namespace Paths { ...@@ -10,6 +10,7 @@ namespace Paths {
std::string getCallHistoryFilepath (); std::string getCallHistoryFilepath ();
std::string getConfigFilepath (); std::string getConfigFilepath ();
std::string getFriendsListFilepath (); std::string getFriendsListFilepath ();
std::string getCapturesDirPath ();
std::string getLogsDirpath (); std::string getLogsDirpath ();
std::string getMessageHistoryFilepath (); std::string getMessageHistoryFilepath ();
std::string getThumbnailsDirPath (); std::string getThumbnailsDirPath ();
......
#include <QDateTime>
#include <QtDebug>
#include "../../app/Paths.hpp"
#include "../../utils.hpp" #include "../../utils.hpp"
#include "../core/CoreManager.hpp" #include "../core/CoreManager.hpp"
...@@ -61,6 +65,17 @@ CallModel::CallModel (shared_ptr<linphone::Call> linphone_call) { ...@@ -61,6 +65,17 @@ CallModel::CallModel (shared_ptr<linphone::Call> linphone_call) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void CallModel::setRecordFile (shared_ptr<linphone::CallParams> &call_params) {
call_params->setRecordFile(
Paths::getCapturesDirPath() +
::Utils::qStringToLinphoneString(
QDateTime::currentDateTime().toString("yyyy-MM-dd_hh:mm:ss")
) + ".mkv"
);
}
// -----------------------------------------------------------------------------
void CallModel::accept () { void CallModel::accept () {
CoreManager::getInstance()->getCore()->acceptCall(m_linphone_call); CoreManager::getInstance()->getCore()->acceptCall(m_linphone_call);
} }
...@@ -78,8 +93,9 @@ void CallModel::transfer () { ...@@ -78,8 +93,9 @@ void CallModel::transfer () {
} }
void CallModel::acceptVideoRequest () { void CallModel::acceptVideoRequest () {
shared_ptr<linphone::CallParams> params = m_linphone_call->getCurrentParams()->copy(); shared_ptr<linphone::CallParams> params = CoreManager::getInstance()->getCore()->createCallParams(m_linphone_call);
params->enableVideo(true); params->enableVideo(true);
CoreManager::getInstance()->getCore()->acceptCallUpdate(m_linphone_call, params); CoreManager::getInstance()->getCore()->acceptCallUpdate(m_linphone_call, params);
} }
...@@ -87,6 +103,47 @@ void CallModel::rejectVideoRequest () { ...@@ -87,6 +103,47 @@ void CallModel::rejectVideoRequest () {
CoreManager::getInstance()->getCore()->acceptCallUpdate(m_linphone_call, m_linphone_call->getCurrentParams()); CoreManager::getInstance()->getCore()->acceptCallUpdate(m_linphone_call, m_linphone_call->getCurrentParams());
} }
void CallModel::takeSnapshot () {
static QString old_name;
QString new_name = QDateTime::currentDateTime().toString("yyyy-MM-dd_hh:mm:ss") + ".jpg";
if (new_name == old_name) {
qWarning() << "Unable to take snapshot. Wait one second.";
return;
}
old_name = new_name;
qInfo() << "Take snapshot of call:" << &m_linphone_call;
m_linphone_call->takeVideoSnapshot(
Paths::getCapturesDirPath() + ::Utils::qStringToLinphoneString(new_name)
);
}
void CallModel::startRecording () {
if (m_recording)
return;
qInfo() << "Start recording call:" << &m_linphone_call;
m_linphone_call->startRecording();
m_recording = true;
emit recordingChanged(true);
}
void CallModel::stopRecording () {
if (m_recording) {
qInfo() << "Stop recording call:" << &m_linphone_call;
m_recording = false;
m_linphone_call->stopRecording();
emit recordingChanged(false);
}
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
QString CallModel::getSipAddress () const { QString CallModel::getSipAddress () const {
...@@ -215,3 +272,7 @@ bool CallModel::getUpdating () const { ...@@ -215,3 +272,7 @@ bool CallModel::getUpdating () const {
return true; return true;
} }
bool CallModel::getRecording () const {
return m_recording;
}
...@@ -21,6 +21,8 @@ class CallModel : public QObject { ...@@ -21,6 +21,8 @@ class CallModel : public QObject {
Q_PROPERTY(bool videoEnabled READ getVideoEnabled WRITE setVideoEnabled NOTIFY statusChanged); Q_PROPERTY(bool videoEnabled READ getVideoEnabled WRITE setVideoEnabled NOTIFY statusChanged);
Q_PROPERTY(bool updating READ getUpdating NOTIFY statusChanged) Q_PROPERTY(bool updating READ getUpdating NOTIFY statusChanged)
Q_PROPERTY(bool recording READ getRecording NOTIFY recordingChanged);
public: public:
enum CallStatus { enum CallStatus {
CallStatusConnected, CallStatusConnected,
...@@ -40,6 +42,8 @@ public: ...@@ -40,6 +42,8 @@ public:
return m_linphone_call; return m_linphone_call;
} }
static void setRecordFile (shared_ptr<linphone::CallParams> &call_params);
Q_INVOKABLE void accept (); Q_INVOKABLE void accept ();
Q_INVOKABLE void acceptWithVideo (); Q_INVOKABLE void acceptWithVideo ();
Q_INVOKABLE void terminate (); Q_INVOKABLE void terminate ();
...@@ -48,10 +52,16 @@ public: ...@@ -48,10 +52,16 @@ public:
Q_INVOKABLE void acceptVideoRequest (); Q_INVOKABLE void acceptVideoRequest ();
Q_INVOKABLE void rejectVideoRequest (); Q_INVOKABLE void rejectVideoRequest ();
Q_INVOKABLE void takeSnapshot ();
Q_INVOKABLE void startRecording ();
Q_INVOKABLE void stopRecording ();
signals: signals:
void statusChanged (CallStatus status); void statusChanged (CallStatus status);
void microMutedChanged (bool status); void microMutedChanged (bool status);
void videoRequested (); void videoRequested ();
void recordingChanged (bool status);
private: private:
QString getSipAddress () const; QString getSipAddress () const;
...@@ -75,9 +85,12 @@ private: ...@@ -75,9 +85,12 @@ private:
bool getUpdating () const; bool getUpdating () const;
bool getRecording () const;
bool m_micro_muted = false; bool m_micro_muted = false;
bool m_paused_by_remote = false; bool m_paused_by_remote = false;
bool m_paused_by_user = false; bool m_paused_by_user = false;
bool m_recording = false;
std::shared_ptr<linphone::Call> m_linphone_call; std::shared_ptr<linphone::Call> m_linphone_call;
}; };
......
...@@ -70,6 +70,7 @@ void CallsListModel::launchAudioCall (const QString &sip_uri) const { ...@@ -70,6 +70,7 @@ void CallsListModel::launchAudioCall (const QString &sip_uri) const {
shared_ptr<linphone::CallParams> params = core->createCallParams(nullptr); shared_ptr<linphone::CallParams> params = core->createCallParams(nullptr);
params->enableVideo(false); params->enableVideo(false);
CallModel::setRecordFile(params);
core->inviteAddressWithParams(address, params); core->inviteAddressWithParams(address, params);
} }
...@@ -84,6 +85,7 @@ void CallsListModel::launchVideoCall (const QString &sip_uri) const { ...@@ -84,6 +85,7 @@ void CallsListModel::launchVideoCall (const QString &sip_uri) const {
shared_ptr<linphone::CallParams> params = core->createCallParams(nullptr); shared_ptr<linphone::CallParams> params = core->createCallParams(nullptr);
params->enableEarlyMediaSending(true); params->enableEarlyMediaSending(true);
params->enableVideo(true); params->enableVideo(true);
CallModel::setRecordFile(params);
core->inviteAddressWithParams(address, params); core->inviteAddressWithParams(address, params);
} }
......
...@@ -33,11 +33,19 @@ Rectangle { ...@@ -33,11 +33,19 @@ Rectangle {
Component.onCompleted: this.connect(call, 'videoRequested', function () { Component.onCompleted: this.connect(call, 'videoRequested', function () {
var dialog var dialog
// Close window if call is ended. // Close dialog after 10s.
var timeout = Utils.setTimeout(incall, 10000, function () {
call.statusChanged.disconnect(endedHandler)
dialog.close()
call.rejectVideoRequest()
})
// Close dialog if call is ended.
var endedHandler = function (status) { var endedHandler = function (status) {
if (status === CallModel.CallStatusEnded) { if (status === CallModel.CallStatusEnded) {
dialog.close() Utils.clearTimeout(timeout)
call.statusChanged.disconnect(endedHandler) call.statusChanged.disconnect(endedHandler)
dialog.close()
} }
} }
...@@ -46,6 +54,7 @@ Rectangle { ...@@ -46,6 +54,7 @@ Rectangle {
dialog = Utils.openConfirmDialog(window, { dialog = Utils.openConfirmDialog(window, {
descriptionText: qsTr('acceptVideoDescription'), descriptionText: qsTr('acceptVideoDescription'),
exitHandler: function (status) { exitHandler: function (status) {
Utils.clearTimeout(timeout)
call.statusChanged.disconnect(endedHandler) call.statusChanged.disconnect(endedHandler)
if (status) { if (status) {
...@@ -118,43 +127,37 @@ Rectangle { ...@@ -118,43 +127,37 @@ Rectangle {
width: parent.width - cameraActions.width - callQuality.width - CallStyle.header.contactDescription.width width: parent.width - cameraActions.width - callQuality.width - CallStyle.header.contactDescription.width
} }
// -----------------------------------------------------------------------
// Video actions.
// -----------------------------------------------------------------------
Loader { Loader {
id: cameraActions id: cameraActions
anchors.right: parent.right anchors.right: parent.right
active: call.videoEnabled && call.status !== CallModel.CallStatusEnded active: call.status !== CallModel.CallStatusEnded
sourceComponent: ActionBar { sourceComponent: ActionBar {
iconSize: CallStyle.header.iconSize iconSize: CallStyle.header.iconSize
ActionButton { ActionButton {
icon: 'screenshot' icon: 'screenshot'
visible: call.videoEnabled
FileDialog { onClicked: call.takeSnapshot()
id: fileDialog
folder: shortcuts.home
selectExisting: false
title: qsTr('saveScreenshotTitle')
onAccepted: cameraLoader.item.saveScreenshot(fileUrl)
} }
onClicked: { ActionSwitch {
// TODO: At this moment, FileDialog does not support default filename, use this name in the future: enabled: call.recording
//'linphone ' + ((new Date()).toLocaleString(Qt.locale(), 'yyyy-MM-dd hh:mm:ss')) + '.jpg'
cameraLoader.item.takeScreenshot()
fileDialog.open()
}
}
ActionButton {
icon: 'record' icon: 'record'
useStates: false
onClicked: !enabled ? call.startRecording() : call.stopRecording()
} }
ActionButton { ActionButton {
icon: 'fullscreen' icon: 'fullscreen'
visible: call.videoEnabled
} }
} }
} }
...@@ -242,15 +245,13 @@ Rectangle { ...@@ -242,15 +245,13 @@ Rectangle {
} }
Loader { Loader {
id: cameraLoader
anchors.centerIn: parent anchors.centerIn: parent
sourceComponent: call.videoEnabled ? camera : avatar sourceComponent: call.videoEnabled ? camera : avatar
} }
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Buttons. // Action Buttons.
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
Item { Item {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment