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 "libmythbase/mythconfig.h"
34
35#include "cardutil.h"
36#include "channelscan_sm.h"
37#include "channelscanner.h"
38#include "iptvchannelfetcher.h"
42#if CONFIG_DVB // for bug in gcc 8.3
44#endif
50#include "scanmonitor.h"
51#include "scanwizardconfig.h"
52
53#define LOC QString("ChScan: ")
54
56{
58
59 if (m_scanMonitor)
60 {
62 m_scanMonitor = nullptr;
63 }
64}
65
67{
69 {
70 delete m_sigmonScanner;
71 m_sigmonScanner = nullptr;
72 }
73
74 if (m_channel)
75 {
76 delete m_channel;
77 m_channel = nullptr;
78 }
79
80 if (m_iptvScanner)
81 {
83 delete m_iptvScanner;
84 m_iptvScanner = nullptr;
85 }
86
87#if CONFIG_VBOX
88 if (m_vboxScanner)
89 {
90 m_vboxScanner->Stop();
91 delete m_vboxScanner;
92 m_vboxScanner = nullptr;
93 }
94#endif
95
96#if !defined( _WIN32 )
97 if (m_externRecScanner)
98 {
99 m_externRecScanner->Stop();
100 delete m_externRecScanner;
101 m_externRecScanner = nullptr;
102 }
103#endif
104
105 if (m_scanMonitor)
106 {
108 m_scanMonitor = nullptr;
109 }
110}
111
113 int scantype,
114 uint cardid,
115 const QString &inputname,
116 uint sourceid,
117 bool do_ignore_signal_timeout,
118 bool do_follow_nit,
119 bool do_test_decryption,
120 bool do_fta_only,
121 bool do_lcn_only,
122 bool do_complete_only,
123 bool do_full_channel_search,
124 bool do_remove_duplicates,
125 bool do_add_full_ts,
126 ServiceRequirements service_requirements,
127 // Needed for particular scans
128 uint mplexid, // TransportScan
129 const QMap<QString,QString> &startChan, // NITAddScan
130 const QString &freq_std, // FullScan
131 const QString &mod, // FullScan
132 const QString &tbl, // FullScan
133 const QString &tbl_start, // FullScan optional
134 const QString &tbl_end) // FullScan optional
135{
136 m_freeToAirOnly = do_fta_only;
137 m_channelNumbersOnly = do_lcn_only;
138 m_completeOnly = do_complete_only;
139 m_fullSearch = do_full_channel_search;
140 m_removeDuplicates = do_remove_duplicates;
141 m_addFullTS = do_add_full_ts;
142 m_serviceRequirements = service_requirements;
143 m_sourceid = sourceid;
144
145 PreScanCommon(scantype, cardid, inputname,
146 sourceid, do_ignore_signal_timeout, do_test_decryption);
147
148 LOG(VB_CHANSCAN, LOG_INFO, LOC + "Scan()");
149
150 if (!m_sigmonScanner)
151 {
152 LOG(VB_CHANSCAN, LOG_ERR, LOC + "Scan(): scanner does not exist...");
153 return;
154 }
155
158
159 bool ok = false;
160
161 // "Full Scan"
162 if ((ScanTypeSetting::FullScan_ATSC == scantype) ||
163 (ScanTypeSetting::FullScan_DVBC == scantype) ||
164 (ScanTypeSetting::FullScan_DVBT == scantype) ||
165 (ScanTypeSetting::FullScan_DVBT2 == scantype) ||
167 {
168 LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("ScanTransports(%1, %2, %3)")
169 .arg(freq_std, mod, tbl));
170
171 // HACK HACK HACK -- begin
172 // if using QAM we may need additional time... (at least with HD-3000)
173 if ((mod.startsWith("qam", Qt::CaseInsensitive)) &&
175 {
177 }
178 // HACK HACK HACK -- end
179
181
183 sourceid, freq_std, mod, tbl, tbl_start, tbl_end);
184 }
185 // "Full Scan (Tuned)"
186 else if ((ScanTypeSetting::NITAddScan_DVBT == scantype) ||
187 (ScanTypeSetting::NITAddScan_DVBT2 == scantype) ||
188 (ScanTypeSetting::NITAddScan_DVBS == scantype) ||
189 (ScanTypeSetting::NITAddScan_DVBS2 == scantype) ||
191 {
192 LOG(VB_CHANSCAN, LOG_INFO, LOC + "ScanTransports()");
193
194 ok = m_sigmonScanner->ScanTransportsStartingOn(sourceid, startChan);
195 }
196 // "Scan of All Existing Transports"
197 else if (ScanTypeSetting::FullTransportScan == scantype)
198 {
199 LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("ScanExistingTransports of source %1")
200 .arg(sourceid));
201
202 ok = m_sigmonScanner->ScanExistingTransports(sourceid, do_follow_nit);
203 if (ok)
204 {
206 }
207 else
208 {
209 InformUser(tr("Error tuning to transport"));
210 Teardown();
211 }
212 }
213 else if ((ScanTypeSetting::DVBUtilsImport == scantype) && !m_channels.empty())
214 {
215 ok = true;
216
217 LOG(VB_CHANSCAN, LOG_INFO, LOC +
218 QString("ScanForChannels for source %1").arg(sourceid));
219
220 QString card_type = CardUtil::GetRawInputType(cardid);
221 QString sub_type = card_type;
222 if (card_type == "DVB")
223 {
224 QString device = CardUtil::GetVideoDevice(cardid);
225
226 ok = !device.isEmpty();
227 if (ok)
228 sub_type = CardUtil::ProbeDVBType(device).toUpper();
229 }
230
231 if (ok)
232 {
233 ok = m_sigmonScanner->ScanForChannels(sourceid, freq_std,
234 sub_type, m_channels);
235 }
236 if (ok)
237 {
239 }
240 else
241 {
242 InformUser(tr("Error tuning to transport"));
243 Teardown();
244 }
245 }
246 else if (ScanTypeSetting::IPTVImportMPTS == scantype)
247 {
248 if (m_iptvChannels.empty())
249 {
250 LOG(VB_CHANSCAN, LOG_INFO, LOC + "IPTVImportMPTS: no channels");
251 }
252 else
253 {
254 LOG(VB_CHANSCAN, LOG_INFO, LOC +
255 QString("ScanIPTVChannels(%1) IPTV MPTS").arg(sourceid));
256
258 if (ok)
260 else
261 {
262 InformUser(tr("Error scanning MPTS in IPTV"));
263 Teardown();
264 }
265 }
266 }
267 else if (ScanTypeSetting::TransportScan == scantype)
268 {
269 LOG(VB_CHANSCAN, LOG_INFO, LOC +
270 QString("ScanTransport(%1)").arg(mplexid));
271
272 ok = m_sigmonScanner->ScanTransport(mplexid, do_follow_nit);
273 }
274 else if (ScanTypeSetting::CurrentTransportScan == scantype)
275 {
276 QString sistandard = "mpeg";
277 LOG(VB_CHANSCAN, LOG_INFO, LOC +
278 "ScanCurrentTransport(" + sistandard + ")");
279 ok = m_sigmonScanner->ScanCurrentTransport(sistandard);
280 }
281 else if (ScanTypeSetting::ExternRecImport == scantype)
282 {
283 LOG(VB_CHANSCAN, LOG_INFO, LOC +
284 "Importing channels from External Recorder");
285 ok = ImportExternRecorder(cardid, inputname, sourceid);
286 }
287
288 if (!ok)
289 {
290 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to handle tune complete.");
291 InformUser(tr("Programmer Error: "
292 "Failed to handle tune complete."));
293 }
294}
295
297 uint sourceid, CardUtil::INPUT_TYPES cardtype, const QString &file)
298{
299 m_sourceid = sourceid;
300 m_channels.clear();
301
303 switch (cardtype) {
307 break;
310 break;
313 break;
316 break;
320 break;
321 default:
323 break;
324 }
325
329 else
330 {
331 DTVConfParser parser(type, sourceid, file);
332
333 ret = parser.Parse();
335 m_channels = parser.GetChannels();
336 }
337
339 {
340 QString msg;
342 msg = tr("Failed to parse '%1'").arg(file);
344 msg = tr("Programmer Error : incorrect card type");
345 else
346 msg = tr("Failed to open '%1'").arg(file);
347
348 InformUser(msg);
349 }
350
351 return ret;
352}
353
354bool ChannelScanner::ImportM3U([[maybe_unused]] uint cardid,
355 [[maybe_unused]] const QString &inputname,
356 uint sourceid,
357 bool is_mpts)
358{
359 m_sourceid = sourceid;
360
361 if (!m_scanMonitor)
362 m_scanMonitor = new ScanMonitor(this);
363
364 // Create an IPTV scan object
365 m_iptvScanner = new IPTVChannelFetcher(cardid, inputname, sourceid,
366 is_mpts, m_scanMonitor);
367
368 MonitorProgress(false, false, false, false);
369
371
372 if (is_mpts)
374
375 return true;
376}
377
378bool ChannelScanner::ImportVBox([[maybe_unused]] uint cardid,
379 [[maybe_unused]] const QString &inputname,
380 uint sourceid,
381 [[maybe_unused]] bool ftaOnly,
382 [[maybe_unused]] ServiceRequirements serviceType)
383{
384 m_sourceid = sourceid;
385#if CONFIG_VBOX
386 if (!m_scanMonitor)
387 m_scanMonitor = new ScanMonitor(this);
388
389 // Create a VBox scan object
390 m_vboxScanner = new VBoxChannelFetcher(cardid, inputname, sourceid, ftaOnly, serviceType, m_scanMonitor);
391
392 MonitorProgress(false, false, false, false);
393
394 m_vboxScanner->Scan();
395
396 return true;
397#else
398 return false;
399#endif
400}
401
402bool ChannelScanner::ImportExternRecorder([[maybe_unused]] uint cardid,
403 [[maybe_unused]] const QString &inputname,
404 uint sourceid)
405{
406 m_sourceid = sourceid;
407#if !defined( _WIN32 )
408 if (!m_scanMonitor)
409 m_scanMonitor = new ScanMonitor(this);
410
411 // Create a External Recorder Channel Fetcher
412 m_externRecScanner = new ExternRecChannelScanner(cardid,
413 inputname,
414 sourceid,
416
417 MonitorProgress(false, false, false, false);
418
419 m_externRecScanner->Scan();
420
421 return true;
422#else
423 return false;
424#endif
425}
426
427bool ChannelScanner::ImportHDHR([[maybe_unused]] uint cardid,
428 [[maybe_unused]] const QString &inputname,
429 uint sourceid,
430 [[maybe_unused]] ServiceRequirements serviceType)
431{
432 m_sourceid = sourceid;
433#if CONFIG_HDHOMERUN
434 if (!m_scanMonitor)
435 m_scanMonitor = new ScanMonitor(this);
436
437 // Create a HDHomeRun scan object
438 m_hdhrScanner = new HDHRChannelFetcher(cardid, inputname, sourceid, serviceType, m_scanMonitor);
439
440 MonitorProgress(false, false, false, false);
441
442 m_hdhrScanner->Scan();
443
444 return true;
445#else
446 return false;
447#endif
448}
449
451 int scantype,
452 uint cardid,
453 const QString &inputname,
454 uint sourceid,
455 [[maybe_unused]] bool do_ignore_signal_timeout,
456 bool do_test_decryption)
457{
458 bool monitor_snr = false;
459 std::chrono::milliseconds signal_timeout = 1s;
460 std::chrono::milliseconds channel_timeout = 40s;
461 CardUtil::GetTimeouts(cardid, signal_timeout, channel_timeout);
462
463 QString device = CardUtil::GetVideoDevice(cardid);
464 if (device.isEmpty())
465 {
466 LOG(VB_GENERAL, LOG_ERR, LOC + "No Device");
467 InformUser(tr("Programmer Error: No Device"));
468 return;
469 }
470
471 if (!m_scanMonitor)
472 m_scanMonitor = new ScanMonitor(this);
473
474 QString card_type = CardUtil::GetRawInputType(cardid);
475
476#if CONFIG_DVB
477 if ("DVB" == card_type)
478 {
479 QString sub_type = CardUtil::ProbeDVBType(device).toUpper();
480 bool need_nit = (("QAM" == sub_type) ||
481 ("QPSK" == sub_type) ||
482 ("OFDM" == sub_type) ||
483 ("DVB_T2" == sub_type));
484
485 // Ugh, Some DVB drivers don't fully support signal monitoring...
486 if ((ScanTypeSetting::TransportScan == scantype) ||
488 {
489 signal_timeout = (do_ignore_signal_timeout) ?
490 channel_timeout * 10 : signal_timeout;
491 }
492
493 // ensure a minimal signal timeout of 1 second
494 signal_timeout = std::max(signal_timeout, 1000ms);
495
496 // Make sure that channel_timeout is at least 7 seconds to catch
497 // at least one SDT section. kDVBTableTimeout in ChannelScanSM
498 // ensures that we catch the NIT then.
499 channel_timeout = std::max(channel_timeout, static_cast<int>(need_nit) * 7 * 1000ms);
500
501 m_channel = new DVBChannel(device);
502 }
503#endif
504
505#if CONFIG_V4L2
506 if (("V4L" == card_type) || ("MPEG" == card_type))
507 m_channel = new V4LChannel(nullptr, device);
508#endif
509
510#if CONFIG_HDHOMERUN
511 if ("HDHOMERUN" == card_type)
512 {
513 m_channel = new HDHRChannel(nullptr, device);
514 monitor_snr = true;
515 }
516#endif // CONFIG_HDHOMERUN
517
518#if CONFIG_SATIP
519 if ("SATIP" == card_type)
520 {
521 m_channel = new SatIPChannel(nullptr, device);
522 }
523#endif
524
525#if CONFIG_ASI
526 if ("ASI" == card_type)
527 {
528 m_channel = new ASIChannel(nullptr, device);
529 }
530#endif // CONFIG_ASI
531
532#if CONFIG_IPTV
533 if ("FREEBOX" == card_type)
534 {
535 m_channel = new IPTVChannel(nullptr, device);
536 }
537#endif
538
539#if CONFIG_VBOX
540 if ("VBOX" == card_type)
541 {
542 m_channel = new IPTVChannel(nullptr, device);
543 }
544#endif
545
546#if !defined( _WIN32 )
547 if ("EXTERNAL" == card_type)
548 {
549 m_channel = new ExternalChannel(nullptr, device);
550 }
551#endif
552
553 if (!m_channel)
554 {
555 LOG(VB_GENERAL, LOG_ERR, LOC + "Channel not created");
556 InformUser(tr("Programmer Error: Channel not created"));
557 return;
558 }
559
560 // Explicitly set the cardid
561 m_channel->SetInputID(cardid);
562
563 // If the backend is running this may fail...
564 if (!m_channel->Open())
565 {
566 LOG(VB_GENERAL, LOG_ERR, LOC + "Channel could not be opened");
567 InformUser(tr("Channel could not be opened."));
568 return;
569 }
570
572
573 m_sigmonScanner = new ChannelScanSM(lis, card_type, m_channel,
574 sourceid, signal_timeout, channel_timeout,
575 inputname, do_test_decryption);
576
577 // If we know the channel types we can give the signal monitor a hint.
578 // Since we unfortunately do not record this info in the DB, we cannot
579 // do this for the other scan types and have to guess later on...
580 switch (scantype)
581 {
584 break;
587 break;
590 break;
593 break;
596 break;
599 break;
602 break;
605 break;
608 break;
609 default:
610 break;
611 }
612
613 // Signal Meters are connected here
615 if (mon)
616 mon->AddListener(lis);
617
618 bool using_rotor = false;
619
620#if CONFIG_DVB
622 if (dvbm && mon)
623 {
624 monitor_snr = true;
626 }
627#endif // CONFIG_DVB
628
629 bool monitor_lock = mon != nullptr;
630 bool monitor_strength = mon != nullptr;
631 MonitorProgress(monitor_lock, monitor_strength, monitor_snr, using_rotor);
632}
#define LOC
ServiceRequirements
-*- Mode: c++ -*-
Definition: asichannel.h:15
static QString GetRawInputType(uint inputid)
Definition: cardutil.h:292
static QString ProbeDVBType(const QString &device)
Definition: cardutil.cpp:725
INPUT_TYPES
all the different inputs
Definition: cardutil.h:50
static QString GetVideoDevice(uint inputid)
Definition: cardutil.h:294
static bool GetTimeouts(uint inputid, std::chrono::milliseconds &signal_timeout, std::chrono::milliseconds &channel_timeout)
Definition: cardutil.cpp:2288
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: freesurround.h:24
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39