18 #define LOC QString("RAOP Conn: ")
19 #define MAX_PACKET_SIZE 2048
25 #define TIMING_REQUEST 0x52
26 #define TIMING_RESPONSE 0x53
28 #define FIRSTSYNC (0x54 | 0x80)
29 #define RANGE_RESEND 0x55
30 #define AUDIO_RESEND 0x56
31 #define AUDIO_DATA 0x60
32 #define FIRSTAUDIO_DATA (0x60 | 0x80)
36 #define AUDIOCARD_BUFFER 500
40 #define AUDIO_BUFFER 100
50 LOG(VB_GENERAL, LOG_DEBUG,
51 LOC + QString(
"Sending(%1): ").arg(str.length()) + str);
52 QTextStream *q =
this;
59 QByteArray
id,
int port)
60 : QObject(parent), m_watchdogTimer(NULL), m_socket(socket),
61 m_textStream(NULL), m_hardwareId(id),
62 m_incomingHeaders(), m_incomingContent(), m_incomingPartial(
false),
64 m_dataSocket(NULL), m_dataPort(port),
65 m_clientControlSocket(NULL), m_clientControlPort(0),
66 m_clientTimingSocket(NULL), m_clientTimingPort(0),
68 m_audio(NULL), m_codec(NULL), m_codeccontext(NULL),
69 m_channels(2), m_sampleSize(16), m_frameRate(44100),
70 m_framesPerPacket(352),m_dequeueAudioTimer(NULL),
71 m_queueLength(0), m_streamingStarted(
false),
72 m_allowVolumeControl(
true),
75 m_lastSequence(0), m_lastTimestamp(0),
76 m_currentTimestamp(0), m_nextSequence(0), m_nextTimestamp(0),
77 m_bufferLength(0), m_timeLastSync(0),
78 m_cardLatency(-1), m_adjustedLatency(-1), m_audioStarted(
false),
80 m_masterTimeStamp(0), m_deviceTimeStamp(0), m_networkLatency(0),
83 m_progressStart(0), m_progressCurrent(0), m_progressEnd(0)
94 client->deleteLater();
185 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to connect client socket signal.");
191 if (!connect(
m_dataSocket, SIGNAL(newDatagram(QByteArray, QHostAddress, quint16)),
192 this, SLOT(
udpDataReady(QByteArray, QHostAddress, quint16))))
194 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to connect data socket signal.");
202 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to bind to a port for data.");
206 LOG(VB_GENERAL, LOG_INFO,
LOC +
207 QString(
"Bound to port %1 for incoming data").arg(
m_dataPort));
247 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
248 QString(
"Packet doesn't start with valid Rtp Header (0x%1)")
249 .arg((uint8_t)buf[0], 0, 16));
261 case FIRSTAUDIO_DATA:
274 case TIMING_RESPONSE:
279 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
280 QString(
"Packet type (0x%1) not handled")
288 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
289 QString(
"Received packet %1 too late, ignoring")
294 if (type == AUDIO_DATA || type == FIRSTAUDIO_DATA)
308 if (type == AUDIO_RESEND)
312 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
313 QString(
"Received required resend %1 (with ts:%2 last:%3)")
318 LOG(VB_GENERAL, LOG_WARNING,
LOC +
319 QString(
"Received unexpected resent packet %1")
325 QList<AudioData> *decoded =
new QList<AudioData>();
330 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error decoding audio"));
336 frames.
data = decoded;
343 bool first = (uint8_t)buf[0] == 0x90;
344 const char *req = buf.constData();
345 uint64_t current_ts = qFromBigEndian(*(uint32_t *)(req + 4));
346 uint64_t next_ts = qFromBigEndian(*(uint32_t *)(req + 16));
357 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"Receiving first SYNC packet"));
361 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"Receiving SYNC packet"));
364 timeval
t; gettimeofday(&t, NULL);
367 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"SYNC: cur:%1 next:%2 time:%3")
372 int64_t currentLatency = 0LL;
379 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
380 QString(
"RAOP timestamps: about to play:%1 desired:%2 latency:%3")
381 .arg(audiots).arg(m_currentTimestamp)
382 .arg(currentLatency));
385 delay += currentLatency;
387 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
388 QString(
"Queue=%1 buffer=%2ms ideal=%3ms diffts:%4ms")
395 (-currentLatency > AUDIOCARD_BUFFER))
402 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
403 QString(
"Too much delay (%1ms), adjusting")
413 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"Drop %1 packets").arg(res));
430 int16_t missed = (got < expected) ?
431 (int16_t)(((int32_t)got + UINT16_MAX + 1) - expected) :
434 LOG(VB_GENERAL, LOG_INFO,
LOC +
435 QString(
"Missed %1 packet(s): expected %2 got %3 ts:%4")
436 .arg(missed).arg(expected).arg(got).arg(timestamp));
440 req[1] = RANGE_RESEND | 0x80;
442 *(
uint16_t *)(req + 4) = qToBigEndian(expected);
443 *(
uint16_t *)(req + 6) = qToBigEndian(missed);
449 for (
uint16_t count = 0; count < missed; count++)
451 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Sent resend for %1")
452 .arg(expected + count));
453 m_resends.insert(expected + count, timestamp);
457 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to send resend request.");
470 QMutableMapIterator<uint16_t,uint64_t> it(
m_resends);
476 LOG(VB_GENERAL, LOG_WARNING,
LOC +
477 QString(
"Never received resend packet %1").arg(it.key()));
493 gettimeofday(&t, NULL);
497 req[1] = TIMING_REQUEST | 0x80;
502 *(uint32_t *)(req + 4) = (uint32_t)0;
503 *(uint64_t *)(req + 8) = (uint64_t)0;
504 *(uint64_t *)(req + 16) = (uint64_t)0;
505 *(uint32_t *)(req + 24) = qToBigEndian((uint32_t)t.tv_sec);
506 *(uint32_t *)(req + 28) = qToBigEndian((uint32_t)t.tv_usec);
510 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to send resend time request.");
513 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
514 QString(
"Requesting master time (Local %1.%2)")
515 .arg(t.tv_sec).arg(t.tv_usec));
527 const char *req = buf.constData();
529 t1.tv_sec = qFromBigEndian(*(uint32_t *)(req + 8));
530 t1.tv_usec = qFromBigEndian(*(uint32_t *)(req + 12));
532 gettimeofday(&t2, NULL);
533 uint64_t time1, time2;
534 time1 = t1.tv_sec * 1000 + t1.tv_usec / 1000;
535 time2 = t2.tv_sec * 1000 + t2.tv_usec / 1000;
536 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"Read back time (Local %1.%2)")
537 .arg(t1.tv_sec).arg(t1.tv_usec));
541 LOG(VB_AUDIO, LOG_DEBUG,
LOC + QString(
"Network Latency: %1ms")
542 .arg(m_networkLatency));
546 uint32_t sec = qFromBigEndian(*(uint32_t *)(req + 24));
547 uint32_t ticks = qFromBigEndian(*(uint32_t *)(req + 28));
555 return (int64_t)sec * 1000LL + (((int64_t)ticks * 1000LL) >> 32);
562 if ((uint8_t)buf[0] != 0x80 && (uint8_t)buf[0] != 0x90)
569 if ((uint8_t)buf[0] == 0x90 && type == FIRSTSYNC)
573 if (type != FIRSTAUDIO_DATA)
578 if (type != AUDIO_DATA && type != FIRSTAUDIO_DATA && type != AUDIO_RESEND)
581 const char *
ptr = buf.constData();
582 if (type == AUDIO_RESEND)
586 seq = qFromBigEndian(*(
uint16_t *)(ptr + 2));
587 timestamp = qFromBigEndian(*(uint32_t *)(ptr + 4));
594 const QByteArray *
buf,
595 QList<AudioData> *
dest)
597 const char *data_in = buf->constData();
598 int len = buf->size();
599 if (type == AUDIO_RESEND)
609 int aeslen = len & ~0xf;
610 unsigned char iv[16];
611 unsigned char decrypted_data[MAX_PACKET_SIZE];
612 memcpy(iv,
m_AESIV.constData(),
sizeof(iv));
613 AES_cbc_encrypt((
const unsigned char *)data_in,
614 decrypted_data, aeslen,
616 memcpy(decrypted_data + aeslen, data_in + aeslen, len - aeslen);
621 av_init_packet(&tmp_pkt);
622 tmp_pkt.data = decrypted_data;
625 uint32_t frames_added = 0;
626 uint8_t *
samples = (uint8_t *)av_mallocz(AVCODEC_MAX_AUDIO_FRAME_SIZE);
627 while (tmp_pkt.size > 0)
631 data_size, &tmp_pkt);
640 int num_samples = data_size /
641 (ctx->channels * av_get_bytes_per_sample(ctx->sample_fmt));
643 frames_added += num_samples;
647 block.
frames = num_samples;
667 timeval
t; gettimeofday(&t, NULL);
668 uint64_t dtime = (t.tv_sec * 1000 + t.tv_usec / 1000) -
m_timeLastSync;
674 if (buffered > AUDIOCARD_BUFFER)
686 int max_packets = ((AUDIOCARD_BUFFER - buffered)
689 uint64_t timestamp = 0;
691 QMapIterator<uint64_t,AudioPacket> packet_it(
m_audioQueue);
692 while (packet_it.hasNext() && i <= max_packets)
696 timestamp = packet_it.key();
707 LOG(VB_GENERAL, LOG_ERR,
LOC +
708 QString(
"Audio discontinuity seen. Played %1 (%3) expected %2")
714 QList<AudioData>::iterator it = frames.
data->begin();
715 for (; it != frames.
data->end(); ++it)
726 if (offset > data->
length)
730 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
731 QString(
"ProcessAudio: Dropping %1 frames to catch up "
759 QMutableMapIterator<uint64_t,AudioPacket> packet_it(
m_audioQueue);
760 while (packet_it.hasNext())
763 if (packet_it.key() < timestamp)
768 QList<AudioData>::iterator it = frames.
data->begin();
769 for (; it != frames.
data->end(); ++it)
795 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Closing connection after inactivity.");
818 QTcpSocket *socket = (QTcpSocket *)sender();
822 QByteArray
data = socket->readAll();
823 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"readClient(%1): ")
824 .arg(data.size()) + data.constData());
833 QTextStream stream(data);
837 line = stream.readLine();
838 if (line.size() == 0)
840 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"Header(%1) = %2")
841 .arg(
m_socket->peerAddress().toString())
844 if (line.contains(
"Content-Length:"))
849 while (!line.isNull());
856 int pos = stream.pos();
879 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"Content(%1) = %2")
886 const QByteArray &content)
888 if (header.isEmpty())
893 if (!tags.contains(
"CSeq"))
895 LOG(VB_GENERAL, LOG_ERR,
LOC +
"ProcessRequest: Didn't find CSeq");
899 QString option = header[0].left(header[0].indexOf(
" "));
904 uint64_t RTPtimestamp;
905 if (tags.contains(
"RTP-Info"))
908 QString
data = tags[
"RTP-Info"];
909 QStringList
items = data.split(
";");
910 foreach (QString item, items)
912 if (item.startsWith(
"seq"))
914 RTPseq = item.mid(item.indexOf(
"=") + 1).trimmed().toUShort();
916 else if (item.startsWith(
"rtptime"))
918 RTPtimestamp = item.mid(item.indexOf(
"=") + 1).trimmed().toUInt();
921 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"RTP-Info: seq=%1 rtptime=%2")
922 .arg(RTPseq).arg(RTPtimestamp));
931 if (!tags.contains(
"Authorization"))
944 LOG(VB_GENERAL, LOG_INFO,
LOC +
"RAOP client authenticated");
948 LOG(VB_GENERAL, LOG_INFO,
LOC +
"RAOP authentication failed");
955 if (tags.contains(
"Apple-Challenge"))
957 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"Received Apple-Challenge"));
962 int tosize = RSA_size(
LoadKey());
963 uint8_t *to =
new uint8_t[tosize];
965 QByteArray challenge =
966 QByteArray::fromBase64(tags[
"Apple-Challenge"].toLatin1());
967 int challenge_size = challenge.size();
968 if (challenge_size != 16)
970 LOG(VB_GENERAL, LOG_ERR,
LOC +
971 QString(
"Decoded challenge size %1, expected 16")
972 .arg(challenge_size));
973 if (challenge_size > 16)
978 unsigned char from[38];
979 memcpy(from, challenge.constData(), challenge_size);
981 if (
m_socket->localAddress().protocol() ==
982 QAbstractSocket::IPv4Protocol)
984 uint32_t ip =
m_socket->localAddress().toIPv4Address();
985 ip = qToBigEndian(ip);
986 memcpy(from + i, &ip, 4);
989 else if (
m_socket->localAddress().protocol() ==
990 QAbstractSocket::IPv6Protocol)
992 Q_IPV6ADDR ip =
m_socket->localAddress().toIPv6Address();
994 "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\xff\xff",
997 memcpy(from + i, &ip[12], 4);
1002 memcpy(from + i, &ip, 16);
1006 memcpy(from + i,
m_hardwareId.constData(), AIRPLAY_HARDWARE_ID_SIZE);
1007 i += AIRPLAY_HARDWARE_ID_SIZE;
1012 memset(from + i, 0, pad);
1016 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
1017 QString(
"Full base64 response: '%1' size %2")
1018 .arg(QByteArray((
const char *)from, i).toBase64().constData())
1021 RSA_private_encrypt(i, from, to,
LoadKey(), RSA_PKCS1_PADDING);
1023 QByteArray base64 = QByteArray((
const char *)to, tosize).toBase64();
1026 for (
int pos = base64.size() - 1; pos > 0; pos--)
1028 if (base64[pos] ==
'=')
1033 LOG(VB_GENERAL, LOG_DEBUG, QString(
"tSize=%1 tLen=%2 tResponse=%3")
1034 .arg(tosize).arg(base64.size()).arg(base64.constData()));
1038 if (option ==
"OPTIONS")
1040 *
m_textStream <<
"Public: ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, "
1041 "TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER, POST, GET\r\n";
1043 else if (option ==
"ANNOUNCE")
1046 foreach (QString line, lines)
1048 if (line.startsWith(
"a=rsaaeskey:"))
1050 QString key = line.mid(12).trimmed();
1051 QByteArray decodedkey = QByteArray::fromBase64(key.toLatin1());
1052 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
1053 QString(
"RSAAESKey: %1 (decoded size %2)")
1054 .arg(key).arg(decodedkey.size()));
1059 char *decryptedkey =
new char[
size];
1060 if (RSA_private_decrypt(decodedkey.size(),
1061 (
const unsigned char *)decodedkey.constData(),
1062 (
unsigned char *)decryptedkey,
1063 LoadKey(), RSA_PKCS1_OAEP_PADDING))
1065 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
1066 "Successfully decrypted AES key from RSA.");
1067 AES_set_decrypt_key((
const unsigned char *)decryptedkey,
1072 LOG(VB_GENERAL, LOG_WARNING,
LOC +
1073 "Failed to decrypt AES key from RSA.");
1075 delete [] decryptedkey;
1078 else if (line.startsWith(
"a=aesiv:"))
1080 QString aesiv = line.mid(8).trimmed();
1081 m_AESIV = QByteArray::fromBase64(aesiv.toLatin1());
1082 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
1083 QString(
"AESIV: %1 (decoded size %2)")
1084 .arg(aesiv).arg(
m_AESIV.size()));
1086 else if (line.startsWith(
"a=fmtp:"))
1089 QString
format = line.mid(7).trimmed();
1090 QList<QString>
fmts = format.split(
' ');
1091 foreach (QString fmt, fmts)
1095 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
1096 QString(
"Audio parameter: %1").arg(fmt));
1104 else if (option ==
"SETUP")
1106 if (tags.contains(
"Transport"))
1112 int control_port = 0;
1113 int timing_port = 0;
1114 QString
data = tags[
"Transport"];
1115 QStringList
items = data.split(
";");
1116 bool events =
false;
1118 foreach (QString item, items)
1120 if (item.startsWith(
"control_port"))
1121 control_port = item.mid(item.indexOf(
"=") + 1).trimmed().toUInt();
1122 else if (item.startsWith(
"timing_port"))
1123 timing_port = item.mid(item.indexOf(
"=") + 1).trimmed().toUInt();
1124 else if (item.startsWith(
"events"))
1128 LOG(VB_GENERAL, LOG_INFO,
LOC +
1129 QString(
"Negotiated setup with client %1 on port %2")
1130 .arg(
m_socket->peerAddress().toString())
1132 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
1133 QString(
"control port: %1 timing port: %2")
1134 .arg(control_port).arg(timing_port));
1146 int controlbind_port =
1149 if (controlbind_port < 0)
1151 LOG(VB_GENERAL, LOG_ERR,
LOC +
1152 QString(
"Failed to bind to client control port. "
1153 "Control of audio stream may fail"));
1157 LOG(VB_GENERAL, LOG_INFO,
LOC +
1158 QString(
"Bound to client control port %1 on port %2")
1159 .arg(control_port).arg(controlbind_port));
1163 SIGNAL(newDatagram(QByteArray, QHostAddress, quint16)),
1165 SLOT(
udpDataReady(QByteArray, QHostAddress, quint16)));
1175 int timingbind_port =
1178 if (timingbind_port < 0)
1180 LOG(VB_GENERAL, LOG_ERR,
LOC +
1181 QString(
"Failed to bind to client timing port. "
1182 "Timing of audio stream will be incorrect"));
1186 LOG(VB_GENERAL, LOG_INFO,
LOC +
1187 QString(
"Bound to client timing port %1 on port %2")
1188 .arg(timing_port).arg(timingbind_port));
1192 SIGNAL(newDatagram(QByteArray, QHostAddress, quint16)),
1194 SLOT(
udpDataReady(QByteArray, QHostAddress, quint16)));
1202 client->disconnect();
1220 LOG(VB_GENERAL, LOG_ERR,
LOC +
1221 "Failed to find a port for RAOP events server.");
1225 LOG(VB_GENERAL, LOG_INFO,
LOC +
1226 QString(
"Listening for RAOP events on port %1").arg(
m_eventPort));
1241 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
1242 QString(
"Audio hardware latency: %1ms")
1250 foreach (QString item, items)
1256 if (item.startsWith(
"control_port"))
1258 newdata +=
"control_port=" + QString::number(controlbind_port);
1260 else if (item.startsWith(
"timing_port"))
1262 newdata +=
"timing_port=" + QString::number(timingbind_port);
1264 else if (item.startsWith(
"events"))
1266 newdata +=
"event_port=" + QString::number(
m_eventPort);
1278 newdata +=
"server_port=" + QString::number(
m_dataPort);
1289 LOG(VB_GENERAL, LOG_ERR,
LOC +
1290 "No Transport details found - Ignoring");
1293 else if (option ==
"RECORD")
1308 else if (option ==
"FLUSH")
1319 *
m_textStream <<
"RTP-Info: rtptime=" << QString::number(timestamp);
1323 else if (option ==
"SET_PARAMETER")
1325 if (tags.contains(
"Content-Type"))
1327 if (tags[
"Content-Type"] ==
"text/parameters")
1329 QString
name = content.left(content.indexOf(
":"));
1330 QString param = content.mid(content.indexOf(
":") + 1).trimmed();
1332 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
1333 QString(
"text/parameters: name=%1 parem=%2")
1334 .arg(name).arg(param));
1338 float volume = (param.toFloat() + 30.0f) * 100.0f / 30.0f;
1341 LOG(VB_GENERAL, LOG_INFO,
1342 LOC + QString(
"Setting volume to %1 (raw %3)")
1343 .arg(volume).arg(param));
1346 else if (name ==
"progress")
1348 QStringList
items = param.split(
"/");
1349 if (items.size() == 3)
1360 LOG(VB_GENERAL, LOG_INFO,
1361 LOC +QString(
"Progress: %1/%2")
1366 else if(tags[
"Content-Type"] ==
"image/jpeg")
1371 else if (tags[
"Content-Type"] ==
"application/x-dmap-tagged")
1374 QMap<QString,QString> map =
decodeDMAP(content);
1375 LOG(VB_GENERAL, LOG_INFO,
1376 QString(
"Receiving Title:%1 Artist:%2 Album:%3 Format:%4")
1377 .arg(map[
"minm"]).arg(map[
"asar"])
1378 .arg(map[
"asal"]).arg(map[
"asfm"]));
1382 else if (option ==
"TEARDOWN")
1389 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"Command not handled: %1")
1401 *stream <<
"RTSP/1.0 401 Unauthorised\r\n";
1402 *stream <<
"Server: AirTunes/130.14\r\n";
1403 *stream <<
"WWW-Authenticate: Digest realm=\"raop\", ";
1404 *stream <<
"nonce=\"" +
m_nonce +
"\"\r\n";
1405 *stream <<
"CSeq: " << cseq <<
"\r\n";
1408 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
1409 QString(
"Finished Authentication request %2, Send: %3")
1410 .arg(cseq).arg(socket->flush()));
1414 QString &option, QString &cseq)
1418 *stream <<
"Server: AirTunes/130.14\r\n";
1419 *stream <<
"CSeq: " << cseq <<
"\r\n";
1422 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"Finished %1 %2 , Send: %3")
1423 .arg(option).arg(cseq).arg(socket->flush()));
1434 QMutexLocker locker(&lock);
1439 QString sName(
"/RAOPKey.rsa" );
1440 FILE *file = fopen(
GetConfDir().toUtf8() + sName.toUtf8(),
"rb");
1450 g_rsa = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL);
1456 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
1457 QString(
"Loaded RSA private key (%1)").arg(RSA_check_key(
g_rsa)));
1470 if (lines.isEmpty())
1473 for (
int i = 0; i < lines.size(); i++)
1475 int index = lines[i].indexOf(
":");
1478 result.insert(lines[i].left(index).trimmed(),
1479 lines[i].mid(index + 1).trimmed());
1488 QTextStream stream(lines);
1493 line = stream.readLine();
1499 while (!line.isNull());
1513 int hour = time / 3600;
1514 int minute = (time - hour * 3600) / 60;
1515 int seconds = time - hour * 3600 - minute * 60;
1520 str += QString(
"%1:").arg(hour);
1526 str += QString(
"%1:").arg(minute);
1531 str += QString::number(seconds);
1554 QMap<QString,QString> result;
1556 while (offset < dmap.size())
1558 QString tag = dmap.mid(offset, 4);
1560 uint32_t length = qFromBigEndian(*(uint32_t *)(dmap.constData() + offset));
1561 offset +=
sizeof(uint32_t);
1562 QString content = QString::fromUtf8(dmap.mid(offset,
1563 length).constData());
1565 result.insert(tag, content);
1579 m_codec = avcodec_find_decoder(CODEC_ID_ALAC);
1582 LOG(VB_GENERAL, LOG_ERR,
LOC
1583 +
"Failed to create ALAC decoder- going silent...");
1590 unsigned char *extradata =
new unsigned char[36];
1591 memset(extradata, 0, 36);
1594 LOG(VB_GENERAL, LOG_ERR,
LOC +
1595 "Creating decoder but haven't seen audio format.");
1600 extradata[12] = (fs >> 24) & 0xff;
1601 extradata[13] = (fs >> 16) & 0xff;
1602 extradata[14] = (fs >> 8) & 0xff;
1603 extradata[15] = fs & 0xff;
1615 LOG(VB_GENERAL, LOG_ERR,
LOC +
1616 "Failed to open ALAC decoder - going silent...");
1620 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Opened ALAC decoder.");
1650 LOG(VB_GENERAL, LOG_ERR,
LOC +
1651 "Failed to open audio device. Going silent...");
1658 if (!error.isEmpty())
1660 LOG(VB_GENERAL, LOG_ERR,
LOC +
1661 QString(
"Audio not initialised. Message was '%1'")
1669 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Opened audio device.");
1708 int16_t *
samples = (int16_t *)av_mallocz(AVCODEC_MAX_AUDIO_FRAME_SIZE);
1715 usleep(AUDIOCARD_BUFFER * 1000);
1717 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"AudioCardLatency: ts=%1ms")
1719 return AUDIOCARD_BUFFER - (int64_t)audiots;
1724 LOG(VB_GENERAL, LOG_INFO,
LOC +
1725 QString(
"New connection from %1:%2 for RAOP events server.")
1726 .arg(client->peerAddress().toString()).arg(client->peerPort()));
1735 QTcpSocket *client =
static_cast<QTcpSocket *
>(sender());
1737 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
1738 QString(
"%1:%2 disconnected from RAOP events server.")
1739 .arg(client->peerAddress().toString()).arg(client->peerPort()));