#include <QUrl>
// These includes are only required by ignoreSigpipe, which is unix-only
#ifndef __WINDOWS__
#include <signal.h>
#include <unistd.h>
#endif
// 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 "control/controlpushbutton.h"
#include "encoder/encoder.h"
#include "encoder/encoderbroadcastsettings.h"
#ifdef __OPUS__
#include "encoder/encoderopus.h"
#endif
#include "engine/sidechain/shoutconnection.h"
#include "mixer/playerinfo.h"
#include "moc_shoutconnection.cpp"
#include "preferences/usersettings.h"
#include "recording/defs_recording.h"
#include "track/track.h"
#include "util/logger.h"
namespace {
const int kConnectRetries = 30;
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
const int kMaxShoutFailures = 3;
const mixxx::Logger kLogger("ShoutConnection");
}
ShoutConnection::ShoutConnection(BroadcastProfilePtr profile,
UserSettingsPointer pConfig)
: m_pTextCodec(nullptr),
m_pMetaData(),
m_pShout(nullptr),
m_pShoutMetaData(nullptr),
m_iMetaDataLife(0),
m_iShoutStatus(0),
m_iShoutFailures(0),
m_pConfig(pConfig),
m_pProfile(profile),
m_encoder(nullptr),
m_pMasterSamplerate(new ControlProxy("[Master]", "samplerate", this)),
m_pBroadcastEnabled(new ControlProxy(BROADCAST_PREF_KEY, "enabled", this)),
m_custom_metadata(false),
m_firstCall(false),
m_format_is_mp3(false),
m_format_is_ov(false),
m_format_is_opus(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_retryCount(0),
m_reconnectFirstDelay(0.0),
m_reconnectPeriod(5.0),
m_noDelayFirstReconnect(true),
m_limitReconnects(true),
m_maximumRetries(10) {
setStatus(BroadcastProfile::STATUS_UNCONNECTED);
setState(NETWORKSTREAMWORKER_STATE_INIT);
// shout_init() should've already been called by now
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));
}
#ifdef SHOUT_TLS
// Libshout defaults to SHOUT_TLS_AUTO if build with SHOUT_TLS
// Sometimes autodetection fails, resulting into no metadata send
// https://bugs.launchpad.net/mixxx/+bug/1817395
if (shout_set_tls(m_pShout, SHOUT_TLS_DISABLED) != SHOUTERR_SUCCESS) {
errorDialog(tr("Error setting tls mode:"),
shout_get_error(m_pShout));
}
#endif
}
ShoutConnection::~ShoutConnection() {
delete m_pMasterSamplerate;
if (m_pShoutMetaData)
shout_metadata_free(m_pShoutMetaData);
if (m_pShout) {
shout_close(m_pShout);
shout_free(m_pShout);
}
// 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
VERIFY_OR_DEBUG_ASSERT(!isRunning()) {
qWarning() << "ShoutOutput::~ShoutOutput(): Thread didn't die.\
Ignored but file a bug report if problems rise!";
}
}
bool ShoutConnection::isConnected() {
if (m_pShout) {
m_iShoutStatus = shout_get_connected(m_pShout);
if (m_iShoutStatus == SHOUTERR_CONNECTED)
return true;
}
return false;
}
// Only called when applying settings while broadcasting is active
void ShoutConnection::applySettings() {
// Do nothing if profile or Live Broadcasting is disabled
if(!m_pBroadcastEnabled->toBool()
|| !m_pProfile->getEnabled())
return;
// Setting the profile's enabled value to false tells the
// connection's thread to exit, so no need to call
// processDisconnect manually
int dStatus = getStatus();
if((dStatus == BroadcastProfile::STATUS_UNCONNECTED
|| dStatus == BroadcastProfile::STATUS_FAILURE)) {
serverConnect();
}
}
QByteArray