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