Commit 23939aaa authored by Ronan Abhamon's avatar Ronan Abhamon

feat(core): supports H264 download

Co-authored-by: 's avatarDanmei Chen <danmei.chen@belledonne-communications.com>
parent 7c24e07f
...@@ -145,6 +145,7 @@ set(SOURCES ...@@ -145,6 +145,7 @@ set(SOURCES
src/components/core/CoreHandlers.cpp src/components/core/CoreHandlers.cpp
src/components/core/CoreManager.cpp src/components/core/CoreManager.cpp
src/components/core/messages-count-notifier/AbstractMessagesCountNotifier.cpp src/components/core/messages-count-notifier/AbstractMessagesCountNotifier.cpp
src/components/file/FileDownloader.cpp
src/components/file/FileExtractor.cpp src/components/file/FileExtractor.cpp
src/components/notifier/Notifier.cpp src/components/notifier/Notifier.cpp
src/components/other/clipboard/Clipboard.cpp src/components/other/clipboard/Clipboard.cpp
...@@ -163,8 +164,8 @@ set(SOURCES ...@@ -163,8 +164,8 @@ set(SOURCES
src/components/timeline/TimelineModel.cpp src/components/timeline/TimelineModel.cpp
src/components/url-handlers/UrlHandlers.cpp src/components/url-handlers/UrlHandlers.cpp
src/utils/LinphoneUtils.cpp src/utils/LinphoneUtils.cpp
src/utils/Utils.cpp
src/utils/QExifImageHeader.cpp src/utils/QExifImageHeader.cpp
src/utils/Utils.cpp
) )
set(HEADERS set(HEADERS
...@@ -202,6 +203,7 @@ set(HEADERS ...@@ -202,6 +203,7 @@ set(HEADERS
src/components/core/CoreHandlers.hpp src/components/core/CoreHandlers.hpp
src/components/core/CoreManager.hpp src/components/core/CoreManager.hpp
src/components/core/messages-count-notifier/AbstractMessagesCountNotifier.hpp src/components/core/messages-count-notifier/AbstractMessagesCountNotifier.hpp
src/components/file/FileDownloader.hpp
src/components/file/FileExtractor.hpp src/components/file/FileExtractor.hpp
src/components/notifier/Notifier.hpp src/components/notifier/Notifier.hpp
src/components/other/clipboard/Clipboard.hpp src/components/other/clipboard/Clipboard.hpp
......
...@@ -1003,6 +1003,29 @@ your friend&apos;s SIP address or username.</translation> ...@@ -1003,6 +1003,29 @@ your friend&apos;s SIP address or username.</translation>
<translation>New attachment received!</translation> <translation>New attachment received!</translation>
</message> </message>
</context> </context>
<context>
<name>OnlineInstallerDialog</name>
<message>
<source>confirm</source>
<translation>CONFIRM</translation>
</message>
<message>
<source>onlineInstallerExtractingDescription</source>
<translation>Extracting %1...</translation>
</message>
<message>
<source>onlineInstallerDownloadingDescription</source>
<translation>Downloading %1...</translation>
</message>
<message>
<source>onlineInstallerFinishedDescription</source>
<translation>%1 is now installed!</translation>
</message>
<message>
<source>onlineInstallerFailedDescription</source>
<translation>Failed to install %1!</translation>
</message>
</context>
<context> <context>
<name>OutgoingMessage</name> <name>OutgoingMessage</name>
<message> <message>
...@@ -1672,4 +1695,11 @@ your friend&apos;s SIP address or username.</translation> ...@@ -1672,4 +1695,11 @@ your friend&apos;s SIP address or username.</translation>
<translation>CONFIRM</translation> <translation>CONFIRM</translation>
</message> </message>
</context> </context>
<context>
<name>linphone-utils</name>
<message>
<source>downloadCodecDescription</source>
<translation>Do you want to download %1 (%2)?</translation>
</message>
</context>
</TS> </TS>
...@@ -1001,6 +1001,29 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt; ...@@ -1001,6 +1001,29 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation>Pièce jointe reçue !</translation> <translation>Pièce jointe reçue !</translation>
</message> </message>
</context> </context>
<context>
<name>OnlineInstallerDialog</name>
<message>
<source>confirm</source>
<translation>CONFIRM</translation>
</message>
<message>
<source>onlineInstallerExtractingDescription</source>
<translation>Extraction de %1...</translation>
</message>
<message>
<source>onlineInstallerDownloadingDescription</source>
<translation>Téléchargement de %1...</translation>
</message>
<message>
<source>onlineInstallerFinishedDescription</source>
<translation>Installation de %1 terminée !</translation>
</message>
<message>
<source>onlineInstallerFailedDescription</source>
<translation>L&apos;installation de %1 a échoué !</translation>
</message>
</context>
<context> <context>
<name>OutgoingMessage</name> <name>OutgoingMessage</name>
<message> <message>
...@@ -1670,4 +1693,11 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt; ...@@ -1670,4 +1693,11 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<translation>CONFIRMER</translation> <translation>CONFIRMER</translation>
</message> </message>
</context> </context>
<context>
<name>linphone-utils</name>
<message>
<source>downloadCodecDescription</source>
<translation>Voulez-vous installer %1 (%2) ?</translation>
</message>
</context>
</TS> </TS>
...@@ -1001,6 +1001,29 @@ ...@@ -1001,6 +1001,29 @@
<translation>Получен новый файл!</translation> <translation>Получен новый файл!</translation>
</message> </message>
</context> </context>
<context>
<name>OnlineInstallerDialog</name>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>onlineInstallerExtractingDescription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>onlineInstallerDownloadingDescription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>onlineInstallerFinishedDescription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>onlineInstallerFailedDescription</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>OutgoingMessage</name> <name>OutgoingMessage</name>
<message> <message>
...@@ -1670,4 +1693,11 @@ ...@@ -1670,4 +1693,11 @@
<translation>ПОДТВЕРДИТЬ</translation> <translation>ПОДТВЕРДИТЬ</translation>
</message> </message>
</context> </context>
<context>
<name>linphone-utils</name>
<message>
<source>downloadCodecDescription</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS> </TS>
...@@ -1003,6 +1003,29 @@ arkadaşınızın SIP adresini veya kullanıcı adını girin.</translation> ...@@ -1003,6 +1003,29 @@ arkadaşınızın SIP adresini veya kullanıcı adını girin.</translation>
<translation>Yeni ek alındı!</translation> <translation>Yeni ek alındı!</translation>
</message> </message>
</context> </context>
<context>
<name>OnlineInstallerDialog</name>
<message>
<source>confirm</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>onlineInstallerExtractingDescription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>onlineInstallerDownloadingDescription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>onlineInstallerFinishedDescription</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>onlineInstallerFailedDescription</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>OutgoingMessage</name> <name>OutgoingMessage</name>
<message> <message>
...@@ -1672,4 +1695,11 @@ arkadaşınızın SIP adresini veya kullanıcı adını girin.</translation> ...@@ -1672,4 +1695,11 @@ arkadaşınızın SIP adresini veya kullanıcı adını girin.</translation>
<translation>ONAYLA</translation> <translation>ONAYLA</translation>
</message> </message>
</context> </context>
<context>
<name>linphone-utils</name>
<message>
<source>downloadCodecDescription</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS> </TS>
...@@ -332,6 +332,7 @@ ...@@ -332,6 +332,7 @@
<file>ui/modules/Linphone/Contact/ContactDescription.qml</file> <file>ui/modules/Linphone/Contact/ContactDescription.qml</file>
<file>ui/modules/Linphone/Contact/Contact.qml</file> <file>ui/modules/Linphone/Contact/Contact.qml</file>
<file>ui/modules/Linphone/Contact/MessagesCounter.qml</file> <file>ui/modules/Linphone/Contact/MessagesCounter.qml</file>
<file>ui/modules/Linphone/Dialog/OnlineInstallerDialog.qml</file>
<file>ui/modules/Linphone/Menus/SipAddressesMenu.qml</file> <file>ui/modules/Linphone/Menus/SipAddressesMenu.qml</file>
<file>ui/modules/Linphone/Notifications/NotificationBasic.qml</file> <file>ui/modules/Linphone/Notifications/NotificationBasic.qml</file>
<file>ui/modules/Linphone/Notifications/NotificationNewVersionAvailable.qml</file> <file>ui/modules/Linphone/Notifications/NotificationNewVersionAvailable.qml</file>
...@@ -357,6 +358,7 @@ ...@@ -357,6 +358,7 @@
<file>ui/modules/Linphone/Styles/Contact/ContactDescriptionStyle.qml</file> <file>ui/modules/Linphone/Styles/Contact/ContactDescriptionStyle.qml</file>
<file>ui/modules/Linphone/Styles/Contact/ContactStyle.qml</file> <file>ui/modules/Linphone/Styles/Contact/ContactStyle.qml</file>
<file>ui/modules/Linphone/Styles/Contact/MessagesCounterStyle.qml</file> <file>ui/modules/Linphone/Styles/Contact/MessagesCounterStyle.qml</file>
<file>ui/modules/Linphone/Styles/Dialog/OnlineInstallerDialogStyle.qml</file>
<file>ui/modules/Linphone/Styles/Menus/SipAddressesMenuStyle.qml</file> <file>ui/modules/Linphone/Styles/Menus/SipAddressesMenuStyle.qml</file>
<file>ui/modules/Linphone/Styles/Notifications/NotificationBasicStyle.qml</file> <file>ui/modules/Linphone/Styles/Notifications/NotificationBasicStyle.qml</file>
<file>ui/modules/Linphone/Styles/Notifications/NotificationReceivedCallStyle.qml</file> <file>ui/modules/Linphone/Styles/Notifications/NotificationReceivedCallStyle.qml</file>
......
...@@ -176,10 +176,6 @@ void App::initContentApp () { ...@@ -176,10 +176,6 @@ void App::initContentApp () {
Cli::executeCommand(command); Cli::executeCommand(command);
}); });
// Add plugins directory.
addLibraryPath(::Utils::coreStringToAppString(Paths::getPluginsDirPath()));
qInfo() << QStringLiteral("Library paths:") << libraryPaths();
mustBeIconified = mParser->isSet("iconified"); mustBeIconified = mParser->isSet("iconified");
} }
...@@ -405,6 +401,7 @@ void App::registerTypes () { ...@@ -405,6 +401,7 @@ void App::registerTypes () {
registerType<ConferenceHelperModel>("ConferenceHelperModel"); registerType<ConferenceHelperModel>("ConferenceHelperModel");
registerType<ConferenceModel>("ConferenceModel"); registerType<ConferenceModel>("ConferenceModel");
registerType<ContactsListProxyModel>("ContactsListProxyModel"); registerType<ContactsListProxyModel>("ContactsListProxyModel");
registerType<FileDownloader>("FileDownloader");
registerType<FileExtractor>("FileExtractor"); registerType<FileExtractor>("FileExtractor");
registerType<SipAddressesProxyModel>("SipAddressesProxyModel"); registerType<SipAddressesProxyModel>("SipAddressesProxyModel");
registerType<SoundPlayer>("SoundPlayer"); registerType<SoundPlayer>("SoundPlayer");
......
...@@ -159,7 +159,7 @@ void Logger::log (QtMsgType type, const QMessageLogContext &context, const QStri ...@@ -159,7 +159,7 @@ void Logger::log (QtMsgType type, const QMessageLogContext &context, const QStri
contextStr = contextArr.constData(); contextStr = contextArr.constData();
} }
#else #else
(void)context; Q_UNUSED(context);
#endif // ifdef QT_MESSAGELOGCONTEXT #endif // ifdef QT_MESSAGELOGCONTEXT
QByteArray localMsg = msg.toLocal8Bit(); QByteArray localMsg = msg.toLocal8Bit();
......
...@@ -40,6 +40,7 @@ namespace { ...@@ -40,6 +40,7 @@ namespace {
constexpr char cPathAssistantConfig[] = "/linphone/assistant/"; constexpr char cPathAssistantConfig[] = "/linphone/assistant/";
constexpr char cPathAvatars[] = "/avatars/"; constexpr char cPathAvatars[] = "/avatars/";
constexpr char cPathCaptures[] = "/Linphone/captures/"; constexpr char cPathCaptures[] = "/Linphone/captures/";
constexpr char cPathCodecs[] = "/codecs/";
constexpr char cPathLogs[] = "/logs/"; constexpr char cPathLogs[] = "/logs/";
constexpr char cPathPlugins[] = "/plugins/"; constexpr char cPathPlugins[] = "/plugins/";
constexpr char cPathThumbnails[] = "/thumbnails/"; constexpr char cPathThumbnails[] = "/thumbnails/";
...@@ -181,6 +182,10 @@ string Paths::getCapturesDirPath () { ...@@ -181,6 +182,10 @@ string Paths::getCapturesDirPath () {
return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + cPathCaptures); return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + cPathCaptures);
} }
string Paths::getCodecsDirPath () {
return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + cPathCodecs);
}
string Paths::getConfigFilePath (const QString &configPath, bool writable) { string Paths::getConfigFilePath (const QString &configPath, bool writable) {
const QString path = configPath.isEmpty() const QString path = configPath.isEmpty()
? getAppConfigFilePath() ? getAppConfigFilePath()
...@@ -217,10 +222,6 @@ string Paths::getPackageMsPluginsDirPath () { ...@@ -217,10 +222,6 @@ string Paths::getPackageMsPluginsDirPath () {
return getReadableDirPath(getAppPackageMsPluginsDirPath()); return getReadableDirPath(getAppPackageMsPluginsDirPath());
} }
string Paths::getPluginsDirPath () {
return getReadableDirPath(getAppPluginsDirPath());
}
string Paths::getRootCaFilePath () { string Paths::getRootCaFilePath () {
return getReadableFilePath(getAppRootCaFilePath()); return getReadableFilePath(getAppRootCaFilePath());
} }
......
...@@ -34,15 +34,15 @@ namespace Paths { ...@@ -34,15 +34,15 @@ namespace Paths {
std::string getAvatarsDirPath (); std::string getAvatarsDirPath ();
std::string getCallHistoryFilePath (); std::string getCallHistoryFilePath ();
std::string getCapturesDirPath (); std::string getCapturesDirPath ();
std::string getCodecsDirPath ();
std::string getConfigFilePath (const QString &configPath = QString(), bool writable = true); std::string getConfigFilePath (const QString &configPath = QString(), bool writable = true);
std::string getDownloadDirPath ();
std::string getFactoryConfigFilePath (); std::string getFactoryConfigFilePath ();
std::string getFriendsListFilePath (); std::string getFriendsListFilePath ();
std::string getDownloadDirPath ();
std::string getLogsDirPath (); std::string getLogsDirPath ();
std::string getMessageHistoryFilePath (); std::string getMessageHistoryFilePath ();
std::string getPackageDataDirPath (); std::string getPackageDataDirPath ();
std::string getPackageMsPluginsDirPath (); std::string getPackageMsPluginsDirPath ();
std::string getPluginsDirPath ();
std::string getRootCaFilePath (); std::string getRootCaFilePath ();
std::string getThumbnailsDirPath (); std::string getThumbnailsDirPath ();
std::string getUserCertificatesDirPath (); std::string getUserCertificatesDirPath ();
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "conference/ConferenceModel.hpp" #include "conference/ConferenceModel.hpp"
#include "contacts/ContactsListProxyModel.hpp" #include "contacts/ContactsListProxyModel.hpp"
#include "core/CoreManager.hpp" #include "core/CoreManager.hpp"
#include "file/FileDownloader.hpp"
#include "file/FileExtractor.hpp" #include "file/FileExtractor.hpp"
#include "presence/OwnPresenceModel.hpp" #include "presence/OwnPresenceModel.hpp"
#include "settings/AccountSettingsModel.hpp" #include "settings/AccountSettingsModel.hpp"
......
...@@ -20,17 +20,18 @@ ...@@ -20,17 +20,18 @@
* Author: Ronan Abhamon * Author: Ronan Abhamon
*/ */
#include "../../app/paths/Paths.hpp"
#include "../../utils/Utils.hpp" #include "../../utils/Utils.hpp"
#include "../core/CoreManager.hpp" #include "../core/CoreManager.hpp"
#include "AbstractCodecsModel.hpp" #include "AbstractCodecsModel.hpp"
using namespace std;
// ============================================================================= // =============================================================================
using namespace std;
static inline shared_ptr<linphone::PayloadType> getCodecFromMap (const QVariantMap &map) { static inline shared_ptr<linphone::PayloadType> getCodecFromMap (const QVariantMap &map) {
return map.value("__codec").value<shared_ptr<linphone::PayloadType> >(); return map.value("__codec").value<shared_ptr<linphone::PayloadType>>();
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
...@@ -65,12 +66,12 @@ void AbstractCodecsModel::enableCodec (int id, bool status) { ...@@ -65,12 +66,12 @@ void AbstractCodecsModel::enableCodec (int id, bool status) {
Q_ASSERT(id >= 0 && id < mCodecs.count()); Q_ASSERT(id >= 0 && id < mCodecs.count());
QVariantMap &map = mCodecs[id]; QVariantMap &map = mCodecs[id];
shared_ptr<linphone::PayloadType> codec = ::getCodecFromMap(map); shared_ptr<linphone::PayloadType> codec = getCodecFromMap(map);
if (codec) {
codec->enable(status); codec->enable(status);
map["enabled"] = codec->enabled(); map["enabled"] = codec->enabled();
emit dataChanged(index(id, 0), index(id, 0));
emit dataChanged(index(id, 0), index(id, 0)); }
} }
void AbstractCodecsModel::moveCodec (int source, int destination) { void AbstractCodecsModel::moveCodec (int source, int destination) {
...@@ -81,12 +82,12 @@ void AbstractCodecsModel::setBitrate (int id, int bitrate) { ...@@ -81,12 +82,12 @@ void AbstractCodecsModel::setBitrate (int id, int bitrate) {
Q_ASSERT(id >= 0 && id < mCodecs.count()); Q_ASSERT(id >= 0 && id < mCodecs.count());
QVariantMap &map = mCodecs[id]; QVariantMap &map = mCodecs[id];
shared_ptr<linphone::PayloadType> codec = ::getCodecFromMap(map); shared_ptr<linphone::PayloadType> codec = getCodecFromMap(map);
if (codec) {
codec->setNormalBitrate(bitrate); codec->setNormalBitrate(bitrate);
map["bitrate"] = codec->getNormalBitrate(); map["bitrate"] = codec->getNormalBitrate();
emit dataChanged(index(id, 0), index(id, 0));
emit dataChanged(index(id, 0), index(id, 0)); }
} }
void AbstractCodecsModel::setRecvFmtp (int id, const QString &recvFmtp) { void AbstractCodecsModel::setRecvFmtp (int id, const QString &recvFmtp) {
...@@ -94,11 +95,11 @@ void AbstractCodecsModel::setRecvFmtp (int id, const QString &recvFmtp) { ...@@ -94,11 +95,11 @@ void AbstractCodecsModel::setRecvFmtp (int id, const QString &recvFmtp) {
QVariantMap &map = mCodecs[id]; QVariantMap &map = mCodecs[id];
shared_ptr<linphone::PayloadType> codec = ::getCodecFromMap(map); shared_ptr<linphone::PayloadType> codec = ::getCodecFromMap(map);
if (codec) {
codec->setRecvFmtp(::Utils::appStringToCoreString(recvFmtp)); codec->setRecvFmtp(Utils::appStringToCoreString(recvFmtp));
map["recvFmtp"] = ::Utils::coreStringToAppString(codec->getRecvFmtp()); map["recvFmtp"] = Utils::coreStringToAppString(codec->getRecvFmtp());
emit dataChanged(index(id, 0), index(id, 0));
emit dataChanged(index(id, 0), index(id, 0)); }
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
...@@ -110,6 +111,8 @@ bool AbstractCodecsModel::moveRows ( ...@@ -110,6 +111,8 @@ bool AbstractCodecsModel::moveRows (
const QModelIndex &destinationParent, const QModelIndex &destinationParent,
int destinationChild int destinationChild
) { ) {
// TODO: Do not move downloadable codecs.
int limit = sourceRow + count - 1; int limit = sourceRow + count - 1;
{ {
...@@ -139,9 +142,13 @@ bool AbstractCodecsModel::moveRows ( ...@@ -139,9 +142,13 @@ bool AbstractCodecsModel::moveRows (
} }
// Update linphone codecs list. // Update linphone codecs list.
list<shared_ptr<linphone::PayloadType> > codecs; list<shared_ptr<linphone::PayloadType>> codecs;
for (const auto &map : mCodecs) for (const auto &map : mCodecs) {
codecs.push_back(::getCodecFromMap(map)); // Do not update downloadable codecs.
shared_ptr<linphone::PayloadType> codec = getCodecFromMap(map);
if (codec)
codecs.push_back(codec);
}
updateCodecs(codecs); updateCodecs(codecs);
endMoveRows(); endMoveRows();
...@@ -157,15 +164,40 @@ void AbstractCodecsModel::addCodec (shared_ptr<linphone::PayloadType> &codec) { ...@@ -157,15 +164,40 @@ void AbstractCodecsModel::addCodec (shared_ptr<linphone::PayloadType> &codec) {
map["bitrate"] = codec->getNormalBitrate(); map["bitrate"] = codec->getNormalBitrate();
map["channels"] = codec->getChannels(); map["channels"] = codec->getChannels();
map["clockRate"] = codec->getClockRate(); map["clockRate"] = codec->getClockRate();
map["description"] = ::Utils::coreStringToAppString(codec->getDescription()); map["description"] = Utils::coreStringToAppString(codec->getDescription());
map["enabled"] = codec->enabled(); map["enabled"] = codec->enabled();
map["encoderDescription"] = ::Utils::coreStringToAppString(codec->getEncoderDescription()); map["encoderDescription"] = Utils::coreStringToAppString(codec->getEncoderDescription());
map["isUsable"] = codec->isUsable(); // TODO: Notify in UI when unusable. map["isUsable"] = codec->isUsable(); // TODO: Notify in UI when unusable.
map["isVbr"] = codec->isVbr(); map["isVbr"] = codec->isVbr();
map["mime"] = ::Utils::coreStringToAppString(codec->getMimeType()); map["mime"] = Utils::coreStringToAppString(codec->getMimeType());
map["number"] = codec->getNumber(); map["number"] = codec->getNumber();
map["recvFmtp"] = ::Utils::coreStringToAppString(codec->getRecvFmtp()); map["recvFmtp"] = Utils::coreStringToAppString(codec->getRecvFmtp());
map["__codec"] = QVariant::fromValue(codec); map["__codec"] = QVariant::fromValue(codec);
mCodecs << map; mCodecs << map;
} }
void AbstractCodecsModel::addDownloadableCodec (
const QString &mime,
const QString &downloadUrl,
const QString &encoderDescription
) {
QVariantMap map;
map["mime"] = mime;
map["downloadUrl"] = downloadUrl;
map["encoderDescription"] = encoderDescription;
mCodecs << map;
}
QVariantMap AbstractCodecsModel::getCodecInfo (const QString &mime) const {
for (const auto &codec : mCodecs)
if (codec.value("mime") == mime)
return codec;
return QVariantMap();
};
QString AbstractCodecsModel::getCodecsFolder () const {
return Utils::coreStringToAppString(Paths::getCodecsDirPath());
}
...@@ -36,6 +36,8 @@ namespace linphone { ...@@ -36,6 +36,8 @@ namespace linphone {
class AbstractCodecsModel : public QAbstractListModel { class AbstractCodecsModel : public QAbstractListModel {
Q_OBJECT; Q_OBJECT;
Q_PROPERTY(QString codecsFolder READ getCodecsFolder CONSTANT);
public: public:
AbstractCodecsModel (QObject *parent = Q_NULLPTR); AbstractCodecsModel (QObject *parent = Q_NULLPTR);
virtual ~AbstractCodecsModel () = default; virtual ~AbstractCodecsModel () = default;
...@@ -51,6 +53,10 @@ public: ...@@ -51,6 +53,10 @@ public:
Q_INVOKABLE void setBitrate (int id, int bitrate); Q_INVOKABLE void setBitrate (int id, int bitrate);
Q_INVOKABLE void setRecvFmtp (int id, const QString &recvFmtp); Q_INVOKABLE void setRecvFmtp (int id, const QString &recvFmtp);
Q_INVOKABLE virtual void reload () {};
Q_INVOKABLE QVariantMap getCodecInfo (const QString &mime) const;
protected: protected:
bool moveRows ( bool moveRows (
const QModelIndex &sourceParent, const QModelIndex &sourceParent,
...@@ -61,10 +67,12 @@ protected: ...@@ -61,10 +67,12 @@ protected:
) override; ) override;
void addCodec (std::shared_ptr<linphone::PayloadType> &codec); void addCodec (std::shared_ptr<linphone::PayloadType> &codec);
void addDownloadableCodec (const QString &mime, const QString &downloadUrl, const QString &encoderDescription);
QString getCodecsFolder () const;
virtual void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType> > &codecs) = 0; virtual void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType>> &codecs) = 0;
private:
QList<QVariantMap> mCodecs; QList<QVariantMap> mCodecs;
}; };
......
...@@ -24,15 +24,15 @@ ...@@ -24,15 +24,15 @@
#include "AudioCodecsModel.hpp" #include "AudioCodecsModel.hpp"
using namespace std;
// ============================================================================= // =============================================================================
using namespace std;
AudioCodecsModel::AudioCodecsModel (QObject *parent) : AbstractCodecsModel(parent) { AudioCodecsModel::AudioCodecsModel (QObject *parent) : AbstractCodecsModel(parent) {
for (auto &codec : CoreManager::getInstance()->getCore()->getAudioPayloadTypes()) for (auto &codec : CoreManager::getInstance()->getCore()->getAudioPayloadTypes())
addCodec(codec); addCodec(codec);
} }
void AudioCodecsModel::updateCodecs (list<shared_ptr<linphone::PayloadType> > &codecs) { void AudioCodecsModel::updateCodecs (list<shared_ptr<linphone::PayloadType>> &codecs) {
CoreManager::getInstance()->getCore()->setAudioPayloadTypes(codecs); CoreManager::getInstance()->getCore()->setAudioPayloadTypes(codecs);
} }
...@@ -34,8 +34,8 @@ public: ...@@ -34,8 +34,8 @@ public:
AudioCodecsModel (QObject *parent = Q_NULLPTR); AudioCodecsModel (QObject *parent = Q_NULLPTR);
~AudioCodecsModel () = default; ~AudioCodecsModel () = default;
protected: private:
void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType> > &codecs) override; void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType>> &codecs) override;
}; };
#endif // AUDIO_CODECS_MODEL_H_ #endif // AUDIO_CODECS_MODEL_H_
...@@ -20,19 +20,79 @@ ...@@ -20,19 +20,79 @@
* Author: Ronan Abhamon * Author: Ronan Abhamon
*/ */
#include <QDirIterator>
#include <QLibrary>
#include "../../app/paths/Paths.hpp"
#include "../../utils/Utils.hpp"
#include "../core/CoreManager.hpp" #include "../core/CoreManager.hpp"
#include "VideoCodecsModel.hpp" #include "VideoCodecsModel.hpp"
// =============================================================================
using namespace std; using namespace std;
// ============================================================================= namespace {
constexpr char cH264Description[] = "Provided by CISCO SYSTEM,INC";
#ifdef Q_OS_LINUX
constexpr char cLibraryExtension[] = "so";
#ifdef Q_PROCESSOR_X86_64
constexpr char cPluginUrlH264[] = "http://ciscobinary.openh264.org/libopenh264-1.7.0-linux64.4.so.bz2";
#else
constexpr char cPluginUrlH264[] = "http://ciscobinary.openh264.org/libopenh264-1.7.0-linux32.4.so.bz2";
#endif // ifdef Q_PROCESSOR_X86_64
#elif defined(Q_OS_WIN)
constexpr char cLibraryExtension[] = "dll";
#ifdef Q_OS_WIN64
constexpr char cPluginUrlH264[] = "http://ciscobinary.openh264.org/openh264-1.7.0-win64.dll.bz2";
#elif defined(Q_OS_WIN32)
constexpr char cPluginUrlH264[] = "http://ciscobinary.openh264.org/openh264-1.7.0-win32.dll.bz2";
#endif // ifdef Q_OS_WIN64
#endif // ifdef Q_OS_LINUX
}
VideoCodecsModel::VideoCodecsModel (QObject *parent) : AbstractCodecsModel(parent) { VideoCodecsModel::VideoCodecsModel (QObject *parent) : AbstractCodecsModel(parent) {
for (auto &codec : CoreManager::getInstance()->getCore()->getVideoPayloadTypes()) load();
addCodec(codec);
} }
void VideoCodecsModel::updateCodecs (list<shared_ptr<linphone::PayloadType> > &codecs) { void VideoCodecsModel::updateCodecs (list<shared_ptr<linphone::PayloadType>> &codecs) {
CoreManager::getInstance()->getCore()->setVideoPayloadTypes(codecs); CoreManager::getInstance()->getCore()->setVideoPayloadTypes(codecs);
} }
void VideoCodecsModel::load () {
mCodecs.clear();
shared_ptr<linphone::Core> core = CoreManager::getInstance()->getCore();
// Load downloaded codecs like OpenH264.
#if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
QDirIterator it(Utils::coreStringToAppString(Paths::getCodecsDirPath()));
while (it.hasNext()) {
QFileInfo info(it.next());
if (info.suffix() == cLibraryExtension)
QLibrary(info.filePath()).load();
}
core->reloadMsPlugins("");
#endif
// Add codecs.
auto codecs = core->getVideoPayloadTypes();
for (auto &codec : codecs)
addCodec(codec);
// Add downloadable codecs.
#if defined(Q_OS_LINUX) || defined(Q_OS_WIN)
if (find_if(codecs.begin(), codecs.end(), [](const shared_ptr<linphone::PayloadType> &codec) {
return codec->getMimeType() == "H264";
}) == codecs.end())
addDownloadableCodec("H264", cPluginUrlH264, cH264Description);
#endif
}
void VideoCodecsModel::reload () {
beginResetModel();
load();
endResetModel();
}
...@@ -34,8 +34,11 @@ public: ...@@ -34,8 +34,11 @@ public:
VideoCodecsModel (QObject *parent = Q_NULLPTR); VideoCodecsModel (QObject *parent = Q_NULLPTR);
~VideoCodecsModel () = default; ~VideoCodecsModel () = default;
protected: private:
void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType> > &codecs) override; void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType>> &codecs) override;
void load ();
void reload () override;
}; };
#endif // VIDEO_CODECS_MODEL_H_ #endif // VIDEO_CODECS_MODEL_H_
...@@ -30,5 +30,5 @@ MessagesCountNotifier::MessagesCountNotifier (QObject *parent) : AbstractMessage ...@@ -30,5 +30,5 @@ MessagesCountNotifier::MessagesCountNotifier (QObject *parent) : AbstractMessage
void MessagesCountNotifier::notifyUnreadMessagesCount (int n) { void MessagesCountNotifier::notifyUnreadMessagesCount (int n) {
// TODO. // TODO.
(void)n; Q_UNUSED(n);
} }
/*
* FileDownloader.cpp
* Copyright (C) 2017-2018 Belledonne Communications, Grenoble, France
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Created on: February 6, 2018
* Author: Danmei Chen
*/
#include "../../app/paths/Paths.hpp"
#include "../../utils/Utils.hpp"
#include "../core/CoreManager.hpp"
#include "FileDownloader.hpp"
// =============================================================================
namespace {
constexpr char cDefaultFileName[] = "download";
}
static QString getDownloadFilePath (const QString &folder, const QUrl &url) {
QFileInfo fileInfo(url.path());
QString fileName = fileInfo.fileName();
if (fileName.isEmpty())
fileName = cDefaultFileName;
fileName.prepend(folder);
if (!QFile::exists(fileName))
return fileName;
// Already exists, don't overwrite.
QString baseName = fileInfo.completeBaseName();
if (baseName.isEmpty())
baseName = cDefaultFileName;
QString suffix = fileInfo.suffix();
if (!suffix.isEmpty())
suffix.prepend(".");
for (int i = 1; true; ++i) {
fileName = folder + baseName + "(" + QString::number(i) + ")" + suffix;
if (!QFile::exists(fileName))
break;
}
return fileName;
}
static bool isHttpRedirect (QNetworkReply *reply) {
Q_CHECK_PTR(reply);
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
return statusCode == 301 || statusCode == 302 || statusCode == 303
|| statusCode == 305 || statusCode == 307 || statusCode == 308;
}
// -----------------------------------------------------------------------------
void FileDownloader::download () {
if (mDownloading) {
qWarning() << "Unable to download file. Already downloading!";
return;
}
setDownloading(true);
QNetworkRequest request(mUrl);
mNetworkReply = mManager.get(request);
#if QT_CONFIG(ssl)
QObject::connect(mNetworkReply, &QNetworkReply::sslErrors, this, &FileDownloader::handleSslErrors);
#endif
QObject::connect(mNetworkReply, &QNetworkReply::downloadProgress, this, &FileDownloader::handleDownloadProgress);
QObject::connect(mNetworkReply, &QNetworkReply::readyRead, this, &FileDownloader::handleReadyData);
QObject::connect(mNetworkReply, &QNetworkReply::finished, this, &FileDownloader::handleDownloadFinished);
if (mDownloadFolder.isEmpty()) {
mDownloadFolder = CoreManager::getInstance()->getSettingsModel()->getDownloadFolder();
emit downloadFolderChanged(mDownloadFolder);
}
// TODO: Deal with connection error like timeout.
Q_ASSERT(!mDestinationFile.isOpen());
mDestinationFile.setFileName(getDownloadFilePath(QDir::cleanPath(mDownloadFolder) + QDir::separator(), mUrl));
if (!mDestinationFile.open(QIODevice::WriteOnly))
emitOutputError();
}
bool FileDownloader::remove () {
return mDestinationFile.exists() && !mDestinationFile.isOpen() && mDestinationFile.remove();
}
void FileDownloader::emitOutputError () {
qWarning() << QStringLiteral("Could not write into `%1` (%2).")
.arg(mDestinationFile.fileName()).arg(mDestinationFile.errorString());
mNetworkReply->abort();
}
void FileDownloader::handleReadyData () {
QByteArray data = mNetworkReply->readAll();
if (mDestinationFile.write(data) == -1)
emitOutputError();
}
void FileDownloader::handleDownloadFinished() {
QNetworkReply::NetworkError error = mNetworkReply->error();
if (error != QNetworkReply::NoError) {
if (error != QNetworkReply::OperationCanceledError)
qWarning() << QStringLiteral("Download of %1 failed: %2")
.arg(mUrl.toString()).arg(mNetworkReply->errorString());
mDestinationFile.remove();
emit downloadFailed();
} else {
// TODO: Deal with redirection.
if (isHttpRedirect(mNetworkReply)) {
qWarning() << QStringLiteral("Request was redirected.");
mDestinationFile.remove();
emit downloadFailed();
} else {
mDestinationFile.close();
emit downloadFinished(mDestinationFile.fileName());
}
}
mNetworkReply->deleteLater();
setDownloading(false);
}
void FileDownloader::handleSslErrors (const QList<QSslError> &sslErrors) {
#if QT_CONFIG(ssl)
for (const QSslError &error : sslErrors)
qWarning() << QStringLiteral("SSL error: %1").arg(error.errorString());
#else
Q_UNUSED(sslErrors);
#endif
}
void FileDownloader::handleDownloadProgress (qint64 readBytes, qint64 totalBytes) {
setReadBytes(readBytes);
setTotalBytes(totalBytes);
}
// -----------------------------------------------------------------------------
QUrl FileDownloader::getUrl () const {
return mUrl;
}
void FileDownloader::setUrl (const QUrl &url) {
if (mDownloading) {
qWarning() << QStringLiteral("Unable to set url, a file is downloading.");
return;
}
if (mUrl != url) {
mUrl = url;
emit urlChanged(mUrl);
}
}
QString FileDownloader::getDownloadFolder () const {
return mDownloadFolder;
}
void FileDownloader::setDownloadFolder (const QString &downloadFolder) {
if (mDownloading) {
qWarning() << QStringLiteral("Unable to set download folder, a file is downloading.");
return;
}
if (mDownloadFolder != downloadFolder) {
mDownloadFolder = downloadFolder;
emit downloadFolderChanged(mDownloadFolder);
}
}
qint64 FileDownloader::getReadBytes () const {
return mReadBytes;
}
void FileDownloader::setReadBytes (qint64 readBytes) {
if (mReadBytes != readBytes) {
mReadBytes = readBytes;
emit readBytesChanged(readBytes);
}
}
qint64 FileDownloader::getTotalBytes () const {
return mTotalBytes;
}
void FileDownloader::setTotalBytes (qint64 totalBytes) {
if (mTotalBytes != totalBytes) {
mTotalBytes = totalBytes;
emit totalBytesChanged(totalBytes);
}
}
bool FileDownloader::getDownloading () const {
return mDownloading;
}
void FileDownloader::setDownloading (bool downloading) {
if (mDownloading != downloading) {
mDownloading = downloading;
emit downloadingChanged(downloading);
}
}
/*
* FileDownloader.hpp
* Copyright (C) 2017-2018 Belledonne Communications, Grenoble, France
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Created on: February 6, 2018
* Author: Danmei Chen
*/
#include <QObject>
#include <QtNetwork>
// =============================================================================
class QSslError;
class FileDownloader : public QObject {
Q_OBJECT;
// TODO: Add an error property to use in UI.
Q_PROPERTY(QUrl url READ getUrl WRITE setUrl NOTIFY urlChanged);
Q_PROPERTY(QString downloadFolder READ getDownloadFolder WRITE setDownloadFolder NOTIFY downloadFolderChanged);
Q_PROPERTY(qint64 readBytes READ getReadBytes NOTIFY readBytesChanged);
Q_PROPERTY(qint64 totalBytes READ getTotalBytes NOTIFY totalBytesChanged);
Q_PROPERTY(bool downloading READ getDownloading NOTIFY downloadingChanged);
public:
Q_INVOKABLE void download ();
Q_INVOKABLE bool remove();
signals:
void urlChanged (const QUrl &url);
void downloadFolderChanged (const QString &downloadFolder);
void readBytesChanged (qint64 readBytes);
void totalBytesChanged (qint64 totalBytes);
void downloadingChanged (bool downloading);
void downloadFinished (const QString &filePath);
void downloadFailed();
private:
QUrl getUrl () const;
void setUrl (const QUrl &url);
QString getDownloadFolder () const;
void setDownloadFolder (const QString &downloadFolder);
qint64 getReadBytes () const;
void setReadBytes (qint64 readBytes);
qint64 getTotalBytes () const;
void setTotalBytes (qint64 totalBytes);
bool getDownloading () const;
void setDownloading (bool downloading);
void emitOutputError ();
void handleReadyData ();
void handleDownloadFinished ();
void handleSslErrors (const QList<QSslError> &errors);
void handleDownloadProgress (qint64 readBytes, qint64 totalBytes);
QUrl mUrl;
QString mDownloadFolder;
QFile mDestinationFile;
qint64 mReadBytes = 0;
qint64 mTotalBytes = 0;
bool mDownloading = false;
QPointer<QNetworkReply> mNetworkReply;
QNetworkAccessManager mManager;
};
...@@ -20,7 +20,9 @@ ...@@ -20,7 +20,9 @@
* Author: Ronan Abhamon * Author: Ronan Abhamon
*/ */
#include <mz_os.h>
#include <mz_strm_bzip.h> #include <mz_strm_bzip.h>
#include <mz_strm.h>
#include <mz.h> #include <mz.h>
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
...@@ -31,17 +33,61 @@ ...@@ -31,17 +33,61 @@
using namespace std; using namespace std;
static int openMinizipStream (void **stream, const char *filePath) { class FileExtractor::ExtractStream {
*stream = nullptr; public:
if (!mz_stream_bzip_create(stream)) ExtractStream () : mFileStream(nullptr), mBzipStream(nullptr) {}
return MZ_MEM_ERROR;
Q_CHECK_PTR(*stream); ~ExtractStream () {
qInfo() << QStringLiteral("Opening `%1`...").arg(filePath); if (mBzipStream) {
return mz_stream_bzip_open(*stream, filePath, MZ_OPEN_MODE_READ); mz_stream_bzip_close(mBzipStream);
} mz_stream_bzip_delete(&mBzipStream);
}
if (mFileStream) {
mz_stream_os_close(mFileStream);
mz_stream_os_delete(&mFileStream);
}
}
void *getInternalStream () const {
return mBzipStream;
}
int load (const char *filePath) {
Q_ASSERT(!mFileStream);
Q_ASSERT(!mBzipStream);
// 1. Open file stream.
if (!mz_stream_os_create(&mFileStream))
return MZ_MEM_ERROR;
Q_CHECK_PTR(mFileStream);
int error;
if ((error = mz_stream_os_open(mFileStream, filePath, MZ_OPEN_MODE_READ)) != MZ_OK)
return error;
// 2. Open bzip stream.
if (!mz_stream_bzip_create(&mBzipStream))
return MZ_MEM_ERROR;
Q_CHECK_PTR(mBzipStream);
if ((error = mz_stream_bzip_open(mBzipStream, NULL, MZ_OPEN_MODE_READ)) != MZ_OK)
return error;
// 3. Link file stream to bzip stream.
return mz_stream_set_base(mBzipStream, mFileStream);
}
private:
void *mFileStream;
void *mBzipStream;
};
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
FileExtractor::FileExtractor (QObject *parent) : QObject(parent) {}
FileExtractor::~FileExtractor () {}
void FileExtractor::extract () { void FileExtractor::extract () {
if (mExtracting) { if (mExtracting) {
qWarning() << "Unable to extract file. Already extracting!"; qWarning() << "Unable to extract file. Already extracting!";
...@@ -56,7 +102,9 @@ void FileExtractor::extract () { ...@@ -56,7 +102,9 @@ void FileExtractor::extract () {
// 1. Open archive stream. // 1. Open archive stream.
// TODO: Test extension. // TODO: Test extension.
int error = openMinizipStream(&mStream, mFile.toLatin1().constData()); Q_ASSERT(!mStream);
mStream.reset(new ExtractStream());
int error = mStream->load(mFile.toLatin1().constData());
if (error != MZ_OK) { if (error != MZ_OK) {
emitExtractFailed(error); emitExtractFailed(error);
return; return;
...@@ -145,10 +193,15 @@ void FileExtractor::setExtracting (bool extracting) { ...@@ -145,10 +193,15 @@ void FileExtractor::setExtracting (bool extracting) {
} }
void FileExtractor::clean () { void FileExtractor::clean () {
mz_stream_bzip_delete(&mStream); mStream.reset(nullptr);
mDestinationFile.close(); mDestinationFile.close();
mTimer->stop();
mTimer->deleteLater(); if (mTimer) {
mTimer->stop();
mTimer->deleteLater();
mTimer = nullptr;
}
setExtracting(false); setExtracting(false);
} }
...@@ -175,14 +228,19 @@ void FileExtractor::emitOutputError () { ...@@ -175,14 +228,19 @@ void FileExtractor::emitOutputError () {
void FileExtractor::handleExtraction () { void FileExtractor::handleExtraction () {
char buffer[4096]; char buffer[4096];
int32_t readBytes = mz_stream_bzip_read(mStream, buffer, sizeof buffer);
void *stream = mStream.data()->getInternalStream();
int32_t readBytes = mz_stream_bzip_read(stream, buffer, sizeof buffer);
if (readBytes == 0) if (readBytes == 0)
emitExtractFinished(); emitExtractFinished();
else if (readBytes < 0) else if (readBytes < 0)
emitExtractFailed(readBytes); emitExtractFailed(readBytes);
else { else {
setReadBytes(mReadBytes + readBytes); int64_t inputReadBytes;
if (mDestinationFile.write(buffer, sizeof buffer) == -1) mz_stream_bzip_get_prop_int64(stream, MZ_STREAM_PROP_TOTAL_IN, &inputReadBytes);
setReadBytes(inputReadBytes);
if (mDestinationFile.write(buffer, readBytes) == -1)
emitOutputError(); emitOutputError();
} }
} }
...@@ -30,8 +30,12 @@ ...@@ -30,8 +30,12 @@
// Supports only bzip file. // Supports only bzip file.
class FileExtractor : public QObject { class FileExtractor : public QObject {
class ExtractStream;
Q_OBJECT; Q_OBJECT;
// TODO: Add an error property to use in UI.
Q_PROPERTY(QString file READ getFile WRITE setFile NOTIFY fileChanged); Q_PROPERTY(QString file READ getFile WRITE setFile NOTIFY fileChanged);
Q_PROPERTY(QString extractFolder READ getExtractFolder WRITE setExtractFolder NOTIFY extractFolderChanged); Q_PROPERTY(QString extractFolder READ getExtractFolder WRITE setExtractFolder NOTIFY extractFolderChanged);
Q_PROPERTY(qint64 readBytes READ getReadBytes NOTIFY readBytesChanged); Q_PROPERTY(qint64 readBytes READ getReadBytes NOTIFY readBytesChanged);
...@@ -39,6 +43,9 @@ class FileExtractor : public QObject { ...@@ -39,6 +43,9 @@ class FileExtractor : public QObject {
Q_PROPERTY(bool extracting READ getExtracting NOTIFY extractingChanged); Q_PROPERTY(bool extracting READ getExtracting NOTIFY extractingChanged);
public: public:
FileExtractor (QObject *parent = nullptr);
~FileExtractor ();
Q_INVOKABLE void extract (); Q_INVOKABLE void extract ();
signals: signals:
...@@ -82,7 +89,7 @@ private: ...@@ -82,7 +89,7 @@ private:
qint64 mTotalBytes = 0; qint64 mTotalBytes = 0;
bool mExtracting = false; bool mExtracting = false;
void *mStream = nullptr; QScopedPointer<ExtractStream> mStream;
QTimer *mTimer = nullptr; QTimer *mTimer = nullptr;
}; };
......
...@@ -41,12 +41,12 @@ ...@@ -41,12 +41,12 @@
#endif // ifndef UTILS_NO_BREAK #endif // ifndef UTILS_NO_BREAK
namespace Utils { namespace Utils {
inline QString coreStringToAppString (const std::string &string) { inline QString coreStringToAppString (const std::string &str) {
return QString::fromLocal8Bit(string.c_str(), int(string.size())); return QString::fromLocal8Bit(str.c_str(), int(str.size()));
} }
inline std::string appStringToCoreString (const QString &string) { inline std::string appStringToCoreString (const QString &str) {
return string.toLocal8Bit().constData(); return qPrintable(str);
} }
// Reverse function of strstr. // Reverse function of strstr.
......
...@@ -4,7 +4,6 @@ import QtQuick.Layouts 1.3 ...@@ -4,7 +4,6 @@ import QtQuick.Layouts 1.3
import Common 1.0 import Common 1.0
import Common.Styles 1.0 import Common.Styles 1.0
import Linphone 1.0
import Utils 1.0 import Utils 1.0
import 'ComboBox.js' as Logic import 'ComboBox.js' as Logic
......
...@@ -20,10 +20,10 @@ function attachVirtualWindow (component, properties, exitStatusHandler) { ...@@ -20,10 +20,10 @@ function attachVirtualWindow (component, properties, exitStatusHandler) {
properties: properties properties: properties
}) })
object.exitStatus.connect(detachVirtualWindow)
if (exitStatusHandler) { if (exitStatusHandler) {
object.exitStatus.connect(exitStatusHandler) object.exitStatus.connect(exitStatusHandler)
} }
object.exitStatus.connect(detachVirtualWindow)
virtualWindow.setContent(object) virtualWindow.setContent(object)
......
...@@ -212,6 +212,8 @@ Row { ...@@ -212,6 +212,8 @@ Row {
color: ChatStyle.entry.message.file.status.bar.contentItem.color color: ChatStyle.entry.message.file.status.bar.contentItem.color
height: parent.height height: parent.height
width: progressBar.visualPosition * parent.width width: progressBar.visualPosition * parent.width
radius: ChatStyle.entry.message.file.status.bar.radius
} }
} }
} }
......
...@@ -8,8 +8,16 @@ import Linphone.Styles 1.0 ...@@ -8,8 +8,16 @@ import Linphone.Styles 1.0
// ============================================================================= // =============================================================================
Column { Column {
id: codecsViewer
// ---------------------------------------------------------------------------
property alias model: view.model property alias model: view.model
// ---------------------------------------------------------------------------
signal downloadRequested (var codecInfo)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Header. // Header.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
...@@ -75,9 +83,9 @@ Column { ...@@ -75,9 +83,9 @@ Column {
height: count * CodecsViewerStyle.attribute.height height: count * CodecsViewerStyle.attribute.height
// ----------------------------------------------------------------------- // -------------------------------------------------------------------------
// One codec. // One codec.
// ----------------------------------------------------------------------- // -------------------------------------------------------------------------
delegate: MouseArea { delegate: MouseArea {
id: dragArea id: dragArea
...@@ -110,6 +118,8 @@ Column { ...@@ -110,6 +118,8 @@ Column {
Rectangle { Rectangle {
id: content id: content
readonly property bool isDownloadable: Boolean($codec.downloadUrl)
Drag.active: dragArea.held Drag.active: dragArea.held
Drag.source: dragArea Drag.source: dragArea
Drag.hotSpot.x: width / 2 Drag.hotSpot.x: width / 2
...@@ -139,25 +149,26 @@ Column { ...@@ -139,25 +149,26 @@ Column {
CodecAttribute { CodecAttribute {
Layout.preferredWidth: CodecsViewerStyle.column.encoderDescriptionWidth Layout.preferredWidth: CodecsViewerStyle.column.encoderDescriptionWidth
text: $codec.encoderDescription text: $codec.encoderDescription || ''
} }
CodecAttribute { CodecAttribute {
Layout.preferredWidth: CodecsViewerStyle.column.clockRateWidth Layout.preferredWidth: CodecsViewerStyle.column.clockRateWidth
text: $codec.clockRate text: $codec.clockRate || ''
} }
NumericField { NumericField {
Layout.preferredWidth: CodecsViewerStyle.column.bitrateWidth Layout.preferredWidth: CodecsViewerStyle.column.bitrateWidth
readOnly: !$codec.isVbr readOnly: content.isDownloadable || !$codec.isVbr
text: $codec.bitrate text: $codec.bitrate || ''
onEditingFinished: view.model.setBitrate(index, text) onEditingFinished: view.model.setBitrate(index, text)
} }
TextField { TextField {
Layout.preferredWidth: CodecsViewerStyle.column.recvFmtpWidth Layout.preferredWidth: CodecsViewerStyle.column.recvFmtpWidth
text: $codec.recvFmtp readOnly: content.isDownloadable
text: $codec.recvFmtp || ''
onEditingFinished: view.model.setRecvFmtp(index, text) onEditingFinished: view.model.setRecvFmtp(index, text)
} }
...@@ -165,9 +176,11 @@ Column { ...@@ -165,9 +176,11 @@ Column {
Switch { Switch {
Layout.fillWidth: true Layout.fillWidth: true
checked: $codec.enabled checked: Boolean($codec.enabled)
onClicked: view.model.enableCodec(index, !checked) onClicked: !checked && content.isDownloadable
? downloadRequested($codec)
: view.model.enableCodec(index, !checked)
} }
} }
} }
......
import QtQuick 2.7
import QtQuick.Controls 2.2
import Common 1.0
import Linphone 1.0
import Linphone.Styles 1.0
import Utils 1.0
// =============================================================================
DialogPlus {
id: dialog
// ---------------------------------------------------------------------------
property alias downloadUrl: fileDownloader.url
property alias installFolder: fileDownloader.downloadFolder
property bool extract: false
property string fileName
property bool _installing: false
property int _exitStatus: -1 // Not downloaded for the moment.
// ---------------------------------------------------------------------------
function install () {
dialog._installing = true
fileDownloader.download()
}
function _endInstall (exitStatus) {
if (dialog.extract)
fileDownloader.remove()
dialog._exitStatus = exitStatus
dialog._installing = false
}
// ---------------------------------------------------------------------------
// TODO: Improve one day. Do not launch download directly.
// Provide a download function (window.attachVirtualWindow cannot call
// function after creation at this moment).
Component.onCompleted: dialog.install()
// ---------------------------------------------------------------------------
buttons: [
// TODO: Add a retry button???
TextButtonB {
enabled: !dialog._installing && !fileDownloader.downloading && !fileExtractor.extracting
text: qsTr('confirm')
onClicked: exit(1)
}
]
centeredButtons: true
descriptionText: {
var str
if (dialog.extracting) {
str = qsTr('onlineInstallerExtractingDescription')
} else if (dialog._installing) {
str = qsTr('onlineInstallerDownloadingDescription')
} else if (dialog._exitStatus > 0) {
str = qsTr('onlineInstallerFinishedDescription')
} else {
str = qsTr('onlineInstallerFailedDescription')
}
return str.replace('%1', dialog.fileName)
}
height: OnlineInstallerDialogStyle.height
width: OnlineInstallerDialogStyle.width
Column {
anchors.verticalCenter: parent.verticalCenter
width: parent.width
spacing: OnlineInstallerDialogStyle.column.spacing
ProgressBar {
id: progressBar
property var target: fileDownloader
height: OnlineInstallerDialogStyle.column.bar.height
width: parent.width
to: target.totalBytes
value: target.readBytes
indeterminate : true
background: Rectangle {
color: OnlineInstallerDialogStyle.column.bar.background.color
radius: OnlineInstallerDialogStyle.column.bar.radius
}
contentItem: Item {
Rectangle {
color: dialog._exitStatus
? OnlineInstallerDialogStyle.column.bar.contentItem.color.normal
: OnlineInstallerDialogStyle.column.bar.contentItem.color.failed
height: parent.height
radius: OnlineInstallerDialogStyle.column.bar.radius
width: progressBar.visualPosition * parent.width
}
}
}
Text {
anchors.right: parent.right
color: OnlineInstallerDialogStyle.column.text.color
font.pointSize: OnlineInstallerDialogStyle.column.text.pointSize
text: {
var fileSize = Utils.formatSize(fileDownloader.totalBytes)
return Utils.formatSize(fileDownloader.readBytes) + '/' + fileSize
}
}
FileDownloader {
id: fileDownloader
onDownloadFailed: dialog._endInstall(0)
onDownloadFinished: {
fileExtractor.file = filePath
if (dialog.extract) {
progressBar.target = fileExtractor
fileExtractor.extract()
} else {
dialog._endInstall(1)
}
}
}
FileExtractor {
id: fileExtractor
extractFolder: dialog.installFolder
onExtractFailed: dialog._endInstall(0)
onExtractFinished: dialog._endInstall(1)
}
}
}
pragma Singleton
import QtQml 2.2
import Colors 1.0
import Units 1.0
// =============================================================================
QtObject {
property int height: 200
property int width: 400
property QtObject column: QtObject {
property int spacing: 6
property QtObject bar: QtObject {
property int height: 20
property int radius: 6
property QtObject background: QtObject {
property color color: Colors.f
}
property QtObject contentItem: QtObject {
property QtObject color: QtObject {
property color failed: Colors.error
property color normal: Colors.z
}
}
}
property QtObject text: QtObject {
property color color: Colors.r
property int pointSize: Units.dp * 11
}
}
}
...@@ -23,6 +23,8 @@ singleton ContactDescriptionStyle 1.0 Contact/ContactDescriptionSty ...@@ -23,6 +23,8 @@ singleton ContactDescriptionStyle 1.0 Contact/ContactDescriptionSty
singleton ContactStyle 1.0 Contact/ContactStyle.qml singleton ContactStyle 1.0 Contact/ContactStyle.qml
singleton MessagesCounterStyle 1.0 Contact/MessagesCounterStyle.qml singleton MessagesCounterStyle 1.0 Contact/MessagesCounterStyle.qml
singleton OnlineInstallerDialogStyle 1.0 Dialog/OnlineInstallerDialogStyle.qml
singleton SipAddressesMenuStyle 1.0 Menus/SipAddressesMenuStyle.qml singleton SipAddressesMenuStyle 1.0 Menus/SipAddressesMenuStyle.qml
singleton NotificationBasicStyle 1.0 Notifications/NotificationBasicStyle.qml singleton NotificationBasicStyle 1.0 Notifications/NotificationBasicStyle.qml
......
...@@ -4,8 +4,12 @@ ...@@ -4,8 +4,12 @@
.pragma library .pragma library
.import Linphone 1.0 as Linphone
.import 'qrc:/ui/scripts/Utils/utils.js' as Utils .import 'qrc:/ui/scripts/Utils/utils.js' as Utils
// =============================================================================
// Contact/SIP address helpers.
// ============================================================================= // =============================================================================
function _getDisplayNameFromQuotedString (str) { function _getDisplayNameFromQuotedString (str) {
...@@ -84,3 +88,43 @@ function getContactUsername (contact) { ...@@ -84,3 +88,43 @@ function getContactUsername (contact) {
name = _getUsername(object) name = _getUsername(object)
return name == null ? 'Bad EGG' : name return name == null ? 'Bad EGG' : name
} }
// =============================================================================
// Codec helpers.
// =============================================================================
function openCodecOnlineInstallerDialog (window, codecInfo, cb) {
var VideoCodecsModel = Linphone.VideoCodecsModel
window.attachVirtualWindow(Utils.buildDialogUri('ConfirmDialog'), {
descriptionText: qsTr('downloadCodecDescription')
.replace('%1', codecInfo.mime)
.replace('%2', codecInfo.encoderDescription)
}, function (status) {
if (status) {
window.attachVirtualWindow(buildDialogUri('OnlineInstallerDialog'), {
downloadUrl: codecInfo.downloadUrl,
extract: true,
fileName: codecInfo.mime,
installFolder: VideoCodecsModel.codecsFolder
}, function (status) {
if (status) {
VideoCodecsModel.reload()
}
if (cb) {
cb(window)
}
})
}
else if (cb) {
cb(window)
}
})
}
// =============================================================================
// QML helpers.
// =============================================================================
function buildDialogUri (component) {
return 'qrc:/ui/modules/Linphone/Dialog/' + component + '.qml'
}
...@@ -2,6 +2,7 @@ import QtQuick 2.7 ...@@ -2,6 +2,7 @@ import QtQuick 2.7
import Common 1.0 import Common 1.0
import Linphone 1.0 import Linphone 1.0
import LinphoneUtils 1.0
import App.Styles 1.0 import App.Styles 1.0
...@@ -50,8 +51,16 @@ AssistantAbstractView { ...@@ -50,8 +51,16 @@ AssistantAbstractView {
onActivateStatusChanged: { onActivateStatusChanged: {
requestBlock.stop(error) requestBlock.stop(error)
if (!error.length) { if (!error.length) {
window.unlockView() function quitToHome (window) {
window.setView('Home') window.unlockView()
window.setView('Home')
}
var codecInfo = VideoCodecsModel.getCodecInfo('H264')
if (codecInfo.downloadUrl) {
LinphoneUtils.openCodecOnlineInstallerDialog(window, codecInfo, quitToHome)
} else {
quitToHome(window)
}
} }
} }
} }
......
...@@ -2,6 +2,7 @@ import QtQuick 2.7 ...@@ -2,6 +2,7 @@ import QtQuick 2.7
import Common 1.0 import Common 1.0
import Linphone 1.0 import Linphone 1.0
import LinphoneUtils 1.0
import App.Styles 1.0 import App.Styles 1.0
...@@ -62,8 +63,16 @@ AssistantAbstractView { ...@@ -62,8 +63,16 @@ AssistantAbstractView {
onActivateStatusChanged: { onActivateStatusChanged: {
requestBlock.stop(error) requestBlock.stop(error)
if (!error.length) { if (!error.length) {
window.unlockView() function quitToHome (window) {
window.setView('Home') window.unlockView()
window.setView('Home')
}
var codecInfo = VideoCodecsModel.getCodecInfo('H264')
if (codecInfo.downloadUrl) {
LinphoneUtils.openCodecOnlineInstallerDialog(window, codecInfo, quitToHome)
} else {
quitToHome(window)
}
} }
} }
} }
......
...@@ -2,6 +2,7 @@ import QtQuick 2.7 ...@@ -2,6 +2,7 @@ import QtQuick 2.7
import Common 1.0 import Common 1.0
import Linphone 1.0 import Linphone 1.0
import LinphoneUtils 1.0
import App.Styles 1.0 import App.Styles 1.0
...@@ -17,6 +18,8 @@ AssistantAbstractView { ...@@ -17,6 +18,8 @@ AssistantAbstractView {
title: qsTr('useLinphoneSipAccountTitle') title: qsTr('useLinphoneSipAccountTitle')
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
Column { Column {
...@@ -86,7 +89,14 @@ AssistantAbstractView { ...@@ -86,7 +89,14 @@ AssistantAbstractView {
onLoginStatusChanged: { onLoginStatusChanged: {
requestBlock.stop(error) requestBlock.stop(error)
if (!error.length) { if (!error.length) {
window.setView('Home') var codecInfo = VideoCodecsModel.getCodecInfo('H264')
if (codecInfo.downloadUrl) {
LinphoneUtils.openCodecOnlineInstallerDialog(window, codecInfo, function cb (window) {
window.setView('Home')
})
} else {
window.setView('Home')
}
} }
} }
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
.import Linphone 1.0 as Linphone .import Linphone 1.0 as Linphone
.import 'qrc:/ui/scripts/LinphoneUtils/linphone-utils.js' as LinphoneUtils
// ============================================================================= // =============================================================================
function showVideoPreview (account) { function showVideoPreview (account) {
...@@ -23,3 +25,7 @@ function updateVideoPreview () { ...@@ -23,3 +25,7 @@ function updateVideoPreview () {
function hideVideoPreview () { function hideVideoPreview () {
window.detachVirtualWindow() window.detachVirtualWindow()
} }
function handleCodecDownloadRequested (codecInfo) {
LinphoneUtils.openCodecOnlineInstallerDialog(window, codecInfo)
}
...@@ -142,6 +142,8 @@ TabContainer { ...@@ -142,6 +142,8 @@ TabContainer {
CodecsViewer { CodecsViewer {
model: VideoCodecsModel model: VideoCodecsModel
width: parent.width width: parent.width
onDownloadRequested: Logic.handleCodecDownloadRequested(codecInfo)
} }
} }
} }
......
...@@ -12,7 +12,6 @@ import App.Styles 1.0 ...@@ -12,7 +12,6 @@ import App.Styles 1.0
ApplicationWindow { ApplicationWindow {
id: window id: window
minimumHeight: SettingsWindowStyle.height minimumHeight: SettingsWindowStyle.height
minimumWidth: SettingsWindowStyle.width minimumWidth: SettingsWindowStyle.width
......
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