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;
98 if (!alternative.isEmpty())
100 else if (use_control)
104 requestHeaders.append(QString(
"%1 %2 RTSP/1.0").arg(method, uri));
105 requestHeaders.append(QString(
"User-Agent: MythTV Ceton Recorder"));
108 requestHeaders.append(QString(
"Session: %1").arg(
m_sessionId));
111 for(
int i = 0; i <
headers->count(); i++)
113 const QString& header =
headers->at(i);
114 requestHeaders.append(header);
117 requestHeaders.append(QString(
"\r\n"));
118 QString request = requestHeaders.join(
"\r\n");
121 LOG(VB_RECORD, LOG_DEBUG,
LOC + QString(
"write: %1").arg(request));
122 m_socket->write(request.toLatin1());
130 static const QRegularExpression kFirstLineRE {
"^RTSP/1.0 (\\d+) ([^\r\n]+)" };
131 static const QRegularExpression kHeaderRE { R
"(^([^:]+):\s*([^\r\n]+))" };
132 static const QRegularExpression kBlankLineRE { R
"(^[\r\n]*$)" };
134 bool firstLine =
true;
139 bool ready =
m_socket->waitForReadyRead(30 * 1000);
142 LOG(VB_RECORD, LOG_ERR,
LOC +
"RTSP server did not respond after 30s");
148 QString line =
m_socket->readLine();
149 LOG(VB_RECORD, LOG_DEBUG,
LOC + QString(
"read: %1").arg(line));
151 QRegularExpressionMatch match;
154 match = kFirstLineRE.match(line);
155 if (!match.hasMatch())
159 QString(
"Could not parse first line of response: '%1'")
164 QStringList parts = match.capturedTexts();
171 QString(
"Server couldn't process the request: '%1'")
179 match = kBlankLineRE.match(line);
180 if (match.hasMatch())
break;
182 match = kHeaderRE.match(line);
183 if (!match.hasMatch())
190 QStringList parts = match.capturedTexts();
205 [](
const QString& key) ->
bool
206 {return key.compare(
"CSeq", Qt::CaseInsensitive) == 0;});
212 LOG(VB_RECORD, LOG_WARNING,
LOC +
213 QString(
"Expected CSeq of %1 but got %2")
219 if (contentLength > 0)
224 while (bytesRead < contentLength)
226 if (
m_socket->bytesAvailable() == 0)
229 int count =
m_socket->read(data+bytesRead, contentLength-bytesRead);
238 LOG(VB_RECORD, LOG_DEBUG,
LOC +
248 static const QRegularExpression kSeparatorRE {
",\\s*" };
263 QTextStream stream(lines);
268 line = stream.readLine();
274 while (!line.isNull());
294 for (
int i = 0; i < header.size(); i++)
296 QString entry = header[i].trimmed();
303 QStringList
args = entry.split(
"=");
305 parameters.insert(
args[0].trimmed(),
306 args.size() > 1 ?
args[1].trimmed() : QString());
331 headers.append(
"Accept: application/sdp");
341 for (
const QString& line : std::as_const(lines))
343 if (line.startsWith(
"m="))
350 if (!line.startsWith(
"m=video"))
355 QStringList
args = line.split(
" ");
356 if (
args[2] ==
"RTP/AVP" &&
args[3] ==
"33")
362 if (line.startsWith(
"c="))
368 if (line.startsWith(
"a=control:"))
373 QString url = line.mid(10).trimmed();
389 LOG(VB_RECORD, LOG_ERR,
LOC +
"expected content to be type "
390 "\"m=video 0 RTP/AVP 33\" but it appears not to be");
399 ushort &rtpPort, ushort &rtcpPort,
402 LOG(VB_GENERAL, LOG_INFO, QString(
"CetonRTSP: ") +
403 QString(
"Transport: RTP/AVP;unicast;client_port=%1-%2")
404 .arg(clientPort1).arg(clientPort2));
406 QStringList extraHeaders;
408 QString(
"Transport: RTP/AVP;unicast;client_port=%1-%2")
409 .arg(clientPort1).arg(clientPort2));
417 if (session.isEmpty())
419 LOG(VB_RECORD, LOG_ERR,
LOC +
420 "session id not found in SETUP response");
423 if (session.size() < 8)
425 LOG(VB_RECORD, LOG_WARNING,
LOC +
426 "invalid session id received");
430 if (params.contains(
"timeout"))
432 m_timeout = std::chrono::seconds(params[
"timeout"].toInt());
436 if (params.contains(
"ssrc"))
439 ssrc = params[
"ssrc"].toUInt(&ok, 16);
441 if (params.contains(
"server_port"))
443 QString line = params[
"server_port"];
444 QStringList val = line.split(
"-");
446 rtpPort = val[0].toInt();
447 rtcpPort = val.size() > 1 ? val[1].toInt() : 0;
481 LOG(VB_RECORD, LOG_DEBUG,
LOC +
482 QString(
"Start KeepAlive, every %1s").arg(
timeout.count()));
491 LOG(VB_RECORD, LOG_DEBUG,
LOC +
"Stop KeepAlive");
498 LOG(VB_RECORD, LOG_DEBUG,
LOC +
"Sending KeepAlive");