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"
30 #include "mpeg/streamlisteners.h"
31 
32 #define LOC QString("CetonSH[%1](%2): ").arg(m_inputId).arg(m_device)
33 
34 QMap<QString,CetonStreamHandler*> CetonStreamHandler::s_handlers;
35 QMap<QString,uint> CetonStreamHandler::s_handlersRefCnt;
37 QMap<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 
110 CetonStreamHandler::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 
167  if (m_usingCablecard)
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 {
231  if (IsCableCardInstalled())
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 {
289  if (IsCableCardInstalled())
290  {
292  }
293  else
294  {
297  }
298 }
299 
301 {
302  bool result = false;
303  if (m_usingCablecard)
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 = (modulation == "qam_256") ? "2" :
321  (modulation == "qam_64") ? "0" :
322  (modulation == "ntsc-m") ? "4" :
323  (modulation == "8vsb") ? "6" :
324  "";
325  if (modulation_id == "")
326  return false;
327 
328  m_lastFrequency = frequency;
329  m_lastModulation = modulation;
330 
331  QUrlQuery params;
332  params.addQueryItem("instance_id", QString::number(m_tuner));
333  params.addQueryItem("frequency", QString::number(frequency));
334  params.addQueryItem("modulation",modulation_id);
335  params.addQueryItem("tuner","1");
336  params.addQueryItem("demod","1");
337  params.addQueryItem("rst_chnl","0");
338  params.addQueryItem("force_tune","0");
339 
340  QString response;
341  uint status = 0;
342  bool result = HttpRequest(
343  "POST", "/tune_request.cgi", params, response, status);
344 
345  if (!result)
346  {
347  LOG(VB_GENERAL, LOG_ERR, LOC +
348  QString("TuneFrequency() - HTTP status = %1 - response = %2")
349  .arg(status).arg(response));
350  }
351 
352  return result;
353 }
354 
356 {
357  LOG(VB_RECORD, LOG_INFO, LOC + QString("TuneProgram(%1)").arg(program));
358 
359  QStringList program_list = GetProgramList();
360  if (!program_list.contains(QString::number(program)))
361  {
362  LOG(VB_GENERAL, LOG_ERR, LOC +
363  QString("TuneProgram(%1) - Requested program not in the program list")
364  .arg(program));
365  return false;
366  };
367 
368 
369  m_lastProgram = program;
370 
371  QUrlQuery params;
372  params.addQueryItem("instance_id", QString::number(m_tuner));
373  params.addQueryItem("program", QString::number(program));
374 
375  QString response;
376  uint status = 0;
377  bool result = HttpRequest(
378  "POST", "/program_request.cgi", params, response, status);
379 
380  if (!result)
381  {
382  LOG(VB_GENERAL, LOG_ERR, LOC +
383  QString("TuneProgram() - HTTP status = %1 - response = %2")
384  .arg(status).arg(response));
385  }
386 
387  return result;
388 }
389 
390 bool CetonStreamHandler::PerformTuneVChannel(const QString &vchannel)
391 {
392  LOG(VB_RECORD, LOG_INFO, LOC + QString("PerformTuneVChannel(%1)")
393  .arg(vchannel));
394 
395  QUrlQuery params;
396  params.addQueryItem("instance_id", QString::number(m_tuner));
397  params.addQueryItem("channel", vchannel);
398 
399  QString response;
400  uint status = 0;
401  bool result = HttpRequest(
402  "POST", "/channel_request.cgi", params, response, status);
403 
404  if (!result)
405  {
406  LOG(VB_GENERAL, LOG_ERR, LOC +
407  QString("PerformTuneVChannel() - HTTP status = %1 - response = %2")
408  .arg(status).arg(response));
409  }
410 
411  return result;
412 }
413 
414 
415 bool CetonStreamHandler::TuneVChannel(const QString &vchannel)
416 {
417  if (GetVar("cas", "VirtualChannelNumber") == vchannel)
418  {
419  LOG(VB_RECORD, LOG_INFO, LOC + QString("Not Re-Tuning channel %1")
420  .arg(vchannel));
421  return true;
422  }
423 
424  if ((vchannel != "0") && (m_lastVchannel != "0"))
426 
427  LOG(VB_RECORD, LOG_INFO, LOC + QString("TuneVChannel(%1)").arg(vchannel));
428 
429  m_lastVchannel = vchannel;
430 
431  return PerformTuneVChannel(vchannel);
432 }
433 
435 {
436  LOG(VB_RECORD, LOG_INFO, LOC + QString("ClearProgramNumber()"));
437  PerformTuneVChannel("0");
438  for(int i=0; i<50; i++)
439  {
440  if (GetVar("mux", "ProgramNumber") == "0")
441  return;
442  usleep(20ms);
443  };
444 
445  LOG(VB_GENERAL, LOG_ERR, LOC + "Program number failed to clear");
446 }
447 
449 {
450  for(int i = 1; i <= 30; i++)
451  {
452  QString prog = GetVar("mux", "ProgramNumber");
453  LOG(VB_RECORD, LOG_INFO, LOC +
454  QString("GetProgramNumber() got %1 on attempt %2")
455  .arg(prog).arg(i));
456 
457  uint prognum = prog.toUInt();
458  if (prognum != 0)
459  return prognum;
460 
461  usleep(100ms);
462  };
463 
464  LOG(VB_GENERAL, LOG_ERR, LOC +
465  "GetProgramNumber() failed to get a non-zero program number");
466 
467  return 0;
468 }
469 
471  const QString &section, const QString &variable) const
472 {
473  QString loc = LOC + QString("DoGetVar(%1,%2,%3,%4) - ")
474  .arg(m_ipAddress).arg(m_tuner).arg(section,variable);
475 
476  QUrlQuery params;
477  params.addQueryItem("i", QString::number(m_tuner));
478  params.addQueryItem("s", section);
479  params.addQueryItem("v", variable);
480 
481  QString response;
482  uint status = 0;
483  if (!HttpRequest("GET", "/get_var.json", params, response, status))
484  {
485  LOG(VB_GENERAL, LOG_ERR, loc +
486  QString("HttpRequest failed - %1").arg(response));
487  return {};
488  }
489 
490  static const QRegularExpression regex { "^\\{ \"?result\"?: \"(.*)\" \\}$"};
491  auto match = regex.match(response);
492  if (!match.hasMatch())
493  {
494  LOG(VB_GENERAL, LOG_ERR, loc +
495  QString("unexpected http response: -->%1<--").arg(response));
496  return {};
497  }
498 
499  QString result = match.captured(1);
500  LOG(VB_RECORD, LOG_DEBUG, loc + QString("got: -->%1<--").arg(result));
501  return result;
502 }
503 
505 {
506  QString loc = LOC + QString("CetonHTTP: DoGetProgramList(%1,%2) - ")
507  .arg(m_ipAddress).arg(m_tuner);
508 
509  QUrlQuery params;
510  params.addQueryItem("i", QString::number(m_tuner));
511 
512  QString response;
513  uint status = 0;
514  if (!HttpRequest("GET", "/get_pat.json", params, response, status))
515  {
516  LOG(VB_GENERAL, LOG_ERR,
517  loc + QString("HttpRequest failed - %1").arg(response));
518  return {};
519  }
520 
521  static const QRegularExpression regex(
522  R"(^\{ "?length"?: \d+(, "?results"?: \[ (.*) \])? \}$)");
523 
524  auto match = regex.match(response);
525  if (!match.hasMatch())
526  {
527  LOG(VB_GENERAL, LOG_ERR,
528  loc + QString("returned unexpected output: -->%1<--")
529  .arg(response));
530  return {};
531  }
532 
533  LOG(VB_RECORD, LOG_DEBUG, loc + QString("got: -->%1<--")
534  .arg(match.captured(2)));
535  return match.captured(2).split(", ");
536 }
537 
539  const QString &method, const QString &script,
540  const QUrlQuery &params,
541  QString &response, uint &status_code) const
542 {
543  QUrl url;
544  auto *request = new QNetworkRequest();
545  QByteArray data;
547 
548  url.setScheme("http");
549  url.setHost(m_ipAddress);
550  url.setPath(script);
551 
552  // Specify un-cached access to the device
553  // The next two lines are automagically added by Qt
554  // request->setRawHeader("Cache-Control", "no-cache");
555  // request->setRawHeader("Pragma", "no-cache");
556  request->setAttribute(QNetworkRequest::CacheLoadControlAttribute,
557  QNetworkRequest::AlwaysNetwork);
558 
559  if ("GET" == method)
560  {
561  url.setQuery(params);
562  request->setUrl(url);
563  if (manager->download(request, &data))
564  {
565  response = QString(data);
566  status_code = 200;
567  return true;
568  }
569 
570  response = "Download failed";
571  status_code = 500;
572  return false;
573  }
574  if ("POST" == method)
575  {
576  request->setUrl(url);
577  request->setHeader(QNetworkRequest::ContentTypeHeader,
578  "application/x-www-form-urlencoded");
579  data = params.query(QUrl::FullyEncoded).toUtf8();
580  // Next line automagically added by Qt
581  // request->setHeader(QNetworkRequest::ContentLengthHeader, data.size());
582  if (manager->post(request, &data))
583  {
584  response = QString(data);
585  status_code = 200;
586  return true;
587  }
588 
589  response = "Download failed";
590  status_code = 500;
591  return false;
592  }
593 
594  delete request;
595 
596  response = "Unsupported HttpRequest method";
597  status_code = 500;
598  return false;
599 }
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:102
CetonStreamHandler::GetVar
QString GetVar(const QString &section, const QString &variable) const
Definition: cetonstreamhandler.cpp:470
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:390
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:190
CetonStreamHandler::TunerOff
bool TunerOff(void)
Definition: cetonstreamhandler.cpp:300
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:287
CetonStreamHandler::VerifyTuning
bool VerifyTuning(void)
Definition: cetonstreamhandler.cpp:229
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
CetonStreamHandler::TuneVChannel
bool TuneVChannel(const QString &vchannel)
Definition: cetonstreamhandler.cpp:415
MythDownloadManager
Definition: mythdownloadmanager.h:48
streamlisteners.h
CetonStreamHandler::Connect
bool Connect(void)
Definition: cetonstreamhandler.cpp:199
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:144
cetonchannel.h
mythlogging.h
CetonStreamHandler::CetonStreamHandler
CetonStreamHandler(const QString &device, int inputid)
Definition: cetonstreamhandler.cpp:110
CetonStreamHandler::IsConnected
bool IsConnected(void) const
Definition: cetonstreamhandler.cpp:224
CetonStreamHandler::TuneProgram
bool TuneProgram(uint program)
Definition: cetonstreamhandler.cpp:355
CetonStreamHandler::GetProgramNumber
uint GetProgramNumber(void) const
Definition: cetonstreamhandler.cpp:448
CetonStreamHandler::m_connected
bool m_connected
Definition: cetonstreamhandler.h:69
IPTVStreamHandler::m_useRtpStreaming
bool m_useRtpStreaming
Definition: iptvstreamhandler.h:109
IPTVTuningData
Definition: iptvtuningdata.h:21
cetonstreamhandler.h
CetonStreamHandler::EnterPowerSavingMode
bool EnterPowerSavingMode(void)
Definition: cetonstreamhandler.cpp:208
uint
unsigned int uint
Definition: compat.h:81
CetonStreamHandler::ClearProgramNumber
void ClearProgramNumber(void)
Definition: cetonstreamhandler.cpp:434
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:32
CetonStreamHandler::Return
static void Return(CetonStreamHandler *&ref, int inputid)
Definition: cetonstreamhandler.cpp:72
CetonStreamHandler::GetProgramList
QStringList GetProgramList()
Definition: cetonstreamhandler.cpp:504
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:39
StreamHandler::m_streamDataList
StreamDataList m_streamDataList
Definition: streamhandler.h:145
CetonStreamHandler::HttpRequest
bool HttpRequest(const QString &method, const QString &script, const QUrlQuery &params, QString &response, uint &status_code) const
Definition: cetonstreamhandler.cpp:538
CetonStreamHandler::TuneFrequency
bool TuneFrequency(uint frequency, const QString &modulation)
Definition: cetonstreamhandler.cpp:311
IPTVStreamHandler
Definition: iptvstreamhandler.h:79
CetonStreamHandler::m_ipAddress
QString m_ipAddress
Definition: cetonstreamhandler.h:65
mythdownloadmanager.h
StreamHandler::m_device
QString m_device
Definition: streamhandler.h:111
CetonStreamHandler::Open
bool Open(void)
Definition: cetonstreamhandler.cpp:185
GetMythDownloadManager
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
Definition: mythdownloadmanager.cpp:145