00001 #include <QTimer>
00002 #include <QtEndian>
00003 #include <QNetworkInterface>
00004
00005 #include "mthread.h"
00006 #include "mythlogging.h"
00007 #include "mythcorecontext.h"
00008
00009 #include "bonjourregister.h"
00010 #include "mythraopconnection.h"
00011 #include "mythraopdevice.h"
00012
00013 MythRAOPDevice* MythRAOPDevice::gMythRAOPDevice = NULL;
00014 MThread* MythRAOPDevice::gMythRAOPDeviceThread = NULL;
00015 QMutex* MythRAOPDevice::gMythRAOPDeviceMutex = new QMutex(QMutex::Recursive);
00016
00017 #define LOC QString("RAOP Device: ")
00018
00019 bool MythRAOPDevice::Create(void)
00020 {
00021 QMutexLocker locker(gMythRAOPDeviceMutex);
00022
00023
00024 if (!MythRAOPConnection::LoadKey())
00025 {
00026 LOG(VB_GENERAL, LOG_ERR, LOC + "Aborting startup - no key found.");
00027 return false;
00028 }
00029
00030
00031 if (!gMythRAOPDeviceThread)
00032 gMythRAOPDeviceThread = new MThread("RAOPDevice");
00033 if (!gMythRAOPDeviceThread)
00034 {
00035 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RAOP device thread.");
00036 return false;
00037 }
00038
00039
00040 if (!gMythRAOPDevice)
00041 gMythRAOPDevice = new MythRAOPDevice();
00042 if (!gMythRAOPDevice)
00043 {
00044 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RAOP device object.");
00045 return false;
00046 }
00047
00048
00049 if (!gMythRAOPDeviceThread->isRunning())
00050 {
00051 gMythRAOPDevice->moveToThread(gMythRAOPDeviceThread->qthread());
00052 QObject::connect(
00053 gMythRAOPDeviceThread->qthread(), SIGNAL(started()),
00054 gMythRAOPDevice, SLOT(Start()));
00055 gMythRAOPDeviceThread->start(QThread::LowestPriority);
00056 }
00057
00058
00059 LOG(VB_GENERAL, LOG_INFO, LOC + "Created RAOP device objects.");
00060 return true;
00061 }
00062
00063 QString MythRAOPDevice::HardwareId()
00064 {
00065 QString key = "AirPlayId";
00066 QString id = gCoreContext->GetSetting(key);
00067 int size = id.size();
00068 if (size == 12)
00069 return id;
00070
00071 QByteArray ba;
00072 for (int i = 0; i < RAOP_HARDWARE_ID_SIZE; i++)
00073 ba.append((random() % 80) + 33);
00074 id = ba.toHex();
00075
00076 gCoreContext->SaveSetting(key, id);
00077 return id;
00078 }
00079
00080 void MythRAOPDevice::Cleanup(void)
00081 {
00082 LOG(VB_GENERAL, LOG_INFO, LOC + "Cleaning up.");
00083
00084 if (gMythRAOPDevice)
00085 gMythRAOPDevice->Teardown();
00086
00087 QMutexLocker locker(gMythRAOPDeviceMutex);
00088 if (gMythRAOPDeviceThread)
00089 {
00090 gMythRAOPDeviceThread->exit();
00091 gMythRAOPDeviceThread->wait();
00092 }
00093 delete gMythRAOPDeviceThread;
00094 gMythRAOPDeviceThread = NULL;
00095
00096 delete gMythRAOPDevice;
00097 gMythRAOPDevice = NULL;
00098 }
00099
00100 MythRAOPDevice::MythRAOPDevice()
00101 : ServerPool(), m_name(QString("MythTV")), m_bonjour(NULL), m_valid(false),
00102 m_lock(new QMutex(QMutex::Recursive)), m_setupPort(5000)
00103 {
00104 m_hardwareId = QByteArray::fromHex(HardwareId().toAscii());
00105 }
00106
00107 MythRAOPDevice::~MythRAOPDevice()
00108 {
00109 Teardown();
00110
00111 delete m_lock;
00112 m_lock = NULL;
00113 }
00114
00115 void MythRAOPDevice::Teardown(void)
00116 {
00117 QMutexLocker locker(m_lock);
00118
00119
00120 m_valid = false;
00121
00122
00123 delete m_bonjour;
00124 m_bonjour = NULL;
00125
00126
00127 foreach (MythRAOPConnection* client, m_clients)
00128 {
00129 disconnect(client->GetSocket(), 0, 0, 0);
00130 delete client;
00131 }
00132 m_clients.clear();
00133 }
00134
00135 void MythRAOPDevice::Start(void)
00136 {
00137 QMutexLocker locker(m_lock);
00138
00139
00140 if (m_valid)
00141 return;
00142
00143
00144 connect(this, SIGNAL(newConnection(QTcpSocket *)),
00145 this, SLOT(newConnection(QTcpSocket *)));
00146
00147 int baseport = m_setupPort;
00148 m_setupPort = tryListeningPort(m_setupPort, RAOP_PORT_RANGE);
00149
00150 if (m_setupPort < 0)
00151 {
00152 LOG(VB_GENERAL, LOG_ERR, LOC +
00153 "Failed to find a port for incoming connections.");
00154 }
00155 else
00156 {
00157 LOG(VB_GENERAL, LOG_INFO, LOC +
00158 QString("Listening for connections on port %1").arg(m_setupPort));
00159
00160 m_bonjour = new BonjourRegister(this);
00161
00162
00163 int multiple = m_setupPort - baseport;
00164 if (multiple > 0)
00165 m_name += QString::number(multiple);
00166
00167 QByteArray name = m_hardwareId.toHex();
00168 name.append("@");
00169 name.append(m_name);
00170 name.append(" on ");
00171 name.append(gCoreContext->GetHostName());
00172 QByteArray type = "_raop._tcp";
00173 QByteArray txt;
00174 txt.append(6); txt.append("tp=UDP");
00175 txt.append(8); txt.append("sm=false");
00176 txt.append(8); txt.append("sv=false");
00177 txt.append(4); txt.append("ek=1");
00178 txt.append(6); txt.append("et=0,1");
00179 txt.append(6); txt.append("cn=0,1");
00180 txt.append(4); txt.append("ch=2");
00181 txt.append(5); txt.append("ss=16");
00182 txt.append(8); txt.append("sr=44100");
00183 txt.append(8); txt.append("pw=false");
00184 txt.append(4); txt.append("vn=3");
00185 txt.append(9); txt.append("txtvers=1");
00186 txt.append(8); txt.append("md=0,1,2");
00187 txt.append(9); txt.append("vs=130.14");
00188 txt.append(7); txt.append("da=true");
00189
00190 LOG(VB_GENERAL, LOG_INFO, QString("Registering service %1.%2 port %3 TXT %4")
00191 .arg(QString(name)).arg(QString(type)).arg(m_setupPort).arg(QString(txt)));
00192 if (!m_bonjour->Register(m_setupPort, type, name, txt))
00193 {
00194 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to register service.");
00195 return;
00196 }
00197 }
00198
00199 m_valid = true;
00200 return;
00201 }
00202
00203 bool MythRAOPDevice::NextInAudioQueue(MythRAOPConnection *conn)
00204 {
00205 QMutexLocker locker(m_lock);
00206 QList<MythRAOPConnection *>::iterator it;
00207 for (it = m_clients.begin(); it != m_clients.end(); ++it)
00208 if (!(*it)->HasAudio())
00209 return conn == (*it);
00210 return true;
00211 }
00212
00213 void MythRAOPDevice::newConnection(QTcpSocket *client)
00214 {
00215 QMutexLocker locker(m_lock);
00216 LOG(VB_GENERAL, LOG_INFO, LOC + QString("New connection from %1:%2")
00217 .arg(client->peerAddress().toString()).arg(client->peerPort()));
00218
00219 int port = 6000;
00220 while (port < (6000 + RAOP_PORT_RANGE))
00221 {
00222 bool found = false;
00223 foreach (MythRAOPConnection* client, m_clients)
00224 {
00225 if (client->GetDataPort() == port)
00226 {
00227 found = true;
00228 port++;
00229 }
00230 }
00231 if (!found)
00232 break;
00233 }
00234
00235 MythRAOPConnection *obj =
00236 new MythRAOPConnection(this, client, m_hardwareId, port);
00237 if (obj->Init())
00238 {
00239 m_clients.append(obj);
00240 connect(client, SIGNAL(disconnected()), this, SLOT(deleteClient()));
00241 return;
00242 }
00243
00244 LOG(VB_GENERAL, LOG_ERR, LOC +
00245 "Failed to initialise client connection - closing.");
00246 delete obj;
00247 client->disconnectFromHost();
00248 delete client;
00249 }
00250
00251 void MythRAOPDevice::deleteClient(void)
00252 {
00253 QMutexLocker locker(m_lock);
00254 QList<MythRAOPConnection *>::iterator it;
00255 for (it = m_clients.begin(); it != m_clients.end(); ++it)
00256 {
00257 if ((*it)->GetSocket()->state() == QTcpSocket::UnconnectedState)
00258 {
00259 LOG(VB_GENERAL, LOG_INFO, LOC + "Removing client connection.");
00260 m_clients.removeOne(*it);
00261 delete *it;
00262 return;
00263 }
00264 }
00265 }