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 = false;
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 & channel : m_channels)
77  {
78  if (channel.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  auto cit = find(m_channels.begin(), m_channels.end(), chanid);
98 
99  if ((chanid != 0U) && (cit != m_channels.end()))
100  {
101  if (!setchan)
102  {
103  ok = IsTunable((mplexid_restriction || chanid_restriction)
104  ? (*cit).m_chanNum : startchannel);
105  }
106  else
107  ok = SetChannelByString((*cit).m_chanNum);
108 
109  if (ok)
110  {
111  if (mplexid_restriction || chanid_restriction)
112  startchannel = (*cit).m_chanNum;
113  msg2 = QString("selected to '%1' instead.")
114  .arg(startchannel);
115  msg_error = false;
116  }
117  }
118  }
119 
120  LOG(VB_GENERAL, ((msg_error) ? LOG_ERR : LOG_WARNING), LOC +
121  msg1 + "\n\t\t\t" + msg2);
122 
123  return ok;
124 }
125 
126 bool ChannelBase::IsTunable(const QString &channum) const
127 {
128  QString loc = LOC + QString("IsTunable(%1)").arg(channum);
129 
130  if (!m_inputId)
131  {
132  LOG(VB_GENERAL, LOG_ERR, loc + " " +
133  QString("Requested non-existant input"));
134 
135  return false;
136  }
137 
138  uint mplexid_restriction = 0;
139  uint chanid_restriction = 0;
140  if (!IsInputAvailable(mplexid_restriction, chanid_restriction))
141  {
142  LOG(VB_GENERAL, LOG_ERR, loc + " " +
143  QString("Requested channel is on input '%1' "
144  "which is in a busy input group")
145  .arg(m_inputId));
146 
147  return false;
148  }
149 
150  // Fetch tuning data from the database.
151  QString tvformat;
152  QString modulation;
153  QString freqtable;
154  QString freqid;
155  QString dtv_si_std;
156  int finetune = 0;
157  uint64_t frequency = 0;
158  int mpeg_prog_num = 0;
159  uint atsc_major = 0;
160  uint atsc_minor = 0;
161  uint mplexid = 0;
162  uint chanid = 0;
163  uint tsid = 0;
164  uint netid = 0;
165  bool commfree = false;
166 
167  if (!ChannelUtil::GetChannelData(m_sourceId, chanid, channum,
168  tvformat, modulation, freqtable, freqid,
169  finetune, frequency, dtv_si_std,
170  mpeg_prog_num, atsc_major, atsc_minor,
171  tsid, netid, mplexid, commfree))
172  {
173  LOG(VB_GENERAL, LOG_ERR, loc + " " +
174  QString("Failed to find channel in DB on input '%1' ")
175  .arg(m_inputId));
176 
177  return false;
178  }
179 
180  if ((mplexid_restriction && (mplexid != mplexid_restriction)) ||
181  (!mplexid_restriction &&
182  chanid_restriction && (chanid != chanid_restriction)))
183  {
184  LOG(VB_GENERAL, LOG_ERR, loc + " " +
185  QString("Channel is valid, but tuner is busy "
186  "on different multiplex/channel (%1 != %2) / (%3 != %4)")
187  .arg(mplexid).arg(mplexid_restriction)
188  .arg(chanid).arg(chanid_restriction));
189 
190  return false;
191  }
192 
193  return true;
194 }
195 
197 {
198  if (!chanid)
199  {
200  if (!m_inputId)
201  return 0;
202 
203  chanid = ChannelUtil::GetChanID(m_sourceId, m_curChannelName);
204  }
205 
206  uint mplexid_restriction = 0;
207  uint chanid_restriction = 0;
208  (void)IsInputAvailable(mplexid_restriction, chanid_restriction);
209 
211  m_channels, chanid, mplexid_restriction, chanid_restriction,
212  direction);
213 }
214 
215 uint ChannelBase::GetNextChannel(const QString &channum, ChannelChangeDirection direction) const
216 {
217  if (!m_inputId)
218  return 0;
219 
220  uint chanid = ChannelUtil::GetChanID(m_sourceId, channum);
221  return GetNextChannel(chanid, direction);
222 }
223 
225  uint &mplexid_restriction, uint &chanid_restriction) const
226 {
227  if (!m_inputId)
228  {
229  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("no m_inputId"));
230  return false;
231  }
232 
233  InputInfo info;
234 
235  mplexid_restriction = 0;
236  chanid_restriction = 0;
237 
238  vector<uint> inputids = CardUtil::GetConflictingInputs(m_inputId);
239  for (uint inputid : inputids)
240  {
241  if (RemoteIsBusy(inputid, info))
242  {
243  LOG(VB_CHANNEL, LOG_DEBUG, LOC +
244  QString("Input %1 is busy on %2/%3")
245  .arg(info.m_inputId)
246  .arg(info.m_chanId).arg(info.m_mplexId));
247  if (info.m_sourceId != m_sourceId)
248  {
249  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Input is busy"));
250  return false;
251  }
252  mplexid_restriction = info.m_mplexId;
253  chanid_restriction = info.m_chanId;
254  }
255  }
256 
257  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Input is free on %1/%2")
258  .arg(mplexid_restriction).arg(chanid_restriction));
259  return true;
260 }
261 
264 {
265  if (!m_system)
266  return true;
267 
268  m_system->Term(true);
269 
270  delete m_system;
271  m_system = nullptr;
272  return true;
273 }
274 
276 void ChannelBase::HandleScript(const QString &freqid)
277 {
278  QMutexLocker locker(&m_system_lock);
279 
280  bool ok = true;
281  m_systemStatus = 0; // unknown
282 
283  if (!m_inputId)
284  {
285  m_systemStatus = 2; // failed
286  HandleScriptEnd(true);
287  return;
288  }
289 
290  if (m_externalChanger.isEmpty())
291  {
292  m_systemStatus = 3; // success
293  HandleScriptEnd(true);
294  return;
295  }
296 
297  if (freqid.isEmpty())
298  {
299  LOG(VB_GENERAL, LOG_WARNING, LOC +
300  "A channel changer is set, but the freqid field is empty."
301  "\n\t\t\tWe will return success to ease setup pains, "
302  "but no script is will actually run.");
303  m_systemStatus = 3; // success
304  HandleScriptEnd(true);
305  return;
306  }
307 
308  // It's possible we simply never reaped the process, check status first.
309  if (m_system)
310  GetScriptStatus(true);
311 
312  // If it's still running, try killing it
313  if (m_system)
314  ok = KillScript();
315 
316  // The GetScriptStatus() call above can reset m_systemStatus with
317  // the exit status of the last channel change script invocation, so
318  // we must set it to pending here.
319  m_systemStatus = 1; // pending
320 
321  if (!ok)
322  {
323  LOG(VB_GENERAL, LOG_ERR, LOC +
324  "Can not execute channel changer, previous call to script "
325  "is still running.");
326  m_systemStatus = 2; // failed
327  HandleScriptEnd(ok);
328  }
329  else
330  {
331  if (m_externalChanger.toLower() == "internal")
332  {
333  ok = ChangeInternalChannel(freqid, m_inputId);
334  if (!ok)
335  {
336  LOG(VB_GENERAL, LOG_ERR, LOC + "Can not execute internal channel "
337  "changer.");
338  m_systemStatus = 2; // failed
339  }
340  else
341  m_systemStatus = 3; // success
342 
343  HandleScriptEnd(ok);
344  }
345  else
346  {
347  ok = ChangeExternalChannel(m_externalChanger, freqid);
348  if (!ok)
349  {
350  LOG(VB_GENERAL, LOG_ERR, LOC + "Can not execute channel changer.");
351  m_systemStatus = 2; // failed
352  HandleScriptEnd(ok);
353  }
354  }
355  }
356 }
357 
358 bool ChannelBase::ChangeInternalChannel(const QString &freqid,
359  uint inputid) const
360 {
361 #ifdef USING_FIREWIRE
362  FirewireDevice *device = nullptr;
363  QString fwnode = CardUtil::GetFirewireChangerNode(inputid);
364  uint64_t guid = string_to_guid(fwnode);
365  QString fwmodel = CardUtil::GetFirewireChangerModel(inputid);
366 
367  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Internal channel change to %1 "
368  "on inputid %2, GUID %3 (%4)").arg(freqid).arg(inputid)
369  .arg(fwnode).arg(fwmodel));
370 
371 #ifdef USING_LINUX_FIREWIRE
372  // cppcheck-suppress redundantAssignment
373  device = new LinuxFirewireDevice(
374  guid, 0, 100, true);
375 #endif // USING_LINUX_FIREWIRE
376 
377 #ifdef USING_OSX_FIREWIRE
378  // cppcheck-suppress redundantAssignment
379  device = new DarwinFirewireDevice(guid, 0, 100);
380 #endif // USING_OSX_FIREWIRE
381 
382  if (!device)
383  return false;
384 
385  if (!device->OpenPort())
386  return false;
387 
388  if (!device->SetChannel(fwmodel, 0, freqid.toUInt()))
389  {
390  device->ClosePort();
391  delete device;
392  device = nullptr;
393  return false;
394  }
395 
396  device->ClosePort();
397  delete device;
398  device = nullptr;
399  return true;
400 #else
401  Q_UNUSED(freqid);
402  Q_UNUSED(inputid);
403  return false;
404 #endif
405 }
406 
408 bool ChannelBase::ChangeExternalChannel(const QString &changer,
409  const QString &freqid)
410 {
411  if (m_system)
412  return false;
413 
414  if (changer.isEmpty() || freqid.isEmpty())
415  return false;
416 
417  QString command = QString("%1 %2").arg(changer).arg(freqid);
418  LOG(VB_CHANNEL, LOG_INFO, LOC +
419  QString("Running command: %1").arg(command));
420 
421  m_system = new MythSystemLegacy(command, kMSRunShell | kMSRunBackground);
422  m_system->Run();
423 
424  return true;
425 }
426 
428 {
429  if (!m_system)
430  return m_systemStatus;
431 
432  if (!holding_lock)
433  m_system_lock.lock();
434 
435  m_systemStatus = m_system->Wait();
436  if (m_systemStatus != GENERIC_EXIT_RUNNING &&
437  m_systemStatus != GENERIC_EXIT_START)
438  {
439  delete m_system;
440  m_system = nullptr;
441 
442  HandleScriptEnd(m_systemStatus == GENERIC_EXIT_OK);
443  }
444 
445  LOG(VB_CHANNEL, LOG_DEBUG, LOC + QString("GetScriptStatus() %1")
446  .arg(m_systemStatus));
447 
448  uint ret = 0;
449  switch(m_systemStatus)
450  {
451  case GENERIC_EXIT_OK:
452  ret = 3; // success
453  break;
455  case GENERIC_EXIT_START:
456  ret = 1; // pending
457  break;
458  default:
459  ret = 2; // fail
460  break;
461  }
462 
463  LOG(VB_CHANNEL, LOG_DEBUG, LOC + QString("GetScriptStatus() %1 -> %2")
464  .arg(m_systemStatus). arg(ret));
465 
466  m_systemStatus = ret;
467 
468  if (!holding_lock)
469  m_system_lock.unlock();
470 
471  return ret;
472 }
473 
476 {
477  if (ok)
478  {
479  LOG(VB_CHANNEL, LOG_INFO, LOC + "Channel change script succeeded.");
480  if (m_inputId)
481  {
482  // Set this as the future start channel for this source
483  m_startChanNum = m_curChannelName;
484  }
485  }
486  else
487  {
488  LOG(VB_GENERAL, LOG_ERR, LOC + "Channel change script failed.");
489  }
490 }
491 
492 int ChannelBase::GetChanID(void) const
493 {
494  if (!m_inputId)
495  return -1;
496 
497  int found = 0;
498  int visible = -1;
499  int id = -1;
501 
502  query.prepare("SELECT chanid,visible FROM channel "
503  "WHERE deleted IS NULL AND "
504  " channum = :CHANNUM AND "
505  " sourceid = :SOURCEID");
506  query.bindValueNoNull(":CHANNUM", m_curChannelName);
507  query.bindValue(":SOURCEID", m_sourceId);
508 
509  if (!query.exec() || !query.isActive())
510  {
511  MythDB::DBError("fetching chanid", query);
512  return -1;
513  }
514 
515  while (query.next())
516  {
517  if (query.value(1).toInt() > 0)
518  {
519  ++found;
520  visible = query.value(0).toInt();
521  }
522  else
523  id = query.value(0).toInt();
524  }
525 
526  if (!found)
527  {
528  LOG(VB_GENERAL, LOG_INFO,
529  QString("No visible channel ids for %1 on sourceid %2")
530  .arg(m_curChannelName).arg(m_sourceId));
531  }
532 
533  if (found > 1)
534  {
535  LOG(VB_GENERAL, LOG_WARNING,
536  QString("Found multiple visible channel ids for %1 on sourceid %2")
537  .arg(m_curChannelName).arg(m_sourceId));
538  }
539 
540  return (visible >= 0 ? visible : id);
541 }
542 
547 {
548  if (!m_inputId)
549  {
550  if (m_pParent)
551  m_inputId = m_pParent->GetInputId();
552  else
553  m_inputId = CardUtil::GetFirstInputID(GetDevice());
554  }
555 
556  if (!m_inputId)
557  {
558  LOG(VB_GENERAL, LOG_ERR,
559  "InitializeInput(): Programmer error, no parent.");
560  return false;
561  }
562 
564  query.prepare(
565  "SELECT sourceid, inputname, "
566  " startchan, externalcommand, "
567  " tunechan "
568  "FROM capturecard "
569  "WHERE cardid = :INPUTID");
570  query.bindValue(":INPUTID", m_inputId);
571 
572  if (!query.exec() || !query.isActive())
573  {
574  MythDB::DBError("ChannelBase::InitializeInput", query);
575  return false;
576  }
577  if (!query.size())
578  {
579  LOG(VB_GENERAL, LOG_ERR, LOC +
580  QString("No capturecard record in database for input %1")
581  .arg(m_inputId));
582  return false;
583  }
584 
585  query.next();
586 
587  m_sourceId = query.value(0).toUInt();
588  m_name = query.value(1).toString();
589  m_startChanNum = query.value(2).toString();
590  m_externalChanger = query.value(3).toString();
591  m_tuneToChannel = query.value(4).toString();
592 
593  if (0 == m_sourceId)
594  {
595  LOG(VB_GENERAL, LOG_ERR, LOC +
596  QString("No video source defined for input %1")
597  .arg(m_inputId));
598  return false;
599  }
600 
601  m_channels = ChannelUtil::GetChannels(m_sourceId, false);
602  QString order = gCoreContext->GetSetting("ChannelOrdering", "channum");
603  ChannelUtil::SortChannels(m_channels, order);
604 
605  if (!IsExternalChannelChangeSupported() &&
606  !m_externalChanger.isEmpty())
607  {
608  LOG(VB_GENERAL, LOG_WARNING, LOC + "External Channel changer is "
609  "set, but this device does not support it.");
610  m_externalChanger.clear();
611  }
612 
613  // print it
614  LOG(VB_CHANNEL, LOG_INFO, LOC +
615  QString("Input #%1: '%2' schan(%3) sourceid(%4)")
616  .arg(m_inputId).arg(m_name).arg(m_startChanNum)
617  .arg(m_sourceId));
618 
619  return true;
620 }
621 
626  const QString &oldChanNum,
627  const QString &newChanNum)
628 {
629  bool skip = (m_name.isEmpty() ||
630  m_startChanNum.isEmpty() ||
631  m_startChanNum != oldChanNum ||
632  m_sourceId != sourceid);
633  if (!skip)
634  m_startChanNum = newChanNum;
635 
636  if (GetSourceID() == sourceid && oldChanNum == m_curChannelName)
637  m_curChannelName = newChanNum;
638 
639  StoreInputChannels();
640 }
641 
646 {
648 
649  if (m_name.isEmpty() || m_startChanNum.isEmpty())
650  return;
651 
652  query.prepare(
653  "UPDATE capturecard "
654  "SET startchan = :STARTCHAN "
655  "WHERE cardid = :CARDINPUTID");
656  query.bindValue(":STARTCHAN", m_startChanNum);
657  query.bindValue(":CARDINPUTID", m_inputId);
658 
659  if (!query.exec() || !query.isActive())
660  MythDB::DBError("StoreInputChannels", query);
661 }
662 
663 bool ChannelBase::CheckChannel(const QString &channum) const
664 {
666  if (!query.isConnected())
667  return false;
668 
669  query.prepare(
670  "SELECT channel.chanid "
671  "FROM channel, capturecard "
672  "WHERE channel.deleted IS NULL AND "
673  " channel.channum = :CHANNUM AND "
674  " channel.sourceid = capturecard.sourceid AND "
675  " capturecard.cardid = :INPUTID AND "
676  " capturecard.hostname = :HOSTNAME");
677  query.bindValue(":CHANNUM", channum);
678  query.bindValue(":INPUTID", m_inputId);
679  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
680 
681  if (!query.exec() || !query.isActive())
682  {
683  MythDB::DBError("checkchannel", query);
684  }
685  else if (query.size() > 0)
686  return true;
687 
688  LOG(VB_CHANNEL, LOG_ERR, LOC +
689  QString("Failed to find channel(%1) on input (%2).")
690  .arg(channum).arg(m_inputId));
691  return false;
692 }
693 
695  TVRec *tvrec,
696  const GeneralDBOptions &genOpt,
697  const DVBDBOptions &dvbOpt,
698  const FireWireDBOptions &fwOpt,
699  const QString &startchannel,
700  bool enter_power_save_mode,
701  QString &rbFileExt,
702  bool setchan)
703 {
704  rbFileExt = "ts";
705 
706  ChannelBase *channel = nullptr;
707  if (genOpt.m_inputType == "DVB")
708  {
709 #ifdef USING_DVB
710  channel = new DVBChannel(genOpt.m_videoDev, tvrec);
711  auto *dvbchannel = dynamic_cast<DVBChannel*>(channel);
712  if (dvbchannel != nullptr)
713  dvbchannel->SetSlowTuning(dvbOpt.m_dvbTuningDelay);
714 #endif
715  }
716  else if (genOpt.m_inputType == "FIREWIRE")
717  {
718 #ifdef USING_FIREWIRE
719  channel = new FirewireChannel(tvrec, genOpt.m_videoDev, fwOpt);
720 #else
721  Q_UNUSED(fwOpt);
722 #endif
723  }
724  else if (genOpt.m_inputType == "HDHOMERUN")
725  {
726 #ifdef USING_HDHOMERUN
727  channel = new HDHRChannel(tvrec, genOpt.m_videoDev);
728 #endif
729  }
730  else if ((genOpt.m_inputType == "IMPORT") ||
731  (genOpt.m_inputType == "DEMO") ||
732  (genOpt.m_inputType == "MPEG" &&
733  genOpt.m_videoDev.toLower().startsWith("file:")))
734  {
735  channel = new DummyChannel(tvrec);
736  rbFileExt = "mpg";
737  }
738 #ifdef USING_IPTV
739  else if (genOpt.m_inputType == "FREEBOX") // IPTV
740  { // NOLINTNEXTLINE(bugprone-branch-clone)
741  channel = new IPTVChannel(tvrec, genOpt.m_videoDev);
742  }
743 #endif
744 #ifdef USING_VBOX
745  else if (genOpt.m_inputType == "VBOX")
746  {
747  channel = new IPTVChannel(tvrec, genOpt.m_videoDev);
748  }
749 #endif
750 #ifdef USING_ASI
751  else if (genOpt.m_inputType == "ASI")
752  {
753  channel = new ASIChannel(tvrec, genOpt.m_videoDev);
754  }
755 #endif
756 #ifdef USING_CETON
757  else if (genOpt.m_inputType == "CETON")
758  {
759  channel = new CetonChannel(tvrec, genOpt.m_videoDev);
760  }
761 #endif
762  else if (genOpt.m_inputType == "V4L2ENC")
763  {
764 #ifdef USING_V4L2
765  channel = new V4LChannel(tvrec, genOpt.m_videoDev);
766 #endif
767  if (genOpt.m_inputType == "MPEG")
768  rbFileExt = "mpg";
769  }
770  else if (CardUtil::IsV4L(genOpt.m_inputType))
771  {
772 #ifdef USING_V4L2
773  channel = new V4LChannel(tvrec, genOpt.m_videoDev);
774 #endif
775  if (genOpt.m_inputType != "HDPVR")
776  {
777  if (genOpt.m_inputType != "MPEG")
778  rbFileExt = "nuv";
779  else
780  rbFileExt = "mpg";
781  }
782  }
783  else if (genOpt.m_inputType == "EXTERNAL")
784  {
785  channel = new ExternalChannel(tvrec, genOpt.m_videoDev);
786  }
787 
788  if (!channel)
789  {
790  QString msg = QString(
791  "%1 card configured on video device %2, \n"
792  "but MythTV was not compiled with %3 support. \n"
793  "\n"
794  "Recompile MythTV with %4 support or remove the card \n"
795  "from the configuration and restart MythTV.")
796  .arg(genOpt.m_inputType).arg(genOpt.m_videoDev)
797  .arg(genOpt.m_inputType).arg(genOpt.m_inputType);
798  LOG(VB_GENERAL, LOG_ERR, "ChannelBase: CreateChannel() Error: \n" +
799  msg + "\n");
800  return nullptr;
801  }
802 
803  if (!channel->Open())
804  {
805  LOG(VB_GENERAL, LOG_ERR, "ChannelBase: CreateChannel() Error: " +
806  QString("Failed to open device %1").arg(genOpt.m_videoDev));
807  delete channel;
808  return nullptr;
809  }
810 
811  QString channum = startchannel;
812  channel->Init(channum, setchan);
813 
814  if (enter_power_save_mode)
815  {
816  if (channel &&
817  ((genOpt.m_inputType == "DVB" && dvbOpt.m_dvbOnDemand) ||
818  genOpt.m_inputType == "HDHOMERUN" ||
819  CardUtil::IsV4L(genOpt.m_inputType)))
820  {
821  channel->Close();
822  }
823  else if (setchan)
824  {
825  auto *dtvchannel = dynamic_cast<DTVChannel*>(channel);
826  if (dtvchannel)
827  dtvchannel->EnterPowerSavingMode();
828  }
829  }
830 
831  return channel;
832 }
833 
835 {
836  if (!IsExternalChannelChangeSupported())
837  return false;
838 
839  if (!m_inputId)
840  {
841  LOG(VB_GENERAL, LOG_ERR, LOC +
842  QString("IsExternalChannelChangeInUse: "
843  "non-existant input"));
844  return false;
845  }
846 
847  return !m_externalChanger.isEmpty();
848 }
849 
851 {
852  return m_pParent ? m_pParent->GetMajorId() : m_inputId;
853 }
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:783
virtual int GetChanID(void) const
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:864
virtual void HandleScriptEnd(bool ok)
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:253
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
static vector< uint > GetConflictingInputs(uint inputid)
Definition: cardutil.cpp:2051
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)
uint m_chanId
chanid restriction if applicable
Definition: inputinfo.h:51
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
QString m_videoDev
Definition: tv_rec.h:70
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
static void SortChannels(ChannelInfoList &list, const QString &order, bool eliminate_duplicates=false)
uint m_inputId
unique key in DB for this input
Definition: inputinfo.h:49
void HandleScript(const QString &freqid)
virtual bool IsExternalChannelChangeInUse(void)
bool isConnected(void) const
Only updated once during object creation.
Definition: mythdbcon.h:135
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.
MSqlQuery query(MSqlQuery::InitCon())
bool m_dvbOnDemand
Definition: tv_rec.h:86
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:142
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:240
#define GENERIC_EXIT_RUNNING
Process is running.
Definition: exitcodes.h:25
static QString GetFirewireChangerModel(uint inputid)
Definition: cardutil.cpp:1571
uint m_sourceId
associated channel listings source
Definition: inputinfo.h:48
bool KillScript(void)
unsigned int uint
Definition: compat.h:140
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
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:35
uint m_dvbTuningDelay
Definition: tv_rec.h:87
const char * m_name
Definition: ParseText.cpp:329
static QString GetFirewireChangerNode(uint inputid)
Definition: cardutil.cpp:1554
#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)
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808
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
bool ChangeInternalChannel(const QString &freqid, uint cardinputid) const
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
Definition: mythdbcon.cpp:869
QString m_inputType
Definition: tv_rec.h:73
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
uint m_mplexId
mplexid restriction if applicable
Definition: inputinfo.h:50
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
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:137
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)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23