MythTV  master
channelscanner.cpp
Go to the documentation of this file.
1 /* -*- Mode: c++ -*-
2  * vim: set expandtab tabstop=4 shiftwidth=4:
3  *
4  * Original Project
5  * MythTV http://www.mythtv.org
6  *
7  * Copyright (c) 2004, 2005 John Pullan <john@pullan.org>
8  * Copyright (c) 2005 - 2007 Daniel Kristjansson
9  *
10  * Description:
11  * Collection of classes to provide channel scanning functionallity
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
26  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
27  *
28  */
29 
30 #include <algorithm>
31 using namespace std;
32 
33 #include "analogsignalmonitor.h"
34 #include "iptvchannelfetcher.h"
35 #include "dvbsignalmonitor.h"
36 #include "scanwizardconfig.h"
37 #include "channelscan_sm.h"
38 #include "channelscanner.h"
39 #include "hdhrchannel.h"
40 #include "scanmonitor.h"
41 #include "asichannel.h"
42 #ifdef USING_DVB // for bug in gcc 8.3
43 #include "dvbchannel.h"
44 #endif
45 #include "v4lchannel.h"
46 #include "iptvchannel.h"
47 #include "ExternalChannel.h"
48 #include "cardutil.h"
49 
50 #define LOC QString("ChScan: ")
51 
53 {
55 
56  if (m_scanMonitor)
57  {
58  m_scanMonitor->deleteLater();
59  m_scanMonitor = nullptr;
60  }
61 }
62 
64 {
65  if (m_sigmonScanner)
66  {
67  delete m_sigmonScanner;
68  m_sigmonScanner = nullptr;
69  }
70 
71  if (m_channel)
72  {
73  delete m_channel;
74  m_channel = nullptr;
75  }
76 
77  if (m_iptvScanner)
78  {
79  m_iptvScanner->Stop();
80  delete m_iptvScanner;
81  m_iptvScanner = nullptr;
82  }
83 
84 #ifdef USING_VBOX
85  if (m_vboxScanner)
86  {
87  m_vboxScanner->Stop();
88  delete m_vboxScanner;
89  m_vboxScanner = nullptr;
90  }
91 #endif
92 
93 #if !defined( USING_MINGW ) && !defined( _MSC_VER )
94  if (m_ExternRecScanner)
95  {
96  m_ExternRecScanner->Stop();
97  delete m_ExternRecScanner;
98  m_ExternRecScanner = nullptr;
99  }
100 #endif
101 
102  if (m_scanMonitor)
103  {
104  m_scanMonitor->deleteLater();
105  m_scanMonitor = nullptr;
106  }
107 }
108 
109 // full scan of existing transports broken
110 // existing transport scan broken
112  int scantype,
113  uint cardid,
114  const QString &inputname,
115  uint sourceid,
116  bool do_ignore_signal_timeout,
117  bool do_follow_nit,
118  bool do_test_decryption,
119  bool do_fta_only,
120  bool do_lcn_only,
121  bool do_complete_only,
122  bool do_full_channel_search,
123  bool do_add_full_ts,
124  ServiceRequirements service_requirements,
125  // stuff needed for particular scans
126  uint mplexid /* TransportScan */,
127  const QMap<QString,QString> &startChan /* NITAddScan */,
128  const QString &freq_std /* FullScan */,
129  const QString &mod /* FullScan */,
130  const QString &tbl /* FullScan */,
131  const QString &tbl_start /* FullScan optional */,
132  const QString &tbl_end /* FullScan optional */)
133 {
134  m_freeToAirOnly = do_fta_only;
135  m_channelNumbersOnly = do_lcn_only;
136  m_completeOnly = do_complete_only;
137  m_fullSearch = do_full_channel_search;
138  m_addFullTS = do_add_full_ts;
139  m_serviceRequirements = service_requirements;
140  m_sourceid = sourceid;
141 
142  PreScanCommon(scantype, cardid, inputname,
143  sourceid, do_ignore_signal_timeout, do_test_decryption);
144 
145  LOG(VB_CHANSCAN, LOG_INFO, LOC + "Scan()");
146 
147  if (!m_sigmonScanner)
148  {
149  LOG(VB_CHANSCAN, LOG_ERR, LOC + "Scan(): scanner does not exist...");
150  return;
151  }
152 
153  m_sigmonScanner->StartScanner();
154  m_scanMonitor->ScanUpdateStatusText("");
155 
156  bool ok = false;
157 
158  if ((ScanTypeSetting::FullScan_ATSC == scantype) ||
159  (ScanTypeSetting::FullScan_DVBC == scantype) ||
160  (ScanTypeSetting::FullScan_DVBT == scantype) ||
161  (ScanTypeSetting::FullScan_DVBT2 == scantype) ||
162  (ScanTypeSetting::FullScan_Analog == scantype))
163  {
164  LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("ScanTransports(%1, %2, %3)")
165  .arg(freq_std).arg(mod).arg(tbl));
166 
167  // HACK HACK HACK -- begin
168  // if using QAM we may need additional time... (at least with HD-3000)
169  if ((mod.startsWith("qam", Qt::CaseInsensitive)) &&
170  (m_sigmonScanner->GetSignalTimeout() < 1000))
171  {
172  m_sigmonScanner->SetSignalTimeout(1000);
173  }
174  // HACK HACK HACK -- end
175 
176  m_sigmonScanner->SetAnalog(ScanTypeSetting::FullScan_Analog == scantype);
177 
178  ok = m_sigmonScanner->ScanTransports(
179  sourceid, freq_std, mod, tbl, tbl_start, tbl_end);
180  }
181  else if ((ScanTypeSetting::NITAddScan_DVBT == scantype) ||
182  (ScanTypeSetting::NITAddScan_DVBT2 == scantype) ||
183  (ScanTypeSetting::NITAddScan_DVBS == scantype) ||
184  (ScanTypeSetting::NITAddScan_DVBS2 == scantype) ||
185  (ScanTypeSetting::NITAddScan_DVBC == scantype))
186  {
187  LOG(VB_CHANSCAN, LOG_INFO, LOC + "ScanTransports()");
188 
189  ok = m_sigmonScanner->ScanTransportsStartingOn(sourceid, startChan);
190  }
191  else if (ScanTypeSetting::FullTransportScan == scantype)
192  {
193  LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("ScanExistingTransports(%1)")
194  .arg(sourceid));
195 
196  ok = m_sigmonScanner->ScanExistingTransports(sourceid, do_follow_nit);
197  if (ok)
198  {
199  m_scanMonitor->ScanPercentComplete(0);
200  }
201  else
202  {
203  InformUser(tr("Error tuning to transport"));
204  Teardown();
205  }
206  }
207  else if ((ScanTypeSetting::DVBUtilsImport == scantype) && !m_channels.empty())
208  {
209  ok = true;
210 
211  LOG(VB_CHANSCAN, LOG_INFO, LOC +
212  QString("ScanForChannels(%1)").arg(sourceid));
213 
214  QString card_type = CardUtil::GetRawInputType(cardid);
215  QString sub_type = card_type;
216  if (card_type == "DVB")
217  {
218  QString device = CardUtil::GetVideoDevice(cardid);
219 
220  ok = !device.isEmpty();
221  if (ok)
222  sub_type = CardUtil::ProbeDVBType(device).toUpper();
223  }
224 
225  if (ok)
226  {
227  ok = m_sigmonScanner->ScanForChannels(sourceid, freq_std,
228  sub_type, m_channels);
229  }
230  if (ok)
231  {
232  m_scanMonitor->ScanPercentComplete(0);
233  }
234  else
235  {
236  InformUser(tr("Error tuning to transport"));
237  Teardown();
238  }
239  }
240  else if (ScanTypeSetting::IPTVImportMPTS == scantype)
241  {
242  if (m_iptv_channels.empty())
243  {
244  LOG(VB_CHANSCAN, LOG_INFO, LOC + "IPTVImportMPTS: no channels");
245  }
246  else
247  {
248  LOG(VB_CHANSCAN, LOG_INFO, LOC +
249  QString("ScanIPTVChannels(%1) IPTV MPTS").arg(sourceid));
250 
251  if ((ok = m_sigmonScanner->ScanIPTVChannels(sourceid, m_iptv_channels)))
252  m_scanMonitor->ScanPercentComplete(0);
253  else
254  {
255  InformUser(tr("Error scanning MPTS in IPTV"));
256  Teardown();
257  }
258  }
259  }
260  else if (ScanTypeSetting::TransportScan == scantype)
261  {
262  LOG(VB_CHANSCAN, LOG_INFO, LOC +
263  QString("ScanTransport(%1)").arg(mplexid));
264 
265  ok = m_sigmonScanner->ScanTransport(mplexid, do_follow_nit);
266  }
267  else if (ScanTypeSetting::CurrentTransportScan == scantype)
268  {
269  QString sistandard = "mpeg";
270  LOG(VB_CHANSCAN, LOG_INFO, LOC +
271  "ScanCurrentTransport(" + sistandard + ")");
272  ok = m_sigmonScanner->ScanCurrentTransport(sistandard);
273  }
274  else if (ScanTypeSetting::ExternRecImport == scantype)
275  {
276  LOG(VB_CHANSCAN, LOG_INFO, LOC +
277  "Importing channels from External Recorder");
278  ok = ImportExternRecorder(cardid, inputname, sourceid);
279  }
280 
281  if (!ok)
282  {
283  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to handle tune complete.");
284  InformUser(tr("Programmer Error: "
285  "Failed to handle tune complete."));
286  }
287 }
288 
290  uint sourceid, int cardtype, const QString &file)
291 {
292  m_sourceid = sourceid;
293  m_channels.clear();
294 
296  type = ((CardUtil::DVBT == cardtype) ||
297  (CardUtil::DVBT2 == cardtype)) ? DTVConfParser::OFDM : type;
298  type = (CardUtil::QPSK == cardtype) ? DTVConfParser::QPSK : type;
299  type = (CardUtil::DVBC == cardtype) ? DTVConfParser::QAM : type;
300  type = (CardUtil::DVBS2 == cardtype) ? DTVConfParser::DVBS2 : type;
301  type = ((CardUtil::ATSC == cardtype) ||
302  (CardUtil::HDHOMERUN == cardtype)) ? DTVConfParser::ATSC : type;
303 
307  else
308  {
309  DTVConfParser parser(type, sourceid, file);
310 
311  ret = parser.Parse();
312  if (DTVConfParser::OK == ret)
313  m_channels = parser.GetChannels();
314  }
315 
316  if (DTVConfParser::OK != ret)
317  {
318  QString msg = (DTVConfParser::ERROR_PARSE == ret) ?
319  tr("Failed to parse '%1'").arg(file) :
321  tr("Programmer Error : incorrect card type") :
322  tr("Failed to open '%1'").arg(file));
323 
324  InformUser(msg);
325  }
326 
327  return ret;
328 }
329 
330 bool ChannelScanner::ImportM3U(uint cardid, const QString &inputname,
331  uint sourceid, bool is_mpts)
332 {
333  (void) cardid;
334  (void) inputname;
335  m_sourceid = sourceid;
336 
337  if (!m_scanMonitor)
338  m_scanMonitor = new ScanMonitor(this);
339 
340  // Create an IPTV scan object
341  m_iptvScanner = new IPTVChannelFetcher(cardid, inputname, sourceid,
342  is_mpts, m_scanMonitor);
343 
344  MonitorProgress(false, false, false, false);
345 
346  m_iptvScanner->Scan();
347 
348  if (is_mpts)
349  m_iptv_channels = m_iptvScanner->GetChannels();
350 
351  return true;
352 }
353 
354 bool ChannelScanner::ImportVBox(uint cardid, const QString &inputname, uint sourceid,
355  bool ftaOnly, ServiceRequirements serviceType)
356 {
357  m_sourceid = sourceid;
358 #ifdef USING_VBOX
359  if (!m_scanMonitor)
360  m_scanMonitor = new ScanMonitor(this);
361 
362  // Create a VBox scan object
363  m_vboxScanner = new VBoxChannelFetcher(cardid, inputname, sourceid, ftaOnly, serviceType, m_scanMonitor);
364 
365  MonitorProgress(false, false, false, false);
366 
367  m_vboxScanner->Scan();
368 
369  return true;
370 #else
371  (void) cardid;
372  (void) inputname;
373  return false;
374 #endif
375 }
376 
377 bool ChannelScanner::ImportExternRecorder(uint cardid, const QString &inputname,
378  uint sourceid)
379 {
380  m_sourceid = sourceid;
381 #if !defined( USING_MINGW ) && !defined( _MSC_VER )
382  if (!m_scanMonitor)
383  m_scanMonitor = new ScanMonitor(this);
384 
385  // Create a External Recorder Channel Fetcher
386  m_ExternRecScanner = new ExternRecChannelScanner(cardid,
387  inputname,
388  sourceid,
389  m_scanMonitor);
390 
391  MonitorProgress(false, false, false, false);
392 
393  m_ExternRecScanner->Scan();
394 
395  return true;
396 #else
397  (void) cardid;
398  (void) inputname;
399 
400  return false;
401 #endif
402 }
403 
405  int scantype,
406  uint cardid,
407  const QString &inputname,
408  uint sourceid,
409  bool do_ignore_signal_timeout,
410  bool do_test_decryption)
411 {
412  uint signal_timeout = 1000;
413  uint channel_timeout = 40000;
414  CardUtil::GetTimeouts(cardid, signal_timeout, channel_timeout);
415 
416  QString device = CardUtil::GetVideoDevice(cardid);
417  if (device.isEmpty())
418  {
419  LOG(VB_GENERAL, LOG_ERR, LOC + "No Device");
420  InformUser(tr("Programmer Error: No Device"));
421  return;
422  }
423 
424  if (!m_scanMonitor)
425  m_scanMonitor = new ScanMonitor(this);
426 
427  QString card_type = CardUtil::GetRawInputType(cardid);
428 
429  if ("DVB" == card_type)
430  {
431  QString sub_type = CardUtil::ProbeDVBType(device).toUpper();
432  bool need_nit = (("QAM" == sub_type) ||
433  ("QPSK" == sub_type) ||
434  ("OFDM" == sub_type) ||
435  ("DVB_T2" == sub_type));
436 
437  // Ugh, Some DVB drivers don't fully support signal monitoring...
438  if ((ScanTypeSetting::TransportScan == scantype) ||
440  {
441  signal_timeout = (do_ignore_signal_timeout) ?
442  channel_timeout * 10 : signal_timeout;
443  }
444 
445  // ensure a minimal signal timeout of 1 second
446  signal_timeout = max(signal_timeout, 1000U);
447 
448  // Make sure that channel_timeout is at least 7 seconds to catch
449  // at least one SDT section. kDVBTableTimeout in ChannelScanSM
450  // ensures that we catch the NIT then.
451  channel_timeout = max(channel_timeout, need_nit * 7 * 1000U);
452  }
453 
454 #ifdef USING_DVB
455  if ("DVB" == card_type)
456  m_channel = new DVBChannel(device);
457 #endif
458 
459 #ifdef USING_V4L2
460  if (("V4L" == card_type) || ("MPEG" == card_type))
461  m_channel = new V4LChannel(nullptr, device);
462 #endif
463 
464 #ifdef USING_HDHOMERUN
465  if ("HDHOMERUN" == card_type)
466  {
467  m_channel = new HDHRChannel(nullptr, device);
468  }
469 #endif // USING_HDHOMERUN
470 
471 #ifdef USING_ASI
472  if ("ASI" == card_type)
473  {
474  m_channel = new ASIChannel(nullptr, device);
475  }
476 #endif // USING_ASI
477 
478 #ifdef USING_IPTV
479  if ("FREEBOX" == card_type)
480  {
481  m_channel = new IPTVChannel(nullptr, device);
482  }
483 #endif
484 
485 #ifdef USING_VBOX
486  if ("VBOX" == card_type)
487  {
488  m_channel = new IPTVChannel(nullptr, device);
489  }
490 #endif
491 
492 #if !defined( USING_MINGW ) && !defined( _MSC_VER )
493  if ("EXTERNAL" == card_type)
494  {
495  m_channel = new ExternalChannel(nullptr, device);
496  }
497 #endif
498 
499  if (!m_channel)
500  {
501  LOG(VB_GENERAL, LOG_ERR, LOC + "Channel not created");
502  InformUser(tr("Programmer Error: Channel not created"));
503  return;
504  }
505 
506  // explicitly set the cardid
507  m_channel->SetInputID(cardid);
508 
509  // If the backend is running this may fail...
510  if (!m_channel->Open())
511  {
512  LOG(VB_GENERAL, LOG_ERR, LOC + "Channel could not be opened");
513  InformUser(tr("Channel could not be opened."));
514  return;
515  }
516 
517  ScanMonitor *lis = m_scanMonitor;
518 
519  m_sigmonScanner = new ChannelScanSM(lis, card_type, m_channel,
520  sourceid, signal_timeout, channel_timeout,
521  inputname, do_test_decryption);
522 
523  // If we know the channel types we can give the signal montior a hint.
524  // Since we unfortunately do not record this info in the DB, we cannot
525  // do this for the other scan types and have to guess later on...
526  switch (scantype)
527  {
529  m_sigmonScanner->SetScanDTVTunerType(DTVTunerType::kTunerTypeATSC);
530  break;
532  m_sigmonScanner->SetScanDTVTunerType(DTVTunerType::kTunerTypeDVBC);
533  break;
535  m_sigmonScanner->SetScanDTVTunerType(DTVTunerType::kTunerTypeDVBT);
536  break;
538  m_sigmonScanner->SetScanDTVTunerType(DTVTunerType::kTunerTypeDVBT2);
539  break;
541  m_sigmonScanner->SetScanDTVTunerType(DTVTunerType::kTunerTypeDVBT);
542  break;
544  m_sigmonScanner->SetScanDTVTunerType(DTVTunerType::kTunerTypeDVBT2);
545  break;
547  m_sigmonScanner->SetScanDTVTunerType(DTVTunerType::kTunerTypeDVBS1);
548  break;
550  m_sigmonScanner->SetScanDTVTunerType(DTVTunerType::kTunerTypeDVBS2);
551  break;
553  m_sigmonScanner->SetScanDTVTunerType(DTVTunerType::kTunerTypeDVBC);
554  break;
555  default:
556  break;
557  }
558 
559  // Signal Meters are connected here
560  SignalMonitor *mon = m_sigmonScanner->GetSignalMonitor();
561  if (mon)
562  mon->AddListener(lis);
563 
564  DVBSignalMonitor *dvbm = nullptr;
565  bool using_rotor = false;
566 
567 #ifdef USING_DVB
568  // cppcheck-suppress redundantAssignment
569  dvbm = m_sigmonScanner->GetDVBSignalMonitor();
570  if (dvbm && mon)
572 #endif // USING_DVB
573 
574  MonitorProgress(mon, mon, dvbm, using_rotor);
575 }
void AddListener(SignalMonitorListener *listener)
static const int kTunerTypeDVBT
static const uint64_t kDVBSigMon_WaitForPos
Wait for rotor to complete turning the antenna.
static const int kTunerTypeATSC
static QString ProbeDVBType(const QString &device)
Definition: cardutil.cpp:675
virtual void Teardown(void)
Provides interface to the tuning hardware when using DVB drivers.
Definition: dvbchannel.h:29
virtual bool ImportM3U(uint cardid, const QString &inputname, uint sourceid, bool is_mpts)
static const int kTunerTypeDVBS1
static bool GetTimeouts(uint inputid, uint &signal_timeout, uint &channel_timeout)
Definition: cardutil.cpp:2098
static const int kTunerTypeDVBS2
static const int kTunerTypeDVBT2
static const int kTunerTypeDVBC
virtual bool ImportExternRecorder(uint cardid, const QString &inputname, uint sourceid)
Parses dvb-utils channel scanner output files.
Definition: dtvconfparser.h:75
unsigned int uint
Definition: compat.h:140
virtual DTVConfParser::return_t ImportDVBUtils(uint sourceid, int cardtype, const QString &file)
Implements tuning for TV cards using the V4L driver API, both versions 1 and 2.
Definition: v4lchannel.h:32
void Scan(int scantype, uint cardid, const QString &inputname, uint sourceid, bool do_ignore_signal_timeout, bool do_follow_nit, bool do_test_decryption, bool do_fta_only, bool do_lcn_only, bool do_complete_only, bool do_full_search, bool do_add_full_ts, ServiceRequirements service_requirements, uint mplexid, const QMap< QString, QString > &startChan, const QString &freq_std, const QString &mod, const QString &tbl, const QString &tbl_start=QString(), const QString &tbl_end=QString())
static QString GetVideoDevice(uint inputid)
Definition: cardutil.h:273
Signal monitoring base class.
Definition: signalmonitor.h:32
Scanning class for cards that support a SignalMonitor class.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
#define LOC
ServiceRequirements
static QString GetRawInputType(uint inputid)
Definition: cardutil.h:271
virtual bool ImportVBox(uint cardid, const QString &inputname, uint sourceid, bool ftaOnly, ServiceRequirements serviceType)
Scans the transport when there is no tuner (for ASI)
virtual void PreScanCommon(int scantype, uint cardid, const QString &inputname, uint sourceid, bool do_ignore_signal_timeout, bool do_test_decryption)
virtual ~ChannelScanner()
bool HasFlags(uint64_t _flags) const