7 #include <QRegularExpression>
26 #if QT_VERSION < QT_VERSION_CHECK(5,15,2)
27 #define capturedView capturedRef
30 #define LOC QString("SatIPRTSP[%1]: ").arg(m_inputId)
31 #define LOC2 QString("SatIPRTSP[%1](%2): ").arg(m_inputId).arg(m_requestUrl.toString())
53 QTcpSocket ctrl_socket;
56 bool ok = ctrl_socket.waitForConnected(10 * 1000);
59 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Could not connect to server %1:%2")
64 QStringList requestHeaders;
65 requestHeaders.append(QString(
"%1 %2 RTSP/1.0").arg(msg,
m_requestUrl.toString()));
66 requestHeaders.append(QString(
"User-Agent: MythTV Sat>IP client"));
67 requestHeaders.append(QString(
"CSeq: %1").arg(++
m_cseq));
71 requestHeaders.append(QString(
"Session: %1").arg(
m_sessionid));
74 if (additionalheaders !=
nullptr)
76 for (
auto& adhdr : *additionalheaders)
78 requestHeaders.append(adhdr);
82 requestHeaders.append(
"\r\n");
84 for (
const auto& requestLine : requestHeaders)
86 LOG(VB_RECORD, LOG_DEBUG,
LOC +
"sendMessage " +
87 QString(
"write: %1").arg(requestLine.simplified()));
90 QString request = requestHeaders.join(
"\r\n");
91 ctrl_socket.write(request.toLatin1());
95 static const QRegularExpression kFirstLineRE {
"^RTSP/1.0 (\\d+) ([^\r\n]+)" };
96 static const QRegularExpression kHeaderRE { R
"(^([^:]+):\s*([^\r\n]+))" };
97 static const QRegularExpression kBlankLineRE { R
"(^[\r\n]*$)" };
99 bool firstLine =
true;
102 if (!ctrl_socket.canReadLine())
104 bool ready = ctrl_socket.waitForReadyRead(10 * 1000);
107 LOG(VB_RECORD, LOG_ERR,
LOC +
"RTSP server did not respond after 10s");
113 QString line = ctrl_socket.readLine();
114 LOG(VB_RECORD, LOG_DEBUG,
LOC +
"sendMessage " +
115 QString(
"read: %1").arg(line.simplified()));
117 QRegularExpressionMatch match;
120 match = kFirstLineRE.match(line);
121 if (!match.hasMatch())
123 LOG(VB_RECORD, LOG_WARNING,
LOC +
124 QString(
"Could not parse first line of response: '%1'")
125 .arg(line.simplified()));
129 QStringList parts = match.capturedTexts();
130 int responseCode = parts.at(1).toInt();
131 const QString& responseMsg = parts.at(2);
133 LOG(VB_RECORD, LOG_DEBUG,
LOC + QString(
"response code:%1 message:%2")
134 .arg(responseCode).arg(responseMsg));
136 if (responseCode != 200)
138 LOG(VB_RECORD, LOG_WARNING,
LOC +
139 QString(
"Server couldn't process the request: '%1'")
147 match = kBlankLineRE.match(line);
148 if (match.hasMatch())
break;
150 match = kHeaderRE.match(line);
151 if (!match.hasMatch())
153 LOG(VB_RECORD, LOG_WARNING,
LOC +
154 QString(
"Could not parse response header: '%1'")
155 .arg(line.simplified()));
158 QStringList parts = match.capturedTexts();
169 if (cSeq != QString(
"%1").arg(
m_cseq))
171 LOG(VB_RECORD, LOG_WARNING,
LOC +
172 QString(
"Expected CSeq of %1 but got %2").arg(
m_cseq).arg(cSeq));
175 ctrl_socket.disconnectFromHost();
176 if (ctrl_socket.state() != QAbstractSocket::UnconnectedState)
178 ctrl_socket.waitForDisconnected();
187 LOG(VB_RECORD, LOG_DEBUG,
LOC2 + QString(
"SETUP"));
191 LOG(VB_RECORD, LOG_WARNING,
LOC +
192 QString(
"Port %1 is used but SatIP specifies RTSP port 554").arg(
m_requestUrl.port()));
197 LOG(VB_RECORD, LOG_ERR,
LOC +
198 QString(
"Invalid port %1 using 554 instead").arg(
m_requestUrl.port()));
205 QString(
"Transport: RTP/AVP;unicast;client_port=%1-%2")
206 .arg(clientPort1).arg(clientPort2));
210 LOG(VB_RECORD, LOG_ERR,
LOC +
"Failed to send SETUP message");
220 LOG(VB_RECORD, LOG_ERR,
LOC +
"SETUP response did not contain the com.ses.streamID field");
226 static const QRegularExpression sessionTimeoutRegex {
227 "^([^\\r\\n]+);timeout=([0-9]+)?", QRegularExpression::CaseInsensitiveOption };
229 if (!match.hasMatch())
231 LOG(VB_RECORD, LOG_ERR,
LOC +
232 QString(
"Failed to extract session id from session header ('%1')")
238 ? std::chrono::seconds(match.capturedView(2).toInt() / 2)
241 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"Sat>IP protocol timeout:%1 s")
246 LOG(VB_RECORD, LOG_ERR,
LOC +
"SETUP response did not contain the Session field");
250 LOG(VB_RECORD, LOG_DEBUG,
LOC +
251 QString(
"Setup completed, sessionID = %1, streamID = %2, timeout = %3s")
253 .arg(duration_cast<std::chrono::seconds>(
m_timeout).count()));
260 LOG(VB_RECORD, LOG_DEBUG,
LOC +
"Play(pids_str) " + pids_str);
267 LOG(VB_RECORD, LOG_ERR,
LOC +
"Failed to send PLAY message");
278 LOG(VB_RECORD, LOG_DEBUG,
LOC2 +
"TEARDOWN");
288 LOG(VB_RECORD, LOG_ERR,
LOC +
"Teardown failed");
305 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"StartKeepAliveRequested(%1) m_timer:%2")
311 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"StopKeepAliveRequested() m_timer:%1").arg(
m_timer));
321 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"Sending KeepAlive timer %1").arg(
timerEvent->timerId()));