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)
265 else
266 {
267 InformUser(tr("Error scanning MPTS in IPTV"));
268 Teardown();
269 }
270 }
271 }
272 else if (ScanTypeSetting::TransportScan == scantype)
273 {
274 LOG(VB_CHANSCAN, LOG_INFO, LOC +
275 QString("ScanTransport(%1)").arg(mplexid));
276
277 ok = m_sigmonScanner->ScanTransport(mplexid, do_follow_nit);
278 }
279 else if (ScanTypeSetting::CurrentTransportScan == scantype)
280 {
281 QString sistandard = "mpeg";
282 LOG(VB_CHANSCAN, LOG_INFO, LOC +
283 "ScanCurrentTransport(" + sistandard + ")");
284 ok = m_sigmonScanner->ScanCurrentTransport(sistandard);
285 }
286 else if (ScanTypeSetting::ExternRecImport == scantype)
287 {
288 LOG(VB_CHANSCAN, LOG_INFO, LOC +
289 "Importing channels from External Recorder");
290 ok = ImportExternRecorder(cardid, inputname, sourceid);
291 }
292
293 if (!ok)
294 {
295 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to handle tune complete.");
296 InformUser(tr("Programmer Error: "
297 "Failed to handle tune complete."));
298 }
299}
300
302 uint sourceid, CardUtil::INPUT_TYPES cardtype, const QString &file)
303{
304 m_sourceid = sourceid;
305 m_channels.clear();
306
308 switch (cardtype) {
312 break;
315 break;
318 break;
321 break;
325 break;
326 default:
328 break;
329 }
330
334 else
335 {
336 DTVConfParser parser(type, sourceid, file);
337
338 ret = parser.Parse();
340 m_channels = parser.GetChannels();
341 }
342
344 {
345 QString msg;
347 msg = tr("Failed to parse '%1'").arg(file);
349 msg = tr("Programmer Error : incorrect card type");
350 else
351 msg = tr("Failed to open '%1'").arg(file);
352
353 InformUser(msg);
354 }
355
356 return ret;
357}
358
359bool ChannelScanner::ImportM3U([[maybe_unused]] uint cardid,
360 [[maybe_unused]] const QString &inputname,
361 uint sourceid,
362 bool is_mpts)
363{
364 m_sourceid = sourceid;
365
366 if (!m_scanMonitor)
367 m_scanMonitor = new ScanMonitor(this);
368
369 // Create an IPTV scan object
370 m_iptvScanner = new IPTVChannelFetcher(cardid, inputname, sourceid,
371 is_mpts, m_scanMonitor);
372
373 MonitorProgress(false, false, false, false);
374
376
377 if (is_mpts)
379
380 return true;
381}
382
383bool ChannelScanner::ImportVBox([[maybe_unused]] uint cardid,
384 [[maybe_unused]] const QString &inputname,
385 uint sourceid,
386 [[maybe_unused]] bool ftaOnly,
387 [[maybe_unused]] ServiceRequirements serviceType)
388{
389 m_sourceid = sourceid;
390#if CONFIG_VBOX
391 if (!m_scanMonitor)
392 m_scanMonitor = new ScanMonitor(this);
393
394 // Create a VBox scan object
395 m_vboxScanner = new VBoxChannelFetcher(cardid, inputname, sourceid, ftaOnly, serviceType, m_scanMonitor);
396
397 MonitorProgress(false, false, false, false);
398
399 m_vboxScanner->Scan();
400
401 return true;
402#else
403 return false;
404#endif
405}
406
407bool ChannelScanner::ImportExternRecorder([[maybe_unused]] uint cardid,
408 [[maybe_unused]] const QString &inputname,
409 uint sourceid)
410{
411 m_sourceid = sourceid;
412#ifndef Q_OS_WINDOWS
413 if (!m_scanMonitor)
414 m_scanMonitor = new ScanMonitor(this);
415
416 // Create a External Recorder Channel Fetcher
417 m_externRecScanner = new ExternRecChannelScanner(cardid,
418 inputname,
419 sourceid,
421
422 MonitorProgress(false, false, false, false);
423
424 m_externRecScanner->Scan();
425
426 return true;
427#else
428 return false;
429#endif
430}
431
432bool ChannelScanner::ImportHDHR([[maybe_unused]] uint cardid,
433 [[maybe_unused]] const QString &inputname,
434 uint sourceid,
435 [[maybe_unused]] ServiceRequirements serviceType)
436{
437 m_sourceid = sourceid;
438#if CONFIG_HDHOMERUN
439 if (!m_scanMonitor)
440 m_scanMonitor = new ScanMonitor(this);
441
442 // Create a HDHomeRun scan object
443 m_hdhrScanner = new HDHRChannelFetcher(cardid, inputname, sourceid, serviceType, m_scanMonitor);
444
445 MonitorProgress(false, false, false, false);
446
447 m_hdhrScanner->Scan();
448
449 return true;
450#else
451 return false;
452#endif
453}
454
456 int scantype,
457 uint cardid,
458 const QString &inputname,
459 uint sourceid,
460 [[maybe_unused]] bool do_ignore_signal_timeout,
461 bool do_test_decryption)
462{
463 bool monitor_snr = false;
464 std::chrono::milliseconds signal_timeout = 1s;
465 std::chrono::milliseconds channel_timeout = 40s;
466 CardUtil::GetTimeouts(cardid, signal_timeout, channel_timeout);
467
468 QString device = CardUtil::GetVideoDevice(cardid);
469 if (device.isEmpty())
470 {
471 LOG(VB_GENERAL, LOG_ERR, LOC + "No Device");
472 InformUser(tr("Programmer Error: No Device"));
473 return;
474 }
475
476 if (!m_scanMonitor)
477 m_scanMonitor = new ScanMonitor(this);
478
479 QString card_type = CardUtil::GetRawInputType(cardid);
480
481#if CONFIG_DVB
482 if ("DVB" == card_type)
483 {
484 QString sub_type = CardUtil::ProbeDVBType(device).toUpper();
485 bool need_nit = (("QAM" == sub_type) ||
486 ("QPSK" == sub_type) ||
487 ("OFDM" == sub_type) ||
488 ("DVB_T2" == sub_type));
489
490 // Ugh, Some DVB drivers don't fully support signal monitoring...
491 if ((ScanTypeSetting::TransportScan == scantype) ||
493 {
494 signal_timeout = (do_ignore_signal_timeout) ?
495 channel_timeout * 10 : signal_timeout;
496 }
497
498 // ensure a minimal signal timeout of 1 second
499 signal_timeout = std::max(signal_timeout, 1000ms);
500
501 // Make sure that channel_timeout is at least 7 seconds to catch
502 // at least one SDT section. kDVBTableTimeout in ChannelScanSM
503 // ensures that we catch the NIT then.
504 channel_timeout = std::max(channel_timeout, static_cast<int>(need_nit) * 7 * 1000ms);
505
506 m_channel = new DVBChannel(device);
507 }
508#endif
509
510#if CONFIG_V4L2
511 if (("V4L" == card_type) || ("MPEG" == card_type))
512 m_channel = new V4LChannel(nullptr, device);
513#endif
514
515#if CONFIG_HDHOMERUN
516 if ("HDHOMERUN" == card_type)
517 {
518 m_channel = new HDHRChannel(nullptr, device);
519 monitor_snr = true;
520 }
521#endif // CONFIG_HDHOMERUN
522
523#if CONFIG_SATIP
524 if ("SATIP" == card_type)
525 {
526 m_channel = new SatIPChannel(nullptr, device);
527 }
528#endif
529
530#if CONFIG_ASI
531 if ("ASI" == card_type)
532 {
533 m_channel = new ASIChannel(nullptr, device);
534 }
535#endif // CONFIG_ASI
536
537#if CONFIG_IPTV
538 if ("FREEBOX" == card_type)
539 {
540 m_channel = new IPTVChannel(nullptr, device);
541 }
542#endif
543
544#if CONFIG_VBOX
545 if ("VBOX" == card_type)
546 {
547 m_channel = new IPTVChannel(nullptr, device);
548 }
549#endif
550
551#ifndef Q_OS_WINDOWS
552 if ("EXTERNAL" == card_type)
553 {
554 m_channel = new ExternalChannel(nullptr, device);
555 }
556#endif
557
558 if (!m_channel)
559 {
560 LOG(VB_GENERAL, LOG_ERR, LOC + "Channel not created");
561 InformUser(tr("Programmer Error: Channel not created"));
562 return;
563 }
564
565 // Explicitly set the cardid
566 m_channel->SetInputID(cardid);
567
568 // If the backend is running this may fail...
569 if (!m_channel->Open())
570 {
571 LOG(VB_GENERAL, LOG_ERR, LOC + "Channel could not be opened");
572 InformUser(tr("Channel could not be opened."));
573 return;
574 }
575
577
578 m_sigmonScanner = new ChannelScanSM(lis, card_type, m_channel,
579 sourceid, signal_timeout, channel_timeout,
580 inputname, do_test_decryption);
581
582 // If we know the channel types we can give the signal monitor a hint.
583 // Since we unfortunately do not record this info in the DB, we cannot
584 // do this for the other scan types and have to guess later on...
585 switch (scantype)
586 {
589 break;
592 break;
595 break;
598 break;
601 break;
604 break;
607 break;
610 break;
613 break;
614 default:
615 break;
616 }
617
618 // Signal Meters are connected here
620 if (mon)
621 mon->AddListener(lis);
622
623 bool using_rotor = false;
624
625#if CONFIG_DVB
627 if (dvbm && mon)
628 {
629 monitor_snr = true;
631 }
632#endif // CONFIG_DVB
633
634 bool monitor_lock = mon != nullptr;
635 bool monitor_strength = mon != nullptr;
636 MonitorProgress(monitor_lock, monitor_strength, monitor_snr, using_rotor);
637}
#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:2294
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:32
unsigned int uint
Definition: compat.h:60
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39