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