11 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
12 #include <QStringConverter>
32 #if OPENSSL_VERSION_NUMBER < 0x030000000L
33 #define EVP_PKEY_get_id EVP_PKEY_id
34 #define EVP_PKEY_get_size EVP_PKEY_size
37 #define LOC QString("RAOP Conn: ")
46 static constexpr uint8_t
SYNC { 0x54 };
68 LOG(VB_PLAYBACK, LOG_DEBUG,
69 LOC + QString(
"Sending(%1): ").arg(str.length()) + str.trimmed());
70 QTextStream *q =
this;
77 QByteArray
id,
int port)
80 m_hardwareId(std::move(id)),
82 m_cctx(EVP_CIPHER_CTX_new()),
85 #if OPENSSL_VERSION_NUMBER < 0x030000000L
89 m_cipher = EVP_CIPHER_fetch(
nullptr,
"AES-128-CBC",
nullptr);
100 client->deleteLater();
131 nc->UnRegister(
this,
m_id);
134 EVP_CIPHER_CTX_free(
m_cctx);
136 #if OPENSSL_VERSION_NUMBER >= 0x030000000L
207 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
214 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
"Failed to connect client socket signal.");
223 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
"Failed to connect data socket signal.");
231 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
"Failed to bind to a port for data.");
235 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
236 QString(
"Bound to port %1 for incoming data").arg(
m_dataPort));
272 uint64_t firstframe = 0;
276 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
277 QString(
"Packet doesn't start with valid Rtp Header (0x%1)")
278 .arg((uint8_t)buf[0], 0, 16));
308 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
309 QString(
"Packet type (0x%1) not handled")
317 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
318 QString(
"Received packet %1 too late, ignoring")
341 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
342 QString(
"Received required resend %1 (with ts:%2 last:%3)")
348 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
349 QString(
"Received unexpected resent packet %1")
356 auto *decoded =
new QList<AudioData>();
361 LOG(VB_PLAYBACK, LOG_ERR,
LOC + QString(
"Error decoding audio"));
373 bool first = (uint8_t)buf[0] == 0x90;
374 const char *req = buf.constData();
375 uint64_t current_ts = qFromBigEndian(*(uint32_t *)(req + 4));
376 uint64_t next_ts = qFromBigEndian(*(uint32_t *)(req + 16));
388 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Receiving %1SYNC packet")
389 .arg(first ?
"first " :
""));
393 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"SYNC: cur:%1 next:%2 time:%3")
399 std::chrono::milliseconds currentLatency = 0ms;
406 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
407 QString(
"RAOP timestamps: about to play:%1 desired:%2 latency:%3")
409 .arg(currentLatency.count()));
412 delay += currentLatency;
414 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
415 QString(
"Queue=%1 buffer=%2ms ideal=%3ms diffts:%4ms")
429 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
430 QString(
"Too much delay (%1ms), adjusting")
440 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Drop %1 packets").arg(res));
457 int16_t missed = (got < expected) ?
458 (int16_t)(((int32_t)got + UINT16_MAX + 1) - expected) :
461 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
462 QString(
"Missed %1 packet(s): expected %2 got %3 ts:%4")
463 .arg(missed).arg(expected).arg(got).arg(timestamp.count()));
467 *(
uint16_t *)(&req[4]) = qToBigEndian(expected);
468 *(
uint16_t *)(&req[6]) = qToBigEndian(missed);
472 == (qint64)req.size())
474 for (
uint16_t count = 0; count < missed; count++)
476 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Sent resend for %1")
477 .arg(expected + count));
478 m_resends.insert(expected + count, timestamp);
483 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
"Failed to send resend request.");
497 QMutableMapIterator<uint16_t,std::chrono::milliseconds> it(
m_resends);
503 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
504 QString(
"Never received resend packet %1").arg(it.key()));
520 uint32_t ntpTicks {0};
521 auto usecs = nowAsDuration<std::chrono::microseconds>();
524 std::array<uint8_t,32> req {
530 *(uint32_t *)(&req[24]) = qToBigEndian(ntpSec);
531 *(uint32_t *)(&req[28]) = qToBigEndian(ntpTicks);
536 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
"Failed to send resend time request.");
539 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
540 QString(
"Requesting master time (Local %1.%2)")
541 .arg(ntpSec,8,16,QChar(
'0')).arg(ntpTicks,8,16,QChar(
'0')));
552 const char *req = buf.constData();
554 uint32_t ntpSec = qFromBigEndian(*(uint32_t *)(req + 8));
555 uint32_t ntpTicks = qFromBigEndian(*(uint32_t *)(req + 12));
557 auto time2 = nowAsDuration<std::chrono::milliseconds>();
558 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Read back time (Local %1.%2)")
559 .arg(ntpSec,8,16,QChar(
'0')).arg(ntpTicks,8,16,QChar(
'0')));
563 LOG(VB_AUDIO, LOG_DEBUG,
LOC + QString(
"Network Latency: %1ms")
568 uint32_t sec = qFromBigEndian(*(uint32_t *)(req + 24));
569 uint32_t ticks = qFromBigEndian(*(uint32_t *)(req + 28));
570 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Source NTP clock time %1.%2")
571 .arg(sec,8,16,QChar(
'0')).arg(ticks,8,16,QChar(
'0')));
574 std::chrono::milliseconds master =
NTPToLocal(sec, ticks);
590 return std::chrono::milliseconds((((int64_t)sec -
CLOCK_EPOCH) * 1000LL) +
591 (((int64_t)ticks * 1000LL) >> 32));
594 uint32_t &ntpSec, uint32_t &ntpTicks)
596 ntpSec = duration_cast<std::chrono::seconds>(usec).count() +
CLOCK_EPOCH;
597 ntpTicks = ((usec % 1s).count() << 32) / 1000000;
601 auto micros = durationFromTimeval<std::chrono::microseconds>(
t);
619 if ((uint8_t)buf[0] != 0x80 && (uint8_t)buf[0] != 0x90)
638 const char *ptr = buf.constData();
643 seq = qFromBigEndian(*(
uint16_t *)(ptr + 2));
644 timestamp = qFromBigEndian(*(uint32_t *)(ptr + 4));
651 const QByteArray *buf,
652 QList<AudioData> *
dest)
654 const char *data_in = buf->constData();
655 int len = buf->size();
666 int aeslen = len & ~0xf;
669 std::array<uint8_t,MAX_PACKET_SIZE> decrypted_data {};
670 EVP_CIPHER_CTX_reset(
m_cctx);
671 EVP_CIPHER_CTX_set_padding(
m_cctx, 0);
673 #
if OPENSSL_VERSION_NUMBER < 0x030000000L
676 reinterpret_cast<const uint8_t *
>(
m_aesIV.data()))
679 reinterpret_cast<const uint8_t *
>(
m_aesIV.data()),
684 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
685 QString(
"EVP_DecryptInit_ex failed. (%1)")
686 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
688 else if (EVP_DecryptUpdate(
m_cctx, decrypted_data.data(), &outlen1,
689 reinterpret_cast<const uint8_t *
>(data_in),
692 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
693 QString(
"EVP_DecryptUpdate failed. (%1)")
694 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
696 else if (EVP_DecryptFinal_ex(
m_cctx, decrypted_data.data() + outlen1, &outlen2) != 1)
698 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
699 QString(
"EVP_DecryptFinal_ex failed. (%1)")
700 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
702 std::copy(data_in + aeslen, data_in + len,
703 decrypted_data.data() + aeslen);
706 AVPacket *tmp_pkt = av_packet_alloc();
707 if (tmp_pkt ==
nullptr)
709 tmp_pkt->data = decrypted_data.data();
712 uint32_t frames_added = 0;
714 while (tmp_pkt->size > 0)
727 int num_samples = data_size /
728 (ctx->ch_layout.nb_channels * av_get_bytes_per_sample(ctx->sample_fmt));
730 frames_added += num_samples;
734 tmp_pkt->data += ret;
735 tmp_pkt->size -= ret;
737 av_packet_free(&tmp_pkt);
752 auto dtime = nowAsDuration<std::chrono::milliseconds>() -
m_timeLastSync;
773 std::chrono::milliseconds timestamp = 0ms;
775 QMapIterator<std::chrono::milliseconds,AudioPacket> packet_it(
m_audioQueue);
776 while (packet_it.hasNext() && i <= max_packets)
780 timestamp = packet_it.key();
791 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
792 QString(
"Audio discontinuity seen. Played %1 (%3) expected %2")
798 for (
const auto & data : std::as_const(*frames.
data))
808 offset = std::min(offset, data.length);
811 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
812 QString(
"ProcessAudio: Dropping %1 frames to catch up "
818 data.length - offset,
819 std::chrono::milliseconds(timestamp), framecnt);
843 QMutableMapIterator<std::chrono::milliseconds,AudioPacket> packet_it(
m_audioQueue);
844 while (packet_it.hasNext())
847 if (packet_it.key() < timestamp)
852 for (
const auto & data : std::as_const(*frames.
data))
876 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Closing connection after inactivity.");
899 auto *socket = qobject_cast<QTcpSocket *>(sender());
903 QByteArray data = socket->readAll();
906 QByteArray printable = data.left(32);
907 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"readClient(%1): %2%3")
909 .arg(printable.toHex().toUpper().data(),
910 data.size() > 32 ?
"..." :
""));
919 QTextStream stream(data);
923 line = stream.readLine();
924 if (line.size() == 0)
926 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Header(%1) = %2")
927 .arg(
m_socket->peerAddress().toString(), line));
929 if (line.contains(
"Content-Length:"))
934 while (!line.isNull());
941 int pos = stream.pos();
961 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Content(%1) = %2")
970 if (header.isEmpty())
975 if (!tags.contains(
"CSeq"))
977 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
"ProcessRequest: Didn't find CSeq");
981 QString option = header[0].left(header[0].indexOf(
" "));
986 uint64_t RTPtimestamp = 0;
987 if (tags.contains(
"RTP-Info"))
990 QString data = tags[
"RTP-Info"];
991 QStringList items = data.split(
";");
992 for (
const QString& item : std::as_const(items))
994 if (item.startsWith(
"seq"))
996 RTPseq = item.mid(item.indexOf(
"=") + 1).trimmed().toUShort();
998 else if (item.startsWith(
"rtptime"))
1000 RTPtimestamp = item.mid(item.indexOf(
"=") + 1).trimmed().toUInt();
1003 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"RTP-Info: seq=%1 rtptime=%2")
1004 .arg(RTPseq).arg(RTPtimestamp));
1013 if (!tags.contains(
"Authorization"))
1026 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"RAOP client authenticated");
1030 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"RAOP authentication failed");
1037 if (tags.contains(
"Apple-Challenge"))
1039 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Received Apple-Challenge"));
1045 std::vector<uint8_t>to;
1048 QByteArray challenge =
1049 QByteArray::fromBase64(tags[
"Apple-Challenge"].toLatin1());
1050 int challenge_size = challenge.size();
1051 if (challenge_size != 16)
1053 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1054 QString(
"Base64 decoded challenge size %1, expected 16")
1055 .arg(challenge_size));
1056 challenge_size = std::min(challenge_size, 16);
1060 std::array<uint8_t,38> from {};
1061 std::copy(challenge.cbegin(), challenge.cbegin() + challenge_size,
1063 i += challenge_size;
1064 if (
m_socket->localAddress().protocol() ==
1065 QAbstractSocket::IPv4Protocol)
1067 uint32_t ip =
m_socket->localAddress().toIPv4Address();
1068 ip = qToBigEndian(ip);
1069 memcpy(&from[i], &ip, 4);
1072 else if (
m_socket->localAddress().protocol() ==
1073 QAbstractSocket::IPv6Protocol)
1075 Q_IPV6ADDR ip =
m_socket->localAddress().toIPv6Address();
1077 "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\xff\xff",
1080 memcpy(&from[i], &ip[12], 4);
1085 memcpy(&from[i], &ip, 16);
1096 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1097 QString(
"Hardware MAC address size %1, expected %2")
1104 memset(&from[i], 0, pad);
1108 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1109 QString(
"Full base64 response: '%1' size %2")
1110 .arg(QByteArray((
char *)from.data(), i).toBase64().constData())
1114 if (
nullptr == pctx)
1116 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
1117 QString(
"Cannot create ENV_PKEY_CTX from key. (%1)")
1118 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
1120 else if (EVP_PKEY_sign_init(pctx) <= 0)
1122 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
1123 QString(
"EVP_PKEY_sign_init failed. (%1)")
1124 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
1126 else if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING) <= 0)
1128 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
1129 QString(
"Cannot set RSA_PKCS1_PADDING on context. (%1)")
1130 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
1132 else if (EVP_PKEY_sign(pctx, to.data(), &tosize,
1133 from.data(), i) <= 0)
1135 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
1136 QString(
"EVP_PKEY_sign failed. (%1)")
1137 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
1140 QByteArray base64 = QByteArray((
const char *)to.data(), tosize).toBase64();
1142 for (
int pos = base64.size() - 1; pos > 0; pos--)
1144 if (base64[pos] ==
'=')
1149 LOG(VB_PLAYBACK, LOG_DEBUG, QString(
"tSize=%1 tLen=%2 tResponse=%3")
1150 .arg(tosize).arg(base64.size()).arg(base64.constData()));
1154 QString responseData;
1155 if (option ==
"OPTIONS")
1157 *
m_textStream <<
"Public: ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, "
1158 "TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER, POST, GET\r\n";
1160 else if (option ==
"ANNOUNCE")
1163 for (
const QString& line : std::as_const(lines))
1165 if (line.startsWith(
"a=rsaaeskey:"))
1167 QString key = line.mid(12).trimmed();
1168 QByteArray decodedkey = QByteArray::fromBase64(key.toLatin1());
1169 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1170 QString(
"RSAAESKey: %1 (base64 decoded size %2)")
1171 .arg(key).arg(decodedkey.size()));
1179 size_t size_out {size};
1180 if (
nullptr == pctx)
1182 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
1183 QString(
"Cannot create ENV_PKEY_CTX from key. (%1)")
1184 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
1186 else if (EVP_PKEY_decrypt_init(pctx) <= 0)
1188 LOG(VB_PLAYBACK, LOG_WARNING,
LOC + QString(
"EVP_PKEY_decrypt_init failed. (%1)")
1189 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
1191 else if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_OAEP_PADDING) <= 0)
1193 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
1194 QString(
"Cannot set RSA_PKCS1_OAEP_PADDING on context. (%1)")
1195 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
1197 else if (EVP_PKEY_decrypt(pctx,
m_sessionKey.data(), &size_out,
1198 (
const unsigned char *)decodedkey.constData(), decodedkey.size()) > 0)
1201 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1202 "Successfully decrypted AES key from RSA.");
1206 LOG(VB_PLAYBACK, LOG_WARNING,
LOC +
1207 QString(
"Failed to decrypt AES key from RSA. (%1)")
1208 .arg(ERR_error_string(ERR_get_error(),
nullptr)));
1212 else if (line.startsWith(
"a=aesiv:"))
1214 QString aesiv = line.mid(8).trimmed();
1215 m_aesIV = QByteArray::fromBase64(aesiv.toLatin1());
1216 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1217 QString(
"AESIV: %1 (base64 decoded size %2)")
1218 .arg(aesiv).arg(
m_aesIV.size()));
1220 else if (line.startsWith(
"a=fmtp:"))
1223 QString format = line.mid(7).trimmed();
1224 QList<QString>
formats = format.split(
' ');
1225 for (
const QString& fmt : std::as_const(
formats))
1229 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1230 QString(
"Audio parameter: %1").arg(fmt));
1238 else if (option ==
"SETUP")
1240 if (tags.contains(
"Transport"))
1243 auto *dev = qobject_cast<MythRAOPDevice*>(parent());
1245 dev->DeleteAllClients(
this);
1249 int control_port = 0;
1250 int timing_port = 0;
1251 QString data = tags[
"Transport"];
1252 QStringList items = data.split(
";");
1253 bool events =
false;
1255 for (
const QString& item : std::as_const(items))
1257 if (item.startsWith(
"control_port"))
1258 control_port = item.mid(item.indexOf(
"=") + 1).trimmed().toUInt();
1259 else if (item.startsWith(
"timing_port"))
1260 timing_port = item.mid(item.indexOf(
"=") + 1).trimmed().toUInt();
1261 else if (item.startsWith(
"events"))
1265 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
1266 QString(
"Negotiated setup with client %1 on port %2")
1267 .arg(
m_socket->peerAddress().toString())
1269 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1270 QString(
"control port: %1 timing port: %2")
1271 .arg(control_port).arg(timing_port));
1283 int controlbind_port =
1286 if (controlbind_port < 0)
1288 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1289 QString(
"Failed to bind to client control port. "
1290 "Control of audio stream may fail"));
1294 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
1295 QString(
"Bound to client control port %1 on port %2")
1296 .arg(control_port).arg(controlbind_port));
1312 int timingbind_port =
1315 if (timingbind_port < 0)
1317 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1318 QString(
"Failed to bind to client timing port. "
1319 "Timing of audio stream will be incorrect"));
1323 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
1324 QString(
"Bound to client timing port %1 on port %2")
1325 .arg(timing_port).arg(timingbind_port));
1338 client->disconnect();
1356 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1357 "Failed to find a port for RAOP events server.");
1361 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
1362 QString(
"Listening for RAOP events on port %1").arg(
m_eventPort));
1377 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1378 QString(
"Audio hardware latency: %1ms")
1386 for (
const QString& item : std::as_const(items))
1392 if (item.startsWith(
"control_port"))
1394 newdata +=
"control_port=" + QString::number(controlbind_port);
1396 else if (item.startsWith(
"timing_port"))
1398 newdata +=
"timing_port=" + QString::number(timingbind_port);
1400 else if (item.startsWith(
"events"))
1402 newdata +=
"event_port=" + QString::number(
m_eventPort);
1414 newdata +=
"server_port=" + QString::number(
m_dataPort);
1425 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1426 "No Transport details found - Ignoring");
1429 else if (option ==
"RECORD")
1444 else if (option ==
"FLUSH")
1459 else if (option ==
"SET_PARAMETER")
1461 if (tags.contains(
"Content-Type"))
1463 if (tags[
"Content-Type"] ==
"text/parameters")
1468 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1469 QString(
"text/parameters: name=%1 parem=%2")
1474 float volume = (param.toFloat() + 30.0F) * 100.0F / 30.0F;
1477 LOG(VB_PLAYBACK, LOG_INFO,
1478 LOC + QString(
"Setting volume to %1 (raw %3)")
1479 .arg(volume).arg(param));
1482 else if (name ==
"progress")
1484 QStringList items = param.split(
"/");
1485 if (items.size() == 3)
1496 LOG(VB_PLAYBACK, LOG_INFO,
1497 LOC +QString(
"Progress: %1/%2")
1503 else if(tags[
"Content-Type"] ==
"image/none")
1508 else if(tags[
"Content-Type"].startsWith(
"image/"))
1514 else if (tags[
"Content-Type"] ==
"application/x-dmap-tagged")
1518 LOG(VB_PLAYBACK, LOG_INFO,
1519 QString(
"Receiving Title:%1 Artist:%2 Album:%3 Format:%4")
1526 else if (option ==
"GET_PARAMETER")
1528 if (tags.contains(
"Content-Type"))
1530 if (tags[
"Content-Type"] ==
"text/parameters")
1534 for (
const QString& line : std::as_const(lines))
1536 if (line ==
"volume")
1540 responseData += QString(
"volume: %1\r\n")
1541 .arg((volume * 30.0F / 100.0F) - 30.0F,1,
'f',6,
'0');
1547 else if (option ==
"TEARDOWN")
1554 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Command not handled: %1")
1566 *stream <<
"RTSP/1.0 401 Unauthorised\r\n";
1567 *stream <<
"Server: AirTunes/130.14\r\n";
1568 *stream <<
"WWW-Authenticate: Digest realm=\"raop\", ";
1569 *stream <<
"nonce=\"" +
m_nonce +
"\"\r\n";
1570 *stream <<
"CSeq: " << cseq <<
"\r\n";
1573 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1574 QString(
"Finished Authentication request %2, Send: %3")
1575 .arg(cseq).arg(socket->flush()));
1579 QString &option, QString &cseq, QString &responseData)
1583 *stream <<
"Server: AirTunes/130.14\r\n";
1584 *stream <<
"CSeq: " << cseq <<
"\r\n";
1585 if (!responseData.isEmpty())
1586 *stream <<
"Content-Length: " << QString::number(responseData.length()) <<
"\r\n\r\n" << responseData;
1590 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"Finished %1 %2 , Send: %3")
1591 .arg(option, cseq, QString::number(socket->flush())));
1601 static QMutex s_lock;
1602 QMutexLocker locker(&s_lock);
1607 QString sName(
"/RAOPKey.rsa" );
1623 int type = EVP_PKEY_type(
id);
1624 if (
type != EVP_PKEY_RSA)
1633 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
"Loaded RSA private key");
1645 if (lines.isEmpty())
1648 for (
const QString& line : std::as_const(lines))
1650 int index = line.indexOf(
":");
1653 result.insert(line.left(index).trimmed(),
1654 line.mid(index + 1).trimmed());
1663 QTextStream stream(lines);
1668 line = stream.readLine();
1674 while (!line.isNull());
1688 QTime time = QTime(0,0).addSecs(timeInSeconds);
1689 return time.toString(time.hour() > 0 ?
"HH:mm:ss" :
"mm:ss");
1699 return std::chrono::milliseconds((frames * 1000ULL) /
m_frameRate);
1716 QMap<QString,QString> result;
1718 while (offset < dmap.size())
1720 QString tag = dmap.mid(offset, 4);
1722 uint32_t length = qFromBigEndian(*(uint32_t *)(dmap.constData() + offset));
1723 offset +=
sizeof(uint32_t);
1724 QString
content = QString::fromUtf8(dmap.mid(offset,
1725 length).constData());
1737 m_codec = avcodec_find_decoder(AV_CODEC_ID_ALAC);
1740 LOG(VB_PLAYBACK, LOG_ERR,
LOC
1741 +
"Failed to create ALAC decoder- going silent...");
1748 auto *extradata =
new unsigned char[36];
1749 memset(extradata, 0, 36);
1752 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1753 "Creating decoder but haven't seen audio format.");
1758 extradata[12] = (fs >> 24) & 0xff;
1759 extradata[13] = (fs >> 16) & 0xff;
1760 extradata[14] = (fs >> 8) & 0xff;
1761 extradata[15] = fs & 0xff;
1773 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1774 "Failed to open ALAC decoder - going silent...");
1778 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
"Opened ALAC decoder.");
1806 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1807 "Failed to open audio device. Going silent...");
1814 if (!
error.isEmpty())
1816 LOG(VB_PLAYBACK, LOG_ERR,
LOC +
1817 QString(
"Audio not initialised. Message was '%1'")
1825 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
"Opened audio device.");
1871 usleep(duration_cast<std::chrono::microseconds>(
AUDIOCARD_BUFFER).count());
1873 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"AudioCardLatency: ts=%1ms")
1874 .arg(audiots.count()));
1880 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
1881 QString(
"New connection from %1:%2 for RAOP events server.")
1882 .arg(client->peerAddress().toString()).arg(client->peerPort()));
1890 auto *client = qobject_cast<QTcpSocket *>(sender());
1893 if (client !=
nullptr)
1894 label = QString(
"%1:%2").arg(client->peerAddress().toString()).arg(client->peerPort());
1896 label = QString(
"unknown");
1898 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC +
1899 QString(
"%1 disconnected from RAOP events server.").arg(label));
1905 auto duration = std::chrono::seconds(
1915 image,
m_dmap, duration, position);
1920 duration, position);