10 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
11 #include <QStringConverter>
31 #if OPENSSL_VERSION_NUMBER < 0x030000000L
32 #define EVP_PKEY_get_id EVP_PKEY_id
33 #define EVP_PKEY_get_size EVP_PKEY_size
36 #define LOC QString("RAOP Conn: ")
45 static constexpr uint8_t
SYNC { 0x54 };
67 LOG(VB_PLAYBACK, LOG_DEBUG,
68 LOC + QString(
"Sending(%1): ").arg(str.length()) + str.trimmed());
69 QTextStream *q =
this;
76 QByteArray
id,
int port)
79 m_hardwareId(
std::move(id)),
83 #if OPENSSL_VERSION_NUMBER < 0x030000000L
86 m_cipher = EVP_CIPHER_fetch(
nullptr,
"AES-128-CBC",
nullptr);
88 m_cctx = EVP_CIPHER_CTX_new();
98 client->deleteLater();
129 nc->UnRegister(
this,
m_id);
132 EVP_CIPHER_CTX_free(
m_cctx);
134 #if OPENSSL_VERSION_NUMBER >= 0x030000000L
205 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
212 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
"Failed to connect client socket signal.");
221 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
"Failed to connect data socket signal.");
229 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
"Failed to bind to a port for data.");
233 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
234 QString(
"Bound to port %1 for incoming data").arg(
m_dataPort));
270 uint64_t firstframe = 0;
274 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
275 QString(
"Packet doesn't start with valid Rtp Header (0x%1)")
276 .arg((uint8_t)buf[0], 0, 16));
306 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
307 QString(
"Packet type (0x%1) not handled")
315 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
316 QString(
"Received packet %1 too late, ignoring")
339 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
340 QString(
"Received required resend %1 (with ts:%2 last:%3)")
346 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
347 QString(
"Received unexpected resent packet %1")
354 auto *decoded =
new QList<AudioData>();
359 LOG(VB_PLAYBACK, LOG_ERR,
LOC + QString(
"Error decoding audio"));
371 bool first = (uint8_t)buf[0] == 0x90;
372 const char *req = buf.constData();
373 uint64_t current_ts = qFromBigEndian(*(uint32_t *)(req + 4));
374 uint64_t next_ts = qFromBigEndian(*(uint32_t *)(req + 16));
386 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Receiving %1SYNC packet")
387 .arg(first ?
"first " :
""));
391 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"SYNC: cur:%1 next:%2 time:%3")
397 std::chrono::milliseconds currentLatency = 0ms;
404 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
405 QString(
"RAOP timestamps: about to play:%1 desired:%2 latency:%3")
407 .arg(currentLatency.count()));
410 delay += currentLatency;
412 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
413 QString(
"Queue=%1 buffer=%2ms ideal=%3ms diffts:%4ms")
427 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
428 QString(
"Too much delay (%1ms), adjusting")
438 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Drop %1 packets").arg(res));
455 int16_t missed = (got < expected) ?
456 (int16_t)(((int32_t)got + UINT16_MAX + 1) - expected) :
459 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
460 QString(
"Missed %1 packet(s): expected %2 got %3 ts:%4")
461 .arg(missed).arg(expected).arg(got).arg(timestamp.count()));
465 *(
uint16_t *)(&req[4]) = qToBigEndian(expected);
466 *(
uint16_t *)(&req[6]) = qToBigEndian(missed);
470 == (qint64)req.size())
472 for (
uint16_t count = 0; count < missed; count++)
474 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Sent resend for %1")
475 .arg(expected + count));
476 m_resends.insert(expected + count, timestamp);
480 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
"Failed to send resend request.");
493 QMutableMapIterator<uint16_t,std::chrono::milliseconds> it(
m_resends);
499 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
500 QString(
"Never received resend packet %1").arg(it.key()));
516 uint32_t ntpTicks {0};
517 auto usecs = nowAsDuration<std::chrono::microseconds>();
520 std::array<uint8_t,32> req {
526 *(uint32_t *)(&req[24]) = qToBigEndian(ntpSec);
527 *(uint32_t *)(&req[28]) = qToBigEndian(ntpTicks);
532 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
"Failed to send resend time request.");
535 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
536 QString(
"Requesting master time (Local %1.%2)")
537 .arg(ntpSec,8,16,QChar(
'0')).arg(ntpTicks,8,16,QChar(
'0')));
548 const char *req = buf.constData();
550 uint32_t ntpSec = qFromBigEndian(*(uint32_t *)(req + 8));
551 uint32_t ntpTicks = qFromBigEndian(*(uint32_t *)(req + 12));
553 auto time2 = nowAsDuration<std::chrono::milliseconds>();
554 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Read back time (Local %1.%2)")
555 .arg(ntpSec,8,16,QChar(
'0')).arg(ntpTicks,8,16,QChar(
'0')));
559 LOG(VB_AUDIO, LOG_DEBUG,
LOC + QString(
"Network Latency: %1ms")
564 uint32_t sec = qFromBigEndian(*(uint32_t *)(req + 24));
565 uint32_t ticks = qFromBigEndian(*(uint32_t *)(req + 28));
566 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Source NTP clock time %1.%2")
567 .arg(sec,8,16,QChar(
'0')).arg(ticks,8,16,QChar(
'0')));
570 std::chrono::milliseconds master =
NTPToLocal(sec, ticks);
586 return std::chrono::milliseconds(((int64_t)sec -
CLOCK_EPOCH) * 1000LL +
587 (((int64_t)ticks * 1000LL) >> 32));
590 uint32_t &ntpSec, uint32_t &ntpTicks)
592 ntpSec = duration_cast<std::chrono::seconds>(usec).count() +
CLOCK_EPOCH;
593 ntpTicks = ((usec % 1s).count() << 32) / 1000000;
597 auto micros = durationFromTimeval<std::chrono::microseconds>(
t);
615 if ((uint8_t)buf[0] != 0x80 && (uint8_t)buf[0] != 0x90)
634 const char *ptr = buf.constData();
639 seq = qFromBigEndian(*(
uint16_t *)(ptr + 2));
640 timestamp = qFromBigEndian(*(uint32_t *)(ptr + 4));
647 const QByteArray *buf,
648 QList<AudioData> *
dest)
650 const char *data_in = buf->constData();
651 int len = buf->size();
662 int aeslen = len & ~0xf;
665 std::array<uint8_t,MAX_PACKET_SIZE> decrypted_data {};
666 EVP_CIPHER_CTX_reset(
m_cctx);
667 EVP_CIPHER_CTX_set_padding(
m_cctx, 0);
669 #
if OPENSSL_VERSION_NUMBER < 0x030000000L
672 reinterpret_cast<const uint8_t *
>(
m_aesIV.data()))
675 reinterpret_cast<const uint8_t *
>(
m_aesIV.data()),
680 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
681 QString(
"EVP_DecryptInit_ex failed. (%1)")
682 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
684 else if (EVP_DecryptUpdate(
m_cctx, decrypted_data.data(), &outlen1,
685 reinterpret_cast<const uint8_t *
>(data_in),
688 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
689 QString(
"EVP_DecryptUpdate failed. (%1)")
690 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
692 else if (EVP_DecryptFinal_ex(
m_cctx, decrypted_data.data() + outlen1, &outlen2) != 1)
694 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
695 QString(
"EVP_DecryptFinal_ex failed. (%1)")
696 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
698 std::copy(data_in + aeslen, data_in + len,
699 decrypted_data.data() + aeslen);
702 AVPacket *tmp_pkt = av_packet_alloc();
703 if (tmp_pkt ==
nullptr)
705 tmp_pkt->data = decrypted_data.data();
708 uint32_t frames_added = 0;
710 while (tmp_pkt->size > 0)
723 int num_samples = data_size /
724 (ctx->ch_layout.nb_channels * av_get_bytes_per_sample(ctx->sample_fmt));
726 frames_added += num_samples;
730 tmp_pkt->data += ret;
731 tmp_pkt->size -= ret;
733 av_packet_free(&tmp_pkt);
748 auto dtime = nowAsDuration<std::chrono::milliseconds>() -
m_timeLastSync;
769 std::chrono::milliseconds timestamp = 0ms;
771 QMapIterator<std::chrono::milliseconds,AudioPacket> packet_it(
m_audioQueue);
772 while (packet_it.hasNext() && i <= max_packets)
776 timestamp = packet_it.key();
787 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
788 QString(
"Audio discontinuity seen. Played %1 (%3) expected %2")
794 for (
const auto & data : qAsConst(*frames.
data))
804 if (offset > data.length)
805 offset = data.length;
808 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
809 QString(
"ProcessAudio: Dropping %1 frames to catch up "
815 data.length - offset,
816 std::chrono::milliseconds(timestamp), framecnt);
837 QMutableMapIterator<std::chrono::milliseconds,AudioPacket> packet_it(
m_audioQueue);
838 while (packet_it.hasNext())
841 if (packet_it.key() < timestamp)
846 for (
const auto & data : qAsConst(*frames.
data))
870 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Closing connection after inactivity.");
893 auto *socket = qobject_cast<QTcpSocket *>(sender());
897 QByteArray data = socket->readAll();
900 QByteArray printable = data.left(32);
901 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"readClient(%1): %2%3")
903 .arg(printable.toHex().toUpper().data(),
904 data.size() > 32 ?
"..." :
""));
913 QTextStream stream(data);
917 line = stream.readLine();
918 if (line.size() == 0)
920 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Header(%1) = %2")
921 .arg(
m_socket->peerAddress().toString(), line));
923 if (line.contains(
"Content-Length:"))
928 while (!line.isNull());
935 int pos = stream.pos();
955 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Content(%1) = %2")
964 if (header.isEmpty())
969 if (!tags.contains(
"CSeq"))
971 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
"ProcessRequest: Didn't find CSeq");
975 QString option = header[0].left(header[0].indexOf(
" "));
980 uint64_t RTPtimestamp = 0;
981 if (tags.contains(
"RTP-Info"))
984 QString data = tags[
"RTP-Info"];
985 QStringList items = data.split(
";");
986 for (
const QString& item : qAsConst(items))
988 if (item.startsWith(
"seq"))
990 RTPseq = item.mid(item.indexOf(
"=") + 1).trimmed().toUShort();
992 else if (item.startsWith(
"rtptime"))
994 RTPtimestamp = item.mid(item.indexOf(
"=") + 1).trimmed().toUInt();
997 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"RTP-Info: seq=%1 rtptime=%2")
998 .arg(RTPseq).arg(RTPtimestamp));
1007 if (!tags.contains(
"Authorization"))
1020 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"RAOP client authenticated");
1024 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"RAOP authentication failed");
1031 if (tags.contains(
"Apple-Challenge"))
1033 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Received Apple-Challenge"));
1039 std::vector<uint8_t>to;
1042 QByteArray challenge =
1043 QByteArray::fromBase64(tags[
"Apple-Challenge"].toLatin1());
1044 int challenge_size = challenge.size();
1045 if (challenge_size != 16)
1047 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1048 QString(
"Base64 decoded challenge size %1, expected 16")
1049 .arg(challenge_size));
1050 if (challenge_size > 16)
1051 challenge_size = 16;
1055 std::array<uint8_t,38> from {};
1056 std::copy(challenge.cbegin(), challenge.cbegin() + challenge_size,
1058 i += challenge_size;
1059 if (
m_socket->localAddress().protocol() ==
1060 QAbstractSocket::IPv4Protocol)
1062 uint32_t ip =
m_socket->localAddress().toIPv4Address();
1063 ip = qToBigEndian(ip);
1064 memcpy(&from[i], &ip, 4);
1067 else if (
m_socket->localAddress().protocol() ==
1068 QAbstractSocket::IPv6Protocol)
1070 Q_IPV6ADDR ip =
m_socket->localAddress().toIPv6Address();
1072 "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\xff\xff",
1075 memcpy(&from[i], &ip[12], 4);
1080 memcpy(&from[i], &ip, 16);
1090 memset(&from[i], 0, pad);
1094 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1095 QString(
"Full base64 response: '%1' size %2")
1096 .arg(QByteArray((
char *)from.data(), i).toBase64().constData())
1100 if (
nullptr == pctx)
1102 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
1103 QString(
"Cannot create ENV_PKEY_CTX from key. (%1)")
1104 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
1106 else if (EVP_PKEY_sign_init(pctx) <= 0)
1108 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
1109 QString(
"EVP_PKEY_sign_init failed. (%1)")
1110 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
1112 else if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING) <= 0)
1114 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
1115 QString(
"Cannot set RSA_PKCS1_PADDING on context. (%1)")
1116 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
1118 else if (EVP_PKEY_sign(pctx, to.data(), &tosize,
1119 from.data(), i) <= 0)
1121 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
1122 QString(
"EVP_PKEY_sign failed. (%1)")
1123 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
1126 QByteArray base64 = QByteArray((
const char *)to.data(), tosize).toBase64();
1128 for (
int pos = base64.size() - 1; pos > 0; pos--)
1130 if (base64[pos] ==
'=')
1135 LOG(VB_PLAYBACK, LOG_DEBUG, QString(
"tSize=%1 tLen=%2 tResponse=%3")
1136 .arg(tosize).arg(base64.size()).arg(base64.constData()));
1140 QString responseData;
1141 if (option ==
"OPTIONS")
1143 *
m_textStream <<
"Public: ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, "
1144 "TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER, POST, GET\r\n";
1146 else if (option ==
"ANNOUNCE")
1149 for (
const QString& line : qAsConst(lines))
1151 if (line.startsWith(
"a=rsaaeskey:"))
1153 QString key = line.mid(12).trimmed();
1154 QByteArray decodedkey = QByteArray::fromBase64(key.toLatin1());
1155 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1156 QString(
"RSAAESKey: %1 (base64 decoded size %2)")
1157 .arg(key).arg(decodedkey.size()));
1165 size_t size_out {size};
1166 if (
nullptr == pctx)
1168 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
1169 QString(
"Cannot create ENV_PKEY_CTX from key. (%1)")
1170 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
1172 else if (EVP_PKEY_decrypt_init(pctx) <= 0)
1174 LOG(VB_PLAYBACK, LOG_WARNING,
LOC + QString(
"EVP_PKEY_decrypt_init failed. (%1)")
1175 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
1177 else if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_OAEP_PADDING) <= 0)
1179 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
1180 QString(
"Cannot set RSA_PKCS1_OAEP_PADDING on context. (%1)")
1181 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
1183 else if (EVP_PKEY_decrypt(pctx,
m_sessionKey.data(), &size_out,
1184 (
const unsigned char *)decodedkey.constData(), decodedkey.size()) > 0)
1187 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1188 "Successfully decrypted AES key from RSA.");
1192 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
1193 QString(
"Failed to decrypt AES key from RSA. (%1)")
1194 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
1198 else if (line.startsWith(
"a=aesiv:"))
1200 QString aesiv = line.mid(8).trimmed();
1201 m_aesIV = QByteArray::fromBase64(aesiv.toLatin1());
1202 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1203 QString(
"AESIV: %1 (base64 decoded size %2)")
1204 .arg(aesiv).arg(
m_aesIV.size()));
1206 else if (line.startsWith(
"a=fmtp:"))
1209 QString format = line.mid(7).trimmed();
1210 QList<QString>
formats = format.split(
' ');
1211 for (
const QString& fmt : qAsConst(
formats))
1215 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1216 QString(
"Audio parameter: %1").arg(fmt));
1224 else if (option ==
"SETUP")
1226 if (tags.contains(
"Transport"))
1229 auto *dev = qobject_cast<MythRAOPDevice*>(parent());
1231 dev->DeleteAllClients(
this);
1235 int control_port = 0;
1236 int timing_port = 0;
1237 QString data = tags[
"Transport"];
1238 QStringList items = data.split(
";");
1239 bool events =
false;
1241 for (
const QString& item : qAsConst(items))
1243 if (item.startsWith(
"control_port"))
1244 control_port = item.mid(item.indexOf(
"=") + 1).trimmed().toUInt();
1245 else if (item.startsWith(
"timing_port"))
1246 timing_port = item.mid(item.indexOf(
"=") + 1).trimmed().toUInt();
1247 else if (item.startsWith(
"events"))
1251 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
1252 QString(
"Negotiated setup with client %1 on port %2")
1253 .arg(
m_socket->peerAddress().toString())
1255 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1256 QString(
"control port: %1 timing port: %2")
1257 .arg(control_port).arg(timing_port));
1269 int controlbind_port =
1272 if (controlbind_port < 0)
1274 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1275 QString(
"Failed to bind to client control port. "
1276 "Control of audio stream may fail"));
1280 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
1281 QString(
"Bound to client control port %1 on port %2")
1282 .arg(control_port).arg(controlbind_port));
1298 int timingbind_port =
1301 if (timingbind_port < 0)
1303 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1304 QString(
"Failed to bind to client timing port. "
1305 "Timing of audio stream will be incorrect"));
1309 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
1310 QString(
"Bound to client timing port %1 on port %2")
1311 .arg(timing_port).arg(timingbind_port));
1324 client->disconnect();
1342 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1343 "Failed to find a port for RAOP events server.");
1347 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
1348 QString(
"Listening for RAOP events on port %1").arg(
m_eventPort));
1363 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1364 QString(
"Audio hardware latency: %1ms")
1372 for (
const QString& item : qAsConst(items))
1378 if (item.startsWith(
"control_port"))
1380 newdata +=
"control_port=" + QString::number(controlbind_port);
1382 else if (item.startsWith(
"timing_port"))
1384 newdata +=
"timing_port=" + QString::number(timingbind_port);
1386 else if (item.startsWith(
"events"))
1388 newdata +=
"event_port=" + QString::number(
m_eventPort);
1400 newdata +=
"server_port=" + QString::number(
m_dataPort);
1411 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1412 "No Transport details found - Ignoring");
1415 else if (option ==
"RECORD")
1430 else if (option ==
"FLUSH")
1445 else if (option ==
"SET_PARAMETER")
1447 if (tags.contains(
"Content-Type"))
1449 if (tags[
"Content-Type"] ==
"text/parameters")
1454 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1455 QString(
"text/parameters: name=%1 parem=%2")
1460 float volume = (param.toFloat() + 30.0F) * 100.0F / 30.0F;
1463 LOG(VB_PLAYBACK, LOG_INFO,
1464 LOC + QString(
"Setting volume to %1 (raw %3)")
1465 .arg(volume).arg(param));
1468 else if (name ==
"progress")
1470 QStringList items = param.split(
"/");
1471 if (items.size() == 3)
1482 LOG(VB_PLAYBACK, LOG_INFO,
1483 LOC +QString(
"Progress: %1/%2")
1489 else if(tags[
"Content-Type"] ==
"image/none")
1494 else if(tags[
"Content-Type"].startsWith(
"image/"))
1500 else if (tags[
"Content-Type"] ==
"application/x-dmap-tagged")
1504 LOG(VB_PLAYBACK, LOG_INFO,
1505 QString(
"Receiving Title:%1 Artist:%2 Album:%3 Format:%4")
1512 else if (option ==
"GET_PARAMETER")
1514 if (tags.contains(
"Content-Type"))
1516 if (tags[
"Content-Type"] ==
"text/parameters")
1520 for (
const QString& line : qAsConst(lines))
1522 if (line ==
"volume")
1526 responseData += QString(
"volume: %1\r\n")
1527 .arg(volume * 30.0F / 100.0F - 30.0F,1,
'f',6,
'0');
1533 else if (option ==
"TEARDOWN")
1540 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Command not handled: %1")
1552 *stream <<
"RTSP/1.0 401 Unauthorised\r\n";
1553 *stream <<
"Server: AirTunes/130.14\r\n";
1554 *stream <<
"WWW-Authenticate: Digest realm=\"raop\", ";
1555 *stream <<
"nonce=\"" +
m_nonce +
"\"\r\n";
1556 *stream <<
"CSeq: " << cseq <<
"\r\n";
1559 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1560 QString(
"Finished Authentication request %2, Send: %3")
1561 .arg(cseq).arg(socket->flush()));
1565 QString &option, QString &cseq, QString &responseData)
1569 *stream <<
"Server: AirTunes/130.14\r\n";
1570 *stream <<
"CSeq: " << cseq <<
"\r\n";
1571 if (!responseData.isEmpty())
1572 *stream <<
"Content-Length: " << QString::number(responseData.length()) <<
"\r\n\r\n" << responseData;
1576 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Finished %1 %2 , Send: %3")
1577 .arg(option, cseq, QString::number(socket->flush())));
1587 static QMutex s_lock;
1588 QMutexLocker locker(&s_lock);
1593 QString sName(
"/RAOPKey.rsa" );
1609 int type = EVP_PKEY_type(
id);
1610 if (
type != EVP_PKEY_RSA)
1619 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
"Loaded RSA private key");
1631 if (lines.isEmpty())
1634 for (
const QString& line : qAsConst(lines))
1636 int index = line.indexOf(
":");
1639 result.insert(line.left(index).trimmed(),
1640 line.mid(index + 1).trimmed());
1649 QTextStream stream(lines);
1654 line = stream.readLine();
1660 while (!line.isNull());
1674 QTime time = QTime(0,0).addSecs(timeInSeconds);
1675 return time.toString(time.hour() > 0 ?
"HH:mm:ss" :
"mm:ss");
1685 return std::chrono::milliseconds((frames * 1000ULL) /
m_frameRate);
1702 QMap<QString,QString> result;
1704 while (offset < dmap.size())
1706 QString tag = dmap.mid(offset, 4);
1708 uint32_t length = qFromBigEndian(*(uint32_t *)(dmap.constData() + offset));
1709 offset +=
sizeof(uint32_t);
1710 QString
content = QString::fromUtf8(dmap.mid(offset,
1711 length).constData());
1723 m_codec = avcodec_find_decoder(AV_CODEC_ID_ALAC);
1726 LOG(VB_PLAYBACK, LOG_ERR,
LOC
1727 +
"Failed to create ALAC decoder- going silent...");
1734 auto *extradata =
new unsigned char[36];
1735 memset(extradata, 0, 36);
1738 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1739 "Creating decoder but haven't seen audio format.");
1744 extradata[12] = (fs >> 24) & 0xff;
1745 extradata[13] = (fs >> 16) & 0xff;
1746 extradata[14] = (fs >> 8) & 0xff;
1747 extradata[15] = fs & 0xff;
1759 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1760 "Failed to open ALAC decoder - going silent...");
1764 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
"Opened ALAC decoder.");
1792 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1793 "Failed to open audio device. Going silent...");
1800 if (!
error.isEmpty())
1802 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1803 QString(
"Audio not initialised. Message was '%1'")
1811 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
"Opened audio device.");
1857 usleep(duration_cast<std::chrono::microseconds>(
AUDIOCARD_BUFFER).count());
1859 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"AudioCardLatency: ts=%1ms")
1860 .arg(audiots.count()));
1866 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
1867 QString(
"New connection from %1:%2 for RAOP events server.")
1868 .arg(client->peerAddress().toString()).arg(client->peerPort()));
1876 auto *client = qobject_cast<QTcpSocket *>(sender());
1879 if (client !=
nullptr)
1880 label = QString(
"%1:%2").arg(client->peerAddress().toString()).arg(client->peerPort());
1882 label = QString(
"unknown");
1884 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1885 QString(
"%1 disconnected from RAOP events server.").arg(label));
1891 auto duration = std::chrono::seconds(
1901 image,
m_dmap, duration, position);
1906 duration, position);