#include <QtDebug>
#include <QUrl>
#include <signal.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 "engine/sidechain/enginebroadcast.h"
#include "broadcast/defs_broadcast.h"
#include "control/controlpushbutton.h"
#include "encoder/encoder.h"
#include "encoder/encodermp3.h"
#include "encoder/encodervorbis.h"
#include "mixer/playerinfo.h"
#include "preferences/usersettings.h"
#include "track/track.h"
static const int kConnectRetries = 30;
static const int kMaxNetworkCache = 491520; // 10 s mp3 @ 192 kbit/s
// Shoutcast default receive buffer 1048576 and autodumpsourcetime 30 s
// http://wiki.shoutcast.com/wiki/SHOUTcast_DNAS_Server_2
static const int kMaxShoutFailures = 3;
EngineBroadcast::EngineBroadcast(UserSettingsPointer pConfig)
: m_pTextCodec(nullptr),
m_pMetaData(),
m_pShout(nullptr),
m_pShoutMetaData(nullptr),
m_iMetaDataLife(0),
m_iShoutStatus(0),
m_iShoutFailures(0),
m_settings(pConfig),
m_encoder(nullptr),
m_pMasterSamplerate(new ControlProxy("[Master]", "samplerate")),
m_custom_metadata(false),
m_firstCall(false),
m_format_is_mp3(false),
m_format_is_ov(false),
m_protocol_is_icecast1(false),
m_protocol_is_icecast2(false),
m_protocol_is_shoutcast(false),
m_ogg_dynamic_update(false),
m_threadWaiting(false),
m_pOutputFifo(nullptr),
m_retryCount(0),
m_reconnectFirstDelay(0.0),
m_reconnectPeriod(5.0),
m_noDelayFirstReconnect(true),
m_limitReconnects(true),
m_maximumRetries(10) {
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(slotEnableCO(double)));
m_pStatusCO = new ControlObject(ConfigKey(BROADCAST_PREF_KEY, "status"));
m_pStatusCO->connectValueChangeRequest(
this, SLOT(slotStatusCO(double)));
m_pStatusCO->setAndConfirm(STATUSCO_UNCONNECTED);
setState(NETWORKSTREAMWORKER_STATE_INIT);
// Initialize libshout
shout_init();
if (!(m_pShout = shout_new())) {
errorDialog(tr("Mixxx encountered a problem"),
tr("Could not allocate shout_t"));
}
if (!(m_pShoutMetaData = shout_metadata_new())) {
errorDialog(tr("Mixxx encountered a problem"),
tr("Could not allocate shout_metadata_t"));
}
setFunctionCode(14);
if (shout_set_nonblocking(m_pShout, 1) != SHOUTERR_SUCCESS) {
errorDialog(tr("Error setting non-blocking mode:"),
shout_get_error(m_pShout));
}
}
EngineBroadcast::~EngineBroadcast() {
m_pBroadcastEnabled->set(0);
m_readSema.release();
// Wait maximum ~4 seconds. User will get annoyed but
// if there is some network problems we let them settle
wait(4000);
// Signal user if thread doesn't die
DEBUG_ASSERT_AND_HANDLE(!isRunning()) {
qWarning() << "EngineBroadcast:~EngineBroadcast(): Thread didn't die.\
Ignored but file a bug report if problems rise!";
}
delete m_pStatusCO;
delete m_pMasterSamplerate;
if (m_pShoutMetaData) {
shout_metadata_free(m_pShoutMetaData);
}
if (m_pShout) {
shout_close(m_pShout);
shout_free(m_pShout);
}
shout_shutdown();
}
bool EngineBroadcast::isConnected() {
if (m_pShout) {
m_iShoutStatus = shout_get_connected(m_pShout);
if (m_iShoutStatus == SHOUTERR_CONNECTED)
return true;
}
return false;
}
QByteArray EngineBroadcast::encodeString(const QString& string) {
if (m_pTextCodec) {
return m_pTextCodec->fromUnicode(string);
}
return string.toLatin1();
}
void EngineBroadcast::updateFromPreferences() {
qDebug() << "EngineBroadcast: updating from preferences";
NetworkStreamWorker::debugState();
double dStatus = m_pStatusCO->get();
if (dStatus == STATUSCO_CONNECTED ||
dStatus == STATUSCO_CONNECTING) {
qDebug() << "EngineBroadcast::updateFromPreferences status:"
<< dStatus
<< ". Can't edit preferences when playing";
return;
}
setState(NETWORKSTREAMWORKER_STATE_BUSY);
if (m_encoder) {
qDebug() << "delete m_encoder";
// delete m_encoder if it has been initialized (with maybe) different
// bitrate
delete m_encoder;
m_encoder = nullptr;
}
m_format_is_mp3 = false;
m_format_is_ov = false;
m_protocol_is_icecast1 = false;
m_protocol_is_icecast2 = false;
m_protocol_is_shoutcast = false;
m_ogg_dynamic_update = false;
// Convert a bunch of QStrings to QByteArrays so we can get regular C char*
// strings to pass to libshout.