1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

refactor: Remove CameraSource singleton

Grossly expose it publicly from Nexus for the time being since Profile is
sometimes constructed from main, while Nexus is also a singleton. So
CameraSource can't be constructed in main and be passed in to Nexus on
construction. This should be temporary, until Nexus's singleton is
removed as well.
This commit is contained in:
Anthony Bilinski 2022-03-11 07:50:42 -08:00
parent 9bf17acca6
commit 61b3cb528a
No known key found for this signature in database
GPG Key ID: 2AA8E0DA1B31FB3C
19 changed files with 86 additions and 70 deletions

View File

@ -71,7 +71,7 @@
*/
CoreAV::CoreAV(std::unique_ptr<ToxAV, ToxAVDeleter> toxav_, CompatibleRecursiveMutex& toxCoreLock,
IAudioSettings& audioSettings_, IGroupSettings& groupSettings_)
IAudioSettings& audioSettings_, IGroupSettings& groupSettings_, CameraSource& cameraSource_)
: audio{nullptr}
, toxav{std::move(toxav_)}
, coreavThread{new QThread{this}}
@ -79,6 +79,7 @@ CoreAV::CoreAV(std::unique_ptr<ToxAV, ToxAVDeleter> toxav_, CompatibleRecursiveM
, coreLock{toxCoreLock}
, audioSettings{audioSettings_}
, groupSettings{groupSettings_}
, cameraSource{cameraSource_}
{
assert(coreavThread);
assert(iterateTimer);
@ -111,7 +112,8 @@ void CoreAV::connectCallbacks()
* @return CoreAV instance on success, {} on failure
*/
CoreAV::CoreAVPtr CoreAV::makeCoreAV(Tox* core, CompatibleRecursiveMutex& toxCoreLock,
IAudioSettings& audioSettings, IGroupSettings& groupSettings)
IAudioSettings& audioSettings, IGroupSettings& groupSettings,
CameraSource& cameraSource)
{
Toxav_Err_New err;
std::unique_ptr<ToxAV, ToxAVDeleter> toxav{toxav_new(core, &err)};
@ -131,7 +133,8 @@ CoreAV::CoreAVPtr CoreAV::makeCoreAV(Tox* core, CompatibleRecursiveMutex& toxCor
assert(toxav != nullptr);
return CoreAVPtr{new CoreAV{std::move(toxav), toxCoreLock, audioSettings, groupSettings}};
return CoreAVPtr{new CoreAV{std::move(toxav), toxCoreLock, audioSettings,
groupSettings, cameraSource}};
}
/**
@ -288,7 +291,8 @@ bool CoreAV::startCall(uint32_t friendNum, bool video)
// Audio backend must be set before making a call
assert(audio != nullptr);
ToxFriendCallPtr call = ToxFriendCallPtr(new ToxFriendCall(friendNum, video, *this, *audio));
ToxFriendCallPtr call = ToxFriendCallPtr(new ToxFriendCall(friendNum, video,
*this, *audio, cameraSource));
// Call object must be owned by this thread or there will be locking problems with Audio
call->moveToThread(thread());
assert(call != nullptr);
@ -722,7 +726,8 @@ void CoreAV::callCallback(ToxAV* toxav, uint32_t friendNum, bool audio, bool vid
// Audio backend must be set before receiving a call
assert(self->audio != nullptr);
ToxFriendCallPtr call = ToxFriendCallPtr(new ToxFriendCall{friendNum, video, *self, *self->audio});
ToxFriendCallPtr call = ToxFriendCallPtr(new ToxFriendCall{friendNum, video,
*self, *self->audio, self->cameraSource});
// Call object must be owned by CoreAV thread or there will be locking problems with Audio
call->moveToThread(self->thread());
assert(call != nullptr);

View File

@ -51,7 +51,8 @@ class CoreAV : public QObject
public:
using CoreAVPtr = std::unique_ptr<CoreAV>;
static CoreAVPtr makeCoreAV(Tox* core, CompatibleRecursiveMutex& toxCoreLock,
IAudioSettings& audioSettings, IGroupSettings& groupSettings);
IAudioSettings& audioSettings, IGroupSettings& groupSettings,
CameraSource&);
void setAudio(IAudioControl& newAudio);
IAudioControl* getAudio();
@ -118,7 +119,7 @@ private:
};
CoreAV(std::unique_ptr<ToxAV, ToxAVDeleter> tox_, CompatibleRecursiveMutex &toxCoreLock,
IAudioSettings& audioSettings_, IGroupSettings& groupSettings_);
IAudioSettings& audioSettings_, IGroupSettings& groupSettings_, CameraSource&);
void connectCallbacks();
void process();
@ -165,4 +166,5 @@ private:
IAudioSettings& audioSettings;
IGroupSettings& groupSettings;
CameraSource& cameraSource;
};

View File

@ -59,7 +59,6 @@ ToxCall::~ToxCall()
{
if (videoEnabled) {
QObject::disconnect(videoInConn);
CameraSource::getInstance().unsubscribe();
}
}
@ -118,10 +117,12 @@ CoreVideoSource* ToxCall::getVideoSource() const
return videoSource;
}
ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av_, IAudioControl& audio_)
ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av_,
IAudioControl& audio_, CameraSource& cameraSource_)
: ToxCall(VideoEnabled, av_, audio_)
, sink(audio_.makeSink())
, friendId{FriendNum}
, cameraSource{cameraSource_}
{
connect(audioSource.get(), &IAudioSource::frameAvailable, this,
[this](const int16_t* pcm, size_t samples, uint8_t chans, uint32_t rate) {
@ -137,13 +138,12 @@ ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av_,
// register video
if (videoEnabled) {
videoSource = new CoreVideoSource();
CameraSource& source = CameraSource::getInstance();
if (source.isNone()) {
source.setupDefault();
if (cameraSource.isNone()) {
cameraSource.setupDefault();
}
source.subscribe();
videoInConn = QObject::connect(&source, &VideoSource::frameAvailable,
cameraSource.subscribe();
videoInConn = QObject::connect(&cameraSource, &VideoSource::frameAvailable,
[&av_, FriendNum](std::shared_ptr<VideoFrame> frame) {
av_.sendCallVideo(FriendNum, frame);
});
@ -155,6 +155,9 @@ ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av_,
ToxFriendCall::~ToxFriendCall()
{
if (videoEnabled) {
cameraSource.unsubscribe();
}
QObject::disconnect(audioSinkInvalid);
}

View File

@ -37,6 +37,7 @@ class AudioFilterer;
class CoreVideoSource;
class CoreAV;
class Group;
class CameraSource;
class ToxCall : public QObject
{
@ -91,7 +92,7 @@ class ToxFriendCall : public ToxCall
Q_OBJECT
public:
ToxFriendCall() = delete;
ToxFriendCall(uint32_t friendId, bool VideoEnabled, CoreAV& av_, IAudioControl& audio_);
ToxFriendCall(uint32_t friendId, bool VideoEnabled, CoreAV& av_, IAudioControl& audio_, CameraSource&);
ToxFriendCall(ToxFriendCall&& other) = delete;
ToxFriendCall& operator=(ToxFriendCall&& other) = delete;
~ToxFriendCall();
@ -110,6 +111,7 @@ private:
TOXAV_FRIEND_CALL_STATE state{TOXAV_FRIEND_CALL_STATE_NONE};
std::unique_ptr<IAudioSink> sink;
uint32_t friendId;
CameraSource& cameraSource;
};
class ToxGroupCall : public ToxCall

View File

@ -67,7 +67,6 @@ void cleanup()
s.sync();
Nexus::destroyInstance();
CameraSource::destroyInstance();
Settings::destroyInstance();
qDebug() << "Cleanup success";
@ -400,13 +399,13 @@ int main(int argc, char* argv[])
// note: Because Settings is shouldering global settings as well as model specific ones it
// cannot be integrated into a central model object yet
nexus.setSettings(&settings);
auto& cameraSource = Nexus::getCameraSource();
// Autologin
// TODO (kriby): Shift responsibility of linking views to model objects from nexus
// Further: generate view instances separately (loginScreen, mainGUI, audio)
Profile* profile = nullptr;
if (autoLogin && Profile::exists(profileName) && !Profile::isEncrypted(profileName)) {
profile = Profile::loadProfile(profileName, QString(), settings, &parser);
profile = Profile::loadProfile(profileName, QString(), settings, &parser, cameraSource);
if (!profile) {
QMessageBox::information(nullptr, QObject::tr("Error"),
QObject::tr("Failed to load profile automatically."));

View File

@ -63,7 +63,10 @@ Nexus::Nexus(QObject* parent)
: QObject(parent)
, profile{nullptr}
, widget{nullptr}
{}
, cameraSource(new CameraSource())
{
assert(cameraSource);
}
Nexus::~Nexus()
{
@ -228,7 +231,7 @@ void Nexus::showMainGUI()
assert(profile);
// Create GUI
widget = new Widget(*profile, *audioControl);
widget = new Widget(*profile, *audioControl, *cameraSource);
// Start GUI
widget->init();
@ -301,7 +304,7 @@ Profile* Nexus::getProfile()
*/
void Nexus::onCreateNewProfile(const QString& name, const QString& pass)
{
setProfile(Profile::createProfile(name, pass, *settings, parser));
setProfile(Profile::createProfile(name, pass, *settings, parser, *cameraSource));
parser = nullptr; // only apply cmdline proxy settings once
}
@ -310,7 +313,7 @@ void Nexus::onCreateNewProfile(const QString& name, const QString& pass)
*/
void Nexus::onLoadProfile(const QString& name, const QString& pass)
{
setProfile(Profile::loadProfile(name, pass, *settings, parser));
setProfile(Profile::loadProfile(name, pass, *settings, parser, *cameraSource));
parser = nullptr; // only apply cmdline proxy settings once
}
/**
@ -344,6 +347,11 @@ Widget* Nexus::getDesktopGUI()
return getInstance().widget;
}
CameraSource& Nexus::getCameraSource()
{
return *getInstance().cameraSource;
}
#ifdef Q_OS_MAC
void Nexus::retranslateUi()
{

View File

@ -20,9 +20,11 @@
#pragma once
#include "audio/iaudiocontrol.h"
#include <QObject>
#include "audio/iaudiocontrol.h"
#include <memory>
class Widget;
class Profile;
@ -30,6 +32,7 @@ class Settings;
class LoginScreen;
class Core;
class QCommandLineParser;
class CameraSource;
#ifdef Q_OS_MAC
class QMenuBar;
@ -53,6 +56,7 @@ public:
static Core* getCore();
static Profile* getProfile();
static Widget* getDesktopGUI();
static CameraSource& getCameraSource();
#ifdef Q_OS_MAC
@ -104,4 +108,5 @@ private:
Widget* widget;
std::unique_ptr<IAudioControl> audioControl;
QCommandLineParser* parser = nullptr;
std::unique_ptr<CameraSource> cameraSource;
};

View File

@ -232,7 +232,7 @@ bool logCreateToxDataError(const CreateToxDataError& error, const QString& userN
QStringList Profile::profiles;
void Profile::initCore(const QByteArray& toxsave, Settings& s, bool isNewProfile)
void Profile::initCore(const QByteArray& toxsave, Settings& s, bool isNewProfile, CameraSource& cameraSource)
{
if (toxsave.isEmpty() && !isNewProfile) {
qCritical() << "Existing toxsave is empty";
@ -265,7 +265,7 @@ void Profile::initCore(const QByteArray& toxsave, Settings& s, bool isNewProfile
return;
}
coreAv = CoreAV::makeCoreAV(core->getTox(), core->getCoreLoopLock(), s, s);
coreAv = CoreAV::makeCoreAV(core->getTox(), core->getCoreLoopLock(), s, s, cameraSource);
if (!coreAv) {
qDebug() << "Failed to start ToxAV";
emit failedToStart();
@ -311,7 +311,7 @@ Profile::Profile(const QString& name_, std::unique_ptr<ToxEncrypt> passkey_, Pat
* @note If the profile is already in use return nullptr.
*/
Profile* Profile::loadProfile(const QString& name, const QString& password, Settings& settings,
const QCommandLineParser* parser)
const QCommandLineParser* parser, CameraSource& cameraSource)
{
if (ProfileLocker::hasLock()) {
qCritical() << "Tried to load profile " << name << ", but another profile is already locked!";
@ -339,7 +339,7 @@ Profile* Profile::loadProfile(const QString& name, const QString& password, Sett
constexpr bool isNewProfile = false;
settings.updateProfileData(p, parser, isNewProfile);
p->initCore(toxsave, settings, isNewProfile);
p->initCore(toxsave, settings, isNewProfile, cameraSource);
p->loadDatabase(password);
return p;
@ -354,7 +354,7 @@ Profile* Profile::loadProfile(const QString& name, const QString& password, Sett
* @note If the profile is already in use return nullptr.
*/
Profile* Profile::createProfile(const QString& name, const QString& password, Settings& settings,
const QCommandLineParser* parser)
const QCommandLineParser* parser, CameraSource& cameraSource)
{
CreateToxDataError error;
Paths& paths = settings.getPaths();
@ -371,7 +371,7 @@ Profile* Profile::createProfile(const QString& name, const QString& password, Se
constexpr bool isNewProfile = true;
settings.updateProfileData(p, parser, isNewProfile);
p->initCore(QByteArray(), settings, isNewProfile);
p->initCore(QByteArray(), settings, isNewProfile, cameraSource);
p->loadDatabase(password);
return p;
}

View File

@ -38,6 +38,7 @@
class Settings;
class QCommandLineParser;
class ToxPk;
class CameraSource;
class Profile : public QObject
{
@ -45,9 +46,9 @@ class Profile : public QObject
public:
static Profile* loadProfile(const QString& name, const QString& password, Settings& settings,
const QCommandLineParser* parser);
const QCommandLineParser* parser, CameraSource&);
static Profile* createProfile(const QString& name, const QString& password, Settings& settings,
const QCommandLineParser* parser);
const QCommandLineParser* parser, CameraSource&);
~Profile();
Core& getCore() const;
@ -108,7 +109,7 @@ private:
static QStringList getFilesByExt(QString extension);
QString avatarPath(const ToxPk& owner, bool forceUnencrypted = false);
bool saveToxSave(QByteArray data);
void initCore(const QByteArray& toxsave, Settings &s, bool isNewProfile);
void initCore(const QByteArray& toxsave, Settings &s, bool isNewProfile, CameraSource&);
private:
std::unique_ptr<AvatarBroadcaster> avatarBroadcaster;

View File

@ -90,8 +90,6 @@ extern "C" {
* @brief Remember how many times we subscribed for RAII
*/
CameraSource* CameraSource::instance{nullptr};
CameraSource::CameraSource()
: deviceThread{new QThread}
, deviceName{"none"}
@ -122,22 +120,6 @@ CameraSource::CameraSource()
// clang-format on
/**
* @brief Returns the singleton instance.
*/
CameraSource& CameraSource::getInstance()
{
if (!instance)
instance = new CameraSource();
return *instance;
}
void CameraSource::destroyInstance()
{
delete instance;
instance = nullptr;
}
/**
* @brief Setup default device
* @note If a device is already open, the source will seamlessly switch to the new device.

View File

@ -36,8 +36,8 @@ class CameraSource : public VideoSource
Q_OBJECT
public:
static CameraSource& getInstance();
static void destroyInstance();
CameraSource();
~CameraSource();
void setupDefault();
bool isNone() const;
@ -53,8 +53,6 @@ signals:
void openFailed();
private:
CameraSource();
~CameraSource();
void stream();
private slots:
@ -78,6 +76,4 @@ private:
std::atomic_bool isNone_;
std::atomic_int subscriptions;
static CameraSource* instance;
};

View File

@ -47,11 +47,12 @@ const int BTN_PANEL_WIDTH = 250;
const auto BTN_STYLE_SHEET_PATH = QStringLiteral("chatForm/fullScreenButtons.css");
}
NetCamView::NetCamView(ToxPk friendPk_, QWidget* parent)
NetCamView::NetCamView(ToxPk friendPk_, CameraSource& cameraSource_, QWidget* parent)
: QWidget(parent)
, selfFrame{nullptr}
, friendPk{friendPk_}
, e(false)
, cameraSource{cameraSource_}
{
verLayout = new QVBoxLayout(this);
setWindowTitle(tr("Tox video"));
@ -167,7 +168,7 @@ NetCamView::~NetCamView()
void NetCamView::show(VideoSource* source, const QString& title)
{
setSource(source);
selfVideoSurface->setSource(&CameraSource::getInstance());
selfVideoSurface->setSource(&cameraSource);
setTitle(title);
QWidget::show();

View File

@ -34,13 +34,14 @@ class QPushButton;
class QKeyEvent;
class QCloseEvent;
class QShowEvent;
class CameraSource;
class NetCamView : public QWidget
{
Q_OBJECT
public:
NetCamView(ToxPk friendPk_, QWidget* parent = nullptr);
NetCamView(ToxPk friendPk_, CameraSource&, QWidget* parent = nullptr);
~NetCamView();
virtual void show(VideoSource* source, const QString& title);
@ -95,4 +96,5 @@ private:
QPushButton* microphoneButton = nullptr;
QPushButton* endVideoButton = nullptr;
QPushButton* exitFullScreenButton = nullptr;
CameraSource& cameraSource;
};

View File

@ -107,13 +107,15 @@ QString secondsToDHMS(quint32 duration)
} // namespace
ChatForm::ChatForm(Profile& profile, Friend* chatFriend, IChatLog& chatLog_,
IMessageDispatcher& messageDispatcher_, DocumentCache& documentCache_, SmileyPack& smileyPack_)
IMessageDispatcher& messageDispatcher_, DocumentCache& documentCache_,
SmileyPack& smileyPack_, CameraSource& cameraSource_)
: GenericChatForm(profile.getCore(), chatFriend, chatLog_, messageDispatcher_,
documentCache_, smileyPack_)
, core{profile.getCore()}
, f(chatFriend)
, isTyping{false}
, lastCallIsVideo{false}
, cameraSource{cameraSource_}
{
setName(f->getDisplayedName());
@ -509,7 +511,7 @@ std::unique_ptr<NetCamView> ChatForm::createNetcam()
{
qDebug() << "creating netcam";
uint32_t friendId = f->getId();
std::unique_ptr<NetCamView> view = std::unique_ptr<NetCamView>(new NetCamView(f->getPublicKey(), this));
std::unique_ptr<NetCamView> view = std::unique_ptr<NetCamView>(new NetCamView(f->getPublicKey(), cameraSource, this));
CoreAV* av = core.getAv();
VideoSource* source = av->getVideoSourceFromCall(friendId);
view->show(source, f->getDisplayedName());

View File

@ -50,7 +50,8 @@ class ChatForm : public GenericChatForm
Q_OBJECT
public:
ChatForm(Profile& profile, Friend* chatFriend, IChatLog& chatLog_,
IMessageDispatcher& messageDispatcher_, DocumentCache&, SmileyPack&);
IMessageDispatcher& messageDispatcher_, DocumentCache&, SmileyPack&,
CameraSource&);
~ChatForm() override;
void setStatusMessage(const QString& newMessage);
@ -141,4 +142,5 @@ private:
bool isTyping;
bool lastCallIsVideo;
std::unique_ptr<NetCamView> netcam;
CameraSource& cameraSource;
};

View File

@ -42,13 +42,12 @@
#include <memory>
SettingsWidget::SettingsWidget(UpdateCheck* updateCheck, IAudioControl& audio,
Core* core, SmileyPack& smileyPack, Widget* parent)
Core* core, SmileyPack& smileyPack, CameraSource& cameraSource, Widget* parent)
: QWidget(parent, Qt::Window)
{
CoreAV* coreAV = core->getAv();
IAudioSettings* audioSettings = &Settings::getInstance();
IVideoSettings* videoSettings = &Settings::getInstance();
CameraSource& camera = CameraSource::getInstance();
setAttribute(Qt::WA_DeleteOnClose);
@ -65,7 +64,7 @@ SettingsWidget::SettingsWidget(UpdateCheck* updateCheck, IAudioControl& audio,
std::unique_ptr<PrivacyForm> pfrm(new PrivacyForm(core));
connect(pfrm.get(), &PrivacyForm::clearAllReceipts, parent, &Widget::clearAllReceipts);
AVForm* rawAvfrm = new AVForm(audio, coreAV, camera, audioSettings, videoSettings);
AVForm* rawAvfrm = new AVForm(audio, coreAV, cameraSource, audioSettings, videoSettings);
std::unique_ptr<AVForm> avfrm(rawAvfrm);
std::unique_ptr<AdvancedForm> expfrm(new AdvancedForm());
std::unique_ptr<AboutForm> abtfrm(new AboutForm(updateCheck));

View File

@ -39,12 +39,13 @@ class ContentLayout;
class UpdateCheck;
class Widget;
class SmileyPack;
class CameraSource;
class SettingsWidget : public QWidget
{
Q_OBJECT
public:
SettingsWidget(UpdateCheck* updateCheck, IAudioControl& audio, Core *core, SmileyPack&, Widget* parent = nullptr);
SettingsWidget(UpdateCheck* updateCheck, IAudioControl& audio, Core *core, SmileyPack&, CameraSource&, Widget* parent = nullptr);
~SettingsWidget();
bool isShown() const;

View File

@ -139,7 +139,8 @@ void Widget::acceptFileTransfer(const ToxFile& file, const QString& path)
Widget* Widget::instance{nullptr};
Widget::Widget(Profile &profile_, IAudioControl& audio_, QWidget* parent)
Widget::Widget(Profile &profile_, IAudioControl& audio_, CameraSource& cameraSource_,
QWidget* parent)
: QMainWindow(parent)
, profile{profile_}
, trayMenu{nullptr}
@ -151,6 +152,7 @@ Widget::Widget(Profile &profile_, IAudioControl& audio_, QWidget* parent)
, settings(Settings::getInstance())
, smileyPack(new SmileyPack())
, documentCache(new DocumentCache(*smileyPack))
, cameraSource{cameraSource_}
{
installEventFilter(this);
QString locale = settings.getTranslation();
@ -292,7 +294,8 @@ void Widget::init()
updateCheck = std::unique_ptr<UpdateCheck>(new UpdateCheck(settings));
connect(updateCheck.get(), &UpdateCheck::updateAvailable, this, &Widget::onUpdateAvailable);
#endif
settingsWidget = new SettingsWidget(updateCheck.get(), audio, core, *smileyPack, this);
settingsWidget = new SettingsWidget(updateCheck.get(), audio, core, *smileyPack,
cameraSource, this);
#if UPDATE_CHECK_ENABLED
updateCheck->checkForUpdate();
#endif
@ -1190,7 +1193,7 @@ void Widget::addFriend(uint32_t friendId, const ToxPk& friendPk)
std::make_shared<ChatHistory>(*newfriend, history, *core, settings,
*friendMessageDispatcher);
auto friendForm = new ChatForm(profile, newfriend, *chatHistory,
*friendMessageDispatcher, *documentCache, *smileyPack);
*friendMessageDispatcher, *documentCache, *smileyPack, cameraSource);
connect(friendForm, &ChatForm::updateFriendActivity, this, &Widget::updateFriendActivity);
friendMessageDispatchers[friendPk] = friendMessageDispatcher;

View File

@ -84,6 +84,7 @@ class Settings;
class IChatLog;
class ChatHistory;
class SmileyPack;
class CameraSource;
class Widget final : public QMainWindow
{
Q_OBJECT
@ -117,7 +118,8 @@ private:
};
public:
explicit Widget(Profile& profile_, IAudioControl& audio_, QWidget* parent = nullptr);
Widget(Profile& profile_, IAudioControl& audio_, CameraSource&,
QWidget* parent = nullptr);
~Widget() override;
void init();
void setCentralWidget(QWidget* widget, const QString& widgetName);
@ -383,6 +385,7 @@ private:
#endif
std::unique_ptr<SmileyPack> smileyPack;
std::unique_ptr<DocumentCache> documentCache;
CameraSource& cameraSource;
};
bool toxActivateEventHandler(const QByteArray& data);