7 #include <QRegularExpression>
25 #define LOC QString("SatIPRTSP[%1]: ").arg(m_streamHandler->m_inputId)
26 #define LOC2 QString("SatIPRTSP[%1](%2): ").arg(m_streamHandler->m_inputId).arg(m_requestUrl.toString())
34 : m_streamHandler(handler)
47 QAbstractSocket::DefaultForPlatform))
49 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to bind RTP socket"));
54 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"RTP socket bound to port %1 (0x%2)")
55 .
arg(port).
arg(port,2,16,QChar(
'0')));
64 QAbstractSocket::DefaultForPlatform))
66 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to bind RTCP socket to port %1").
arg(port));
70 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"RTCP socket bound to port %1 (0x%2)")
71 .
arg(port).
arg(port,2,16,QChar(
'0')));
75 uint desiredsize = 8000000;
77 if (newsize >= desiredsize)
79 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"RTP UDP socket receive buffer size set to %1").
arg(newsize));
83 LOG(VB_GENERAL, LOG_INFO,
LOC +
"RTP UDP socket receive buffer too small\n" +
84 QString(
"\tRTP UDP socket receive buffer size set to %1 but requested %2\n").
arg(newsize).
arg(desiredsize) +
85 QString(
"\tTo prevent UDP packet loss increase net.core.rmem_max e.g. with this command:\n") +
86 QString(
"\tsudo sysctl -w net.core.rmem_max=%1\n").
arg(desiredsize) +
87 QString(
"\tand restart mythbackend."));
102 LOG(VB_RECORD, LOG_DEBUG,
LOC2 + QString(
"SETUP"));
104 if (url.port() != 554)
106 LOG(VB_RECORD, LOG_WARNING,
LOC +
"SatIP specifies RTSP port 554 to be used");
111 LOG(VB_RECORD, LOG_ERR,
LOC + QString(
"Invalid port %1").
arg(url.port()));
117 QString(
"Transport: RTP/AVP;unicast;client_port=%1-%2")
122 LOG(VB_RECORD, LOG_ERR,
LOC +
"Failed to send SETUP message");
126 if (
m_headers.contains(
"COM.SES.STREAMID"))
132 LOG(VB_RECORD, LOG_ERR,
LOC +
"SETUP response did not contain the com.ses.streamID field");
136 QRegExp sessionTimeoutRegex(
137 "^([^\\r\\n]+);timeout=([0-9]+)?", Qt::CaseSensitive, QRegExp::RegExp2);
141 if (sessionTimeoutRegex.indexIn(
m_headers[
"SESSION"]) == -1)
143 LOG(VB_RECORD, LOG_ERR,
LOC +
144 QString(
"Failed to extract session id from session header ('%1')")
148 QStringList parts = sessionTimeoutRegex.capturedTexts();
150 m_timeout = (parts.length() > 1 ? parts.at(2).toInt() / 2 : 30) * 1000;
152 LOG(VB_RECORD, LOG_DEBUG,
LOC + QString(
"Sat>IP protocol timeout:%1 ms").
arg(
m_timeout));
156 LOG(VB_RECORD, LOG_ERR,
LOC +
"SETUP response did not contain the Session field");
160 LOG(VB_RECORD, LOG_DEBUG,
LOC +
161 QString(
"Setup completed, sessionID = %1, streamID = %2, timeout = %3s")
174 LOG(VB_RECORD, LOG_DEBUG,
LOC2 +
"PLAY");
180 QString pids_str = QString(
"pids=%1").arg(!pids.empty() ? pids.join(
",") :
"none");
181 LOG(VB_RECORD, LOG_INFO,
LOC +
"Play(pids) " + pids_str);
184 if (pids.size() > 32)
186 LOG(VB_RECORD, LOG_INFO,
LOC +
187 QString(
"Receive full TS, number of PIDs:%1 is more than 32").
arg(pids.size()));
188 LOG(VB_RECORD, LOG_DEBUG,
LOC + pids_str);
189 pids_str = QString(
"pids=all");
191 url.setQuery(pids_str);
192 LOG(VB_RECORD, LOG_DEBUG,
LOC + pids_str);
196 LOG(VB_RECORD, LOG_ERR,
LOC +
"Failed to send PLAY message");
208 LOG(VB_RECORD, LOG_DEBUG,
LOC2 +
"TEARDOWN");
212 url.setQuery(QString());
219 LOG(VB_RECORD, LOG_ERR,
LOC +
"Teardown failed");
262 QTcpSocket ctrl_socket;
263 ctrl_socket.connectToHost(url.host(), url.port());
265 bool ok = ctrl_socket.waitForConnected(30 * 1000);
268 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Could not connect to server %1:%2").
arg(url.host()).arg(url.port()));
273 headers.append(QString(
"%1 %2 RTSP/1.0").
arg(msg).
arg(url.toString()));
274 headers.append(QString(
"User-Agent: MythTV Sat>IP client"));
275 headers.append(QString(
"CSeq: %1").
arg(++
m_cseq));
282 if (additionalheaders !=
nullptr)
284 for (
auto& adhdr : *additionalheaders)
286 headers.append(adhdr);
290 headers.append(
"\r\n");
292 for (
const auto& requestLine : headers)
294 LOG(VB_RECORD, LOG_DEBUG,
LOC +
"sendMessage " +
295 QString(
"write: %1").
arg(requestLine.simplified()));
298 QString request = headers.join(
"\r\n");
299 ctrl_socket.write(request.toLatin1());
301 QRegularExpression firstLineRE {
"^RTSP/1.0 (\\d+) ([^\r\n]+)" };
302 QRegularExpression headerRE { R
"(^([^:]+):\s*([^\r\n]+))" };
303 QRegularExpression blankLineRE { R"(^[\r\n]*$)" };
305 bool firstLine =
true;
308 if (!ctrl_socket.canReadLine())
310 bool ready = ctrl_socket.waitForReadyRead(10 * 1000);
313 LOG(VB_RECORD, LOG_ERR,
LOC +
"RTSP server did not respond after 10s");
319 QString line = ctrl_socket.readLine();
320 LOG(VB_RECORD, LOG_DEBUG,
LOC +
"sendMessage " +
321 QString(
"read: %1").
arg(line.simplified()));
323 QRegularExpressionMatch match;
326 match = firstLineRE.match(line);
327 if (!match.hasMatch())
332 QStringList parts = match.capturedTexts();
333 int responseCode = parts.at(1).toInt();
334 const QString& responseMsg = parts.at(2);
336 LOG(VB_RECORD, LOG_DEBUG,
LOC + QString(
"response code:%1 message:%2")
337 .
arg(responseCode).
arg(responseMsg));
339 if (responseCode != 200)
347 match = blankLineRE.match(line);
348 if (match.hasMatch())
break;
350 match = headerRE.match(line);
351 if (!match.hasMatch())
355 QStringList parts = match.capturedTexts();
356 m_headers.insert(parts.at(1).toUpper(), parts.at(2));
368 LOG(VB_RECORD, LOG_WARNING,
LOC + QString(
"Expected CSeq of %1 but got %2").
arg(
m_cseq).
arg(cSeq));
371 ctrl_socket.disconnectFromHost();
372 if (ctrl_socket.state() != QAbstractSocket::UnconnectedState)
374 ctrl_socket.waitForDisconnected();
390 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"stopKeepAliveRequested() m_timer:%1").
arg(
m_timer));
401 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"Sending KeepAlive timer %1").
arg(
timerEvent->timerId()));
415 #define LOC_RH QString("SatIPRTSP[%1]: ").arg(m_parent->m_streamHandler->m_inputId)
419 , m_socket(new QUdpSocket(this))
423 QString(
"Starting read helper for UDP (RTP) socket"));
436 while (
m_socket->hasPendingDatagrams())
439 quint16 senderPort = 0;
443 data.resize(
m_socket->pendingDatagramSize());
444 m_socket->readDatagram(data.data(), data.size(), &sender, &senderPort);
454 #define LOC_RTCP QString("SatIPRTCP[%1]: ").arg(m_parent->m_streamHandler->m_inputId)
458 , m_socket(new QUdpSocket(this))
462 QString(
"Starting read helper for UDP (RTCP) socket"));
476 while (
m_socket->hasPendingDatagrams())
479 LOG(VB_RECORD, LOG_INFO,
"SatIPRTSP_RH " +
480 QString(
"Processing RTCP packet(pendingDatagramSize:%1)")
485 quint16 senderPort = 0;
487 QByteArray buf = QByteArray(
m_socket->pendingDatagramSize(), Qt::Uninitialized);
488 m_socket->readDatagram(buf.data(), buf.size(), &sender, &senderPort);
493 LOG(VB_GENERAL, LOG_ERR,
LOC_RTCP +
"Invalid RTCP packet received");
497 QStringList data = pkt.
Data().split(
";");
501 while (!found && i < data.length())
503 const QString& item = data.at(i);
505 if (item.startsWith(
"tuner="))
508 QStringList tuner = item.split(
",");
510 if (tuner.length() > 3)
512 int level = tuner.at(1).toInt();
513 bool lock = tuner.at(2).toInt() != 0;
514 int quality = tuner.at(3).toInt();
517 QString(
"Tuner lock:%1 level:%2 quality:%3").
arg(lock).
arg(level).
arg(quality));
529 #define LOC_WH QString("SatIPRTSP[%1]: ").arg(m_streamHandler->m_inputId)
533 , m_streamHandler(handler)
558 ((exp_seq_num & 0xFFFF) != (seq_num & 0xFFFF)))
572 QString(
"RTP Sequence number mismatch %1!=%2, discarded %3 RTP packets")
573 .
arg(seq_num).
arg(exp_seq_num).
arg(discarded));
580 QString(
"Processing RTP packet(seq:%1 ts:%2, ts_data_size:%3) %4")
590 if (!streamDataList.isEmpty())
592 const unsigned char *data_buffer = ts_packet.
GetTSData();
595 for (
auto sit = streamDataList.cbegin(); sit != streamDataList.cend(); ++sit)
597 remainder = sit.key()->ProcessData(data_buffer, data_length);
607 QString(
"RTP data_length = %1 remainder = %2")
624 QVariant ss = socket->socketOption(QAbstractSocket::ReceiveBufferSizeSocketOption);
625 uint oldsize = ss.toUInt();
626 if (oldsize < rcvbuffersize*2)
628 socket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, rcvbuffersize);
630 ss = socket->socketOption(QAbstractSocket::ReceiveBufferSizeSocketOption);
631 uint newsize = ss.toUInt()/2;