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