summaryrefslogtreecommitdiffstats
path: root/src/broadcast
diff options
context:
space:
mode:
authorStéphane L <Palakis@users.noreply.github.com>2017-08-08 15:13:16 +0200
committerGitHub <noreply@github.com>2017-08-08 15:13:16 +0200
commit193934749a0a67ca709f7444828adecd06d243d0 (patch)
treed5df31eb3eadd9534b069636ce9b5cef72074f25 /src/broadcast
parent6763376334d2ba5ef692ef321f283280b87df035 (diff)
(WIP) Audio streaming engine refactor
This is a WIP to simplify and adapt the live broadcasting parts of the audio engine. It started from this design: Frames from audio engine => FIFO in SoundDeviceNetwork => FIFO in EngineNetworkStream => FIFO in EngineBroadcast => FIFOs in instances of ShoutConnection Buffer sync is done in SoundDeviceNetwork and EngineNetworkStream, but these were designed for a single connection. In the current implementation (multi-broadcasting branch), the buffer sync code has no real effect on ShoutConnection instances. New design: Frames from audio engine => FIFO in SoundDeviceNetwork => FIFOs in instances of ShoutConnection SoundDeviceNetwork is now aware of the several instances of ShoutConnection (as NetworkStreamWorkers), and NetworkStreamWorker now has attributes to allow buffer sync and management by SoundDeviceNetwork. EngineNetworkStream still exists to keep the workers list and its input part (needs rework), and EngineBroadcast has been completely deleted. Its connection list sync responsibilities with BroadcastSettings are now honored by BroadcastManager
Diffstat (limited to 'src/broadcast')
-rw-r--r--src/broadcast/broadcastmanager.cpp141
-rw-r--r--src/broadcast/broadcastmanager.h25
-rw-r--r--src/broadcast/defs_broadcast.h9
3 files changed, 158 insertions, 17 deletions
diff --git a/src/broadcast/broadcastmanager.cpp b/src/broadcast/broadcastmanager.cpp
index e5322c3661..444ea2d6da 100644
--- a/src/broadcast/broadcastmanager.cpp
+++ b/src/broadcast/broadcastmanager.cpp
@@ -1,4 +1,11 @@
-#include "broadcast/broadcastmanager.h"
+// shout.h checks for WIN32 to see if we are on Windows.
+#ifdef WIN64
+#define WIN32
+#endif
+#include <shout/shout.h>
+#ifdef WIN64
+#undef WIN32
+#endif
#include "broadcast/defs_broadcast.h"
#include "engine/enginemaster.h"
@@ -6,28 +13,52 @@
#include "engine/sidechain/enginesidechain.h"
#include "soundio/soundmanager.h"
+#include "broadcast/broadcastmanager.h"
+
BroadcastManager::BroadcastManager(SettingsManager* pSettingsManager,
SoundManager* pSoundManager)
- : m_pConfig(pSettingsManager->settings()) {
- QSharedPointer<EngineNetworkStream> pNetworkStream =
- pSoundManager->getNetworkStream();
-
- if (pNetworkStream) {
- m_pBroadcast = QSharedPointer<EngineBroadcast>(
- new EngineBroadcast(m_pConfig,
- pSettingsManager->broadcastSettings(),
- pNetworkStream->getNumOutputChannels()));
- pNetworkStream->addWorker(m_pBroadcast);
+ : m_pConfig(pSettingsManager->settings()),
+ m_pBroadcastSettings(pSettingsManager->broadcastSettings()),
+ m_pNetworkStream(pSoundManager->getNetworkStream()) {
+ const bool persist = true;
+ m_pBroadcastEnabled = new ControlPushButton(
+ ConfigKey(BROADCAST_PREF_KEY,"enabled"), persist);
+ m_pBroadcastEnabled->setButtonMode(ControlPushButton::TOGGLE);
+ connect(m_pBroadcastEnabled, SIGNAL(valueChanged(double)),
+ this, SLOT(slotControlEnabled(double)));
+
+ m_pStatusCO = new ControlObject(ConfigKey(BROADCAST_PREF_KEY, "status"));
+ m_pStatusCO->setReadOnly();
+ m_pStatusCO->forceSet(STATUSCO_UNCONNECTED);
+
+ // Initialize libshout
+ shout_init();
+
+ // Initialize connections list from the current state of BroadcastSettings
+ QList<BroadcastProfilePtr> profiles = m_pBroadcastSettings->profiles();
+ for(BroadcastProfilePtr profile : profiles) {
+ addConnection(profile);
}
- m_pBroadcastEnabled = new ControlProxy(
- BROADCAST_PREF_KEY, "enabled", this);
- m_pBroadcastEnabled->connectValueChanged(SLOT(slotControlEnabled(double)));
+ // Connect add/remove profiles signals.
+ // Passing the raw pointer from QSharedPointer to connect() is fine, since
+ // connect is trusted that it won't delete the pointer
+ connect(m_pBroadcastSettings.data(), SIGNAL(profileAdded(BroadcastProfilePtr)),
+ this, SLOT(slotProfileAdded(BroadcastProfilePtr)));
+ connect(m_pBroadcastSettings.data(), SIGNAL(profileRemoved(BroadcastProfilePtr)),
+ this, SLOT(slotProfileRemoved(BroadcastProfilePtr)));
+ connect(m_pBroadcastSettings.data(), SIGNAL(profilesChanged()),
+ this, SLOT(slotProfilesChanged()));
}
BroadcastManager::~BroadcastManager() {
// Disable broadcast so when Mixxx starts again it will not connect.
m_pBroadcastEnabled->set(0);
+
+ delete m_pStatusCO;
+ delete m_pBroadcastEnabled;
+
+ shout_shutdown();
}
void BroadcastManager::setEnabled(bool value) {
@@ -39,5 +70,87 @@ bool BroadcastManager::isEnabled() {
}
void BroadcastManager::slotControlEnabled(double v) {
+ if (v > 1.0) {
+ // Wrap around manually .
+ // Wrapping around in WPushbutton does not work
+ // since the status button has 4 states, but this CO is bool
+ m_pBroadcastEnabled->set(0.0);
+ v = 0.0;
+ }
+
+ if (v > 0.0) {
+ slotProfilesChanged();
+ m_pStatusCO->forceSet(STATUSCO_CONNECTED);
+ } else {
+ m_pStatusCO->forceSet(STATUSCO_UNCONNECTED);
+ }
+
emit(broadcastEnabled(v > 0.0));
}
+
+void BroadcastManager::slotProfileAdded(BroadcastProfilePtr profile) {
+ addConnection(profile);
+}
+
+void BroadcastManager::slotProfileRemoved(BroadcastProfilePtr profile) {
+ removeConnection(profile);
+}
+
+void BroadcastManager::slotProfilesChanged() {
+ if(m_pBroadcastEnabled->toBool()) {
+ QVector<NetworkStreamWorkerPtr> workers = m_pNetworkStream->workers();
+ for(NetworkStreamWorkerPtr pWorker : workers) {
+ ShoutConnectionPtr connection = qSharedPointerCast<ShoutConnection>(pWorker);
+ if(connection) {
+ connection->applySettings();
+ }
+ }
+ }
+}
+
+bool BroadcastManager::addConnection(BroadcastProfilePtr profile) {
+ if(!profile)
+ return false;
+
+ if(findConnectionForProfile(profile).isNull() == false) {
+ return false;
+ }
+
+ ShoutConnectionPtr connection(new ShoutConnection(profile, m_pConfig));
+ m_pNetworkStream->addWorker(connection);
+
+ qDebug() << "BroadcastManager::addConnection: created connection for profile" << profile->getProfileName();
+ return true;
+}
+
+bool BroadcastManager::removeConnection(BroadcastProfilePtr profile) {
+ if(!profile)
+ return false;
+
+ ShoutConnectionPtr connection = findConnectionForProfile(profile);
+ if(connection) {
+ // Disabling the profile tells ShoutOutput's thread to disconnect
+ connection->profile()->setEnabled(false);
+ m_pNetworkStream->removeWorker(connection);
+
+ qDebug() << "BroadcastManager::removeConnection: removed connection for profile" << profile->getProfileName();
+ return true;
+ }
+
+ return false;
+}
+
+ShoutConnectionPtr BroadcastManager::findConnectionForProfile(BroadcastProfilePtr profile) {
+ QVector<NetworkStreamWorkerPtr> workers = m_pNetworkStream->workers();
+ for(NetworkStreamWorkerPtr pWorker : workers) {
+ ShoutConnectionPtr connection = qSharedPointerCast<ShoutConnection>(pWorker);
+ if(connection.isNull())
+ continue;
+
+ if(connection->profile() == profile) {
+ return connection;
+ }
+ }
+
+ return ShoutConnectionPtr();
+}
diff --git a/src/broadcast/broadcastmanager.h b/src/broadcast/broadcastmanager.h
index 6d19ac61f9..7d6d555d70 100644
--- a/src/broadcast/broadcastmanager.h
+++ b/src/broadcast/broadcastmanager.h
@@ -3,15 +3,24 @@
#include <QObject>
-#include "engine/sidechain/enginebroadcast.h"
#include "preferences/settingsmanager.h"
#include "preferences/usersettings.h"
+#include "engine/sidechain/enginenetworkstream.h"
+#include "engine/sidechain/shoutconnection.h"
class SoundManager;
+class ControlPushButton;
class BroadcastManager : public QObject {
Q_OBJECT
public:
+ enum StatusCOStates {
+ STATUSCO_UNCONNECTED = 0, // IDLE state, no error
+ STATUSCO_CONNECTING = 1, // 30 s max
+ STATUSCO_CONNECTED = 2, // On Air
+ STATUSCO_FAILURE = 3 // Happens when disconnected by an error
+ };
+
BroadcastManager(SettingsManager* pSettingsManager,
SoundManager* pSoundManager);
virtual ~BroadcastManager();
@@ -29,11 +38,21 @@ class BroadcastManager : public QObject {
private slots:
void slotControlEnabled(double v);
+ void slotProfileAdded(BroadcastProfilePtr profile);
+ void slotProfileRemoved(BroadcastProfilePtr profile);
+ void slotProfilesChanged();
private:
UserSettingsPointer m_pConfig;
- QSharedPointer<EngineBroadcast> m_pBroadcast;
- ControlProxy* m_pBroadcastEnabled;
+ BroadcastSettingsPointer m_pBroadcastSettings;
+ QSharedPointer<EngineNetworkStream> m_pNetworkStream;
+
+ ControlPushButton* m_pBroadcastEnabled;
+ ControlObject* m_pStatusCO;
+
+ bool addConnection(BroadcastProfilePtr profile);
+ bool removeConnection(BroadcastProfilePtr profile);
+ ShoutConnectionPtr findConnectionForProfile(BroadcastProfilePtr profile);
};
#endif /* BROADCAST_BROADCASTMANAGER_H */
diff --git a/src/broadcast/defs_broadcast.h b/src/broadcast/defs_broadcast.h
index ce9414ba01..dad19eaea8 100644
--- a/src/broadcast/defs_broadcast.h
+++ b/src/broadcast/defs_broadcast.h
@@ -26,4 +26,13 @@
#define BROADCAST_FORMAT_MP3 "MP3"
#define BROADCAST_FORMAT_OV "OggVorbis"
+// EngineNetworkStream can't use locking mechanisms to protect its
+// internal worker list against concurrency issues, as it is used by
+// methods called from the audio engine thread.
+// Instead, the internal list has a fixed number of QSharedPointers
+// (which are thread-safe) initialized to null pointers. R/W operations to
+// the workers are then performed on thread-safe QSharedPointers and not
+// onto the thread-unsafe QVector
+#define BROADCAST_MAX_CONNECTIONS 16
+
#endif /* DEFS_BROADCAST_H */