7 #include <QRegularExpression>
20 #define LOC QString("CetonRTSP(%1): ").arg(m_requestUrl.toString())
48 const QString &method,
const QStringList* headers,
49 bool use_control,
bool waitforanswer,
const QString &alternative)
68 QAbstractSocket::ReadWrite);
69 bool ok =
m_socket->waitForConnected();
73 LOG(VB_GENERAL, LOG_ERR,
LOC +
74 QString(
"Could not connect to server %1:%2")
89 trash.resize(std::max((
uint)trash.size(), avail));
93 while (
m_socket->bytesAvailable() > 0);
96 QStringList requestHeaders;
97 requestHeaders.append(QString(
"%1 %2 RTSP/1.0")
99 !alternative.isEmpty() ? alternative :
101 requestHeaders.append(QString(
"User-Agent: MythTV Ceton Recorder"));
104 requestHeaders.append(QString(
"Session: %1").arg(
m_sessionId));
105 if (headers !=
nullptr)
107 for(
int i = 0; i < headers->count(); i++)
109 const QString& header = headers->at(i);
110 requestHeaders.append(header);
113 requestHeaders.append(QString(
"\r\n"));
114 QString request = requestHeaders.join(
"\r\n");
117 LOG(VB_RECORD, LOG_DEBUG,
LOC + QString(
"write: %1").arg(request));
118 m_socket->write(request.toLatin1());
126 static const QRegularExpression kFirstLineRE {
"^RTSP/1.0 (\\d+) ([^\r\n]+)" };
127 static const QRegularExpression kHeaderRE { R
"(^([^:]+):\s*([^\r\n]+))" };
128 static const QRegularExpression kBlankLineRE { R
"(^[\r\n]*$)" };
130 bool firstLine =
true;
135 bool ready =
m_socket->waitForReadyRead(30 * 1000);
138 LOG(VB_RECORD, LOG_ERR,
LOC +
"RTSP server did not respond after 30s");
144 QString line =
m_socket->readLine();
145 LOG(VB_RECORD, LOG_DEBUG,
LOC + QString(
"read: %1").arg(line));
147 QRegularExpressionMatch match;
150 match = kFirstLineRE.match(line);
151 if (!match.hasMatch())
155 QString(
"Could not parse first line of response: '%1'")
160 QStringList parts = match.capturedTexts();
167 QString(
"Server couldn't process the request: '%1'")
175 match = kBlankLineRE.match(line);
176 if (match.hasMatch())
break;
178 match = kHeaderRE.match(line);
179 if (!match.hasMatch())
186 QStringList parts = match.capturedTexts();
201 [](
const QString& key) ->
bool
202 {return key.compare(
"CSeq", Qt::CaseInsensitive) == 0;});
208 LOG(VB_RECORD, LOG_WARNING,
LOC +
209 QString(
"Expected CSeq of %1 but got %2")
215 if (contentLength > 0)
220 while (bytesRead < contentLength)
222 if (
m_socket->bytesAvailable() == 0)
225 int count =
m_socket->read(data+bytesRead, contentLength-bytesRead);
234 LOG(VB_RECORD, LOG_DEBUG,
LOC +
244 static const QRegularExpression kSeparatorRE {
",\\s*" };
259 QTextStream stream(lines);
264 line = stream.readLine();
270 while (!line.isNull());
290 for (
int i = 0; i < header.size(); i++)
292 QString entry = header[i].trimmed();
299 QStringList
args = entry.split(
"=");
301 parameters.insert(
args[0].trimmed(),
302 args.size() > 1 ?
args[1].trimmed() : QString());
327 headers.append(
"Accept: application/sdp");
337 for (
const QString& line : qAsConst(lines))
339 if (line.startsWith(
"m="))
346 if (!line.startsWith(
"m=video"))
351 QStringList
args = line.split(
" ");
352 if (
args[2] ==
"RTP/AVP" &&
args[3] ==
"33")
358 if (line.startsWith(
"c="))
364 if (line.startsWith(
"a=control:"))
369 QString url = line.mid(10).trimmed();
385 LOG(VB_RECORD, LOG_ERR,
LOC +
"expected content to be type "
386 "\"m=video 0 RTP/AVP 33\" but it appears not to be");
395 ushort &rtpPort, ushort &rtcpPort,
398 LOG(VB_GENERAL, LOG_INFO, QString(
"CetonRTSP: ") +
399 QString(
"Transport: RTP/AVP;unicast;client_port=%1-%2")
400 .arg(clientPort1).arg(clientPort2));
402 QStringList extraHeaders;
404 QString(
"Transport: RTP/AVP;unicast;client_port=%1-%2")
405 .arg(clientPort1).arg(clientPort2));
413 if (session.isEmpty())
415 LOG(VB_RECORD, LOG_ERR,
LOC +
416 "session id not found in SETUP response");
419 if (session.size() < 8)
421 LOG(VB_RECORD, LOG_WARNING,
LOC +
422 "invalid session id received");
426 if (params.contains(
"timeout"))
428 m_timeout = std::chrono::seconds(params[
"timeout"].toInt());
432 if (params.contains(
"ssrc"))
435 ssrc = params[
"ssrc"].toUInt(&ok, 16);
437 if (params.contains(
"server_port"))
439 QString line = params[
"server_port"];
440 QStringList val = line.split(
"-");
442 rtpPort = val[0].toInt();
443 rtcpPort = val.size() > 1 ? val[1].toInt() : 0;
477 LOG(VB_RECORD, LOG_DEBUG,
LOC +
478 QString(
"Start KeepAlive, every %1s").arg(
timeout.count()));
487 LOG(VB_RECORD, LOG_DEBUG,
LOC +
"Stop KeepAlive");
494 LOG(VB_RECORD, LOG_DEBUG,
LOC +
"Sending KeepAlive");