MythTV master
cetonstreamhandler.cpp
Go to the documentation of this file.
1
8// POSIX headers
9#include <fcntl.h>
10#include <unistd.h>
11#ifndef _WIN32
12#include <sys/select.h>
13#include <sys/ioctl.h>
14#endif
15
16// Qt headers
17#include <QCoreApplication>
18#include <QRegularExpression>
19#include <QUrl>
20#include <QUrlQuery>
21
22// MythTV headers
25
26#include "cardutil.h"
27#include "cetonchannel.h"
28#include "cetonstreamhandler.h"
29#include "mpeg/mpegstreamdata.h"
31
32#define LOC QString("CetonSH[%1](%2): ").arg(m_inputId).arg(m_device)
33
34QMap<QString,CetonStreamHandler*> CetonStreamHandler::s_handlers;
37QMap<QString, bool> CetonStreamHandler::s_infoQueried;
38
40 int inputid)
41{
42 QMutexLocker locker(&s_handlersLock);
43
44 QString devkey = devname.toUpper();
45
46 QMap<QString,CetonStreamHandler*>::iterator it = s_handlers.find(devkey);
47
48 if (it == s_handlers.end())
49 {
50 auto *newhandler = new CetonStreamHandler(devkey, inputid);
51 newhandler->Open();
52 s_handlers[devkey] = newhandler;
53 s_handlersRefCnt[devkey] = 1;
54
55 LOG(VB_RECORD, LOG_INFO,
56 QString("CetonSH[%1]: Creating new stream handler %2 for %3")
57 .arg(QString::number(inputid), devkey, devname));
58 }
59 else
60 {
61 s_handlersRefCnt[devkey]++;
62 uint rcount = s_handlersRefCnt[devkey];
63 LOG(VB_RECORD, LOG_INFO,
64 QString("CetonSH[%1]: Using existing stream handler %2 for %3")
65 .arg(QString::number(inputid), devkey, devname) +
66 QString(" (%1 in use)").arg(rcount));
67 }
68
69 return s_handlers[devkey];
70}
71
73{
74 QMutexLocker locker(&s_handlersLock);
75
76 QString devname = ref->m_device;
77
78 QMap<QString,uint>::iterator rit = s_handlersRefCnt.find(devname);
79 if (rit == s_handlersRefCnt.end())
80 return;
81
82 QMap<QString,CetonStreamHandler*>::iterator it = s_handlers.find(devname);
83
84 if (*rit > 1)
85 {
86 ref = nullptr;
87 (*rit)--;
88 return;
89 }
90
91 if ((it != s_handlers.end()) && (*it == ref))
92 {
93 LOG(VB_RECORD, LOG_INFO, QString("CetonSH[%1]: Closing handler for %2")
94 .arg(inputid).arg(devname));
95 ref->Close();
96 delete *it;
97 s_handlers.erase(it);
98 }
99 else
100 {
101 LOG(VB_GENERAL, LOG_ERR,
102 QString("CetonSH[%1] Error: Couldn't find handler for %2")
103 .arg(inputid).arg(devname));
104 }
105
106 s_handlersRefCnt.erase(rit);
107 ref = nullptr;
108}
109
110CetonStreamHandler::CetonStreamHandler(const QString &device, int inputid)
112 "", 0, "", 0), inputid)
113{
114 setObjectName("CetonStreamHandler");
115
116 QStringList parts = device.split("-");
117 if (parts.size() != 2)
118 {
119 LOG(VB_GENERAL, LOG_ERR, LOC +
120 QString("Invalid device id %1").arg(m_device));
121 return;
122 }
123 m_ipAddress = parts.at(0);
124
125 QStringList tuner_parts = parts.at(1).split(".");
126 if (tuner_parts.size() == 2)
127 {
128 m_card = tuner_parts.at(0).toUInt();
129 m_tuner = tuner_parts.at(1).toUInt();
130 }
131 else
132 {
133 LOG(VB_GENERAL, LOG_ERR, LOC +
134 QString("Invalid device id %1").arg(m_device));
135 return;
136 }
137
138 if (GetVar("diag", "Host_IP_Address") == "")
139 {
140 LOG(VB_GENERAL, LOG_ERR, LOC +
141 "Ceton tuner does not seem to be available at IP");
142 return;
143 }
144
145 int rtspPort = 8554;
146 QString url = QString("rtsp://%1:%2/cetonmpeg%3")
147 .arg(m_ipAddress).arg(rtspPort).arg(m_tuner);
148 m_tuning = IPTVTuningData(url, 0, IPTVTuningData::kNone, "", 0, "", 0);
149 m_useRtpStreaming = true;
150
151 m_valid = true;
152
153 QString cardstatus = GetVar("cas", "CardStatus");
154 m_usingCablecard = cardstatus == "Inserted";
155
156 if (!s_infoQueried.contains(m_ipAddress))
157 {
158 QString sernum = GetVar("diag", "Host_Serial_Number");
159 QString firmware_ver = GetVar("diag", "Host_Firmware");
160 QString hardware_ver = GetVar("diag", "Hardware_Revision");
161
162 LOG(VB_RECORD, LOG_INFO, LOC +
163 QString("Ceton device %1 initialized. SN: %2, "
164 "Firmware ver. %3, Hardware ver. %4")
165 .arg(m_ipAddress, sernum, firmware_ver, hardware_ver));
166
168 {
169 QString brand = GetVar("cas", "CardManufacturer");
170 QString auth = GetVar("cas", "CardAuthorization");
171
172 LOG(VB_RECORD, LOG_INFO, LOC +
173 QString("Cable card installed (%1) - %2").arg(brand, auth));
174 }
175 else
176 {
177 LOG(VB_RECORD, LOG_INFO, LOC +
178 "Cable card NOT installed (operating in QAM tuner mode)");
179 }
180
181 s_infoQueried.insert(m_ipAddress, true);
182 }
183}
184
186{
187 return Connect();
188}
189
191{
192 if (m_connected)
193 {
194 TunerOff();
195 m_connected = false;
196 }
197}
198
200{
201 if (!m_valid)
202 return false;
203
204 m_connected = true;
205 return true;
206}
207
209{
210 QMutexLocker locker(&m_listenerLock);
211
212 if (!m_streamDataList.empty())
213 {
214 LOG(VB_RECORD, LOG_INFO, LOC +
215 "Ignoring request - video streaming active");
216 return false;
217 }
218
219 locker.unlock(); // _listener_lock
220 TunerOff();
221 return true;
222}
223
225{
226 return m_connected;
227}
228
230{
232 {
233 uint prog = GetVar("mux", "ProgramNumber").toUInt();
234 if (prog == 0)
235 {
236 LOG(VB_RECORD, LOG_WARNING, LOC +
237 "VerifyTuning detected program = 0");
238 return false;
239 }
240 }
241 else
242 {
243 uint frequency = GetVar("tuner", "Frequency").toUInt();
244 if (frequency != m_lastFrequency)
245 {
246 LOG(VB_RECORD, LOG_WARNING, LOC +
247 "VerifyTuning detected wrong frequency");
248 return false;
249 }
250
251 QString modulation = GetVar("tuner", "Modulation");
252 if (modulation.toUpper() != m_lastModulation.toUpper())
253 {
254 LOG(VB_RECORD, LOG_WARNING, LOC +
255 "VerifyTuning detected wrong modulation");
256 return false;
257 }
258
259 uint program = GetVar("mux", "ProgramNumber").toUInt();
260 if (program != m_lastProgram)
261 {
262 LOG(VB_RECORD, LOG_WARNING, LOC +
263 "VerifyTuning detected wrong program");
264 return false;
265 }
266 }
267
268 QString carrier_lock = GetVar("tuner", "CarrierLock");
269 if (carrier_lock != "1")
270 {
271 LOG(VB_RECORD, LOG_WARNING, LOC +
272 "VerifyTuning detected no carrier lock");
273 return false;
274 }
275
276 QString pcr_lock = GetVar("tuner", "PCRLock");
277 if (pcr_lock != "1")
278 {
279 LOG(VB_RECORD, LOG_WARNING, LOC +
280 "VerifyTuning detected no PCR lock");
281 return false;
282 }
283
284 return true;
285}
286
288{
290 {
292 }
293 else
294 {
297 }
298}
299
301{
302 bool result = false;
304 result = TuneVChannel("0");
305 else
306 result = TuneFrequency(0, "qam_256");
307
308 return result;
309}
310
312 uint frequency, const QString &modulation)
313{
314 LOG(VB_RECORD, LOG_INFO, LOC + QString("TuneFrequency(%1, %2)")
315 .arg(frequency).arg(modulation));
316
317 if (frequency >= 100000000)
318 frequency /= 1000;
319
320 QString modulation_id;
321 if (modulation == "qam_256")
322 modulation_id = "2";
323 else if (modulation == "qam_64")
324 modulation_id = "0";
325 else if (modulation == "ntsc-m")
326 modulation_id = "4";
327 else if (modulation == "8vsb")
328 modulation_id = "6";
329 else
330 return false;
331
332 m_lastFrequency = frequency;
333 m_lastModulation = modulation;
334
335 QUrlQuery params;
336 params.addQueryItem("instance_id", QString::number(m_tuner));
337 params.addQueryItem("frequency", QString::number(frequency));
338 params.addQueryItem("modulation",modulation_id);
339 params.addQueryItem("tuner","1");
340 params.addQueryItem("demod","1");
341 params.addQueryItem("rst_chnl","0");
342 params.addQueryItem("force_tune","0");
343
344 QString response;
345 uint status = 0;
346 bool result = HttpRequest(
347 "POST", "/tune_request.cgi", params, response, status);
348
349 if (!result)
350 {
351 LOG(VB_GENERAL, LOG_ERR, LOC +
352 QString("TuneFrequency() - HTTP status = %1 - response = %2")
353 .arg(status).arg(response));
354 }
355
356 return result;
357}
358
360{
361 LOG(VB_RECORD, LOG_INFO, LOC + QString("TuneProgram(%1)").arg(program));
362
363 QStringList program_list = GetProgramList();
364 if (!program_list.contains(QString::number(program)))
365 {
366 LOG(VB_GENERAL, LOG_ERR, LOC +
367 QString("TuneProgram(%1) - Requested program not in the program list")
368 .arg(program));
369 return false;
370 };
371
372
373 m_lastProgram = program;
374
375 QUrlQuery params;
376 params.addQueryItem("instance_id", QString::number(m_tuner));
377 params.addQueryItem("program", QString::number(program));
378
379 QString response;
380 uint status = 0;
381 bool result = HttpRequest(
382 "POST", "/program_request.cgi", params, response, status);
383
384 if (!result)
385 {
386 LOG(VB_GENERAL, LOG_ERR, LOC +
387 QString("TuneProgram() - HTTP status = %1 - response = %2")
388 .arg(status).arg(response));
389 }
390
391 return result;
392}
393
394bool CetonStreamHandler::PerformTuneVChannel(const QString &vchannel)
395{
396 LOG(VB_RECORD, LOG_INFO, LOC + QString("PerformTuneVChannel(%1)")
397 .arg(vchannel));
398
399 QUrlQuery params;
400 params.addQueryItem("instance_id", QString::number(m_tuner));
401 params.addQueryItem("channel", vchannel);
402
403 QString response;
404 uint status = 0;
405 bool result = HttpRequest(
406 "POST", "/channel_request.cgi", params, response, status);
407
408 if (!result)
409 {
410 LOG(VB_GENERAL, LOG_ERR, LOC +
411 QString("PerformTuneVChannel() - HTTP status = %1 - response = %2")
412 .arg(status).arg(response));
413 }
414
415 return result;
416}
417
418
419bool CetonStreamHandler::TuneVChannel(const QString &vchannel)
420{
421 if (GetVar("cas", "VirtualChannelNumber") == vchannel)
422 {
423 LOG(VB_RECORD, LOG_INFO, LOC + QString("Not Re-Tuning channel %1")
424 .arg(vchannel));
425 return true;
426 }
427
428 if ((vchannel != "0") && (m_lastVchannel != "0"))
430
431 LOG(VB_RECORD, LOG_INFO, LOC + QString("TuneVChannel(%1)").arg(vchannel));
432
433 m_lastVchannel = vchannel;
434
435 return PerformTuneVChannel(vchannel);
436}
437
439{
440 LOG(VB_RECORD, LOG_INFO, LOC + QString("ClearProgramNumber()"));
442 for(int i=0; i<50; i++)
443 {
444 if (GetVar("mux", "ProgramNumber") == "0")
445 return;
446 usleep(20ms);
447 };
448
449 LOG(VB_GENERAL, LOG_ERR, LOC + "Program number failed to clear");
450}
451
453{
454 for(int i = 1; i <= 30; i++)
455 {
456 QString prog = GetVar("mux", "ProgramNumber");
457 LOG(VB_RECORD, LOG_INFO, LOC +
458 QString("GetProgramNumber() got %1 on attempt %2")
459 .arg(prog).arg(i));
460
461 uint prognum = prog.toUInt();
462 if (prognum != 0)
463 return prognum;
464
465 usleep(100ms);
466 };
467
468 LOG(VB_GENERAL, LOG_ERR, LOC +
469 "GetProgramNumber() failed to get a non-zero program number");
470
471 return 0;
472}
473
475 const QString &section, const QString &variable) const
476{
477 QString loc = LOC + QString("DoGetVar(%1,%2,%3,%4) - ")
478 .arg(m_ipAddress).arg(m_tuner).arg(section,variable);
479
480 QUrlQuery params;
481 params.addQueryItem("i", QString::number(m_tuner));
482 params.addQueryItem("s", section);
483 params.addQueryItem("v", variable);
484
485 QString response;
486 uint status = 0;
487 if (!HttpRequest("GET", "/get_var.json", params, response, status))
488 {
489 LOG(VB_GENERAL, LOG_ERR, loc +
490 QString("HttpRequest failed - %1").arg(response));
491 return {};
492 }
493
494 static const QRegularExpression regex { "^\\{ \"?result\"?: \"(.*)\" \\}$"};
495 auto match = regex.match(response);
496 if (!match.hasMatch())
497 {
498 LOG(VB_GENERAL, LOG_ERR, loc +
499 QString("unexpected http response: -->%1<--").arg(response));
500 return {};
501 }
502
503 QString result = match.captured(1);
504 LOG(VB_RECORD, LOG_DEBUG, loc + QString("got: -->%1<--").arg(result));
505 return result;
506}
507
509{
510 QString loc = LOC + QString("CetonHTTP: DoGetProgramList(%1,%2) - ")
511 .arg(m_ipAddress).arg(m_tuner);
512
513 QUrlQuery params;
514 params.addQueryItem("i", QString::number(m_tuner));
515
516 QString response;
517 uint status = 0;
518 if (!HttpRequest("GET", "/get_pat.json", params, response, status))
519 {
520 LOG(VB_GENERAL, LOG_ERR,
521 loc + QString("HttpRequest failed - %1").arg(response));
522 return {};
523 }
524
525 static const QRegularExpression regex(
526 R"(^\{ "?length"?: \d+(, "?results"?: \[ (.*) \])? \}$)");
527
528 auto match = regex.match(response);
529 if (!match.hasMatch())
530 {
531 LOG(VB_GENERAL, LOG_ERR,
532 loc + QString("returned unexpected output: -->%1<--")
533 .arg(response));
534 return {};
535 }
536
537 LOG(VB_RECORD, LOG_DEBUG, loc + QString("got: -->%1<--")
538 .arg(match.captured(2)));
539 return match.captured(2).split(", ");
540}
541
543 const QString &method, const QString &script,
544 const QUrlQuery &params,
545 QString &response, uint &status_code) const
546{
547 QUrl url;
548 auto *request = new QNetworkRequest();
549 QByteArray data;
551
552 url.setScheme("http");
553 url.setHost(m_ipAddress);
554 url.setPath(script);
555
556 // Specify un-cached access to the device
557 // The next two lines are automagically added by Qt
558 // request->setRawHeader("Cache-Control", "no-cache");
559 // request->setRawHeader("Pragma", "no-cache");
560 request->setAttribute(QNetworkRequest::CacheLoadControlAttribute,
561 QNetworkRequest::AlwaysNetwork);
562
563 if ("GET" == method)
564 {
565 url.setQuery(params);
566 request->setUrl(url);
567 if (manager->download(request, &data))
568 {
569 response = QString(data);
570 status_code = 200;
571 return true;
572 }
573
574 response = "Download failed";
575 status_code = 500;
576 return false;
577 }
578 if ("POST" == method)
579 {
580 request->setUrl(url);
581 request->setHeader(QNetworkRequest::ContentTypeHeader,
582 "application/x-www-form-urlencoded");
583 data = params.query(QUrl::FullyEncoded).toUtf8();
584 // Next line automagically added by Qt
585 // request->setHeader(QNetworkRequest::ContentLengthHeader, data.size());
586 if (manager->post(request, &data))
587 {
588 response = QString(data);
589 status_code = 200;
590 return true;
591 }
592
593 response = "Download failed";
594 status_code = 500;
595 return false;
596 }
597
598 delete request;
599
600 response = "Unsupported HttpRequest method";
601 status_code = 500;
602 return false;
603}
#define LOC
-*- Mode: c++ -*- CetonStreamHandler Copyright (c) 2011 Ronald Frazier Copyright (c) 2009-2011 Daniel...
CetonStreamHandler(const QString &device, int inputid)
bool IsCableCardInstalled() const
static void Return(CetonStreamHandler *&ref, int inputid)
static QMap< QString, uint > s_handlersRefCnt
static QMap< QString, bool > s_infoQueried
static QMutex s_handlersLock
bool TuneVChannel(const QString &vchannel)
bool IsConnected(void) const
static CetonStreamHandler * Get(const QString &devname, int inputid)
bool TuneFrequency(uint frequency, const QString &modulation)
uint GetProgramNumber(void) const
bool TuneProgram(uint program)
QString GetVar(const QString &section, const QString &variable) const
QStringList GetProgramList()
bool HttpRequest(const QString &method, const QString &script, const QUrlQuery &params, QString &response, uint &status_code) const
static QMap< QString, CetonStreamHandler * > s_handlers
bool PerformTuneVChannel(const QString &vchannel)
IPTVTuningData m_tuning
static void usleep(std::chrono::microseconds time)
Definition: mthread.cpp:335
void setObjectName(const QString &name)
Definition: mthread.cpp:238
StreamDataList m_streamDataList
QString m_device
QRecursiveMutex m_listenerLock
unsigned int uint
Definition: freesurround.h:24
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
static MythSystemLegacyManager * manager