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
32#include <QtGlobal>
33#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
34#include <QtSystemDetection>
35#endif
36
37#include "libmythbase/mythconfig.h"
39
40#include "cardutil.h"
41#include "channelscan_sm.h"
42#include "channelscanner.h"
43#include "iptvchannelfetcher.h"
47#if CONFIG_DVB // for bug in gcc 8.3
49#endif
55#include "scanmonitor.h"
56#include "scanwizardconfig.h"
57
58#define LOC QString("ChScan: ")
59
61{
63
64 if (m_scanMonitor)
65 {
67 m_scanMonitor = nullptr;
68 }
69}
70
72{
74 {
75 delete m_sigmonScanner;
76 m_sigmonScanner = nullptr;
77 }
78
79 if (m_channel)
80 {
81 delete m_channel;
82 m_channel = nullptr;
83 }
84
85 if (m_iptvScanner)
86 {
88 delete m_iptvScanner;
89 m_iptvScanner = nullptr;
90 }
91
92#if CONFIG_VBOX
93 if (m_vboxScanner)
94 {
95 m_vboxScanner->Stop();
96 delete m_vboxScanner;
97 m_vboxScanner = nullptr;
98 }
99#endif
100
101#ifndef Q_OS_WINDOWS
102 if (m_externRecScanner)
103 {
104 m_externRecScanner->Stop();
105 delete m_externRecScanner;
106 m_externRecScanner = nullptr;
107 }
108#endif
109
110 if (m_scanMonitor)
111 {
113 m_scanMonitor = nullptr;
114 }
115}
116
118 int scantype,
119 uint cardid,
120 const QString &inputname,
121 uint sourceid,
122 bool do_ignore_signal_timeout,
123 bool do_follow_nit,
124 bool do_test_decryption,
125 bool do_fta_only,
126 bool do_lcn_only,
127 bool do_complete_only,
128 bool do_full_channel_search,
129 bool do_remove_duplicates,
130 bool do_add_full_ts,
131 ServiceRequirements service_requirements,
132 // Needed for particular scans
133 uint mplexid, // TransportScan
134 const QMap<QString,QString> &startChan, // NITAddScan
135 const QString &freq_std, // FullScan
136 const QString &mod, // FullScan
137 const QString &tbl, // FullScan
138 const QString &tbl_start, // FullScan optional
139 const QString &tbl_end) // FullScan optional
140{
141 m_freeToAirOnly = do_fta_only;
142 m_channelNumbersOnly = do_lcn_only;
143 m_completeOnly = do_complete_only;
144 m_fullSearch = do_full_channel_search;
145 m_removeDuplicates = do_remove_duplicates;
146 m_addFullTS = do_add_full_ts;
147 m_serviceRequirements = service_requirements;
148 m_sourceid = sourceid;
149
150 PreScanCommon(scantype, cardid, inputname,
151 sourceid, do_ignore_signal_timeout, do_test_decryption);
152
153 LOG(VB_CHANSCAN, LOG_INFO, LOC + "Scan()");
154
155 if (!m_sigmonScanner)
156 {
157 LOG(VB_CHANSCAN, LOG_ERR, LOC + "Scan(): scanner does not exist...");
158 return;
159 }
160
163
164 bool ok = false;
165
166 // "Full Scan"
167 if ((ScanTypeSetting::FullScan_ATSC == scantype) ||
168 (ScanTypeSetting::FullScan_DVBC == scantype) ||
169 (ScanTypeSetting::FullScan_DVBT == scantype) ||
170 (ScanTypeSetting::FullScan_DVBT2 == scantype) ||
172 {
173 LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("ScanTransports(%1, %2, %3)")
174 .arg(freq_std, mod, tbl));
175
176 // HACK HACK HACK -- begin
177 // if using QAM we may need additional time... (at least with HD-3000)
178 if ((mod.startsWith("qam", Qt::CaseInsensitive)) &&
180 {
182 }
183 // HACK HACK HACK -- end
184
186
188 sourceid, freq_std, mod, tbl, tbl_start, tbl_end);
189 }
190 // "Full Scan (Tuned)"
191 else if ((ScanTypeSetting::NITAddScan_DVBT == scantype) ||
192 (ScanTypeSetting::NITAddScan_DVBT2 == scantype) ||
193 (ScanTypeSetting::NITAddScan_DVBS == scantype) ||
194 (ScanTypeSetting::NITAddScan_DVBS2 == scantype) ||
196 {
197 LOG(VB_CHANSCAN, LOG_INFO, LOC + "ScanTransports()");
198
199 ok = m_sigmonScanner->ScanTransportsStartingOn(sourceid, startChan);
200 }
201 // "Scan of All Existing Transports"
202 else if (ScanTypeSetting::FullTransportScan == scantype)
203 {
204 LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("ScanExistingTransports of source %1")
205 .arg(sourceid));
206
207 ok = m_sigmonScanner->ScanExistingTransports(sourceid, do_follow_nit);
208 if (ok)
209 {
211 }
212 else
213 {
214 InformUser(tr("Error tuning to transport"));
215 Teardown();
216 }
217 }
218 else if ((ScanTypeSetting::DVBUtilsImport == scantype) && !m_channels.empty())
219 {
220 ok = true;
221
222 LOG(VB_CHANSCAN, LOG_INFO, LOC +
223 QString("ScanForChannels for source %1").arg(sourceid));
224
225 QString card_type = CardUtil::GetRawInputType(cardid);
226 QString sub_type = card_type;
227 if (card_type == "DVB")
228 {
229 QString device = CardUtil::GetVideoDevice(cardid);
230
231 ok = !device.isEmpty();
232 if (ok)
233 sub_type = CardUtil::ProbeDVBType(device).toUpper();
234 }
235
236 if (ok)
237 {
238 ok = m_sigmonScanner->ScanForChannels(sourceid, freq_std,
239 sub_type, m_channels);
240 }
241 if (ok)
242 {
244 }
245 else
246 {
247 InformUser(tr("Error tuning to transport"));
248 Teardown();
249 }
250 }
251 else if (ScanTypeSetting::IPTVImportMPTS == scantype)
252 {
253 if (m_iptvChannels.empty())
254 {
255 LOG(VB_CHANSCAN, LOG_INFO, LOC + "IPTVImportMPTS: no channels");
256 }
257 else
258 {
259 LOG(VB_CHANSCAN, LOG_INFO, LOC +
260 QString("ScanIPTVChannels(%1) IPTV MPTS").arg(sourceid));
261
263 if (ok)
264 {
266 }
267 else
268 {
269 InformUser(tr("Error scanning MPTS in IPTV"));
270 Teardown();
271 }
272 }
273 }
274 else if (ScanTypeSetting::TransportScan == scantype)
275 {
276 LOG(VB_CHANSCAN, LOG_INFO, LOC +
277 QString("ScanTransport(%1)").arg(mplexid));
278
279 ok = m_sigmonScanner->ScanTransport(mplexid, do_follow_nit);
280 }
281 else if (ScanTypeSetting::CurrentTransportScan == scantype)
282 {
283 QString sistandard = "mpeg";
284 LOG(VB_CHANSCAN, LOG_INFO, LOC +
285 "ScanCurrentTransport(" + sistandard + ")");
286 ok = m_sigmonScanner->ScanCurrentTransport(sistandard);
287 }
288 else if (ScanTypeSetting::ExternRecImport == scantype)
289 {
290 LOG(VB_CHANSCAN, LOG_INFO, LOC +
291 "Importing channels from External Recorder");
292 ok = ImportExternRecorder(cardid, inputname, sourceid);
293 }
294
295 if (!ok)
296 {
297 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to handle tune complete.");
298 InformUser(tr("Programmer Error: "
299 "Failed to handle tune complete."));
300 }
301}
302
304 uint sourceid, CardUtil::INPUT_TYPES cardtype, const QString &file)
305{
306 m_sourceid = sourceid;
307 m_channels.clear();
308
310 switch (cardtype) {
314 break;
317 break;
320 break;
323 break;
327 break;
328 default:
330 break;
331 }
332
335 {
337 }
338 else
339 {
340 DTVConfParser parser(type, sourceid, file);
341
342 ret = parser.Parse();
344 m_channels = parser.GetChannels();
345 }
346
348 {
349 QString msg;
351 msg = tr("Failed to parse '%1'").arg(file);
353 msg = tr("Programmer Error : incorrect card type");
354 else
355 msg = tr("Failed to open '%1'").arg(file);
356
357 InformUser(msg);
358 }
359
360 return ret;
361}
362
363bool ChannelScanner::ImportM3U([[maybe_unused]] uint cardid,
364 [[maybe_unused]] const QString &inputname,
365 uint sourceid,
366 bool is_mpts)
367{
368 m_sourceid = sourceid;
369
370 if (!m_scanMonitor)
371 m_scanMonitor = new ScanMonitor(this);
372
373 // Create an IPTV scan object
374 m_iptvScanner = new IPTVChannelFetcher(cardid, inputname, sourceid,
375 is_mpts, m_scanMonitor);
376
377 MonitorProgress(false, false, false, false);
378
380
381 if (is_mpts)
383
384 return true;
385}
386
387bool ChannelScanner::ImportVBox([[maybe_unused]] uint cardid,
388 [[maybe_unused]] const QString &inputname,
389 uint sourceid,
390 [[maybe_unused]] bool ftaOnly,
391 [[maybe_unused]] ServiceRequirements serviceType)
392{
393 m_sourceid = sourceid;
394#if CONFIG_VBOX
395 if (!m_scanMonitor)
396 m_scanMonitor = new ScanMonitor(this);
397
398 // Create a VBox scan object
399 m_vboxScanner = new VBoxChannelFetcher(cardid, inputname, sourceid, ftaOnly, serviceType, m_scanMonitor);
400
401 MonitorProgress(false, false, false, false);
402
403 m_vboxScanner->Scan();
404
405 return true;
406#else
407 return false;
408#endif
409}
410
411bool ChannelScanner::ImportExternRecorder([[maybe_unused]] uint cardid,
412 [[maybe_unused]] const QString &inputname,
413 uint sourceid)
414{
415 m_sourceid = sourceid;
416#ifndef Q_OS_WINDOWS
417 if (!m_scanMonitor)
418 m_scanMonitor = new ScanMonitor(this);
419
420 // Create a External Recorder Channel Fetcher
421 m_externRecScanner = new ExternRecChannelScanner(cardid,
422 inputname,
423 sourceid,
425
426 MonitorProgress(false, false, false, false);
427
428 m_externRecScanner->Scan();
429
430 return true;
431#else
432 return false;
433#endif
434}
435
436bool ChannelScanner::ImportHDHR([[maybe_unused]] uint cardid,
437 [[maybe_unused]] const QString &inputname,
438 uint sourceid,
439 [[maybe_unused]] ServiceRequirements serviceType)
440{
441 m_sourceid = sourceid;
442#if CONFIG_HDHOMERUN
443 if (!m_scanMonitor)
444 m_scanMonitor = new ScanMonitor(this);
445
446 // Create a HDHomeRun scan object
447 m_hdhrScanner = new HDHRChannelFetcher(cardid, inputname, sourceid, serviceType, m_scanMonitor);
448
449 MonitorProgress(false, false, false, false);
450
451 m_hdhrScanner->Scan();
452
453 return true;
454#else
455 return false;
456#endif
457}
458
460 int scantype,
461 uint cardid,
462 const QString &inputname,
463 uint sourceid,
464 [[maybe_unused]] bool do_ignore_signal_timeout,
465 bool do_test_decryption)
466{
467 bool monitor_snr = false;
468 std::chrono::milliseconds signal_timeout = 1s;
469 std::chrono::milliseconds channel_timeout = 40s;
470 CardUtil::GetTimeouts(cardid, signal_timeout, channel_timeout);
471
472 QString device = CardUtil::GetVideoDevice(cardid);
473 if (device.isEmpty())
474 {
475 LOG(VB_GENERAL, LOG_ERR, LOC + "No Device");
476 InformUser(tr("Programmer Error: No Device"));
477 return;
478 }
479
480 if (!m_scanMonitor)
481 m_scanMonitor = new ScanMonitor(this);
482
483 QString card_type = CardUtil::GetRawInputType(cardid);
484
485#if CONFIG_DVB
486 if ("DVB" == card_type)
487 {
488 QString sub_type = CardUtil::ProbeDVBType(device).toUpper();
489 bool need_nit = (("QAM" == sub_type) ||
490 ("QPSK" == sub_type) ||
491 ("OFDM" == sub_type) ||
492 ("DVB_T2" == sub_type));
493
494 // Ugh, Some DVB drivers don't fully support signal monitoring...
495 if ((ScanTypeSetting::TransportScan == scantype) ||
497 {
498 signal_timeout = do_ignore_signal_timeout ?
499 channel_timeout * 10 : signal_timeout;
500 }
501
502 // ensure a minimal signal timeout of 1 second
503 signal_timeout = std::max(signal_timeout, 1000ms);
504
505 // Make sure that channel_timeout is at least 7 seconds to catch
506 // at least one SDT section. kDVBTableTimeout in ChannelScanSM
507 // ensures that we catch the NIT then.
508 channel_timeout = std::max(channel_timeout, static_cast<int>(need_nit) * 7 * 1000ms);
509
510 m_channel = new DVBChannel(device);
511 }
512#endif
513
514#if CONFIG_V4L2
515 if (("V4L" == card_type) || ("MPEG" == card_type))
516 m_channel = new V4LChannel(nullptr, device);
517#endif
518
519#if CONFIG_HDHOMERUN
520 if ("HDHOMERUN" == card_type)
521 {
522 m_channel = new HDHRChannel(nullptr, device);
523 monitor_snr = true;
524 }
525#endif // CONFIG_HDHOMERUN
526
527#if CONFIG_SATIP
528 if ("SATIP" == card_type)
529 {
530 m_channel = new SatIPChannel(nullptr, device);
531 }
532#endif
533
534#if CONFIG_ASI
535 if ("ASI" == card_type)
536 {
537 m_channel = new ASIChannel(nullptr, device);
538 }
539#endif // CONFIG_ASI
540
541#if CONFIG_IPTV
542 if ("FREEBOX" == card_type)
543 {
544 m_channel = new IPTVChannel(nullptr, device);
545 }
546#endif
547
548#if CONFIG_VBOX
549 if ("VBOX" == card_type)
550 {
551 m_channel = new IPTVChannel(nullptr, device);
552 }
553#endif
554
555#ifndef Q_OS_WINDOWS
556 if ("EXTERNAL" == card_type)
557 {
558 m_channel = new ExternalChannel(nullptr, device);
559 }
560#endif
561
562 if (!m_channel)
563 {
564 LOG(VB_GENERAL, LOG_ERR, LOC + "Channel not created");
565 InformUser(tr("Programmer Error: Channel not created"));
566 return;
567 }
568
569 // Explicitly set the cardid
570 m_channel->SetInputID(cardid);
571
572 // If the backend is running this may fail...
573 if (!m_channel->Open())
574 {
575 LOG(VB_GENERAL, LOG_ERR, LOC + "Channel could not be opened");
576 InformUser(tr("Channel could not be opened."));
577 return;
578 }
579
581
582 m_sigmonScanner = new ChannelScanSM(lis, card_type, m_channel,
583 sourceid, signal_timeout, channel_timeout,
584 inputname, do_test_decryption);
585
586 // If we know the channel types we can give the signal monitor a hint.
587 // Since we unfortunately do not record this info in the DB, we cannot
588 // do this for the other scan types and have to guess later on...
589 switch (scantype)
590 {
593 break;
596 break;
599 break;
602 break;
605 break;
608 break;
611 break;
614 break;
617 break;
618 default:
619 break;
620 }
621
622 // Signal Meters are connected here
624 if (mon)
625 mon->AddListener(lis);
626
627 bool using_rotor = false;
628
629#if CONFIG_DVB
631 if (dvbm && mon)
632 {
633 monitor_snr = true;
635 }
636#endif // CONFIG_DVB
637
638 bool monitor_lock = mon != nullptr;
639 bool monitor_strength = mon != nullptr;
640 MonitorProgress(monitor_lock, monitor_strength, monitor_snr, using_rotor);
641}
#define LOC
ServiceRequirements
-*- Mode: c++ -*-
Definition: asichannel.h:15
static QString GetRawInputType(uint inputid)
Definition: cardutil.h:294
static QString ProbeDVBType(const QString &device)
Definition: cardutil.cpp:731
INPUT_TYPES
all the different inputs
Definition: cardutil.h:52
static QString GetVideoDevice(uint inputid)
Definition: cardutil.h:296
static bool GetTimeouts(uint inputid, std::chrono::milliseconds &signal_timeout, std::chrono::milliseconds &channel_timeout)
Definition: cardutil.cpp:2300
virtual bool Open(void)=0
Opens the channel changing hardware for use.
void SetInputID(uint _inputid)
Definition: channelbase.h:101
Scanning class for cards that support a SignalMonitor class.
bool ScanTransportsStartingOn(int sourceid, const QMap< QString, QString > &startChan)
Generates a list of frequencies to scan and adds it to the scanTransport list, and then sets the scan...
bool ScanForChannels(uint sourceid, const QString &std, const QString &cardtype, const DTVChannelList &channels)
bool ScanTransport(uint mplexid, bool follow_nit)
void SetAnalog(bool is_analog)
DVBSignalMonitor * GetDVBSignalMonitor(void)
void SetSignalTimeout(std::chrono::milliseconds val)
SignalMonitor * GetSignalMonitor(void)
bool ScanTransports(int SourceID, const QString &std, const QString &mod, const QString &country, const QString &table_start=QString(), const QString &table_end=QString())
Generates a list of frequencies to scan and adds it to the scanTransport list, and then sets the scan...
std::chrono::milliseconds GetSignalTimeout(void) const
bool ScanExistingTransports(uint sourceid, bool follow_nit)
If we are not already scanning a frequency table, this creates a new frequency table from database an...
bool ScanCurrentTransport(const QString &sistandard)
void StartScanner(void)
Starts the ChannelScanSM event loop.
bool ScanIPTVChannels(uint sourceid, const fbox_chan_map_t &iptv_channels)
void SetScanDTVTunerType(DTVTunerType t)
friend class ScanMonitor
bool m_completeOnly
Only complete channels desired post scan?
virtual DTVConfParser::return_t ImportDVBUtils(uint sourceid, CardUtil::INPUT_TYPES cardtype, const QString &file)
virtual ~ChannelScanner()
IPTVChannelFetcher * m_iptvScanner
ChannelScanSM * m_sigmonScanner
virtual bool ImportHDHR(uint cardid, const QString &inputname, uint sourceid, ServiceRequirements serviceType)
virtual void Teardown(void)
ChannelBase * m_channel
virtual void PreScanCommon(int scantype, uint cardid, const QString &inputname, uint sourceid, bool do_ignore_signal_timeout, bool do_test_decryption)
virtual bool ImportM3U(uint cardid, const QString &inputname, uint sourceid, bool is_mpts)
bool m_fullSearch
Extended search for old channels post scan?
bool m_channelNumbersOnly
Only channels with logical channel numbers desired post scan?
fbox_chan_map_t m_iptvChannels
bool m_freeToAirOnly
Only fta channels desired post scan?
bool m_removeDuplicates
Remove duplicate transports and channels?
DTVChannelList m_channels
imported channels
virtual void MonitorProgress(bool, bool, bool, bool)
virtual bool ImportVBox(uint cardid, const QString &inputname, uint sourceid, bool ftaOnly, ServiceRequirements serviceType)
ScanMonitor * m_scanMonitor
bool m_addFullTS
Add MPTS "full transport stream" channels.
ServiceRequirements m_serviceRequirements
Services desired post scan.
virtual void InformUser(const QString &)=0
virtual bool ImportExternRecorder(uint cardid, const QString &inputname, uint sourceid)
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_channel_search, bool do_remove_duplicates, 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())
Parses dvb-utils channel scanner output files.
Definition: dtvconfparser.h:74
static const int kTunerTypeDVBS2
static const int kTunerTypeDVBT
static const int kTunerTypeDVBC
static const int kTunerTypeDVBS1
static const int kTunerTypeDVBT2
static const int kTunerTypeATSC
Provides interface to the tuning hardware when using DVB drivers.
Definition: dvbchannel.h:31
-*- Mode: c++ -*-
void Stop(void)
Stops the scanning thread running.
fbox_chan_map_t GetChannels(void)
void Scan(void)
Scans the given frequency list.
void ScanPercentComplete(int pct)
virtual void deleteLater(void)
Definition: scanmonitor.cpp:91
void ScanUpdateStatusText(const QString &status)
@ CurrentTransportScan
Scans the transport when there is no tuner (for ASI)
Signal monitoring base class.
Definition: signalmonitor.h:31
void AddListener(SignalMonitorListener *listener)
static const uint64_t kDVBSigMon_WaitForPos
Wait for rotor to complete turning the antenna.
bool HasFlags(uint64_t _flags) const
Implements tuning for TV cards using the V4L driver API, both versions 1 and 2.
Definition: v4lchannel.h:33
unsigned int uint
Definition: compat.h:60
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39