7#include <QRegularExpression>
26#define LOC QString("SatIPRTSP[%1]: ").arg(m_inputId)
27#define LOC2 QString("SatIPRTSP[%1](%2): ").arg(m_inputId).arg(m_requestUrl.toString())
49 QTcpSocket ctrl_socket;
52 bool ok = ctrl_socket.waitForConnected(10 * 1000);
55 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Could not connect to server %1:%2")
60 QStringList requestHeaders;
61 requestHeaders.reserve(4 + (additionalheaders ? additionalheaders->size() : 0));
62 requestHeaders.append(QString(
"%1 %2 RTSP/1.0").arg(msg,
m_requestUrl.toString()));
63 requestHeaders.append(QString(
"User-Agent: MythTV Sat>IP client"));
64 requestHeaders.append(QString(
"CSeq: %1").arg(++
m_cseq));
68 requestHeaders.append(QString(
"Session: %1").arg(
m_sessionid));
71 if (additionalheaders !=
nullptr)
73 for (
auto& adhdr : *additionalheaders)
75 requestHeaders.append(adhdr);
79 requestHeaders.append(
"\r\n");
81 for (
const auto& requestLine : requestHeaders)
83 LOG(VB_RECORD, LOG_DEBUG,
LOC +
"sendMessage " +
84 QString(
"write: %1").arg(requestLine.simplified()));
87 QString request = requestHeaders.join(
"\r\n");
88 ctrl_socket.write(request.toLatin1());
92 static const QRegularExpression kFirstLineRE {
"^RTSP/1.0 (\\d+) ([^\r\n]+)" };
93 static const QRegularExpression kHeaderRE { R
"(^([^:]+):\s*([^\r\n]+))" };
94 static const QRegularExpression kBlankLineRE { R
"(^[\r\n]*$)" };
96 bool firstLine =
true;
99 if (!ctrl_socket.canReadLine())
101 bool ready = ctrl_socket.waitForReadyRead(10 * 1000);
104 LOG(VB_RECORD, LOG_ERR,
LOC +
"RTSP server did not respond after 10s");
110 QString line = ctrl_socket.readLine();
111 LOG(VB_RECORD, LOG_DEBUG,
LOC +
"sendMessage " +
112 QString(
"read: %1").arg(line.simplified()));
114 QRegularExpressionMatch match;
117 match = kFirstLineRE.match(line);
118 if (!match.hasMatch())
120 LOG(VB_RECORD, LOG_WARNING,
LOC +
121 QString(
"Could not parse first line of response: '%1'")
122 .arg(line.simplified()));
126 QStringList parts = match.capturedTexts();
127 int responseCode = parts.at(1).toInt();
128 const QString& responseMsg = parts.at(2);
130 LOG(VB_RECORD, LOG_DEBUG,
LOC + QString(
"response code:%1 message:%2")
131 .arg(responseCode).arg(responseMsg));
133 if (responseCode != 200)
135 LOG(VB_RECORD, LOG_WARNING,
LOC +
136 QString(
"Server couldn't process the request: '%1'")
144 match = kBlankLineRE.match(line);
145 if (match.hasMatch())
break;
147 match = kHeaderRE.match(line);
148 if (!match.hasMatch())
150 LOG(VB_RECORD, LOG_WARNING,
LOC +
151 QString(
"Could not parse response header: '%1'")
152 .arg(line.simplified()));
155 QStringList parts = match.capturedTexts();
166 if (cSeq != QString(
"%1").arg(
m_cseq))
168 LOG(VB_RECORD, LOG_WARNING,
LOC +
169 QString(
"Expected CSeq of %1 but got %2").arg(
m_cseq).arg(cSeq));
172 ctrl_socket.disconnectFromHost();
173 if (ctrl_socket.state() != QAbstractSocket::UnconnectedState)
175 ctrl_socket.waitForDisconnected();
184 LOG(VB_RECORD, LOG_DEBUG,
LOC2 + QString(
"SETUP"));
188 LOG(VB_RECORD, LOG_WARNING,
LOC +
189 QString(
"Port %1 is used but SatIP specifies RTSP port 554").arg(
m_requestUrl.port()));
194 LOG(VB_RECORD, LOG_ERR,
LOC +
195 QString(
"Invalid port %1 using 554 instead").arg(
m_requestUrl.port()));
202 QString(
"Transport: RTP/AVP;unicast;client_port=%1-%2")
203 .arg(clientPort1).arg(clientPort2));
207 LOG(VB_RECORD, LOG_ERR,
LOC +
"Failed to send SETUP message");
217 LOG(VB_RECORD, LOG_ERR,
LOC +
"SETUP response did not contain the com.ses.streamID field");
223 static const QRegularExpression sessionTimeoutRegex {
224 "^([^\\r\\n]+);timeout=([0-9]+)?", QRegularExpression::CaseInsensitiveOption };
226 if (!match.hasMatch())
228 LOG(VB_RECORD, LOG_ERR,
LOC +
229 QString(
"Failed to extract session id from session header ('%1')")
235 ? std::chrono::seconds(match.capturedView(2).toInt() / 2)
238 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"Sat>IP protocol timeout:%1 s")
243 LOG(VB_RECORD, LOG_ERR,
LOC +
"SETUP response did not contain the Session field");
247 LOG(VB_RECORD, LOG_DEBUG,
LOC +
248 QString(
"Setup completed, sessionID = %1, streamID = %2, timeout = %3s")
250 .arg(duration_cast<std::chrono::seconds>(
m_timeout).count()));
257 LOG(VB_RECORD, LOG_DEBUG,
LOC +
"Play(pids_str) " + pids_str);
264 LOG(VB_RECORD, LOG_ERR,
LOC +
"Failed to send PLAY message");
275 LOG(VB_RECORD, LOG_DEBUG,
LOC2 +
"TEARDOWN");
285 LOG(VB_RECORD, LOG_ERR,
LOC +
"Teardown failed");
302 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"StartKeepAliveRequested(%1) m_timer:%2")
308 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"StopKeepAliveRequested() m_timer:%1").arg(
m_timer));
318 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"Sending KeepAlive timer %1").arg(
timerEvent->timerId()));
326#include "moc_satiprtsp.cpp"
bool sendMessage(const QString &msg, QStringList *additionalHeaders=nullptr)
std::chrono::seconds m_timeout
bool Setup(const QUrl &url, ushort clientPort1, ushort clientPort2)
bool Play(const QString &pids_str)
void StopKeepAliveRequested(void)
static QMutex s_rtspMutex
void StartKeepAlive(void)
void StartKeepAliveRequested(void)
void timerEvent(QTimerEvent *timerEvent) override
QMap< QString, QString > m_responseHeaders
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
#define LOC
-*- Mode: c++ -*-