MythTV  master
channelbase.cpp
Go to the documentation of this file.
1 // Std C headers
2 #include <cstdlib>
3 #include <cerrno>
4 
5 // POSIX headers
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 
11 // C++ headers
12 #include <iostream>
13 #include <algorithm>
14 using namespace std;
15 
16 // Qt headers
17 #include <QCoreApplication>
18 
19 // MythTV headers
20 #ifdef USING_OSX_FIREWIRE
21 #include "darwinfirewiredevice.h"
22 #endif
23 #ifdef USING_LINUX_FIREWIRE
24 #include "linuxfirewiredevice.h"
25 #endif
26 #include "firewirechannel.h"
27 #include "mythcorecontext.h"
28 #include "cetonchannel.h"
29 #include "dummychannel.h"
30 #include "tvremoteutil.h"
31 #include "channelbase.h"
32 #include "channelutil.h"
33 #include "frequencies.h"
34 #include "hdhrchannel.h"
35 #include "iptvchannel.h"
36 #include "mythlogging.h"
37 #include "asichannel.h"
38 #include "dtvchannel.h"
39 #include "dvbchannel.h"
40 #include "v4lchannel.h"
41 #include "ExternalChannel.h"
42 #include "sourceutil.h"
43 #include "exitcodes.h"
44 #include "cardutil.h"
45 #include "compat.h"
46 #include "inputinfo.h"
47 
48 #define LOC QString("ChannelBase[%1]: ").arg(m_inputid)
49 
51 {
52  QMutexLocker locker(&m_system_lock);
53  if (m_system)
54  KillScript();
55 }
56 
57 bool ChannelBase::Init(QString &startchannel, bool setchan)
58 {
59  bool ok;
60 
61  if (!setchan)
62  ok = IsTunable(startchannel);
63  else
64  ok = SetChannelByString(startchannel);
65 
66  if (ok)
67  return true;
68 
69  // try to find a valid channel if given start channel fails.
70  QString msg1 = QString("Setting start channel '%1' failed, ")
71  .arg(startchannel);
72  QString msg2 = "and we failed to find any suitable channels on any input.";
73  bool msg_error = true;
74 
75  // Attempt to find the requested startchannel
76  for (auto cit = m_channels.begin(); cit != m_channels.end(); ++cit)
77  {
78  if ((*cit).m_channum == startchannel &&
79  IsTunable(startchannel))
80  {
81  LOG(VB_CHANNEL, LOG_INFO, LOC +
82  QString("Found startchannel '%1'").arg(startchannel));
83  return true;
84  }
85  }
86 
87  uint mplexid_restriction = 0;
88  uint chanid_restriction = 0;
89 
90  if (!m_channels.empty() &&
91  IsInputAvailable(mplexid_restriction, chanid_restriction))
92  {
94  m_channels, m_channels[0].m_chanid,
95  mplexid_restriction, chanid_restriction, CHANNEL_DIRECTION_UP);
96 
97  ChannelInfoList::const_iterator cit =
98  find(m_channels.begin(), m_channels.end(), chanid);
99 
100  if (chanid && cit != m_channels.end())
101  {
102  if (!setchan)
103  {
104  ok = IsTunable((mplexid_restriction || chanid_restriction)
105  ? (*cit).m_channum : startchannel);
106  }
107  else
108  ok = SetChannelByString((*cit).m_channum);
109 
110  if (ok)
111  {
112  if (mplexid_restriction || chanid_restriction)
113  startchannel = (*cit).m_channum;
114  msg2 = QString("selected to '%1' instead.")
115  .arg(startchannel);
116  msg_error = false;
117  }
118  }
119  }
120 
121  LOG(VB_GENERAL, ((msg_error) ? LOG_ERR : LOG_WARNING), LOC +
122  msg1 + "\n\t\t\t" + msg2);
123 
124  return ok;
125 }
126 
127 bool ChannelBase::IsTunable(const QString &channum) const
128 {
129  QString loc = LOC + QString("IsTunable(%1)").arg(channum);
130 
131  if (!m_inputid)
132  {
133  LOG(VB_GENERAL, LOG_ERR, loc + " " +
134  QString("Requested non-existant input"));
135 
136  return false;
137  }
138 
139  uint mplexid_restriction = 0;
140  uint chanid_restriction = 0;
141  if (!IsInputAvailable(mplexid_restriction, chanid_restriction))
142  {
143  LOG(VB_GENERAL, LOG_ERR, loc + " " +
144  QString("Requested channel is on input '%1' "
145  "which is in a busy input group")
146  .arg(m_inputid));
147 
148  return false;
149  }
150 
151  // Fetch tuning data from the database.
152  QString tvformat, modulation, freqtable, freqid, dtv_si_std;
153  int finetune;
154  uint64_t frequency;
155  int mpeg_prog_num;
156  uint atsc_major, atsc_minor, mplexid, chanid, tsid, netid;
157  bool commfree;
158 
159  if (!ChannelUtil::GetChannelData(m_sourceid, chanid, channum,
160  tvformat, modulation, freqtable, freqid,
161  finetune, frequency, dtv_si_std,
162  mpeg_prog_num, atsc_major, atsc_minor,
163  tsid, netid, mplexid, commfree))
164  {
165  LOG(VB_GENERAL, LOG_ERR, loc + " " +
166  QString("Failed to find channel in DB on input '%1' ")
167  .arg(m_inputid));
168 
169  return false;
170  }
171 
172  if ((mplexid_restriction && (mplexid != mplexid_restriction)) ||
173  (!mplexid_restriction &&
174  chanid_restriction && (chanid != chanid_restriction)))
175  {
176  LOG(VB_GENERAL, LOG_ERR, loc + " " +
177  QString("Channel is valid, but tuner is busy "
178  "on different multiplex/channel (%1 != %2) / (%3 != %4)")
179  .arg(mplexid).arg(mplexid_restriction)
180  .arg(chanid).arg(chanid_restriction));
181 
182  return false;
183  }
184 
185  return true;
186 }
187 
189 {
190  if (!chanid)
191  {
192  if (!m_inputid)
193  return 0;
194 
195  chanid = ChannelUtil::GetChanID(m_sourceid, m_curchannelname);
196  }
197 
198  uint mplexid_restriction = 0;
199  uint chanid_restriction = 0;
200  (void)IsInputAvailable(mplexid_restriction, chanid_restriction);
201 
203  m_channels, chanid, mplexid_restriction, chanid_restriction,
204  direction);
205 }
206 
207 uint ChannelBase::GetNextChannel(const QString &channum, ChannelChangeDirection direction) const
208 {
209  if (!m_inputid)
210  return 0;
211 
212  uint chanid = ChannelUtil::GetChanID(m_sourceid, channum);
213  return GetNextChannel(chanid, direction);
214 }
215 
217  uint &mplexid_restriction, uint &chanid_restriction) const
218 {
219  if (!m_inputid)
220  {
221  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("no m_inputid"));
222  return false;
223  }
224 
225  InputInfo info;
226 
227  mplexid_restriction = 0;
228  chanid_restriction = 0;
229 
230  vector<uint> inputids = CardUtil::GetConflictingInputs(m_inputid);
231  for (size_t i = 0; i < inputids.size(); ++i)
232  {
233  if (RemoteIsBusy(inputids[i], info))
234  {
235  LOG(VB_CHANNEL, LOG_DEBUG, LOC +
236  QString("Input %1 is busy on %2/%3")
237  .arg(info.m_inputid)
238  .arg(info.m_chanid).arg(info.m_mplexid));
239  if (info.m_sourceid != m_sourceid)
240  {
241  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Input is busy"));
242  return false;
243  }
244  mplexid_restriction = info.m_mplexid;
245  chanid_restriction = info.m_chanid;
246  }
247  }
248 
249  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Input is free on %1/%2")
250  .arg(mplexid_restriction).arg(chanid_restriction));
251  return true;
252 }
253 
256 {
257  if (!m_system)
258  return true;
259 
260  m_system->Term(true);
261 
262  delete m_system;
263  m_system = nullptr;
264  return true;
265 }
266 
268 void ChannelBase::HandleScript(const QString &freqid)
269 {
270  QMutexLocker locker(&m_system_lock);
271 
272  bool ok = true;
273  m_system_status = 0; // unknown
274 
275  if (!m_inputid)
276  {
277  m_system_status = 2; // failed
278  HandleScriptEnd(true);
279  return;
280  }
281 
282  if (m_externalChanger.isEmpty())
283  {
284  m_system_status = 3; // success
285  HandleScriptEnd(true);
286  return;
287  }
288 
289  if (freqid.isEmpty())
290  {
291  LOG(VB_GENERAL, LOG_WARNING, LOC +
292  "A channel changer is set, but the freqid field is empty."
293  "\n\t\t\tWe will return success to ease setup pains, "
294  "but no script is will actually run.");
295  m_system_status = 3; // success
296  HandleScriptEnd(true);
297  return;
298  }
299 
300  // It's possible we simply never reaped the process, check status first.
301  if (m_system)
302  GetScriptStatus(true);
303 
304  // If it's still running, try killing it
305  if (m_system)
306  ok = KillScript();
307 
308  // The GetScriptStatus() call above can reset m_system_status with
309  // the exit status of the last channel change script invocation, so
310  // we must set it to pending here.
311  m_system_status = 1; // pending
312 
313  if (!ok)
314  {
315  LOG(VB_GENERAL, LOG_ERR, LOC +
316  "Can not execute channel changer, previous call to script "
317  "is still running.");
318  m_system_status = 2; // failed
319  HandleScriptEnd(ok);
320  }
321  else
322  {
323  if (m_externalChanger.toLower() == "internal")
324  {
325  ok = ChangeInternalChannel(freqid, m_inputid);
326  if (!ok)
327  {
328  LOG(VB_GENERAL, LOG_ERR, LOC + "Can not execute internal channel "
329  "changer.");
330  m_system_status = 2; // failed
331  }
332  else
333  m_system_status = 3; // success
334 
335  HandleScriptEnd(ok);
336  }
337  else
338  {
339  ok = ChangeExternalChannel(m_externalChanger, freqid);
340  if (!ok)
341  {
342  LOG(VB_GENERAL, LOG_ERR, LOC + "Can not execute channel changer.");
343  m_system_status = 2; // failed
344  HandleScriptEnd(ok);
345  }
346  }
347  }
348 }
349 
350 bool ChannelBase::ChangeInternalChannel(const QString &freqid,
351  uint inputid)
352 {
353 #ifdef USING_FIREWIRE
354  FirewireDevice *device = nullptr;
355  QString fwnode = CardUtil::GetFirewireChangerNode(inputid);
356  uint64_t guid = string_to_guid(fwnode);
357  QString fwmodel = CardUtil::GetFirewireChangerModel(inputid);
358 
359  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Internal channel change to %1 "
360  "on inputid %2, GUID %3 (%4)").arg(freqid).arg(inputid)
361  .arg(fwnode).arg(fwmodel));
362 
363 #ifdef USING_LINUX_FIREWIRE
364  // cppcheck-suppress redundantAssignment
365  device = new LinuxFirewireDevice(
366  guid, 0, 100, true);
367 #endif // USING_LINUX_FIREWIRE
368 
369 #ifdef USING_OSX_FIREWIRE
370  // cppcheck-suppress redundantAssignment
371  device = new DarwinFirewireDevice(guid, 0, 100);
372 #endif // USING_OSX_FIREWIRE
373 
374  if (!device)
375  return false;
376 
377  if (!device->OpenPort())
378  return false;
379 
380  if (!device->SetChannel(fwmodel, 0, freqid.toUInt()))
381  {
382  device->ClosePort();
383  delete device;
384  device = nullptr;
385  return false;
386  }
387 
388  device->ClosePort();
389  delete device;
390  device = nullptr;
391  return true;
392 #else
393  Q_UNUSED(freqid);
394  Q_UNUSED(inputid);
395  return false;
396 #endif
397 }
398 
400 bool ChannelBase::ChangeExternalChannel(const QString &changer,
401  const QString &freqid)
402 {
403  if (m_system)
404  return false;
405 
406  if (changer.isEmpty() || freqid.isEmpty())
407  return false;
408 
409  QString command = QString("%1 %2").arg(changer).arg(freqid);
410  LOG(VB_CHANNEL, LOG_INFO, LOC +
411  QString("Running command: %1").arg(command));
412 
413  m_system = new MythSystemLegacy(command, kMSRunShell | kMSRunBackground);
414  m_system->Run();
415 
416  return true;
417 }
418 
420 {
421  if (!m_system)
422  return m_system_status;
423 
424  if (!holding_lock)
425  m_system_lock.lock();
426 
427  m_system_status = m_system->Wait();
428  if (m_system_status != GENERIC_EXIT_RUNNING &&
429  m_system_status != GENERIC_EXIT_START)
430  {
431  delete m_system;
432  m_system = nullptr;
433 
434  HandleScriptEnd(m_system_status == GENERIC_EXIT_OK);
435  }
436 
437  LOG(VB_CHANNEL, LOG_DEBUG, LOC + QString("GetScriptStatus() %1")
438  .arg(m_system_status));
439 
440  uint ret;
441  switch(m_system_status)
442  {
443  case GENERIC_EXIT_OK:
444  ret = 3; // success
445  break;
447  case GENERIC_EXIT_START:
448  ret = 1; // pending
449  break;
450  default:
451  ret = 2; // fail
452  break;
453  }
454 
455  LOG(VB_CHANNEL, LOG_DEBUG, LOC + QString("GetScriptStatus() %1 -> %2")
456  .arg(m_system_status). arg(ret));
457 
458  m_system_status = ret;
459 
460  if (!holding_lock)
461  m_system_lock.unlock();
462 
463  return ret;
464 }
465 
468 {
469  if (ok)
470  {
471  LOG(VB_CHANNEL, LOG_INFO, LOC + "Channel change script succeeded.");
472  if (m_inputid)
473  {
474  // Set this as the future start channel for this source
475  m_startChanNum = m_curchannelname;
476  }
477  }
478  else
479  {
480  LOG(VB_GENERAL, LOG_ERR, LOC + "Channel change script failed.");
481  }
482 }
483 
484 int ChannelBase::GetChanID(void) const
485 {
486  if (!m_inputid)
487  return -1;
488 
489  int found = 0;
490  int visible = -1;
491  int id = -1;
492  MSqlQuery query(MSqlQuery::InitCon());
493 
494  query.prepare("SELECT chanid,visible FROM channel "
495  "WHERE deleted IS NULL AND "
496  " channum = :CHANNUM AND "
497  " sourceid = :SOURCEID");
498  query.bindValueNoNull(":CHANNUM", m_curchannelname);
499  query.bindValue(":SOURCEID", m_sourceid);
500 
501  if (!query.exec() || !query.isActive())
502  {
503  MythDB::DBError("fetching chanid", query);
504  return -1;
505  }
506 
507  while (query.next())
508  {
509  if (query.value(1).toBool())
510  {
511  ++found;
512  visible = query.value(0).toInt();
513  }
514  else
515  id = query.value(0).toInt();
516  }
517 
518  if (!found)
519  {
520  LOG(VB_GENERAL, LOG_INFO,
521  QString("No visible channel ids for %1 on sourceid %2")
522  .arg(m_curchannelname).arg(m_sourceid));
523  }
524 
525  if (found > 1)
526  {
527  LOG(VB_GENERAL, LOG_WARNING,
528  QString("Found multiple visible channel ids for %1 on sourceid %2")
529  .arg(m_curchannelname).arg(m_sourceid));
530  }
531 
532  return (visible >= 0 ? visible : id);
533 }
534 
539 {
540  if (!m_inputid)
541  {
542  if (m_pParent)
543  m_inputid = m_pParent->GetInputId();
544  else
545  m_inputid = CardUtil::GetFirstInputID(GetDevice());
546  }
547 
548  if (!m_inputid)
549  {
550  LOG(VB_GENERAL, LOG_ERR,
551  "InitializeInput(): Programmer error, no parent.");
552  return false;
553  }
554 
555  MSqlQuery query(MSqlQuery::InitCon());
556  query.prepare(
557  "SELECT sourceid, inputname, "
558  " startchan, externalcommand, "
559  " tunechan "
560  "FROM capturecard "
561  "WHERE cardid = :INPUTID");
562  query.bindValue(":INPUTID", m_inputid);
563 
564  if (!query.exec() || !query.isActive())
565  {
566  MythDB::DBError("ChannelBase::InitializeInput", query);
567  return false;
568  }
569  if (!query.size())
570  {
571  LOG(VB_GENERAL, LOG_ERR, LOC +
572  QString("No capturecard record in database for input %1")
573  .arg(m_inputid));
574  return false;
575  }
576 
577  query.next();
578 
579  m_sourceid = query.value(0).toUInt();
580  m_name = query.value(1).toString();
581  m_startChanNum = query.value(2).toString();
582  m_externalChanger = query.value(3).toString();
583  m_tuneToChannel = query.value(4).toString();
584 
585  if (0 == m_sourceid)
586  {
587  LOG(VB_GENERAL, LOG_ERR, LOC +
588  QString("No video source defined for input %1")
589  .arg(m_inputid));
590  return false;
591  }
592 
593  m_channels = ChannelUtil::GetChannels(m_sourceid, false);
594  QString order = gCoreContext->GetSetting("ChannelOrdering", "channum");
595  ChannelUtil::SortChannels(m_channels, order);
596 
597  if (!IsExternalChannelChangeSupported() &&
598  !m_externalChanger.isEmpty())
599  {
600  LOG(VB_GENERAL, LOG_WARNING, LOC + "External Channel changer is "
601  "set, but this device does not support it.");
602  m_externalChanger.clear();
603  }
604 
605  // print it
606  LOG(VB_CHANNEL, LOG_INFO, LOC +
607  QString("Input #%1: '%2' schan(%3) sourceid(%4)")
608  .arg(m_inputid).arg(m_name).arg(m_startChanNum)
609  .arg(m_sourceid));
610 
611  return true;
612 }
613 
618  const QString &oldChanNum,
619  const QString &newChanNum)
620 {
621  bool skip = (m_name.isEmpty() ||
622  m_startChanNum.isEmpty() ||
623  m_startChanNum != oldChanNum ||
624  m_sourceid != sourceid);
625  if (!skip)
626  m_startChanNum = newChanNum;
627 
628  if (GetSourceID() == sourceid && oldChanNum == m_curchannelname)
629  m_curchannelname = newChanNum;
630 
631  StoreInputChannels();
632 }
633 
638 {
639  MSqlQuery query(MSqlQuery::InitCon());
640 
641  if (m_name.isEmpty() || m_startChanNum.isEmpty())
642  return;
643 
644  query.prepare(
645  "UPDATE capturecard "
646  "SET startchan = :STARTCHAN "
647  "WHERE cardid = :CARDINPUTID");
648  query.bindValue(":STARTCHAN", m_startChanNum);
649  query.bindValue(":CARDINPUTID", m_inputid);
650 
651  if (!query.exec() || !query.isActive())
652  MythDB::DBError("StoreInputChannels", query);
653 }
654 
655 bool ChannelBase::CheckChannel(const QString &channum) const
656 {
657  MSqlQuery query(MSqlQuery::InitCon());
658  if (!query.isConnected())
659  return false;
660 
661  query.prepare(
662  "SELECT channel.chanid "
663  "FROM channel, capturecard "
664  "WHERE channel.deleted IS NULL AND "
665  " channel.channum = :CHANNUM AND "
666  " channel.sourceid = capturecard.sourceid AND "
667  " capturecard.cardid = :INPUTID AND "
668  " capturecard.hostname = :HOSTNAME");
669  query.bindValue(":CHANNUM", channum);
670  query.bindValue(":INPUTID", m_inputid);
671  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
672 
673  if (!query.exec() || !query.isActive())
674  {
675  MythDB::DBError("checkchannel", query);
676  }
677  else if (query.size() > 0)
678  return true;
679 
680  LOG(VB_CHANNEL, LOG_ERR, LOC +
681  QString("Failed to find channel(%1) on input (%2).")
682  .arg(channum).arg(m_inputid));
683  return false;
684 }
685 
687  TVRec *tvrec,
688  const GeneralDBOptions &genOpt,
689  const DVBDBOptions &dvbOpt,
690  const FireWireDBOptions &fwOpt,
691  const QString &startchannel,
692  bool enter_power_save_mode,
693  QString &rbFileExt,
694  bool setchan)
695 {
696  rbFileExt = "ts";
697 
698  ChannelBase *channel = nullptr;
699  if (genOpt.m_inputType == "DVB")
700  {
701 #ifdef USING_DVB
702  channel = new DVBChannel(genOpt.m_videoDev, tvrec);
703  auto *dvbchannel = dynamic_cast<DVBChannel*>(channel);
704  if (dvbchannel != nullptr)
705  dvbchannel->SetSlowTuning(dvbOpt.m_dvbTuningDelay);
706 #endif
707  }
708  else if (genOpt.m_inputType == "FIREWIRE")
709  {
710 #ifdef USING_FIREWIRE
711  channel = new FirewireChannel(tvrec, genOpt.m_videoDev, fwOpt);
712 #else
713  Q_UNUSED(fwOpt);
714 #endif
715  }
716  else if (genOpt.m_inputType == "HDHOMERUN")
717  {
718 #ifdef USING_HDHOMERUN
719  channel = new HDHRChannel(tvrec, genOpt.m_videoDev);
720 #endif
721  }
722  else if ((genOpt.m_inputType == "IMPORT") ||
723  (genOpt.m_inputType == "DEMO") ||
724  (genOpt.m_inputType == "MPEG" &&
725  genOpt.m_videoDev.toLower().startsWith("file:")))
726  {
727  channel = new DummyChannel(tvrec);
728  rbFileExt = "mpg";
729  }
730 #ifdef USING_IPTV
731  else if (genOpt.m_inputType == "FREEBOX") // IPTV
732  { // NOLINTNEXTLINE(bugprone-branch-clone)
733  channel = new IPTVChannel(tvrec, genOpt.m_videoDev);
734  }
735 #endif
736 #ifdef USING_VBOX
737  else if (genOpt.m_inputType == "VBOX")
738  {
739  channel = new IPTVChannel(tvrec, genOpt.m_videoDev);
740  }
741 #endif
742 #ifdef USING_ASI
743  else if (genOpt.m_inputType == "ASI")
744  {
745  channel = new ASIChannel(tvrec, genOpt.m_videoDev);
746  }
747 #endif
748 #ifdef USING_CETON
749  else if (genOpt.m_inputType == "CETON")
750  {
751  channel = new CetonChannel(tvrec, genOpt.m_videoDev);
752  }
753 #endif
754  else if (genOpt.m_inputType == "V4L2ENC")
755  {
756 #ifdef USING_V4L2
757  channel = new V4LChannel(tvrec, genOpt.m_videoDev);
758 #endif
759  if (genOpt.m_inputType == "MPEG")
760  rbFileExt = "mpg";
761  }
762  else if (CardUtil::IsV4L(genOpt.m_inputType))
763  {
764 #ifdef USING_V4L2
765  channel = new V4LChannel(tvrec, genOpt.m_videoDev);
766 #endif
767  if (genOpt.m_inputType != "HDPVR")
768  {
769  if (genOpt.m_inputType != "MPEG")
770  rbFileExt = "nuv";
771  else
772  rbFileExt = "mpg";
773  }
774  }
775  else if (genOpt.m_inputType == "EXTERNAL")
776  {
777  channel = new ExternalChannel(tvrec, genOpt.m_videoDev);
778  }
779 
780  if (!channel)
781  {
782  QString msg = QString(
783  "%1 card configured on video device %2, \n"
784  "but MythTV was not compiled with %3 support. \n"
785  "\n"
786  "Recompile MythTV with %4 support or remove the card \n"
787  "from the configuration and restart MythTV.")
788  .arg(genOpt.m_inputType).arg(genOpt.m_videoDev)
789  .arg(genOpt.m_inputType).arg(genOpt.m_inputType);
790  LOG(VB_GENERAL, LOG_ERR, "ChannelBase: CreateChannel() Error: \n" +
791  msg + "\n");
792  return nullptr;
793  }
794 
795  if (!channel->Open())
796  {
797  LOG(VB_GENERAL, LOG_ERR, "ChannelBase: CreateChannel() Error: " +
798  QString("Failed to open device %1").arg(genOpt.m_videoDev));
799  delete channel;
800  return nullptr;
801  }
802 
803  QString channum = startchannel;
804  channel->Init(channum, setchan);
805 
806  if (enter_power_save_mode)
807  {
808  if (channel &&
809  ((genOpt.m_inputType == "DVB" && dvbOpt.m_dvbOnDemand) ||
810  genOpt.m_inputType == "HDHOMERUN" ||
811  CardUtil::IsV4L(genOpt.m_inputType)))
812  {
813  channel->Close();
814  }
815  else if (setchan)
816  {
817  auto *dtvchannel = dynamic_cast<DTVChannel*>(channel);
818  if (dtvchannel)
819  dtvchannel->EnterPowerSavingMode();
820  }
821  }
822 
823  return channel;
824 }
825 
827 {
828  if (!IsExternalChannelChangeSupported())
829  return false;
830 
831  if (!m_inputid)
832  {
833  LOG(VB_GENERAL, LOG_ERR, LOC +
834  QString("IsExternalChannelChangeInUse: "
835  "non-existant input"));
836  return false;
837  }
838 
839  return !m_externalChanger.isEmpty();
840 }
841 
843 {
844  return m_pParent ? m_pParent->GetMajorId() : m_inputid;
845 }
uint64_t string_to_guid(const QString &guid)
Definition: avcinfo.cpp:14
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:781
bool ChangeInternalChannel(const QString &freqid, uint cardinputid)
virtual int GetChanID(void) const
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:862
virtual void HandleScriptEnd(bool ok)
virtual void Close(void)=0
Closes the channel changing hardware to use.
ChannelChangeDirection
ChannelChangeDirection is an enumeration of possible channel changing directions.
Definition: tv.h:28
static uint GetFirstInputID(const QString &videodevice)
Convenience function for GetInputIDs()
Definition: cardutil.h:247
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
static vector< uint > GetConflictingInputs(uint inputid)
Definition: cardutil.cpp:2057
static ChannelBase * CreateChannel(TVRec *tvrec, const GeneralDBOptions &genOpt, const DVBDBOptions &dvbOpt, const FireWireDBOptions &fwOpt, const QString &startchannel, bool enter_power_save_mode, QString &rbFileExt, bool setchan)
bool RemoteIsBusy(uint inputid, InputInfo &busy_input)
virtual void Renumber(uint sourceid, const QString &oldChanNum, const QString &newChanNum)
Changes a channum if we have it cached anywhere.
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
virtual bool IsTunable(const QString &channum) const
virtual bool Init(QString &startchannel, bool setchan)
Definition: channelbase.cpp:57
int size(void) const
Definition: mythdbcon.h:203
bool isConnected(void)
Only updated once during object creation.
Definition: mythdbcon.h:135
QString m_videoDev
Definition: tv_rec.h:69
uint m_inputid
unique key in DB for this input
Definition: inputinfo.h:71
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Provides interface to the tuning hardware when using DVB drivers.
Definition: dvbchannel.h:29
virtual bool Open(void)=0
Opens the channel changing hardware for use.
uint m_mplexid
mplexid restriction if applicable
Definition: inputinfo.h:72
static void SortChannels(ChannelInfoList &list, const QString &order, bool eliminate_duplicates=false)
void HandleScript(const QString &freqid)
virtual bool IsExternalChannelChangeInUse(void)
QVariant value(int i) const
Definition: mythdbcon.h:198
virtual bool IsInputAvailable(uint &mplexid_restriction, uint &chanid_restriction) const
Switches to another input on hardware, and sets the channel is setstarting is true.
bool m_dvbOnDemand
Definition: tv_rec.h:85
static int GetChanID(int db_mplexid, int service_transport_id, int major_channel, int minor_channel, int program_number)
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:141
QString GetSetting(const QString &key, const QString &defaultval="")
uint GetScriptStatus(bool holding_lock=false)
FirewireChannel Copyright (c) 2005 by Jim Westfall and Dave Abrahams Distributed as part of MythTV un...
bool isActive(void) const
Definition: mythdbcon.h:204
static ChannelInfoList GetChannels(uint sourceid, bool visible_only, const QString &group_by=QString(), uint channel_groupid=0)
Definition: channelutil.h:237
#define GENERIC_EXIT_RUNNING
Process is running.
Definition: exitcodes.h:25
static QString GetFirewireChangerModel(uint inputid)
Definition: cardutil.cpp:1577
bool KillScript(void)
unsigned int uint
Definition: compat.h:140
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:534
bool CheckChannel(const QString &channum) const
virtual void StoreInputChannels(void)
Saves current channel as the default channel for the current input.
Implements tuning for TV cards using the V4L driver API, both versions 1 and 2.
Definition: v4lchannel.h:32
uint m_dvbTuningDelay
Definition: tv_rec.h:86
const char * m_name
Definition: ParseText.cpp:328
static QString GetFirewireChangerNode(uint inputid)
Definition: cardutil.cpp:1560
#define GENERIC_EXIT_START
MythSystemLegacy process starting.
Definition: exitcodes.h:35
run process through shell
Definition: mythsystem.h:41
bool ChangeExternalChannel(const QString &changer, const QString &freqid)
uint m_chanid
chanid restriction if applicable
Definition: inputinfo.h:73
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:806
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
Abstract class providing a generic interface to tuning hardware.
Definition: channelbase.h:31
static bool GetChannelData(uint sourceid, uint &chanid, const QString &channum, QString &tvformat, QString &modulation, QString &freqtable, QString &freqid, int &finetune, uint64_t &frequency, QString &dtv_si_std, int &mpeg_prog_num, uint &atsc_major, uint &atsc_minor, uint &dvb_transportid, uint &dvb_networkid, uint &mplexid, bool &commfree)
virtual uint GetNextChannel(uint chanid, ChannelChangeDirection direction) const
uint m_sourceid
associated channel listings source
Definition: inputinfo.h:70
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
Definition: mythdbcon.cpp:867
QString m_inputType
Definition: tv_rec.h:72
virtual bool SetChannel(const QString &panel_model, uint alt_method, uint channel)
run child in the background
Definition: mythsystem.h:36
#define LOC
Definition: channelbase.cpp:48
virtual bool ClosePort(void)=0
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:602
virtual bool OpenPort(void)=0
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
QString GetHostName(void)
static bool IsV4L(const QString &rawtype)
Definition: cardutil.h:131
int GetMajorID(void)
virtual bool InitializeInput(void)
Fills in input map from DB.
virtual ~ChannelBase(void)
Definition: channelbase.cpp:50
static uint GetNextChannel(const ChannelInfoList &sorted, uint old_chanid, uint mplexid_restriction, uint chanid_restriction, ChannelChangeDirection direction, bool skip_non_visible=true, bool skip_same_channum_and_callsign=false)