mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-17 11:28:07 +00:00
feat(core): supports H264 download
Co-authored-by: Danmei Chen <danmei.chen@belledonne-communications.com>
This commit is contained in:
parent
7c24e07fd8
commit
23939aaa2a
37 changed files with 969 additions and 92 deletions
|
|
@ -145,6 +145,7 @@ set(SOURCES
|
|||
src/components/core/CoreHandlers.cpp
|
||||
src/components/core/CoreManager.cpp
|
||||
src/components/core/messages-count-notifier/AbstractMessagesCountNotifier.cpp
|
||||
src/components/file/FileDownloader.cpp
|
||||
src/components/file/FileExtractor.cpp
|
||||
src/components/notifier/Notifier.cpp
|
||||
src/components/other/clipboard/Clipboard.cpp
|
||||
|
|
@ -163,8 +164,8 @@ set(SOURCES
|
|||
src/components/timeline/TimelineModel.cpp
|
||||
src/components/url-handlers/UrlHandlers.cpp
|
||||
src/utils/LinphoneUtils.cpp
|
||||
src/utils/Utils.cpp
|
||||
src/utils/QExifImageHeader.cpp
|
||||
src/utils/Utils.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
|
|
@ -202,6 +203,7 @@ set(HEADERS
|
|||
src/components/core/CoreHandlers.hpp
|
||||
src/components/core/CoreManager.hpp
|
||||
src/components/core/messages-count-notifier/AbstractMessagesCountNotifier.hpp
|
||||
src/components/file/FileDownloader.hpp
|
||||
src/components/file/FileExtractor.hpp
|
||||
src/components/notifier/Notifier.hpp
|
||||
src/components/other/clipboard/Clipboard.hpp
|
||||
|
|
|
|||
|
|
@ -1003,6 +1003,29 @@ your friend's SIP address or username.</translation>
|
|||
<translation>New attachment received!</translation>
|
||||
</message>
|
||||
</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>
|
||||
<name>OutgoingMessage</name>
|
||||
<message>
|
||||
|
|
@ -1672,4 +1695,11 @@ your friend's SIP address or username.</translation>
|
|||
<translation>CONFIRM</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>linphone-utils</name>
|
||||
<message>
|
||||
<source>downloadCodecDescription</source>
|
||||
<translation>Do you want to download %1 (%2)?</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
|||
|
|
@ -1001,6 +1001,29 @@ Cliquez ici : <a href="%1">%1</a>
|
|||
<translation>Pièce jointe reçue !</translation>
|
||||
</message>
|
||||
</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'installation de %1 a échoué !</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>OutgoingMessage</name>
|
||||
<message>
|
||||
|
|
@ -1670,4 +1693,11 @@ Cliquez ici : <a href="%1">%1</a>
|
|||
<translation>CONFIRMER</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>linphone-utils</name>
|
||||
<message>
|
||||
<source>downloadCodecDescription</source>
|
||||
<translation>Voulez-vous installer %1 (%2) ?</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
|||
|
|
@ -1001,6 +1001,29 @@
|
|||
<translation>Получен новый файл!</translation>
|
||||
</message>
|
||||
</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>
|
||||
<name>OutgoingMessage</name>
|
||||
<message>
|
||||
|
|
@ -1670,4 +1693,11 @@
|
|||
<translation>ПОДТВЕРДИТЬ</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>linphone-utils</name>
|
||||
<message>
|
||||
<source>downloadCodecDescription</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
|||
|
|
@ -1003,6 +1003,29 @@ arkadaşınızın SIP adresini veya kullanıcı adını girin.</translation>
|
|||
<translation>Yeni ek alındı!</translation>
|
||||
</message>
|
||||
</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>
|
||||
<name>OutgoingMessage</name>
|
||||
<message>
|
||||
|
|
@ -1672,4 +1695,11 @@ arkadaşınızın SIP adresini veya kullanıcı adını girin.</translation>
|
|||
<translation>ONAYLA</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>linphone-utils</name>
|
||||
<message>
|
||||
<source>downloadCodecDescription</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
|||
|
|
@ -332,6 +332,7 @@
|
|||
<file>ui/modules/Linphone/Contact/ContactDescription.qml</file>
|
||||
<file>ui/modules/Linphone/Contact/Contact.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/Notifications/NotificationBasic.qml</file>
|
||||
<file>ui/modules/Linphone/Notifications/NotificationNewVersionAvailable.qml</file>
|
||||
|
|
@ -357,6 +358,7 @@
|
|||
<file>ui/modules/Linphone/Styles/Contact/ContactDescriptionStyle.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/Dialog/OnlineInstallerDialogStyle.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/NotificationReceivedCallStyle.qml</file>
|
||||
|
|
|
|||
|
|
@ -176,10 +176,6 @@ void App::initContentApp () {
|
|||
Cli::executeCommand(command);
|
||||
});
|
||||
|
||||
// Add plugins directory.
|
||||
addLibraryPath(::Utils::coreStringToAppString(Paths::getPluginsDirPath()));
|
||||
qInfo() << QStringLiteral("Library paths:") << libraryPaths();
|
||||
|
||||
mustBeIconified = mParser->isSet("iconified");
|
||||
}
|
||||
|
||||
|
|
@ -405,6 +401,7 @@ void App::registerTypes () {
|
|||
registerType<ConferenceHelperModel>("ConferenceHelperModel");
|
||||
registerType<ConferenceModel>("ConferenceModel");
|
||||
registerType<ContactsListProxyModel>("ContactsListProxyModel");
|
||||
registerType<FileDownloader>("FileDownloader");
|
||||
registerType<FileExtractor>("FileExtractor");
|
||||
registerType<SipAddressesProxyModel>("SipAddressesProxyModel");
|
||||
registerType<SoundPlayer>("SoundPlayer");
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ void Logger::log (QtMsgType type, const QMessageLogContext &context, const QStri
|
|||
contextStr = contextArr.constData();
|
||||
}
|
||||
#else
|
||||
(void)context;
|
||||
Q_UNUSED(context);
|
||||
#endif // ifdef QT_MESSAGELOGCONTEXT
|
||||
|
||||
QByteArray localMsg = msg.toLocal8Bit();
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ namespace {
|
|||
constexpr char cPathAssistantConfig[] = "/linphone/assistant/";
|
||||
constexpr char cPathAvatars[] = "/avatars/";
|
||||
constexpr char cPathCaptures[] = "/Linphone/captures/";
|
||||
constexpr char cPathCodecs[] = "/codecs/";
|
||||
constexpr char cPathLogs[] = "/logs/";
|
||||
constexpr char cPathPlugins[] = "/plugins/";
|
||||
constexpr char cPathThumbnails[] = "/thumbnails/";
|
||||
|
|
@ -181,6 +182,10 @@ string Paths::getCapturesDirPath () {
|
|||
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) {
|
||||
const QString path = configPath.isEmpty()
|
||||
? getAppConfigFilePath()
|
||||
|
|
@ -217,10 +222,6 @@ string Paths::getPackageMsPluginsDirPath () {
|
|||
return getReadableDirPath(getAppPackageMsPluginsDirPath());
|
||||
}
|
||||
|
||||
string Paths::getPluginsDirPath () {
|
||||
return getReadableDirPath(getAppPluginsDirPath());
|
||||
}
|
||||
|
||||
string Paths::getRootCaFilePath () {
|
||||
return getReadableFilePath(getAppRootCaFilePath());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,15 +34,15 @@ namespace Paths {
|
|||
std::string getAvatarsDirPath ();
|
||||
std::string getCallHistoryFilePath ();
|
||||
std::string getCapturesDirPath ();
|
||||
std::string getCodecsDirPath ();
|
||||
std::string getConfigFilePath (const QString &configPath = QString(), bool writable = true);
|
||||
std::string getDownloadDirPath ();
|
||||
std::string getFactoryConfigFilePath ();
|
||||
std::string getFriendsListFilePath ();
|
||||
std::string getDownloadDirPath ();
|
||||
std::string getLogsDirPath ();
|
||||
std::string getMessageHistoryFilePath ();
|
||||
std::string getPackageDataDirPath ();
|
||||
std::string getPackageMsPluginsDirPath ();
|
||||
std::string getPluginsDirPath ();
|
||||
std::string getRootCaFilePath ();
|
||||
std::string getThumbnailsDirPath ();
|
||||
std::string getUserCertificatesDirPath ();
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include "conference/ConferenceModel.hpp"
|
||||
#include "contacts/ContactsListProxyModel.hpp"
|
||||
#include "core/CoreManager.hpp"
|
||||
#include "file/FileDownloader.hpp"
|
||||
#include "file/FileExtractor.hpp"
|
||||
#include "presence/OwnPresenceModel.hpp"
|
||||
#include "settings/AccountSettingsModel.hpp"
|
||||
|
|
|
|||
|
|
@ -20,17 +20,18 @@
|
|||
* Author: Ronan Abhamon
|
||||
*/
|
||||
|
||||
#include "../../app/paths/Paths.hpp"
|
||||
#include "../../utils/Utils.hpp"
|
||||
#include "../core/CoreManager.hpp"
|
||||
|
||||
#include "AbstractCodecsModel.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// =============================================================================
|
||||
|
||||
using namespace std;
|
||||
|
||||
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) {
|
|||
Q_ASSERT(id >= 0 && id < mCodecs.count());
|
||||
|
||||
QVariantMap &map = mCodecs[id];
|
||||
shared_ptr<linphone::PayloadType> codec = ::getCodecFromMap(map);
|
||||
|
||||
codec->enable(status);
|
||||
map["enabled"] = codec->enabled();
|
||||
|
||||
emit dataChanged(index(id, 0), index(id, 0));
|
||||
shared_ptr<linphone::PayloadType> codec = getCodecFromMap(map);
|
||||
if (codec) {
|
||||
codec->enable(status);
|
||||
map["enabled"] = codec->enabled();
|
||||
emit dataChanged(index(id, 0), index(id, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractCodecsModel::moveCodec (int source, int destination) {
|
||||
|
|
@ -81,12 +82,12 @@ void AbstractCodecsModel::setBitrate (int id, int bitrate) {
|
|||
Q_ASSERT(id >= 0 && id < mCodecs.count());
|
||||
|
||||
QVariantMap &map = mCodecs[id];
|
||||
shared_ptr<linphone::PayloadType> codec = ::getCodecFromMap(map);
|
||||
|
||||
codec->setNormalBitrate(bitrate);
|
||||
map["bitrate"] = codec->getNormalBitrate();
|
||||
|
||||
emit dataChanged(index(id, 0), index(id, 0));
|
||||
shared_ptr<linphone::PayloadType> codec = getCodecFromMap(map);
|
||||
if (codec) {
|
||||
codec->setNormalBitrate(bitrate);
|
||||
map["bitrate"] = codec->getNormalBitrate();
|
||||
emit dataChanged(index(id, 0), index(id, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractCodecsModel::setRecvFmtp (int id, const QString &recvFmtp) {
|
||||
|
|
@ -94,11 +95,11 @@ void AbstractCodecsModel::setRecvFmtp (int id, const QString &recvFmtp) {
|
|||
|
||||
QVariantMap &map = mCodecs[id];
|
||||
shared_ptr<linphone::PayloadType> codec = ::getCodecFromMap(map);
|
||||
|
||||
codec->setRecvFmtp(::Utils::appStringToCoreString(recvFmtp));
|
||||
map["recvFmtp"] = ::Utils::coreStringToAppString(codec->getRecvFmtp());
|
||||
|
||||
emit dataChanged(index(id, 0), index(id, 0));
|
||||
if (codec) {
|
||||
codec->setRecvFmtp(Utils::appStringToCoreString(recvFmtp));
|
||||
map["recvFmtp"] = Utils::coreStringToAppString(codec->getRecvFmtp());
|
||||
emit dataChanged(index(id, 0), index(id, 0));
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
@ -110,6 +111,8 @@ bool AbstractCodecsModel::moveRows (
|
|||
const QModelIndex &destinationParent,
|
||||
int destinationChild
|
||||
) {
|
||||
// TODO: Do not move downloadable codecs.
|
||||
|
||||
int limit = sourceRow + count - 1;
|
||||
|
||||
{
|
||||
|
|
@ -139,9 +142,13 @@ bool AbstractCodecsModel::moveRows (
|
|||
}
|
||||
|
||||
// Update linphone codecs list.
|
||||
list<shared_ptr<linphone::PayloadType> > codecs;
|
||||
for (const auto &map : mCodecs)
|
||||
codecs.push_back(::getCodecFromMap(map));
|
||||
list<shared_ptr<linphone::PayloadType>> codecs;
|
||||
for (const auto &map : mCodecs) {
|
||||
// Do not update downloadable codecs.
|
||||
shared_ptr<linphone::PayloadType> codec = getCodecFromMap(map);
|
||||
if (codec)
|
||||
codecs.push_back(codec);
|
||||
}
|
||||
updateCodecs(codecs);
|
||||
|
||||
endMoveRows();
|
||||
|
|
@ -157,15 +164,40 @@ void AbstractCodecsModel::addCodec (shared_ptr<linphone::PayloadType> &codec) {
|
|||
map["bitrate"] = codec->getNormalBitrate();
|
||||
map["channels"] = codec->getChannels();
|
||||
map["clockRate"] = codec->getClockRate();
|
||||
map["description"] = ::Utils::coreStringToAppString(codec->getDescription());
|
||||
map["description"] = Utils::coreStringToAppString(codec->getDescription());
|
||||
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["isVbr"] = codec->isVbr();
|
||||
map["mime"] = ::Utils::coreStringToAppString(codec->getMimeType());
|
||||
map["mime"] = Utils::coreStringToAppString(codec->getMimeType());
|
||||
map["number"] = codec->getNumber();
|
||||
map["recvFmtp"] = ::Utils::coreStringToAppString(codec->getRecvFmtp());
|
||||
map["recvFmtp"] = Utils::coreStringToAppString(codec->getRecvFmtp());
|
||||
map["__codec"] = QVariant::fromValue(codec);
|
||||
|
||||
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 {
|
|||
class AbstractCodecsModel : public QAbstractListModel {
|
||||
Q_OBJECT;
|
||||
|
||||
Q_PROPERTY(QString codecsFolder READ getCodecsFolder CONSTANT);
|
||||
|
||||
public:
|
||||
AbstractCodecsModel (QObject *parent = Q_NULLPTR);
|
||||
virtual ~AbstractCodecsModel () = default;
|
||||
|
|
@ -51,6 +53,10 @@ public:
|
|||
Q_INVOKABLE void setBitrate (int id, int bitrate);
|
||||
Q_INVOKABLE void setRecvFmtp (int id, const QString &recvFmtp);
|
||||
|
||||
Q_INVOKABLE virtual void reload () {};
|
||||
|
||||
Q_INVOKABLE QVariantMap getCodecInfo (const QString &mime) const;
|
||||
|
||||
protected:
|
||||
bool moveRows (
|
||||
const QModelIndex &sourceParent,
|
||||
|
|
@ -61,10 +67,12 @@ protected:
|
|||
) override;
|
||||
|
||||
void addCodec (std::shared_ptr<linphone::PayloadType> &codec);
|
||||
void addDownloadableCodec (const QString &mime, const QString &downloadUrl, const QString &encoderDescription);
|
||||
|
||||
virtual void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType> > &codecs) = 0;
|
||||
QString getCodecsFolder () const;
|
||||
|
||||
virtual void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType>> &codecs) = 0;
|
||||
|
||||
private:
|
||||
QList<QVariantMap> mCodecs;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -24,15 +24,15 @@
|
|||
|
||||
#include "AudioCodecsModel.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// =============================================================================
|
||||
|
||||
using namespace std;
|
||||
|
||||
AudioCodecsModel::AudioCodecsModel (QObject *parent) : AbstractCodecsModel(parent) {
|
||||
for (auto &codec : CoreManager::getInstance()->getCore()->getAudioPayloadTypes())
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ public:
|
|||
AudioCodecsModel (QObject *parent = Q_NULLPTR);
|
||||
~AudioCodecsModel () = default;
|
||||
|
||||
protected:
|
||||
void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType> > &codecs) override;
|
||||
private:
|
||||
void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType>> &codecs) override;
|
||||
};
|
||||
|
||||
#endif // AUDIO_CODECS_MODEL_H_
|
||||
|
|
|
|||
|
|
@ -20,19 +20,79 @@
|
|||
* Author: Ronan Abhamon
|
||||
*/
|
||||
|
||||
#include <QDirIterator>
|
||||
#include <QLibrary>
|
||||
|
||||
#include "../../app/paths/Paths.hpp"
|
||||
#include "../../utils/Utils.hpp"
|
||||
#include "../core/CoreManager.hpp"
|
||||
|
||||
#include "VideoCodecsModel.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// =============================================================================
|
||||
|
||||
VideoCodecsModel::VideoCodecsModel (QObject *parent) : AbstractCodecsModel(parent) {
|
||||
for (auto &codec : CoreManager::getInstance()->getCore()->getVideoPayloadTypes())
|
||||
addCodec(codec);
|
||||
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
|
||||
}
|
||||
|
||||
void VideoCodecsModel::updateCodecs (list<shared_ptr<linphone::PayloadType> > &codecs) {
|
||||
VideoCodecsModel::VideoCodecsModel (QObject *parent) : AbstractCodecsModel(parent) {
|
||||
load();
|
||||
}
|
||||
|
||||
void VideoCodecsModel::updateCodecs (list<shared_ptr<linphone::PayloadType>> &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:
|
|||
VideoCodecsModel (QObject *parent = Q_NULLPTR);
|
||||
~VideoCodecsModel () = default;
|
||||
|
||||
protected:
|
||||
void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType> > &codecs) override;
|
||||
private:
|
||||
void updateCodecs (std::list<std::shared_ptr<linphone::PayloadType>> &codecs) override;
|
||||
|
||||
void load ();
|
||||
void reload () override;
|
||||
};
|
||||
|
||||
#endif // VIDEO_CODECS_MODEL_H_
|
||||
|
|
|
|||
|
|
@ -30,5 +30,5 @@ MessagesCountNotifier::MessagesCountNotifier (QObject *parent) : AbstractMessage
|
|||
|
||||
void MessagesCountNotifier::notifyUnreadMessagesCount (int n) {
|
||||
// TODO.
|
||||
(void)n;
|
||||
Q_UNUSED(n);
|
||||
}
|
||||
|
|
|
|||
221
src/components/file/FileDownloader.cpp
Normal file
221
src/components/file/FileDownloader.cpp
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
88
src/components/file/FileDownloader.hpp
Normal file
88
src/components/file/FileDownloader.hpp
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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 @@
|
|||
* Author: Ronan Abhamon
|
||||
*/
|
||||
|
||||
#include <mz_os.h>
|
||||
#include <mz_strm_bzip.h>
|
||||
#include <mz_strm.h>
|
||||
#include <mz.h>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
|
|
@ -31,17 +33,61 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
static int openMinizipStream (void **stream, const char *filePath) {
|
||||
*stream = nullptr;
|
||||
if (!mz_stream_bzip_create(stream))
|
||||
return MZ_MEM_ERROR;
|
||||
Q_CHECK_PTR(*stream);
|
||||
qInfo() << QStringLiteral("Opening `%1`...").arg(filePath);
|
||||
return mz_stream_bzip_open(*stream, filePath, MZ_OPEN_MODE_READ);
|
||||
}
|
||||
class FileExtractor::ExtractStream {
|
||||
public:
|
||||
ExtractStream () : mFileStream(nullptr), mBzipStream(nullptr) {}
|
||||
|
||||
~ExtractStream () {
|
||||
if (mBzipStream) {
|
||||
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 () {
|
||||
if (mExtracting) {
|
||||
qWarning() << "Unable to extract file. Already extracting!";
|
||||
|
|
@ -56,7 +102,9 @@ void FileExtractor::extract () {
|
|||
|
||||
// 1. Open archive stream.
|
||||
// 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) {
|
||||
emitExtractFailed(error);
|
||||
return;
|
||||
|
|
@ -145,10 +193,15 @@ void FileExtractor::setExtracting (bool extracting) {
|
|||
}
|
||||
|
||||
void FileExtractor::clean () {
|
||||
mz_stream_bzip_delete(&mStream);
|
||||
mStream.reset(nullptr);
|
||||
mDestinationFile.close();
|
||||
mTimer->stop();
|
||||
mTimer->deleteLater();
|
||||
|
||||
if (mTimer) {
|
||||
mTimer->stop();
|
||||
mTimer->deleteLater();
|
||||
mTimer = nullptr;
|
||||
}
|
||||
|
||||
setExtracting(false);
|
||||
}
|
||||
|
||||
|
|
@ -175,14 +228,19 @@ void FileExtractor::emitOutputError () {
|
|||
|
||||
void FileExtractor::handleExtraction () {
|
||||
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)
|
||||
emitExtractFinished();
|
||||
else if (readBytes < 0)
|
||||
emitExtractFailed(readBytes);
|
||||
else {
|
||||
setReadBytes(mReadBytes + readBytes);
|
||||
if (mDestinationFile.write(buffer, sizeof buffer) == -1)
|
||||
int64_t inputReadBytes;
|
||||
mz_stream_bzip_get_prop_int64(stream, MZ_STREAM_PROP_TOTAL_IN, &inputReadBytes);
|
||||
setReadBytes(inputReadBytes);
|
||||
if (mDestinationFile.write(buffer, readBytes) == -1)
|
||||
emitOutputError();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,8 +30,12 @@
|
|||
|
||||
// Supports only bzip file.
|
||||
class FileExtractor : public QObject {
|
||||
class ExtractStream;
|
||||
|
||||
Q_OBJECT;
|
||||
|
||||
// TODO: Add an error property to use in UI.
|
||||
|
||||
Q_PROPERTY(QString file READ getFile WRITE setFile NOTIFY fileChanged);
|
||||
Q_PROPERTY(QString extractFolder READ getExtractFolder WRITE setExtractFolder NOTIFY extractFolderChanged);
|
||||
Q_PROPERTY(qint64 readBytes READ getReadBytes NOTIFY readBytesChanged);
|
||||
|
|
@ -39,6 +43,9 @@ class FileExtractor : public QObject {
|
|||
Q_PROPERTY(bool extracting READ getExtracting NOTIFY extractingChanged);
|
||||
|
||||
public:
|
||||
FileExtractor (QObject *parent = nullptr);
|
||||
~FileExtractor ();
|
||||
|
||||
Q_INVOKABLE void extract ();
|
||||
|
||||
signals:
|
||||
|
|
@ -82,7 +89,7 @@ private:
|
|||
qint64 mTotalBytes = 0;
|
||||
bool mExtracting = false;
|
||||
|
||||
void *mStream = nullptr;
|
||||
QScopedPointer<ExtractStream> mStream;
|
||||
|
||||
QTimer *mTimer = nullptr;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -41,12 +41,12 @@
|
|||
#endif // ifndef UTILS_NO_BREAK
|
||||
|
||||
namespace Utils {
|
||||
inline QString coreStringToAppString (const std::string &string) {
|
||||
return QString::fromLocal8Bit(string.c_str(), int(string.size()));
|
||||
inline QString coreStringToAppString (const std::string &str) {
|
||||
return QString::fromLocal8Bit(str.c_str(), int(str.size()));
|
||||
}
|
||||
|
||||
inline std::string appStringToCoreString (const QString &string) {
|
||||
return string.toLocal8Bit().constData();
|
||||
inline std::string appStringToCoreString (const QString &str) {
|
||||
return qPrintable(str);
|
||||
}
|
||||
|
||||
// Reverse function of strstr.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import QtQuick.Layouts 1.3
|
|||
|
||||
import Common 1.0
|
||||
import Common.Styles 1.0
|
||||
import Linphone 1.0
|
||||
import Utils 1.0
|
||||
|
||||
import 'ComboBox.js' as Logic
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ function attachVirtualWindow (component, properties, exitStatusHandler) {
|
|||
properties: properties
|
||||
})
|
||||
|
||||
object.exitStatus.connect(detachVirtualWindow)
|
||||
if (exitStatusHandler) {
|
||||
object.exitStatus.connect(exitStatusHandler)
|
||||
}
|
||||
object.exitStatus.connect(detachVirtualWindow)
|
||||
|
||||
virtualWindow.setContent(object)
|
||||
|
||||
|
|
|
|||
|
|
@ -212,6 +212,8 @@ Row {
|
|||
color: ChatStyle.entry.message.file.status.bar.contentItem.color
|
||||
height: parent.height
|
||||
width: progressBar.visualPosition * parent.width
|
||||
|
||||
radius: ChatStyle.entry.message.file.status.bar.radius
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,16 @@ import Linphone.Styles 1.0
|
|||
// =============================================================================
|
||||
|
||||
Column {
|
||||
id: codecsViewer
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
property alias model: view.model
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
signal downloadRequested (var codecInfo)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Header.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -75,9 +83,9 @@ Column {
|
|||
|
||||
height: count * CodecsViewerStyle.attribute.height
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------
|
||||
// One codec.
|
||||
// -----------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
delegate: MouseArea {
|
||||
id: dragArea
|
||||
|
|
@ -110,6 +118,8 @@ Column {
|
|||
Rectangle {
|
||||
id: content
|
||||
|
||||
readonly property bool isDownloadable: Boolean($codec.downloadUrl)
|
||||
|
||||
Drag.active: dragArea.held
|
||||
Drag.source: dragArea
|
||||
Drag.hotSpot.x: width / 2
|
||||
|
|
@ -139,25 +149,26 @@ Column {
|
|||
|
||||
CodecAttribute {
|
||||
Layout.preferredWidth: CodecsViewerStyle.column.encoderDescriptionWidth
|
||||
text: $codec.encoderDescription
|
||||
text: $codec.encoderDescription || ''
|
||||
}
|
||||
|
||||
CodecAttribute {
|
||||
Layout.preferredWidth: CodecsViewerStyle.column.clockRateWidth
|
||||
text: $codec.clockRate
|
||||
text: $codec.clockRate || ''
|
||||
}
|
||||
|
||||
NumericField {
|
||||
Layout.preferredWidth: CodecsViewerStyle.column.bitrateWidth
|
||||
readOnly: !$codec.isVbr
|
||||
text: $codec.bitrate
|
||||
readOnly: content.isDownloadable || !$codec.isVbr
|
||||
text: $codec.bitrate || ''
|
||||
|
||||
onEditingFinished: view.model.setBitrate(index, text)
|
||||
}
|
||||
|
||||
TextField {
|
||||
Layout.preferredWidth: CodecsViewerStyle.column.recvFmtpWidth
|
||||
text: $codec.recvFmtp
|
||||
readOnly: content.isDownloadable
|
||||
text: $codec.recvFmtp || ''
|
||||
|
||||
onEditingFinished: view.model.setRecvFmtp(index, text)
|
||||
}
|
||||
|
|
@ -165,9 +176,11 @@ Column {
|
|||
Switch {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
145
ui/modules/Linphone/Dialog/OnlineInstallerDialog.qml
Normal file
145
ui/modules/Linphone/Dialog/OnlineInstallerDialog.qml
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
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
|
|||
singleton ContactStyle 1.0 Contact/ContactStyle.qml
|
||||
singleton MessagesCounterStyle 1.0 Contact/MessagesCounterStyle.qml
|
||||
|
||||
singleton OnlineInstallerDialogStyle 1.0 Dialog/OnlineInstallerDialogStyle.qml
|
||||
|
||||
singleton SipAddressesMenuStyle 1.0 Menus/SipAddressesMenuStyle.qml
|
||||
|
||||
singleton NotificationBasicStyle 1.0 Notifications/NotificationBasicStyle.qml
|
||||
|
|
|
|||
|
|
@ -4,8 +4,12 @@
|
|||
|
||||
.pragma library
|
||||
|
||||
.import Linphone 1.0 as Linphone
|
||||
|
||||
.import 'qrc:/ui/scripts/Utils/utils.js' as Utils
|
||||
|
||||
// =============================================================================
|
||||
// Contact/SIP address helpers.
|
||||
// =============================================================================
|
||||
|
||||
function _getDisplayNameFromQuotedString (str) {
|
||||
|
|
@ -84,3 +88,43 @@ function getContactUsername (contact) {
|
|||
name = _getUsername(object)
|
||||
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
|
|||
|
||||
import Common 1.0
|
||||
import Linphone 1.0
|
||||
import LinphoneUtils 1.0
|
||||
|
||||
import App.Styles 1.0
|
||||
|
||||
|
|
@ -50,8 +51,16 @@ AssistantAbstractView {
|
|||
onActivateStatusChanged: {
|
||||
requestBlock.stop(error)
|
||||
if (!error.length) {
|
||||
window.unlockView()
|
||||
window.setView('Home')
|
||||
function quitToHome (window) {
|
||||
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
|
|||
|
||||
import Common 1.0
|
||||
import Linphone 1.0
|
||||
import LinphoneUtils 1.0
|
||||
|
||||
import App.Styles 1.0
|
||||
|
||||
|
|
@ -62,8 +63,16 @@ AssistantAbstractView {
|
|||
onActivateStatusChanged: {
|
||||
requestBlock.stop(error)
|
||||
if (!error.length) {
|
||||
window.unlockView()
|
||||
window.setView('Home')
|
||||
function quitToHome (window) {
|
||||
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
|
|||
|
||||
import Common 1.0
|
||||
import Linphone 1.0
|
||||
import LinphoneUtils 1.0
|
||||
|
||||
import App.Styles 1.0
|
||||
|
||||
|
|
@ -17,6 +18,8 @@ AssistantAbstractView {
|
|||
|
||||
title: qsTr('useLinphoneSipAccountTitle')
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Column {
|
||||
|
|
@ -86,7 +89,14 @@ AssistantAbstractView {
|
|||
onLoginStatusChanged: {
|
||||
requestBlock.stop(error)
|
||||
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 @@
|
|||
|
||||
.import Linphone 1.0 as Linphone
|
||||
|
||||
.import 'qrc:/ui/scripts/LinphoneUtils/linphone-utils.js' as LinphoneUtils
|
||||
|
||||
// =============================================================================
|
||||
|
||||
function showVideoPreview (account) {
|
||||
|
|
@ -23,3 +25,7 @@ function updateVideoPreview () {
|
|||
function hideVideoPreview () {
|
||||
window.detachVirtualWindow()
|
||||
}
|
||||
|
||||
function handleCodecDownloadRequested (codecInfo) {
|
||||
LinphoneUtils.openCodecOnlineInstallerDialog(window, codecInfo)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,6 +142,8 @@ TabContainer {
|
|||
CodecsViewer {
|
||||
model: VideoCodecsModel
|
||||
width: parent.width
|
||||
|
||||
onDownloadRequested: Logic.handleCodecDownloadRequested(codecInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import App.Styles 1.0
|
|||
ApplicationWindow {
|
||||
id: window
|
||||
|
||||
|
||||
minimumHeight: SettingsWindowStyle.height
|
||||
minimumWidth: SettingsWindowStyle.width
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue