MythTV master
channelscan_sm.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// C++ includes
31#include <algorithm>
32#include <thread>
33#include <utility>
34
35// Qt includes
36#include <QMutexLocker>
37#include <QObject>
38
39// MythTV includes - Other Libs
40#include "libmythbase/mthread.h"
41#include "libmythbase/mythdb.h"
43#include "libmythbase/mythconfig.h"
45
46// MythTV includes - General
47#include "cardutil.h"
48#include "channelscan_sm.h"
49#include "channelutil.h"
50#include "frequencies.h"
51#include "scanwizardconfig.h"
52#include "sourceutil.h"
53
54// MythTV includes - DTV
55#include "mpeg/scanstreamdata.h"
57
58// MythTV includes - ATSC
59#include "mpeg/atsctables.h"
60
61// MythTV includes - DVB
62#include "mpeg/dvbtables.h"
64
68
72const std::chrono::milliseconds ChannelScanSM::kDVBTableTimeout = 30s;
74const std::chrono::milliseconds ChannelScanSM::kATSCTableTimeout = 10s;
76const std::chrono::milliseconds ChannelScanSM::kMPEGTableTimeout = 15s;
77
78// Freesat and Sky
79static const uint kRegionUndefined = 0xFFFF; // Not regional
80
81QString ChannelScanSM::loc(const ChannelScanSM *siscan)
82{
83 if (siscan && siscan->m_channel)
84 return QString("ChannelScanSM[%1]").arg(siscan->m_channel->GetInputID());
85 return "ChannelScanSM(u)";
86}
87
88#define LOC (ChannelScanSM::loc(this) + ": ")
89
90static constexpr qint64 kDecryptionTimeout { 4250 };
91
92static const QString kATSCChannelFormat = "%1.%2";
93
95{
96 public:
97 ScannedChannelInfo() = default;
98
99 bool IsEmpty() const
100 {
101 return m_pats.empty() && m_pmts.empty() &&
102 m_programEncryptionStatus.isEmpty() &&
103 !m_mgt && m_cvcts.empty() &&
104 m_tvcts.empty() && m_nits.empty() &&
105 m_sdts.empty() && m_bats.empty();
106 }
107
108 // MPEG
111 QMap<uint,uint> m_programEncryptionStatus; // pnum->enc_status
112
113 // ATSC
114 const MasterGuideTable *m_mgt {nullptr};
117
118 // DVB
122};
123
148 const QString &cardtype, ChannelBase *channel,
149 int sourceID, std::chrono::milliseconds signal_timeout,
150 std::chrono::milliseconds channel_timeout, QString inputname,
151 bool test_decryption)
152 : // Set in constructor
153 m_scanMonitor(scan_monitor),
154 m_channel(channel),
155 m_signalMonitor(SignalMonitor::Init(cardtype, m_channel->GetInputID(),
156 channel, true)),
157 m_sourceID(sourceID),
158 m_signalTimeout(signal_timeout),
159 m_channelTimeout(channel_timeout),
160 m_inputName(std::move(inputname)),
161 m_testDecryption(test_decryption),
162 // Misc
163 m_analogSignalHandler(new AnalogSignalHandler(this))
164{
167
168 // Create a stream data for digital signal monitors
170 if (dtvSigMon)
171 {
172 LOG(VB_CHANSCAN, LOG_INFO, LOC + "Connecting up DTVSignalMonitor");
173 auto *data = new ScanStreamData();
174
176 query.prepare(
177 "SELECT dvb_nit_id, bouquet_id, region_id, lcnoffset "
178 "FROM videosource "
179 "WHERE videosource.sourceid = :SOURCEID");
180 query.bindValue(":SOURCEID", m_sourceID);
181 if (!query.exec() || !query.isActive())
182 {
183 MythDB::DBError("ChannelScanSM", query);
184 }
185 else if (query.next())
186 {
187 int nitid = query.value(0).toInt();
188 data->SetRealNetworkID(nitid);
189 LOG(VB_CHANSCAN, LOG_INFO, LOC +
190 QString("Setting NIT-ID to %1").arg(nitid));
191
192 m_bouquetId = query.value(1).toUInt();
193 m_regionId = query.value(2).toUInt();
194 m_lcnOffset = query.value(3).toUInt();
195 m_nitId = nitid > 0 ? nitid : 0;
196 }
197
198 LOG(VB_CHANSCAN, LOG_INFO, LOC +
199 QString("Freesat/Sky bouquet_id:%1 region_id:%2")
200 .arg(m_bouquetId).arg(m_regionId));
201
202 dtvSigMon->SetStreamData(data);
207
208#if CONFIG_DVB
209 auto *dvbchannel = dynamic_cast<DVBChannel*>(m_channel);
210 if (dvbchannel && dvbchannel->GetRotor())
212#endif
213
214 data->AddMPEGListener(this);
215 data->AddATSCMainListener(this);
216 data->AddDVBMainListener(this);
217 data->AddDVBOtherListener(this);
218 }
219}
220
222{
223 StopScanner();
224 LOG(VB_CHANSCAN, LOG_INFO, LOC + "ChannelScanSM Stopped");
225
226 ScanStreamData *sd = nullptr;
228 {
230 }
231
232 if (m_signalMonitor)
233 {
235 delete m_signalMonitor;
236 m_signalMonitor = nullptr;
237 }
238
239 delete sd;
240
242 {
244 m_analogSignalHandler = nullptr;
245 }
246
248}
249
250void ChannelScanSM::SetAnalog(bool is_analog)
251{
253
254 if (is_analog)
256}
257
259{
260 QMutexLocker locker(&m_lock);
261
262 QString cur_chan = (*m_current).m_friendlyName;
263 QStringList list = cur_chan.split(" ", Qt::SkipEmptyParts);
264 QString freqid = (list.size() >= 2) ? list[1] : cur_chan;
265
266 QString msg = QObject::tr("Updated Channel %1").arg(cur_chan);
267
269 {
270 int chanid = ChannelUtil::CreateChanID(m_sourceID, freqid);
271
272 QString callsign = QString("%1-%2")
273 .arg(ChannelUtil::GetUnknownCallsign()).arg(chanid);
274
276 0 /* mplexid */,
278 chanid,
279 callsign,
280 "" /* service name */,
281 freqid /* channum */,
282 0 /* service id */,
283 0 /* ATSC major channel */,
284 0 /* ATSC minor channel */,
285 false /* use on air guide */,
286 kChannelVisible /* visible */,
287 freqid);
288
289 msg = ok ?
290 QObject::tr("Added Channel %1").arg(cur_chan) :
291 QObject::tr("Failed to add channel %1").arg(cur_chan);
292 }
293 else
294 {
295 // nothing to do here, XMLTV has better info
296 }
297
299
300 // tell UI we are done with these channels
301 if (m_scanning)
302 {
304 m_waitingForTables = false;
306 m_dvbt2Tried = true;
307 }
308}
309
321bool ChannelScanSM::ScanExistingTransports(uint sourceid, bool follow_nit)
322{
323 if (m_scanning)
324 return false;
325
326 m_scanTransports.clear();
328
329 std::vector<uint> multiplexes = SourceUtil::GetMplexIDs(sourceid);
330
331 if (multiplexes.empty())
332 {
333 LOG(VB_CHANSCAN, LOG_ERR, LOC + "Unable to find any transports for " +
334 QString("sourceid %1").arg(sourceid));
335
336 return false;
337 }
338
339 LOG(VB_CHANSCAN, LOG_INFO, LOC +
340 QString("Found %1 transports for ").arg(multiplexes.size()) +
341 QString("sourceid %1").arg(sourceid));
342
343 for (uint multiplex : multiplexes)
344 AddToList(multiplex);
345
346 m_extendScanList = follow_nit;
347 m_waitingForTables = false;
349 if (!m_scanTransports.empty())
350 {
351 m_nextIt = m_scanTransports.begin();
352 m_scanning = true;
353 }
354 else
355 {
356 LOG(VB_CHANSCAN, LOG_ERR, LOC +
357 "Unable to find add any transports for " +
358 QString("sourceid %1").arg(sourceid));
359
360 return false;
361 }
362
363 return m_scanning;
364}
365
366void ChannelScanSM::LogLines(const QString& string)
367{
368 if (VERBOSE_LEVEL_CHECK(VB_CHANSCAN, LOG_DEBUG))
369 {
370 QStringList lines = string.split('\n');
371 for (const QString& line : std::as_const(lines))
372 LOG(VB_CHANSCAN, LOG_DEBUG, line);
373 }
374}
375
377{
378 QMutexLocker locker(&m_lock);
379
380 LOG(VB_CHANSCAN, LOG_INFO, LOC +
381 QString("Got a Program Association Table for %1")
382 .arg((*m_current).m_friendlyName));
383 LogLines(pat->toString());
384
385 // Add pmts to list, so we can do MPEG scan properly.
387 if (nullptr == monitor)
388 return;
389 ScanStreamData *sd = monitor->GetScanStreamData();
390 for (uint i = 0; i < pat->ProgramCount(); ++i)
391 {
392 sd->AddListeningPID(pat->ProgramPID(i));
393 }
394}
395
397{
398 QMutexLocker locker(&m_lock);
399
400 LOG(VB_CHANSCAN, LOG_INFO, LOC +
401 QString("Got a Conditional Access Table for %1")
402 .arg((*m_current).m_friendlyName));
403 LogLines(cat->toString());
404}
405
406void ChannelScanSM::HandlePMT(uint /*program_num*/, const ProgramMapTable *pmt)
407{
408 QMutexLocker locker(&m_lock);
409
410 LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Got a Program Map Table for %1 program %2 (0x%3)")
411 .arg((*m_current).m_friendlyName).arg(pmt->ProgramNumber())
412 .arg(pmt->ProgramNumber(),4,16,QChar('0')));
413 LogLines(pmt->toString());
414
416 pmt->IsEncrypted(GetDTVChannel()->GetSIStandard()))
418
419 UpdateChannelInfo(true);
420}
421
423{
424 QMutexLocker locker(&m_lock);
425
426 LOG(VB_CHANSCAN, LOG_INFO, LOC +
427 QString("Got a Virtual Channel Table for %1")
428 .arg((*m_current).m_friendlyName));
429 LogLines(vct->toString());
430
431 for (uint i = 0; !m_currentTestingDecryption && i < vct->ChannelCount(); ++i)
432 {
433 if (vct->IsAccessControlled(i))
434 {
436 }
437 }
438
439 UpdateChannelInfo(true);
440}
441
443{
444 QMutexLocker locker(&m_lock);
445
446 LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Got the Master Guide for %1")
447 .arg((*m_current).m_friendlyName));
448 LogLines(mgt->toString());
449
450 UpdateChannelInfo(true);
451}
452
461{
462 QMutexLocker locker(&m_lock);
463
464 LOG(VB_CHANSCAN, LOG_INFO, LOC +
465 QString("Got a Service Description Table for %1 section %2/%3")
466 .arg((*m_current).m_friendlyName)
467 .arg(sdt->Section()).arg(sdt->LastSection()));
468 LogLines(sdt->toString());
469
470 // If this is Astra 28.2 add start listening for Freesat BAT and SDTo
471 if (!m_setOtherTables && (
474 {
476 if (nullptr != monitor)
477 {
478 ScanStreamData *stream = monitor->GetScanStreamData();
479 if (nullptr != stream)
480 {
481 stream->SetFreesatAdditionalSI(true);
482 m_setOtherTables = true;
483 // The whole BAT & SDTo group comes round in 10s
485 // Delay processing the SDT until we've seen BATs and SDTos
486 m_otherTableTime = std::chrono::milliseconds(m_timer.elapsed()) + m_otherTableTimeout;
487
488 LOG(VB_CHANSCAN, LOG_INFO, LOC +
489 QString("SDT has OriginalNetworkID %1, look for "
490 "additional Freesat SI").arg(sdt->OriginalNetworkID()));
491 }
492 }
493 }
494
495 if (!m_timer.hasExpired(m_otherTableTime.count()))
496 {
497 // Set the version for the SDT so we see it again.
499 if (nullptr != monitor)
500 {
501 monitor->GetDVBStreamData()->
502 SetVersionSDT(sdt->TSID(), -1, 0);
503 }
504 }
505
506 uint id = sdt->OriginalNetworkID() << 16 | sdt->TSID();
507 m_tsScanned.insert(id);
508
509 for (uint i = 0; !m_currentTestingDecryption && i < sdt->ServiceCount(); ++i)
510 {
511 if (sdt->IsEncrypted(i))
512 {
514 }
515 }
516
517 UpdateChannelInfo(true);
518}
519
521{
522 QMutexLocker locker(&m_lock);
523
524 LOG(VB_CHANSCAN, LOG_INFO, LOC +
525 QString("Got a Network Information Table id %1 for %2 section %3/%4")
526 .arg(nit->NetworkID()).arg((*m_current).m_friendlyName)
527 .arg(nit->Section()).arg(nit->LastSection()));
528 LogLines(nit->toString());
529
530 UpdateChannelInfo(true);
531}
532
534{
535 QMutexLocker locker(&m_lock);
536
537 LOG(VB_CHANSCAN, LOG_INFO, LOC +
538 QString("Got a Bouquet Association Table id %1 for %2 section %3/%4")
539 .arg(bat->BouquetID()).arg((*m_current).m_friendlyName)
540 .arg(bat->Section()).arg(bat->LastSection()));
541 LogLines(bat->toString());
542
543 m_otherTableTime = std::chrono::milliseconds(m_timer.elapsed()) + m_otherTableTimeout;
544
545 for (uint i = 0; i < bat->TransportStreamCount(); ++i)
546 {
547 uint tsid = bat->TSID(i);
548 uint netid = bat->OriginalNetworkID(i);
549 desc_list_t parsed =
552 // Look for default authority
553 const unsigned char *def_auth =
555 const unsigned char *serv_list =
557
558 if (def_auth && serv_list)
559 {
560 DefaultAuthorityDescriptor authority(def_auth);
561 ServiceListDescriptor services(serv_list);
562 if (!authority.IsValid() || !services.IsValid())
563 continue;
564
565 for (uint j = 0; j < services.ServiceCount(); ++j)
566 {
567 // If the default authority is given in the SDT this
568 // overrides any definition in the BAT (or in the NIT)
569 LOG(VB_CHANSCAN, LOG_DEBUG, LOC +
570 QString("Found default authority '%1' in BAT for service nid %2 tid %3 sid %4")
571 .arg(authority.DefaultAuthority())
572 .arg(netid).arg(tsid).arg(services.ServiceID(j)));
573 uint64_t index = ((uint64_t)netid << 32) | (tsid << 16) |
574 services.ServiceID(j);
575 if (! m_defAuthorities.contains(index))
576 m_defAuthorities[index] = authority.DefaultAuthority();
577 }
578 }
579 }
580 UpdateChannelInfo(true);
581}
582
584{
585 QMutexLocker locker(&m_lock);
586#if 0
587 LOG(VB_CHANSCAN, LOG_DEBUG, LOC +
588 QString("Got a Service Description Table (other) for Transport ID %1 section %2/%3")
589 .arg(tsid).arg(sdt->Section()).arg(sdt->LastSection()));
590 LogLines(sdt->toString());
591#endif
592 m_otherTableTime = std::chrono::milliseconds(m_timer.elapsed()) + m_otherTableTimeout;
593
594 uint netid = sdt->OriginalNetworkID();
595
596 for (uint i = 0; i < sdt->ServiceCount(); ++i)
597 {
598 uint serviceId = sdt->ServiceID(i);
599 desc_list_t parsed =
602 // Look for default authority
603 const unsigned char *def_auth =
605 if (def_auth)
606 {
607 DefaultAuthorityDescriptor authority(def_auth);
608 if (!authority.IsValid())
609 continue;
610#if 0
611 LOG(VB_CHANSCAN, LOG_DEBUG, LOC +
612 QString("Found default authority '%1' in SDTo for service nid %2 tid %3 sid %4")
613 .arg(authority.DefaultAuthority())
614 .arg(netid).arg(tsid).arg(serviceId));
615#endif
616 m_defAuthorities[((uint64_t)netid << 32) | (tsid << 16) | serviceId] =
617 authority.DefaultAuthority();
618 }
619 }
620}
621
623{
624 QMutexLocker locker(&m_lock);
625
627
630
631 UpdateChannelInfo(true);
632}
633
635{
636 if (!m_currentInfo || m_currentInfo->m_pmts.empty())
637 {
638 LOG(VB_GENERAL, LOG_ERR, LOC + "Can't monitor decryption -- no pmts");
640 return false;
641 }
642
643 while (true)
644 {
645 uint pnum = 0;
646 QMap<uint, uint>::const_iterator it = m_currentEncryptionStatus.cbegin();
647#if 0
648 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("%1/%2 checked")
649 .arg(currentEncryptionStatusChecked.size())
650 .arg(currentEncryptionStatus.size()));
651#endif
652 while (it != m_currentEncryptionStatus.cend())
653 {
655 {
656 pnum = it.key();
657 break;
658 }
659 ++it;
660 }
661
662 if (!pnum)
663 break;
664
666
667 if (!m_testDecryption)
668 {
670 continue;
671 }
672
673 const ProgramMapTable *pmt = nullptr;
674 for (uint i = 0; !pmt && (i < m_currentInfo->m_pmts.size()); ++i)
675 {
676 pmt = (m_currentInfo->m_pmts[i]->ProgramNumber() == pnum) ?
677 m_currentInfo->m_pmts[i] : nullptr;
678 }
679
680 if (pmt)
681 {
682 QString cur_chan;
683 QString cur_chan_tr;
684 GetCurrentTransportInfo(cur_chan, cur_chan_tr);
685
686 QString msg_tr =
687 QObject::tr("Program %1 Testing Decryption")
688 .arg(pnum);
689 QString msg =
690 QString("%1 -- Testing decryption of program %2")
691 .arg(cur_chan).arg(pnum);
692
694 LOG(VB_CHANSCAN, LOG_INFO, LOC + msg);
695
696#if CONFIG_DVB
697 if (GetDVBChannel())
698 GetDVBChannel()->SetPMT(pmt);
699#endif // CONFIG_DVB
700
702 if (nullptr != monitor)
703 {
705 monitor->GetStreamData()->TestDecryption(pmt);
706 }
707
709 m_timer.start();
710 return true;
711 }
712
713 LOG(VB_GENERAL, LOG_INFO, LOC +
714 QString("Can't monitor decryption of program %1 -- no pmt")
715 .arg(pnum));
716
717 }
718
720 return false;
721}
722
724{
727
728 const DTVChannel *chan = GetDTVChannel();
729
730 if (!chan)
731 return type;
732
733 std::vector<DTVTunerType> tts = chan->GetTunerTypes();
734
735 for (auto & tt : tts)
736 {
737 if (tt == type)
738 return type;
739 }
740
741 if (!tts.empty())
742 return tts[0];
743
744 return type;
745}
746
748{
749 LOG(VB_CHANSCAN, LOG_DEBUG, LOC + QString("%1 NIT nid:%2 fr:%3 section:%4/%5 ts count:%6 ")
750 .arg(__func__).arg(nit->NetworkID()).arg(nit_frequency).arg(nit->Section()).arg(nit->LastSection())
751 .arg(nit->TransportStreamCount()));
752
753 for (uint i = 0; i < nit->TransportStreamCount(); ++i)
754 {
755 uint32_t tsid = nit->TSID(i);
756 uint32_t netid = nit->OriginalNetworkID(i);
757 uint32_t id = netid << 16 | tsid;
758
759 if (m_tsScanned.contains(id) || m_extendTransports.contains(id))
760 continue;
761
762 const desc_list_t& list =
765
766 for (const auto * const item : list)
767 {
768 uint64_t frequency = 0;
769 const MPEGDescriptor desc(item);
770 uint tag = desc.DescriptorTag();
771// QString tagString = desc.DescriptorTagString();
772
774 switch (tag)
775 {
777 {
779 if (cd.IsValid())
780 frequency = cd.FrequencyHz();
782 break;
783 }
785 {
786 switch (desc.DescriptorTagExtension())
787 {
789 {
791 continue; // T2 descriptor not yet used
792 }
793 default:
794 continue; // Next descriptor
795 }
796 }
798 {
800 if (cd.IsValid())
801 frequency = cd.FrequencykHz();
803 break;
804 }
806 {
808 continue; // S2 descriptor not yet used
809 }
811 {
812 const CableDeliverySystemDescriptor cd(desc);
813 if (cd.IsValid())
814 frequency = cd.FrequencyHz();
816 break;
817 }
818 default:
819 continue; // Next descriptor
820 }
821
822 // Have now a delivery system descriptor
823 tt = GuessDTVTunerType(tt);
824 DTVMultiplex tuning;
825 if (tuning.FillFromDeliverySystemDesc(tt, desc))
826 {
827 LOG(VB_CHANSCAN, LOG_DEBUG, QString("NIT onid:%1 add ts(%2):%3 %4")
828 .arg(netid).arg(i).arg(tsid).arg(tuning.toString()));
829 m_extendTransports[id] = tuning;
830 }
831 else
832 {
833 LOG(VB_CHANSCAN, LOG_DEBUG, QString("NIT onid:%1 cannot add ts(%2):%3 fr:%4")
834 .arg(netid).arg(i).arg(tsid).arg(frequency));
835 }
836
837 // Next TS in loop
838 break;
839 }
840 }
841}
842
843bool ChannelScanSM::UpdateChannelInfo(bool wait_until_complete)
844{
845 QMutexLocker locker(&m_mutex);
846
847 if (m_current == m_scanTransports.end())
848 return true;
849
850 if (wait_until_complete && !m_waitingForTables)
851 return true;
852
853 if (wait_until_complete && m_currentTestingDecryption)
854 return false;
855
857 if (!dtv_sm)
858 return false;
859
860 const ScanStreamData *sd = dtv_sm->GetScanStreamData();
861
862 if (!m_currentInfo)
864
865 bool transport_tune_complete = true;
866
867 // MPEG
868
869 // Grab PAT tables
870 pat_vec_t pattmp = sd->GetCachedPATs();
871 QMap<uint,bool> tsid_checked;
872 for (auto & pat : pattmp)
873 {
874 uint tsid = pat->TransportStreamID();
875 if (tsid_checked[tsid])
876 continue;
877 tsid_checked[tsid] = true;
878 if (m_currentInfo->m_pats.contains(tsid))
879 continue;
880
881 if (!wait_until_complete || sd->HasCachedAllPAT(tsid))
882 {
883 m_currentInfo->m_pats[tsid] = sd->GetCachedPATs(tsid);
884 if (!m_currentInfo->m_pmts.empty())
885 {
887 m_currentInfo->m_pmts.clear();
888 }
889 }
890 else
891 {
892 transport_tune_complete = false;
893 }
894 }
895 transport_tune_complete &= !pattmp.empty();
896 sd->ReturnCachedPATTables(pattmp);
897
898 // Grab PMT tables
899 if ((!wait_until_complete || sd->HasCachedAllPMTs()) &&
900 m_currentInfo->m_pmts.empty())
902
903 // ATSC
904 if (!m_currentInfo->m_mgt && sd->HasCachedMGT())
906
907 if ((!wait_until_complete || sd->HasCachedAllCVCTs()) &&
908 m_currentInfo->m_cvcts.empty())
909 {
911 }
912
913 if ((!wait_until_complete || sd->HasCachedAllTVCTs()) &&
914 m_currentInfo->m_tvcts.empty())
915 {
917 }
918
919 // DVB
920 if ((!wait_until_complete || sd->HasCachedAllNIT()) &&
921 (m_currentInfo->m_nits.empty() ||
922 m_timer.hasExpired(m_otherTableTime.count())))
923 {
925 }
926
927 sdt_vec_t sdttmp = sd->GetCachedSDTs();
928 tsid_checked.clear();
929 for (auto & sdt : sdttmp)
930 {
931 uint tsid = sdt->TSID();
932 if (tsid_checked[tsid])
933 continue;
934 tsid_checked[tsid] = true;
935 if (m_currentInfo->m_sdts.contains(tsid))
936 continue;
937
938 if (!wait_until_complete || sd->HasCachedAllSDT(tsid))
939 m_currentInfo->m_sdts[tsid] = sd->GetCachedSDTSections(tsid);
940 }
941 sd->ReturnCachedSDTTables(sdttmp);
942
943 if ((!wait_until_complete || sd->HasCachedAllBATs()) &&
944 (m_currentInfo->m_bats.empty() ||
945 m_timer.hasExpired(m_otherTableTime.count())))
946 {
948 }
949
950 // Check if transport tuning is complete
951 if (transport_tune_complete)
952 {
953 transport_tune_complete &= !m_currentInfo->m_pmts.empty();
954
955 if (!(sd->HasCachedMGT() || sd->HasCachedAnyNIT()))
956 {
957 transport_tune_complete = false;
958 }
959
960 if (sd->HasCachedMGT() || sd->HasCachedAnyVCTs())
961 {
962 transport_tune_complete &= sd->HasCachedMGT();
963 transport_tune_complete &=
964 (!m_currentInfo->m_tvcts.empty() || !m_currentInfo->m_cvcts.empty());
965 }
966 else if (sd->HasCachedAnyNIT() || sd->HasCachedAnySDTs())
967 {
968 transport_tune_complete &= !m_currentInfo->m_nits.empty();
969 transport_tune_complete &= !m_currentInfo->m_sdts.empty();
970 }
971 if (sd->HasCachedAnyBATs())
972 {
973 transport_tune_complete &= !m_currentInfo->m_bats.empty();
974 }
975 if (transport_tune_complete)
976 {
977 uint tsid = dtv_sm->GetTransportID();
978 LOG(VB_CHANSCAN, LOG_INFO, LOC +
979 QString("\nTable status after transport tune complete:") +
980 QString("\nsd->HasCachedAnyNIT(): %1").arg(sd->HasCachedAnyNIT()) +
981 QString("\nsd->HasCachedAnySDTs(): %1").arg(sd->HasCachedAnySDTs()) +
982 QString("\nsd->HasCachedAnyBATs(): %1").arg(sd->HasCachedAnyBATs()) +
983 QString("\nsd->HasCachedAllPMTs(): %1").arg(sd->HasCachedAllPMTs()) +
984 QString("\nsd->HasCachedAllNIT(): %1").arg(sd->HasCachedAllNIT()) +
985 QString("\nsd->HasCachedAllSDT(%1): %2").arg(tsid,5).arg(sd->HasCachedAllSDT(tsid)) +
986 QString("\nsd->HasCachedAllBATs(): %1").arg(sd->HasCachedAllBATs()) +
987 QString("\nsd->HasCachedMGT(): %1").arg(sd->HasCachedMGT()) +
988 QString("\nsd->HasCachedAnyVCTs(): %1").arg(sd->HasCachedAnyVCTs()) +
989 QString("\nsd->HasCachedAllCVCTs(): %1").arg(sd->HasCachedAllCVCTs()) +
990 QString("\nsd->HasCachedAllTVCTs(): %1").arg(sd->HasCachedAllTVCTs()) +
991 QString("\ncurrentInfo->m_pmts.empty(): %1").arg(m_currentInfo->m_pmts.empty()) +
992 QString("\ncurrentInfo->m_nits.empty(): %1").arg(m_currentInfo->m_nits.empty()) +
993 QString("\ncurrentInfo->m_sdts.empty(): %1").arg(m_currentInfo->m_sdts.empty()) +
994 QString("\ncurrentInfo->m_bats.empty(): %1").arg(m_currentInfo->m_bats.empty()) +
995 QString("\ncurrentInfo->m_cvtcs.empty(): %1").arg(m_currentInfo->m_cvcts.empty()) +
996 QString("\ncurrentInfo->m_tvtcs.empty(): %1").arg(m_currentInfo->m_tvcts.empty()));
997 }
998 }
999 if (!wait_until_complete)
1000 transport_tune_complete = true;
1001 if (transport_tune_complete)
1002 {
1003 LOG(VB_CHANSCAN, LOG_INFO, LOC +
1004 QString("transport_tune_complete: wait_until_complete %1").arg(wait_until_complete));
1005 }
1006
1007 if (transport_tune_complete && !m_currentEncryptionStatus.empty())
1009 {
1010 //GetDTVSignalMonitor()->GetStreamData()->StopTestingDecryption();
1011
1013 return false;
1014
1015 QMap<uint, uint>::const_iterator it = m_currentEncryptionStatus.cbegin();
1016 for (; it != m_currentEncryptionStatus.cend(); ++it)
1017 {
1019
1020 if (m_testDecryption)
1021 {
1022 QString msg_tr1 = QObject::tr("Program %1").arg(it.key());
1023 QString msg_tr2 = QObject::tr("Unknown decryption status");
1024 if (kEncEncrypted == *it)
1025 msg_tr2 = QObject::tr("Encrypted");
1026 else if (kEncDecrypted == *it)
1027 msg_tr2 = QObject::tr("Decrypted");
1028 QString msg_tr =QString("%1 %2").arg(msg_tr1, msg_tr2);
1030 }
1031
1032 QString msg = QString("Program %1").arg(it.key());
1033 if (kEncEncrypted == *it)
1034 msg = msg + " -- Encrypted";
1035 else if (kEncDecrypted == *it)
1036 msg = msg + " -- Decrypted";
1037 else if (kEncUnknown == *it)
1038 msg = msg + " -- Unknown decryption status";
1039
1040 LOG(VB_CHANSCAN, LOG_INFO, LOC + msg);
1041 }
1042 }
1043
1044 // Append transports from the NIT to the scan list
1045 if (transport_tune_complete && m_extendScanList &&
1046 !m_currentInfo->m_nits.empty())
1047 {
1048 // Update transport with delivery system descriptors from the NIT
1049 auto it = m_currentInfo->m_nits.begin();
1050 while (it != m_currentInfo->m_nits.end())
1051 {
1052 UpdateScanTransports((*m_current).m_tuning.m_frequency, *it);
1053 ++it;
1054 }
1055 }
1056
1057 // Start scanning next transport if we are done with this one..
1058 if (transport_tune_complete)
1059 {
1060 QString cchan;
1061 QString cchan_tr;
1062 uint cchan_cnt = GetCurrentTransportInfo(cchan, cchan_tr);
1063 m_channelsFound += cchan_cnt;
1064 QString chan_tr = QObject::tr("%1 -- Timed out").arg(cchan_tr);
1065 QString chan = QString( "%1 -- Timed out").arg(cchan);
1066 QString msg_tr = "";
1067 QString msg = "";
1068
1069 if (!m_currentInfo->IsEmpty())
1070 {
1074 item.m_networkID = dtv_sm->GetNetworkID();
1075 item.m_transportID = dtv_sm->GetTransportID();
1076
1078 {
1080 {
1082 }
1083 }
1085 {
1087 }
1089 {
1090 if (m_dvbt2Tried)
1092 else
1094 }
1095
1096 LOG(VB_CHANSCAN, LOG_INFO, LOC +
1097 QString("Adding %1 offset:%2 ss:%3")
1098 .arg(item.m_tuning.toString()).arg(m_current.offset())
1099 .arg(item.m_signalStrength));
1100
1101 LOG(VB_CHANSCAN, LOG_DEBUG, LOC +
1102 QString("%1(%2) m_inputName: %3 ").arg(__FUNCTION__).arg(__LINE__).arg(m_inputName) +
1103 QString("tunerType:%1 %2 ").arg(m_scanDTVTunerType.toInt()).arg(m_scanDTVTunerType.toString()) +
1104 QString("m_modSys:%1 %2 ").arg(item.m_tuning.m_modSys.toInt()).arg(item.m_tuning.m_modSys.toString()) +
1105 QString("m_dvbt2Tried:%1").arg(m_dvbt2Tried));
1106
1108 m_currentInfo = nullptr;
1109 }
1110 else
1111 {
1112 delete m_currentInfo;
1113 m_currentInfo = nullptr;
1114 }
1115
1117 if (HasTimedOut())
1118 {
1119 msg_tr = cchan_cnt ?
1120 QObject::tr("%1 possible channels").arg(cchan_cnt) :
1121 QObject::tr("no channels");
1122 msg_tr = QString("%1, %2").arg(chan_tr, msg_tr);
1123 msg = cchan_cnt ?
1124 QString("%1 possible channels").arg(cchan_cnt) :
1125 QString("no channels");
1126 msg = QString("%1, %2").arg(chan_tr, msg);
1127 }
1128 else if ((m_current != m_scanTransports.end()) &&
1129 m_timer.hasExpired((*m_current).m_timeoutTune.count()) &&
1130 sm && !sm->HasSignalLock())
1131 {
1132 msg_tr = QObject::tr("%1, no signal").arg(chan_tr);
1133 msg = QString("%1, no signal").arg(chan);
1134 }
1135 else
1136 {
1137 msg_tr = QObject::tr("%1 -- Found %2 probable channels")
1138 .arg(cchan_tr).arg(cchan_cnt);
1139
1140 msg = QString("%1 -- Found %2 probable channels")
1141 .arg(cchan).arg(cchan_cnt);
1142 }
1143
1145 LOG(VB_CHANSCAN, LOG_INFO, LOC + msg);
1146
1149
1150 m_setOtherTables = false;
1151 m_otherTableTime = 0ms;
1152
1153 if (m_scanning)
1154 {
1157 m_waitingForTables = false;
1159 m_dvbt2Tried = true;
1160 }
1161 else
1162 {
1165 }
1166
1167 return true;
1168 }
1169
1170 return false;
1171}
1172
1173// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
1174#define PCM_INFO_INIT(SISTD) \
1175 ChannelInsertInfo &info = pnum_to_dbchan[pnum]; \
1176 info.m_dbMplexId = mplexid; info.m_sourceId = m_sourceID; \
1177 info.m_serviceId = pnum; info.m_freqId = freqidStr; \
1178 info.m_siStandard = SISTD;
1179
1181 const VirtualChannelTable *vct, uint i)
1182{
1183 if (vct->ModulationMode(i) == 0x01 /* NTSC Modulation */ ||
1184 vct->ServiceType(i) == 0x01 /* Analog TV */)
1185 {
1186 info.m_siStandard = "ntsc";
1187 info.m_format = "ntsc";
1188 }
1189
1190 info.m_callSign = vct->ShortChannelName(i);
1191
1192 info.m_serviceName = vct->GetExtendedChannelName(i);
1193 if (info.m_serviceName.isEmpty())
1194 info.m_serviceName = vct->ShortChannelName(i);
1195
1196 info.m_chanNum.clear();
1197
1198 info.m_serviceId = vct->ProgramNumber(i);
1199 info.m_atscMajorChannel = vct->MajorChannel(i);
1200 info.m_atscMinorChannel = vct->MinorChannel(i);
1201
1202 info.m_useOnAirGuide = !vct->IsHidden(i) || !vct->IsHiddenInGuide(i);
1203
1204 info.m_hidden = vct->IsHidden(i);
1205 info.m_hiddenInGuide = vct->IsHiddenInGuide(i);
1206
1207 info.m_vctTsId = vct->TransportStreamID();
1208 info.m_vctChanTsId = vct->ChannelTransportStreamID(i);
1209 info.m_isEncrypted |= vct->IsAccessControlled(i);
1210 info.m_isDataService = vct->ServiceType(i) == 0x04;
1211 info.m_isAudioService = vct->ServiceType(i) == 0x03;
1212
1213 info.m_inVct = true;
1214}
1215
1217 const ServiceDescriptionTable *sdt, uint i,
1218 const QMap<uint64_t, QString> &defAuthorities)
1219{
1220 // HACK begin -- special exception for these networks
1221 // this enables useonairguide by default for all matching channels
1222 // (dbver == "1067")
1223 bool force_guide_present = (
1224 // Telenor (NO)
1226#if 0 // #9592#comment:23 - meanwhile my provider changed his signaling
1227 // Kabelplus (AT) formerly Kabelsignal, registered to NDS, see #9592
1228 (sdt->OriginalNetworkID() == 222) ||
1229#endif
1230 // ERT (GR) from the private temporary allocation, see #9592:comment:17
1231 (sdt->OriginalNetworkID() == 65330) ||
1232 // Digitenne (NL) see #13427
1234 );
1235 // HACK end -- special exception for these networks
1236
1237 // Figure out best service name and callsign...
1239 QString callsign;
1240 QString service_name;
1241 if (desc)
1242 {
1243 callsign = desc->ServiceShortName();
1244 if (callsign.trimmed().isEmpty())
1245 {
1246 callsign = QString("%1-%2-%3")
1247 .arg(ChannelUtil::GetUnknownCallsign()).arg(sdt->TSID())
1248 .arg(sdt->ServiceID(i));
1249 }
1250
1251 service_name = desc->ServiceName();
1252 if (service_name.trimmed().isEmpty())
1253 service_name.clear();
1254
1255 info.m_serviceType = desc->ServiceType();
1256 info.m_isDataService =
1257 (desc && !desc->IsDTV() && !desc->IsDigitalAudio());
1258 info.m_isAudioService = (desc && desc->IsDigitalAudio());
1259 delete desc;
1260 }
1261 else
1262 {
1263 LOG(VB_CHANSCAN, LOG_INFO, "ChannelScanSM: " +
1264 QString("No ServiceDescriptor for onid %1 tid %2 sid %3")
1265 .arg(sdt->OriginalNetworkID()).arg(sdt->TSID()).arg(sdt->ServiceID(i)));
1266 }
1267
1268 if (info.m_callSign.isEmpty())
1269 info.m_callSign = callsign;
1270 if (info.m_serviceName.isEmpty())
1271 info.m_serviceName = service_name;
1272
1273 info.m_useOnAirGuide =
1274 sdt->HasEITPresentFollowing(i) ||
1275 sdt->HasEITSchedule(i) ||
1276 force_guide_present;
1277
1278 info.m_hidden = false;
1279 info.m_hiddenInGuide = false;
1280 info.m_serviceId = sdt->ServiceID(i);
1281 info.m_sdtTsId = sdt->TSID();
1282 info.m_origNetId = sdt->OriginalNetworkID();
1283 info.m_inSdt = true;
1284
1285 desc_list_t parsed =
1287 sdt->ServiceDescriptorsLength(i));
1288 // Look for default authority
1289 const unsigned char *def_auth =
1291 if (def_auth)
1292 {
1293 DefaultAuthorityDescriptor authority(def_auth);
1294 if (authority.IsValid())
1295 {
1296#if 0
1297 LOG(VB_CHANSCAN, LOG_DEBUG,
1298 QString("ChannelScanSM: Found default authority '%1' in SDT for service onid %2 tid %3 sid %4")
1299 .arg(authority.DefaultAuthority())
1300 .arg(info.m_origNetId).arg(info.m_sdtTsId).arg(info.m_serviceId));
1301#endif
1302 info.m_defaultAuthority = authority.DefaultAuthority();
1303 return;
1304 }
1305 }
1306
1307 // If no authority in the SDT then use the one found in the BAT or the SDTo
1308 uint64_t index = (uint64_t)info.m_origNetId << 32 |
1309 info.m_sdtTsId << 16 | info.m_serviceId;
1310 if (defAuthorities.contains(index))
1311 info.m_defaultAuthority = defAuthorities[index];
1312
1313 // Is this service relocated from somewhere else?
1315 if (srdesc)
1316 {
1317 info.m_oldOrigNetId = srdesc->OldOriginalNetworkID();
1318 info.m_oldTsId = srdesc->OldTransportID();
1319 info.m_oldServiceId = srdesc->OldServiceID();
1320
1321 LOG(VB_CHANSCAN, LOG_DEBUG, "ChannelScanSM: " +
1322 QString("Service '%1' onid:%2 tid:%3 sid:%4 ")
1323 .arg(info.m_serviceName)
1324 .arg(info.m_origNetId)
1325 .arg(info.m_sdtTsId)
1326 .arg(info.m_serviceId) +
1327 QString(" relocated from onid:%1 tid:%2 sid:%3")
1328 .arg(info.m_oldOrigNetId)
1329 .arg(info.m_oldTsId)
1330 .arg(info.m_oldServiceId));
1331
1332 delete srdesc;
1333 }
1334
1335}
1336
1338 QString &cur_chan, QString &cur_chan_tr) const
1339{
1340 if (m_current.iter() == m_scanTransports.end())
1341 {
1342 cur_chan.clear();
1343 cur_chan_tr.clear();
1344 return 0;
1345 }
1346
1347 uint max_chan_cnt = 0;
1348
1349 QMap<uint,ChannelInsertInfo> list = GetChannelList(m_current, m_currentInfo);
1350 {
1351 for (const auto & info : std::as_const(list))
1352 {
1353 max_chan_cnt +=
1354 (info.m_inPat || info.m_inPmt ||
1355 info.m_inSdt || info.m_inVct) ? 1 : 0;
1356 }
1357 }
1358
1359 QString offset_str_tr = m_current.offset() ?
1360 QObject::tr(" offset %2").arg(m_current.offset()) : "";
1361 cur_chan_tr = QString("%1%2")
1362 .arg((*m_current).m_friendlyName, offset_str_tr);
1363
1364 QString offset_str = m_current.offset() ?
1365 QString(" offset %2").arg(m_current.offset()) : "";
1366 cur_chan = QString("%1%2")
1367 .arg((*m_current).m_friendlyName, offset_str);
1368
1369 return max_chan_cnt;
1370}
1371
1372QMap<uint,ChannelInsertInfo>
1374 ScannedChannelInfo *scan_info) const
1375{
1376 QMap<uint,ChannelInsertInfo> pnum_to_dbchan;
1377
1378 uint mplexid = (*trans_info).m_mplexid;
1379 int freqid = (*trans_info).m_friendlyNum;
1380 QString freqidStr = freqid ? QString::number(freqid) : QString("");
1381 QString iptv_channel = (*trans_info).m_iptvChannel;
1382
1383 // channels.conf
1384 const DTVChannelInfoList &echan = (*trans_info).m_expectedChannels;
1385 for (const auto & chan : echan)
1386 {
1387 uint pnum = chan.m_serviceid;
1388 PCM_INFO_INIT("mpeg");
1389 info.m_serviceName = chan.m_name;
1390 info.m_inChannelsConf = true;
1391 }
1392
1393 // PATs
1394 for (const auto& pat_list : std::as_const(scan_info->m_pats))
1395 {
1396 for (const auto *pat : pat_list)
1397 {
1398 bool could_be_opencable = false;
1399 for (uint i = 0; i < pat->ProgramCount(); ++i)
1400 {
1401 if ((pat->ProgramNumber(i) == 0) &&
1402 (pat->ProgramPID(i) == PID::SCTE_PSIP_PID))
1403 {
1404 could_be_opencable = true;
1405 }
1406 }
1407
1408 for (uint i = 0; i < pat->ProgramCount(); ++i)
1409 {
1410 uint pnum = pat->ProgramNumber(i);
1411 if (pnum)
1412 {
1413 PCM_INFO_INIT("mpeg");
1414 info.m_patTsId = pat->TransportStreamID();
1415 info.m_couldBeOpencable = could_be_opencable;
1416 info.m_inPat = true;
1417 }
1418 }
1419 }
1420 }
1421
1422 // PMTs
1423 for (const auto *pmt : scan_info->m_pmts)
1424 {
1425 uint pnum = pmt->ProgramNumber();
1426 PCM_INFO_INIT("mpeg");
1427 for (uint i = 0; i < pmt->StreamCount(); ++i)
1428 {
1429 info.m_couldBeOpencable |=
1430 (StreamID::OpenCableVideo == pmt->StreamType(i));
1431 }
1432
1434 pmt->ProgramInfo(), pmt->ProgramInfoLength(),
1436
1437 for (auto & desc : descs)
1438 {
1439 RegistrationDescriptor reg(desc);
1440 if (!reg.IsValid())
1441 continue;
1442 if (reg.FormatIdentifierString() == "CUEI" ||
1443 reg.FormatIdentifierString() == "SCTE")
1444 info.m_couldBeOpencable = true;
1445 }
1446
1447 info.m_isEncrypted |= pmt->IsEncrypted(GetDTVChannel()->GetSIStandard());
1448 info.m_inPmt = true;
1449 }
1450
1451 // Cable VCTs
1452 for (const auto *cvct : scan_info->m_cvcts)
1453 {
1454 for (uint i = 0; i < cvct->ChannelCount(); ++i)
1455 {
1456 uint pnum = cvct->ProgramNumber(i);
1457 PCM_INFO_INIT("atsc");
1458 update_info(info, cvct, i);
1459
1460 // One-part channel number, as defined in the ATSC Standard:
1461 // Program and System Information Protocol for Terrestrial Broadcast and Cable
1462 // Doc. A65/2013 7 August 2013 page 35
1463 if ((info.m_atscMajorChannel & 0x3F0) == 0x3F0)
1464 {
1465 info.m_chanNum = QString::number(((info.m_atscMajorChannel & 0x00F) << 10) + info.m_atscMinorChannel);
1466 }
1467 else
1468 {
1469 info.m_chanNum = kATSCChannelFormat.arg(info.m_atscMajorChannel).arg(info.m_atscMinorChannel);
1470 }
1471 }
1472 }
1473
1474 // Terrestrial VCTs
1475 for (const auto *tvct : scan_info->m_tvcts)
1476 {
1477 for (uint i = 0; i < tvct->ChannelCount(); ++i)
1478 {
1479 uint pnum = tvct->ProgramNumber(i);
1480 PCM_INFO_INIT("atsc");
1481 update_info(info, tvct, i);
1482 info.m_chanNum = kATSCChannelFormat.arg(info.m_atscMajorChannel).arg(info.m_atscMinorChannel);
1483 }
1484 }
1485
1486 // SDTs
1487 QString siStandard = (scan_info->m_mgt == nullptr) ? "dvb" : "atsc";
1488 for (const auto& sdt_list : std::as_const(scan_info->m_sdts))
1489 {
1490 for (const auto *sdt_it : sdt_list)
1491 {
1492 for (uint i = 0; i < sdt_it->ServiceCount(); ++i)
1493 {
1494 uint pnum = sdt_it->ServiceID(i);
1495 PCM_INFO_INIT(siStandard);
1496 update_info(info, sdt_it, i, m_defAuthorities);
1497 }
1498 }
1499 }
1500
1501 // NIT
1502 QMap<qlonglong, uint> ukChanNums;
1503 QMap<qlonglong, uint> scnChanNums;
1504 QMap<uint,ChannelInsertInfo>::iterator dbchan_it;
1505 for (dbchan_it = pnum_to_dbchan.begin();
1506 dbchan_it != pnum_to_dbchan.end(); ++dbchan_it)
1507 {
1508 ChannelInsertInfo &info = *dbchan_it;
1509
1510 // NIT
1511 for (const auto *item : scan_info->m_nits)
1512 {
1513 for (uint i = 0; i < item->TransportStreamCount(); ++i)
1514 {
1515 const NetworkInformationTable *nit = item;
1516 if ((nit->TSID(i) == info.m_sdtTsId) &&
1517 (nit->OriginalNetworkID(i) == info.m_origNetId))
1518 {
1519 info.m_netId = nit->NetworkID();
1520 info.m_inNit = true;
1521 }
1522 else
1523 {
1524 continue;
1525 }
1526
1527 // Descriptors in the transport stream loop of this transport in the NIT
1528 const desc_list_t &list =
1531
1532 // Presence of T2 delivery system descriptor indicates DVB-T2 delivery system
1533 // DVB BlueBook A038 (Feb 2019) page 104, paragraph 6.4.6.3
1534 {
1535 const unsigned char *desc =
1538
1539 if (desc)
1540 {
1541 T2DeliverySystemDescriptor t2tdsd(desc);
1542 if (t2tdsd.IsValid())
1543 {
1544 (*trans_info).m_tuning.m_modSys = DTVModulationSystem::kModulationSystem_DVBT2;
1545 }
1546 }
1547 }
1548
1549 // Logical channel numbers
1550 {
1551 const unsigned char *desc =
1554
1555 if (desc)
1556 {
1557 DVBLogicalChannelDescriptor uklist(desc);
1558 if (!uklist.IsValid())
1559 continue;
1560 for (uint j = 0; j < uklist.ChannelCount(); ++j)
1561 {
1562 ukChanNums[((qlonglong)info.m_origNetId<<32) |
1563 uklist.ServiceID(j)] =
1564 uklist.ChannelNumber(j);
1565 }
1566 }
1567 }
1568
1569 // HD Simulcast logical channel numbers
1570 {
1571 const unsigned char *desc =
1574
1575 if (desc)
1576 {
1577 DVBSimulcastChannelDescriptor scnlist(desc);
1578 if (!scnlist.IsValid())
1579 continue;
1580 for (uint j = 0; j < scnlist.ChannelCount(); ++j)
1581 {
1582 scnChanNums[((qlonglong)info.m_origNetId<<32) |
1583 scnlist.ServiceID(j)] =
1584 scnlist.ChannelNumber(j);
1585 }
1586 }
1587 }
1588 }
1589 }
1590 }
1591
1592 // BAT
1593
1594 // Channel numbers for Freesat and Sky on Astra 28.2E
1595 //
1596 // Get the Logical Channel Number (LCN) information from the BAT.
1597 // The first filter is on the bouquet ID.
1598 // Then collect all LCN for the selected region and
1599 // for the common (undefined) region with id 0xFFFF.
1600 // The LCN of the selected region has priority; if
1601 // a service is not defined there then the value of the LCN
1602 // table of the common region is used.
1603 // This works because the BAT of each transport contains
1604 // the LCN of all transports and services for all bouquets.
1605 //
1606 // For reference, this website has the Freesat and Sky channel numbers
1607 // for each bouquet and region:
1608 // https://www.satellite-calculations.com/DVB/28.2E/28E_FreeSat_ChannelNumber.php
1609 // https://www.satellite-calculations.com/DVB/28.2E/28E_Sky_ChannelNumber.php
1610 //
1611
1612 // Lookup table from LCN to service ID
1613 QMap<uint,qlonglong> lcn_sid;
1614
1615 for (const auto *bat : scan_info->m_bats)
1616 {
1617 // Only the bouquet selected by user
1618 if (bat->BouquetID() != m_bouquetId)
1619 continue;
1620
1621 for (uint t = 0; t < bat->TransportStreamCount(); ++t)
1622 {
1623 uint netid = bat->OriginalNetworkID(t);
1624
1625 // No network check to allow scanning on all Sky satellites
1626#if 0
1627 if (!(netid == OriginalNetworkID::SES2 ||
1628 netid == OriginalNetworkID::BBC ||
1629 netid == OriginalNetworkID::SKYNZ ))
1630 continue;
1631#endif
1632 desc_list_t parsed =
1633 MPEGDescriptor::Parse(bat->TransportDescriptors(t),
1634 bat->TransportDescriptorsLength(t));
1635
1636 uint priv_dsid = 0;
1637 for (const auto *item : parsed)
1638 {
1640 {
1642 if (pd.IsValid())
1643 priv_dsid = pd.PrivateDataSpecifier();
1644 }
1645
1646 // Freesat logical channels
1647 if (priv_dsid == PrivateDataSpecifierID::FSAT &&
1649 {
1650 FreesatLCNDescriptor ld(item);
1651 if (ld.IsValid())
1652 {
1653 for (uint i = 0; i<ld.ServiceCount(); i++)
1654 {
1655 uint service_id = ld.ServiceID(i);
1656 for (uint j=0; j<ld.LCNCount(i); j++)
1657 {
1658 uint region_id = ld.RegionID(i,j);
1659 uint lcn = ld.LogicalChannelNumber(i,j);
1660 if (region_id == m_regionId)
1661 {
1662 lcn_sid[lcn] = ((qlonglong)netid<<32) | service_id;
1663 }
1664 else if (region_id == kRegionUndefined)
1665 {
1666 if (lcn_sid.value(lcn,0) == 0)
1667 lcn_sid[lcn] = ((qlonglong)netid<<32) | service_id;
1668 }
1669 }
1670 }
1671 }
1672 }
1673
1674 // Sky logical channels
1675 if (priv_dsid == PrivateDataSpecifierID::BSB1 &&
1677 {
1678 SkyLCNDescriptor ld(item);
1679 if (ld.IsValid())
1680 {
1681 uint region_id = ld.RegionID();
1682 for (uint i = 0; i<ld.ServiceCount(); i++)
1683 {
1684 uint service_id = ld.ServiceID(i);
1685 uint lcn = ld.LogicalChannelNumber(i);
1686 if (region_id == m_regionId)
1687 {
1688 lcn_sid[lcn] = ((qlonglong)netid<<32) | service_id;
1689 }
1690 else if (region_id == kRegionUndefined)
1691 {
1692 if (lcn_sid.value(lcn,0) == 0)
1693 lcn_sid[lcn] = ((qlonglong)netid<<32) | service_id;
1694 }
1695#if 0
1696 LOG(VB_CHANSCAN, LOG_INFO, LOC +
1697 QString("LCN bid:%1 tid:%2 rid:%3 sid:%4 lcn:%5")
1698 .arg(bat->BouquetID()).arg(bat->TSID(t)).arg(region_id).arg(service_id).arg(lcn));
1699#endif
1700 }
1701 }
1702 }
1703 }
1704 }
1705 }
1706
1707 // Create the reverse table from service id to LCN.
1708 // If the service has more than one logical
1709 // channel number the lowest number is used.
1710 QMap<qlonglong, uint> sid_lcn;
1711 QMap<uint, qlonglong>::const_iterator r = lcn_sid.constEnd();
1712 while (r != lcn_sid.constBegin())
1713 {
1714 --r;
1715 qlonglong sid = r.value();
1716 uint lcn = r.key();
1717 sid_lcn[sid] = lcn;
1718 }
1719
1720 // ------------------------------------------------------------------------
1721
1722 // Get DVB Logical Channel Numbers
1723 for (dbchan_it = pnum_to_dbchan.begin();
1724 dbchan_it != pnum_to_dbchan.end(); ++dbchan_it)
1725 {
1726 ChannelInsertInfo &info = *dbchan_it;
1727 qlonglong key = ((qlonglong)info.m_origNetId<<32) | info.m_serviceId;
1728 QMap<qlonglong, uint>::const_iterator it;
1729
1730 // DVB HD Simulcast channel numbers
1731 //
1732 // The HD simulcast channel number table gives the correct channel number
1733 // when HD and SD versions of the same channel are simultaneously broadcast
1734 // and the receiver is capable of receiving the HD signal.
1735 // The latter is assumed correct for a MythTV system.
1736 //
1737 it = scnChanNums.constFind(key);
1738 if (it != scnChanNums.constEnd())
1739 {
1740 info.m_simulcastChannel = *it;
1741 }
1742
1743 // DVB Logical Channel Numbers (a.k.a. UK channel numbers)
1744 it = ukChanNums.constFind(key);
1745 if (it != ukChanNums.constEnd())
1746 {
1747 info.m_logicalChannel = *it;
1748 }
1749
1750 // Freesat and Sky channel numbers
1751 it = sid_lcn.constFind(key);
1752 if (it != sid_lcn.constEnd())
1753 {
1754 info.m_logicalChannel = *it;
1755 }
1756
1757 LOG(VB_CHANSCAN, LOG_INFO, LOC +
1758 QString("service %1 (0x%2) lcn:%3 scn:%4 callsign '%5'")
1759 .arg(info.m_serviceId).arg(info.m_serviceId,4,16,QChar('0'))
1760 .arg(info.m_logicalChannel).arg(info.m_simulcastChannel).arg(info.m_callSign));
1761 }
1762
1763 // Get QAM/SCTE/MPEG channel numbers
1764 for (dbchan_it = pnum_to_dbchan.begin();
1765 dbchan_it != pnum_to_dbchan.end(); ++dbchan_it)
1766 {
1767 ChannelInsertInfo &info = *dbchan_it;
1768
1769 if (!info.m_chanNum.isEmpty())
1770 continue;
1771
1772 if ((info.m_siStandard == "mpeg") ||
1773 (info.m_siStandard == "scte") ||
1774 (info.m_siStandard == "opencable"))
1775 {
1776 if (info.m_freqId.isEmpty())
1777 {
1778 info.m_chanNum = QString("%1-%2")
1779 .arg(info.m_sourceId)
1780 .arg(info.m_serviceId);
1781 }
1782 else
1783 {
1784 info.m_chanNum = QString("%1-%2")
1785 .arg(info.m_freqId)
1786 .arg(info.m_serviceId);
1787 }
1788 }
1789 }
1790
1791 // Get IPTV channel numbers
1792 for (dbchan_it = pnum_to_dbchan.begin();
1793 dbchan_it != pnum_to_dbchan.end(); ++dbchan_it)
1794 {
1795 ChannelInsertInfo &info = *dbchan_it;
1796
1797 if (!info.m_chanNum.isEmpty())
1798 continue;
1799
1800 if (!iptv_channel.isEmpty())
1801 {
1802 info.m_chanNum = iptv_channel;
1803 if (info.m_serviceId)
1804 info.m_chanNum += "-" + QString::number(info.m_serviceId);
1805 }
1806 }
1807
1808 // Check for decryption success
1809 for (dbchan_it = pnum_to_dbchan.begin();
1810 dbchan_it != pnum_to_dbchan.end(); ++dbchan_it)
1811 {
1812 uint pnum = dbchan_it.key();
1813 ChannelInsertInfo &info = *dbchan_it;
1814 info.m_decryptionStatus = scan_info->m_programEncryptionStatus[pnum];
1815 }
1816
1817 return pnum_to_dbchan;
1818}
1819
1821{
1823
1824 uint cardid = m_channel->GetInputID();
1825
1826 DTVTunerType tuner_type;
1827 tuner_type = GuessDTVTunerType(tuner_type);
1828
1829 for (const auto & it : std::as_const(m_channelList))
1830 {
1831 QMap<uint,ChannelInsertInfo> pnum_to_dbchan =
1832 GetChannelList(it.first, it.second);
1833
1834 ScanDTVTransport item((*it.first).m_tuning, tuner_type, cardid);
1835 item.m_iptvTuning = (*(it.first)).m_iptvTuning;
1836 item.m_signalStrength = (*(it.first)).m_signalStrength;
1837 item.m_networkID = (*(it.first)).m_networkID;
1838 item.m_transportID = (*(it.first)).m_transportID;
1839
1840 QMap<uint,ChannelInsertInfo>::iterator dbchan_it;
1841 for (dbchan_it = pnum_to_dbchan.begin();
1842 dbchan_it != pnum_to_dbchan.end(); ++dbchan_it)
1843 {
1844 item.m_channels.push_back(*dbchan_it);
1845 }
1846
1847 if (!item.m_channels.empty())
1848 {
1849 // Add a MPTS channel which can be used to record the entire transport
1850 // stream multiplex. These recordings can be used in stream analyzer software.
1851 if (addFullTS)
1852 {
1853 // Find the first channel that is present in PAT/PMT and use that as
1854 // template for the new MPTS channel.
1856 for (auto & channel : pnum_to_dbchan)
1857 {
1858 if (channel.m_inPat && channel.m_inPmt)
1859 {
1860 info = channel;
1861 break;
1862 }
1863 }
1864
1865 // If we cannot find a valid channel then use the first channel in
1866 // the list as template for the new MPTS channel.
1867 if (info.m_serviceId == 0)
1868 {
1869 dbchan_it = pnum_to_dbchan.begin();
1870 info = *dbchan_it;
1871 }
1872
1873 // Use transport stream ID as (fake) service ID to use in callsign
1874 // and as channel number.
1875 info.m_serviceId = info.m_sdtTsId ? info.m_sdtTsId : info.m_patTsId;
1876 info.m_chanNum = QString("%1").arg(info.m_serviceId);
1877 info.m_logicalChannel = info.m_serviceId;
1878
1879 if (tuner_type == DTVTunerType::kTunerTypeASI)
1880 {
1881 info.m_callSign = QString("MPTS_%1")
1882 .arg(CardUtil::GetDisplayName(cardid));
1883 }
1884 else if (info.m_siStandard == "mpeg" ||
1885 info.m_siStandard == "scte" ||
1886 info.m_siStandard == "opencable")
1887 {
1888 info.m_callSign = QString("MPTS_%1").arg(info.m_freqId);
1889 }
1890 else if (info.m_atscMajorChannel > 0)
1891 {
1892 if (info.m_atscMajorChannel < 0x3F0)
1893 {
1894 info.m_callSign = QString("MPTS_%1").arg(info.m_atscMajorChannel);
1895 }
1896 else
1897 {
1898 info.m_callSign = QString("MPTS_%1").arg(info.m_freqId);
1899 }
1900 }
1901 else if (info.m_serviceId > 0)
1902 {
1903 info.m_callSign = QString("MPTS_%1").arg(info.m_serviceId);
1904 }
1905 else if (!info.m_chanNum.isEmpty())
1906 {
1907 info.m_callSign = QString("MPTS_%1").arg(info.m_chanNum);
1908 }
1909 else
1910 {
1911 info.m_callSign = "MPTS_UNKNOWN";
1912 }
1913
1914 info.m_serviceName = info.m_callSign;
1915 info.m_atscMinorChannel = 0;
1916 info.m_format = "MPTS";
1917 info.m_useOnAirGuide = false;
1918 info.m_isEncrypted = false;
1919 item.m_channels.push_back(info);
1920 }
1921
1922 list.push_back(item);
1923 }
1924 }
1925
1926 return list;
1927}
1928
1930{
1931 return dynamic_cast<DTVSignalMonitor*>(m_signalMonitor);
1932}
1933
1934// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
1936{
1937#if CONFIG_DVB
1938 return dynamic_cast<DVBSignalMonitor*>(m_signalMonitor);
1939#else
1940 return nullptr;
1941#endif
1942}
1943
1945{
1946 return dynamic_cast<DTVChannel*>(m_channel);
1947}
1948
1950{
1951 return dynamic_cast<const DTVChannel*>(m_channel);
1952}
1953
1954// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
1956{
1957#if CONFIG_HDHOMERUN
1958 return dynamic_cast<HDHRChannel*>(m_channel);
1959#else
1960 return nullptr;
1961#endif
1962}
1963
1964// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
1966{
1967#if CONFIG_DVB
1968 return dynamic_cast<DVBChannel*>(m_channel);
1969#else
1970 return nullptr;
1971#endif
1972}
1973
1974// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
1976{
1977#if CONFIG_DVB
1978 return dynamic_cast<const DVBChannel*>(m_channel);
1979#else
1980 return nullptr;
1981#endif
1982}
1983
1984// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
1986{
1987#if CONFIG_V4L2
1988 return dynamic_cast<V4LChannel*>(m_channel);
1989#else
1990 return nullptr;
1991#endif
1992}
1993
1998{
1999 while (m_scannerThread)
2000 {
2001 m_threadExit = true;
2002 if (m_scannerThread->wait(1s))
2003 {
2004 delete m_scannerThread;
2005 m_scannerThread = nullptr;
2006 }
2007 }
2008 m_threadExit = false;
2009 m_scannerThread = new MThread("Scanner", this);
2011}
2012
2017{
2018 LOG(VB_CHANSCAN, LOG_INFO, LOC + "run -- begin");
2019
2020 while (!m_threadExit)
2021 {
2022 if (m_scanning)
2024
2025 std::this_thread::sleep_for(10ms);
2026 }
2027
2028 LOG(VB_CHANSCAN, LOG_INFO, LOC + "run -- end");
2029}
2030
2031// See if we have timed out
2033{
2035 m_timer.hasExpired(kDecryptionTimeout))
2036 {
2038 return true;
2039 }
2040
2041 if (!m_waitingForTables)
2042 return true;
2043
2044#if CONFIG_DVB
2045 // If the rotor is still moving, reset the timer and keep waiting
2047 if (sigmon)
2048 {
2049 const DiSEqCDevRotor *rotor =
2050 GetDVBChannel() ? GetDVBChannel()->GetRotor() : nullptr;
2051 if (rotor)
2052 {
2053 bool was_moving = false;
2054 bool is_moving = false;
2055 sigmon->GetRotorStatus(was_moving, is_moving);
2056 if (was_moving && !is_moving)
2057 {
2058 m_timer.restart();
2059 return false;
2060 }
2061 }
2062 }
2063#endif // CONFIG_DVB
2064
2065 // have the tables have timed out?
2066 if (m_timer.hasExpired(m_channelTimeout.count()))
2067 {
2068 // the channelTimeout alone is only valid if we have seen no tables..
2069 const ScanStreamData *sd = nullptr;
2070 if (GetDTVSignalMonitor())
2072
2073 if (!sd)
2074 return true;
2075
2076 if (sd->HasCachedAnyNIT() || sd->HasCachedAnySDTs())
2077 return m_timer.hasExpired(kDVBTableTimeout.count());
2078 if (sd->HasCachedMGT() || sd->HasCachedAnyVCTs())
2079 return m_timer.hasExpired(kATSCTableTimeout.count());
2080 if (sd->HasCachedAnyPAT() || sd->HasCachedAnyPMTs())
2081 return m_timer.hasExpired(kMPEGTableTimeout.count());
2082
2083 return true;
2084 }
2085
2086 // ok the tables haven't timed out, but have we hit the signal timeout?
2088 if (m_timer.hasExpired((*m_current).m_timeoutTune.count()) &&
2089 sm && !sm->HasSignalLock())
2090 {
2091 const ScanStreamData *sd = nullptr;
2092 if (GetDTVSignalMonitor())
2094
2095 if (!sd)
2096 return true;
2097
2098 // Just is case we temporarily lose the signal after we've seen
2099 // tables...
2100 if (!sd->HasCachedAnyPAT() && !sd->HasCachedAnyPMTs() &&
2101 !sd->HasCachedMGT() && !sd->HasCachedAnyVCTs() &&
2102 !sd->HasCachedAnyNIT() && !sd->HasCachedAnySDTs())
2103 {
2104 return true;
2105 }
2106 }
2107
2108 return false;
2109}
2110
2115{
2116 QMutexLocker locker(&m_lock);
2117
2118 bool do_post_insertion = m_waitingForTables;
2119
2120 if (!HasTimedOut())
2121 return;
2122
2123 if (0 == m_nextIt.offset() && m_nextIt == m_scanTransports.begin())
2124 {
2125 m_channelList.clear();
2126 m_channelsFound = 0;
2127 m_dvbt2Tried = true;
2128 }
2129
2131 {
2132 // If we failed to get a lock with DVB-T try DVB-T2.
2133 m_dvbt2Tried = true;
2135 return;
2136 }
2137
2138 if (0 == m_nextIt.offset() && m_nextIt != m_scanTransports.begin())
2139 {
2140 // Add channel to scanned list and potentially check decryption
2141 if (do_post_insertion && !UpdateChannelInfo(false))
2142 return;
2143
2144 // Stop signal monitor for previous transport
2145 locker.unlock();
2147 locker.relock();
2148 }
2149
2150 m_current = m_nextIt; // Increment current
2151 m_dvbt2Tried = false;
2152
2153 if (m_current != m_scanTransports.end())
2154 {
2156
2157 // Increment nextIt
2159 ++m_nextIt;
2160 }
2161 else if (!m_extendTransports.isEmpty())
2162 {
2163 --m_current;
2164 QMap<uint32_t,DTVMultiplex>::iterator it = m_extendTransports.begin();
2165 while (it != m_extendTransports.end())
2166 {
2167 if (!m_tsScanned.contains(it.key()))
2168 {
2169 QString name = QString("TransportID %1").arg(it.key() & 0xffff);
2171 LOG(VB_CHANSCAN, LOG_INFO, LOC + "Adding " + name + ' ' + item.m_tuning.toString());
2172 m_scanTransports.push_back(item);
2173 m_tsScanned.insert(it.key());
2174 }
2175 ++it;
2176 }
2177 m_extendTransports.clear();
2179 ++m_nextIt;
2180 }
2181 else
2182 {
2184 m_scanning = false;
2186 }
2187}
2188
2190{
2191 const TransportScanItem &item = *transport;
2192
2193#if CONFIG_DVB
2195 if (monitor)
2196 {
2197 // always wait for rotor to finish
2199 monitor->SetRotorTarget(1.0F);
2200 }
2201#endif // CONFIG_DVB
2202
2203 DTVChannel *channel = GetDTVChannel();
2204 if (!channel)
2205 return false;
2206
2207 if (item.m_mplexid > 0 && transport.offset() == 0)
2208 return channel->TuneMultiplex(item.m_mplexid, m_inputName);
2209
2210 if (item.m_tuning.m_sistandard == "MPEG")
2211 {
2212 IPTVTuningData tuning = item.m_iptvTuning;
2213 if (tuning.GetProtocol() == IPTVTuningData::inValid)
2214 tuning.GuessProtocol();
2215 return channel->Tune(tuning, true);
2216 }
2217
2218 const uint64_t freq = item.freq_offset(transport.offset());
2219 DTVMultiplex tuning = item.m_tuning;
2220 tuning.m_frequency = freq;
2221
2223 {
2225 }
2227 {
2228 if (m_dvbt2Tried)
2230 else
2232 }
2233
2234 return channel->Tune(tuning);
2235}
2236
2238{
2239 QString offset_str = (transport.offset()) ?
2240 QObject::tr(" offset %2").arg(transport.offset()) : "";
2241 QString cur_chan = QString("%1%2")
2242 .arg((*m_current).m_friendlyName, offset_str);
2243 QString tune_msg_str =
2244 QObject::tr("ScanTransport Tuning to %1 mplexid(%2)")
2245 .arg(cur_chan).arg((*m_current).m_mplexid);
2246
2247 const TransportScanItem &item = *transport;
2248
2249 if (transport.offset() &&
2250 (item.freq_offset(transport.offset()) == item.freq_offset(0)))
2251 {
2252 m_waitingForTables = false;
2253 return; // nothing to do
2254 }
2255
2256 if (m_channelsFound)
2257 {
2258 QString progress = QObject::tr("Found %n", "", m_channelsFound);
2260 }
2261
2263 LOG(VB_CHANSCAN, LOG_INFO, LOC + tune_msg_str);
2264
2265 if (!Tune(transport))
2266 { // If we did not tune successfully, bail with message
2268 LOG(VB_CHANSCAN, LOG_ERR, LOC +
2269 QString("Failed to tune %1 mplexid(%2) at offset %3")
2270 .arg(item.m_friendlyName).arg(item.m_mplexid)
2271 .arg(transport.offset()));
2272 return;
2273 }
2274
2275 // If we have a DTV Signal Monitor, perform table scanner reset
2276 if (GetDTVSignalMonitor() && GetDTVSignalMonitor()->GetScanStreamData())
2277 {
2281 }
2282
2283 // Start signal monitor for this channel
2284 if (m_signalMonitor)
2286
2287 m_timer.start();
2288 m_waitingForTables = (item.m_tuning.m_sistandard != "analog");
2289}
2290
2296{
2297 LOG(VB_CHANSCAN, LOG_INFO, LOC + "StopScanner");
2298
2299 while (m_scannerThread)
2300 {
2301 m_threadExit = true;
2302 if (m_scannerThread->wait(1s))
2303 {
2304 delete m_scannerThread;
2305 m_scannerThread = nullptr;
2306 }
2307 }
2308
2309 if (m_signalMonitor)
2311}
2312
2318 int SourceID,
2319 const QString &std,
2320 const QString &modulation,
2321 const QString &country,
2322 const QString &table_start,
2323 const QString &table_end)
2324{
2325 LOG(VB_CHANSCAN, LOG_DEBUG, LOC +
2326 QString("%1:%2 ").arg(__FUNCTION__).arg(__LINE__) +
2327 QString("SourceID:%1 ").arg(SourceID) +
2328 QString("std:%1 ").arg(std) +
2329 QString("modulation:%1 ").arg(modulation) +
2330 QString("country:%1 ").arg(country) +
2331 QString("table_start:%1 ").arg(table_start) +
2332 QString("table_end:%1 ").arg(table_end));
2333
2334 QString name("");
2335 if (m_scanning)
2336 return false;
2337
2338 m_scanTransports.clear();
2339 m_nextIt = m_scanTransports.end();
2340
2341 freq_table_list_t tables =
2342 get_matching_freq_tables(std, modulation, country);
2343
2344 if (tables.empty())
2345 {
2346 QString msg = QString("No freq table for (%1, %2, %3) found")
2347 .arg(std, modulation, country);
2349 }
2350 LOG(VB_CHANSCAN, LOG_INFO, LOC +
2351 QString("Looked up freq table (%1, %2, %3) w/%4 entries")
2352 .arg(std, modulation, country, QString::number(tables.size())));
2353
2354 QString start = table_start;
2355 const QString& end = table_end;
2356 // NOLINTNEXTLINE(modernize-loop-convert)
2357 for (auto it = tables.begin(); it != tables.end(); ++it)
2358 {
2359 const FrequencyTable &ft = **it;
2360 int name_num = ft.m_nameOffset;
2361 QString strNameFormat = ft.m_nameFormat;
2363 while (freq <= ft.m_frequencyEnd)
2364 {
2365 name = strNameFormat;
2366 if (strNameFormat.indexOf("%") >= 0)
2367 name = strNameFormat.arg(name_num);
2368
2369 if (start.isEmpty() || name == start)
2370 {
2371 start.clear();
2372
2373 TransportScanItem item(SourceID, std, name, name_num,
2374 freq, ft, m_signalTimeout);
2375 m_scanTransports.push_back(item);
2376
2377 LOG(VB_CHANSCAN, LOG_INFO, LOC + "ScanTransports " +
2378 item.toString());
2379 }
2380
2381 ++name_num;
2382 freq += ft.m_frequencyStep;
2383
2384 if (!end.isEmpty() && name == end)
2385 break;
2386 }
2387 if (!end.isEmpty() && name == end)
2388 break;
2389 }
2390
2391 while (!tables.empty())
2392 {
2393 delete tables.back();
2394 tables.pop_back();
2395 }
2396
2397 m_extendScanList = true;
2398 m_timer.start();
2399 m_waitingForTables = false;
2400
2401 m_nextIt = m_scanTransports.begin();
2403 m_scanning = true;
2404
2405 return true;
2406}
2407
2409 const QString &std,
2410 const QString &cardtype,
2411 const DTVChannelList &channels)
2412{
2413 m_scanTransports.clear();
2414 m_nextIt = m_scanTransports.end();
2415
2416 DTVTunerType tunertype;
2417 tunertype.Parse(cardtype);
2418
2419 auto it = channels.cbegin();
2420 for (uint i = 0; it != channels.cend(); ++it, ++i)
2421 {
2422 DTVTransport tmp = *it;
2423 tmp.m_sistandard = std;
2424 TransportScanItem item(sourceid, QString::number(i),
2425 tunertype, tmp, m_signalTimeout);
2426
2427 m_scanTransports.push_back(item);
2428
2429 LOG(VB_CHANSCAN, LOG_INFO, LOC + "ScanForChannels " + item.toString());
2430 }
2431
2432 if (m_scanTransports.empty())
2433 {
2434 LOG(VB_GENERAL, LOG_ERR, LOC + "ScanForChannels() no transports");
2435 return false;
2436 }
2437
2438 m_timer.start();
2439 m_waitingForTables = false;
2440
2441 m_nextIt = m_scanTransports.begin();
2443 m_scanning = true;
2444
2445 return true;
2446}
2447
2449 const fbox_chan_map_t &iptv_channels)
2450{
2451 m_scanTransports.clear();
2452 m_nextIt = m_scanTransports.end();
2453
2454 fbox_chan_map_t::const_iterator Ichan = iptv_channels.begin();
2455 for (uint idx = 0; Ichan != iptv_channels.end(); ++Ichan, ++idx)
2456 {
2457 TransportScanItem item(sourceid, QString::number(idx),
2458 Ichan.value().m_tuning, Ichan.key(),
2460
2461 m_scanTransports.push_back(item);
2462
2463 LOG(VB_CHANSCAN, LOG_INFO, LOC + "ScanIPTVChannels " + item.toString());
2464 }
2465
2466 if (m_scanTransports.empty())
2467 {
2468 LOG(VB_GENERAL, LOG_ERR, LOC + "ScanIPTVChannels() no transports");
2469 return false;
2470 }
2471
2472 m_timer.start();
2473 m_waitingForTables = false;
2474
2475 m_nextIt = m_scanTransports.begin();
2477 m_scanning = true;
2478
2479 return true;
2480}
2481
2482
2488 int sourceid, const QMap<QString,QString> &startChan)
2489{
2490 auto iter = startChan.find("type");
2491 if (iter == startChan.end())
2492 return false;
2493 iter = startChan.find("std");
2494 if (iter == startChan.end())
2495 return false;
2496
2497 QString si_std = ((*iter).toLower() != "atsc") ? "dvb" : "atsc";
2498 bool ok = false;
2499
2500 if (m_scanning)
2501 return false;
2502
2503 m_scanTransports.clear();
2504 m_nextIt = m_scanTransports.end();
2505
2506 DTVMultiplex tuning;
2507
2509 ok = type.Parse(startChan["type"]);
2510
2511 if (ok)
2512 {
2513 ok = tuning.ParseTuningParams(
2514 type,
2515 startChan["frequency"], startChan["inversion"],
2516 startChan["symbolrate"], startChan["fec"],
2517 startChan["polarity"],
2518 startChan["coderate_hp"], startChan["coderate_lp"],
2519 startChan["constellation"], startChan["trans_mode"],
2520 startChan["guard_interval"], startChan["hierarchy"],
2521 startChan["modulation"], startChan["bandwidth"],
2522 startChan["mod_sys"], startChan["rolloff"]);
2523 }
2524
2525 if (ok)
2526 {
2527 tuning.m_sistandard = si_std;
2528 TransportScanItem item(
2529 sourceid, QObject::tr("Frequency %1").arg(startChan["frequency"]),
2530 tuning, m_signalTimeout);
2531 m_scanTransports.push_back(item);
2532 }
2533
2534 if (!ok)
2535 return false;
2536
2537 m_extendScanList = true;
2538
2539 m_timer.start();
2540 m_waitingForTables = false;
2541
2542 m_nextIt = m_scanTransports.begin();
2544 m_scanning = true;
2545
2546 return true;
2547}
2548
2550{
2552 query.prepare(
2553 "SELECT sourceid, sistandard, transportid, frequency, modulation, mod_sys "
2554 "FROM dtv_multiplex "
2555 "WHERE mplexid = :MPLEXID");
2556 query.bindValue(":MPLEXID", mplexid);
2557 if (!query.exec())
2558 {
2559 MythDB::DBError("ChannelScanSM::AddToList()", query);
2560 return false;
2561 }
2562
2563 if (!query.next())
2564 {
2565 LOG(VB_GENERAL, LOG_ERR, LOC + "AddToList() " +
2566 QString("Failed to locate mplexid(%1) in DB").arg(mplexid));
2567 return false;
2568 }
2569
2570 uint sourceid = query.value(0).toUInt();
2571 QString sistandard = query.value(1).toString();
2572 uint tsid = query.value(2).toUInt();
2573 uint frequency = query.value(3).toUInt();
2574 QString modulation = query.value(4).toString();
2575 QString mod_sys = query.value(5).toString();
2576 DTVModulationSystem delsys;
2577 delsys.Parse(mod_sys);
2579 QString fn = tsid ? QString("Transport ID %1").arg(tsid) :
2580 QString("Multiplex #%1").arg(mplexid);
2581
2582 if (modulation == "8vsb")
2583 {
2584 QString chan = QString("%1 Hz").arg(frequency);
2585 int findFrequency = (query.value(3).toInt() / 1000) - 1750;
2586 for (const auto & list : gChanLists[0].list)
2587 {
2588 if ((list.freq <= findFrequency + 200) &&
2589 (list.freq >= findFrequency - 200))
2590 {
2591 chan = QString("%1").arg(list.name);
2592 }
2593 }
2594 fn = QObject::tr("ATSC Channel %1").arg(chan);
2596 }
2597
2598 tt = GuessDTVTunerType(tt);
2599
2600 TransportScanItem item(sourceid, sistandard, fn, mplexid, m_signalTimeout);
2601
2602 LOG(VB_CHANSCAN, LOG_DEBUG, LOC +
2603 QString("tunertype:%1 %2 sourceid:%3 sistandard:%4 fn:'%5' mplexid:%6")
2604 .arg(tt.toInt()).arg(tt.toString()).arg(sourceid).arg(sistandard, fn).arg(mplexid));
2605
2606 if (item.m_tuning.FillFromDB(tt, mplexid))
2607 {
2608 LOG(VB_CHANSCAN, LOG_INFO, LOC + "Adding " + fn);
2609 m_scanTransports.push_back(item);
2610 return true;
2611 }
2612
2613 LOG(VB_CHANSCAN, LOG_INFO, LOC + "Not adding incomplete transport " + fn);
2614 return false;
2615}
2616
2617bool ChannelScanSM::ScanTransport(uint mplexid, bool follow_nit)
2618{
2619 m_scanTransports.clear();
2620 m_nextIt = m_scanTransports.end();
2621
2622 AddToList(mplexid);
2623
2624 m_timer.start();
2625 m_waitingForTables = false;
2626
2627 m_extendScanList = follow_nit;
2629 if (!m_scanTransports.empty())
2630 {
2631 m_nextIt = m_scanTransports.begin();
2632 m_scanning = true;
2633 return true;
2634 }
2635
2636 return false;
2637}
2638
2639bool ChannelScanSM::ScanCurrentTransport(const QString &sistandard)
2640{
2641 m_scanTransports.clear();
2642 m_nextIt = m_scanTransports.end();
2643
2644 m_signalTimeout = 30s;
2645 QString name;
2646 TransportScanItem item(m_sourceID, sistandard, name, 0, m_signalTimeout);
2647 m_scanTransports.push_back(item);
2648
2649 m_timer.start();
2650 m_waitingForTables = false;
2651 m_extendScanList = false;
2653 m_nextIt = m_scanTransports.begin();
2654 m_scanning = true;
2655 return true;
2656}
2657
2662 const DTVChannelInfoList &channels,
2663 uint mpeg_program_num,
2664 QString &service_name,
2665 QString &callsign,
2666 QString &common_status_info)
2667{
2668 if (channels.empty())
2669 return true;
2670
2671 bool found = false;
2672 for (const auto & channel : channels)
2673 {
2674 LOG(VB_GENERAL, LOG_DEBUG, LOC +
2675 QString("comparing %1 %2 against %3 %4")
2676 .arg(channel.m_serviceid).arg(channel.m_name)
2677 .arg(mpeg_program_num).arg(common_status_info));
2678
2679 if (channel.m_serviceid == mpeg_program_num)
2680 {
2681 found = true;
2682 if (!channel.m_name.isEmpty())
2683 {
2684 service_name = channel.m_name;
2685 callsign = channel.m_name;
2686 }
2687 }
2688 }
2689
2690 if (found)
2691 {
2692 common_status_info += QString(" %1 %2")
2693 .arg(QObject::tr("as"), service_name);
2694 }
2695 else
2696 {
2698 QObject::tr("Skipping %1, not in imported channel map")
2699 .arg(common_status_info));
2700 }
2701
2702 return found;
2703}
std::vector< const TerrestrialVirtualChannelTable * > tvct_vec_t
std::vector< const CableVirtualChannelTable * > cvct_vec_t
Overall structure.
@ kChannelVisible
Definition: channelinfo.h:22
#define LOC
static void update_info(ChannelInsertInfo &info, const VirtualChannelTable *vct, uint i)
#define PCM_INFO_INIT(SISTD)
static const QString kATSCChannelFormat
static constexpr qint64 kDecryptionTimeout
static const uint kRegionUndefined
std::vector< const ProgramMapTable * > pmt_vec_t
QPair< transport_scan_items_it_t, ScannedChannelInfo * > ChannelListItem
cvct_vec_t GetCachedCVCTs(bool current=true) const
const MasterGuideTable * GetCachedMGT(bool current=true) const
bool HasCachedAllTVCTs(bool current=true) const
bool HasCachedAnyVCTs(bool current=true) const
tvct_vec_t GetCachedTVCTs(bool current=true) const
bool HasCachedMGT(bool current=true) const
bool HasCachedAllCVCTs(bool current=true) const
Tells what channels can be found on each transponder for one bouquet (a bunch of channels from one pr...
Definition: dvbtables.h:193
uint TSID(uint i) const
Definition: dvbtables.h:234
uint TransportDescriptorsLength(uint i) const
Definition: dvbtables.h:240
const unsigned char * TransportDescriptors(uint i) const
for(j=0;j<N;j++) x 6.0+p { descriptor() }
Definition: dvbtables.h:244
uint BouquetID() const
Definition: dvbtables.h:208
QString toString(void) const override
Definition: dvbtables.cpp:217
uint OriginalNetworkID(uint i) const
Definition: dvbtables.h:236
uint TransportStreamCount(void) const
Definition: dvbtables.h:229
unsigned long long FrequencyHz(void) const
static QString GetDisplayName(uint inputid)
Definition: cardutil.cpp:1886
static DTVTunerType ConvertToTunerType(DTVModulationSystem delsys)
Definition: cardutil.cpp:813
Abstract class providing a generic interface to tuning hardware.
Definition: channelbase.h:32
virtual int GetInputID(void) const
Definition: channelbase.h:67
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...
QMutex m_lock
The big lock.
QElapsedTimer m_timer
DTVTunerType GuessDTVTunerType(DTVTunerType type) const
void UpdateScanPercentCompleted(void)
void run(void) override
This runs the event loop for ChannelScanSM until 'm_threadExit' is true.
void UpdateScanTransports(uint frequency, const NetworkInformationTable *nit)
QSet< uint32_t > m_tsScanned
std::chrono::milliseconds m_otherTableTimeout
void HandleMGT(const MasterGuideTable *mgt) override
QMap< uint32_t, DTVMultiplex > m_extendTransports
std::chrono::milliseconds m_otherTableTime
DTVTunerType m_scanDTVTunerType
ChannelList m_channelList
Found Channel Info.
DTVChannel * GetDTVChannel(void)
void HandleActiveScan(void)
Handles the TRANSPORT_LIST ChannelScanSM mode.
bool ScanForChannels(uint sourceid, const QString &std, const QString &cardtype, const DTVChannelList &channels)
void HandleEncryptionStatus(uint pnum, bool encrypted) override
transport_scan_items_t m_scanTransports
static const std::chrono::milliseconds kMPEGTableTimeout
No logic here, lets just wait at least 15 seconds.
bool ScanTransport(uint mplexid, bool follow_nit)
void HandleSDTo(uint tsid, const ServiceDescriptionTable *sdt) override
static const std::chrono::milliseconds kATSCTableTimeout
No logic here, lets just wait at least 10 seconds.
void SetAnalog(bool is_analog)
void HandleVCT(uint tsid, const VirtualChannelTable *vct) override
void HandleNIT(const NetworkInformationTable *nit) override
std::chrono::milliseconds m_signalTimeout
static QString loc(const ChannelScanSM *siscan)
void HandleSDT(uint tsid, const ServiceDescriptionTable *sdt) override
uint GetCurrentTransportInfo(QString &chan, QString &chan_tr) const
DVBSignalMonitor * GetDVBSignalMonitor(void)
QMap< uint64_t, QString > m_defAuthorities
DTVSignalMonitor * GetDTVSignalMonitor(void)
MThread * m_scannerThread
static const std::chrono::milliseconds kDVBTableTimeout
SDT's should be sent every 2 seconds and NIT's every 10 seconds, so lets wait at least 30 seconds,...
QMap< uint, bool > m_currentEncryptionStatusChecked
SignalMonitor * GetSignalMonitor(void)
bool UpdateChannelInfo(bool wait_until_complete)
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...
bool Tune(transport_scan_items_it_t transport)
AnalogSignalHandler * m_analogSignalHandler
V4LChannel * GetV4LChannel(void)
chan_info_map_t GetChannelList(transport_scan_items_it_t trans_info, ScannedChannelInfo *scan_info) const
void HandlePMT(uint program_num, const ProgramMapTable *pmt) override
void HandlePAT(const ProgramAssociationTable *pat) override
HDHRChannel * GetHDHRChannel(void)
~ChannelScanSM() override
bool m_currentTestingDecryption
ChannelScanSM(ScanMonitor *scan_monitor, const QString &cardtype, ChannelBase *channel, int sourceID, std::chrono::milliseconds signal_timeout, std::chrono::milliseconds channel_timeout, QString inputname, bool test_decryption)
static void LogLines(const QString &string)
void HandleAllGood(void)
void HandleCAT(const ConditionalAccessTable *cat) override
QMap< uint, uint > m_currentEncryptionStatus
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 HasTimedOut(void)
ScannedChannelInfo * m_currentInfo
transport_scan_items_it_t m_current
std::chrono::milliseconds m_channelTimeout
SignalMonitor * m_signalMonitor
QString m_inputName
void StopScanner(void)
Stops the ChannelScanSM event loop and the signal monitor, blocking until both exit.
bool ScanCurrentTransport(const QString &sistandard)
void HandleBAT(const BouquetAssociationTable *bat) override
void StartScanner(void)
Starts the ChannelScanSM event loop.
transport_scan_items_it_t m_nextIt
DVBChannel * GetDVBChannel(void)
ChannelBase * m_channel
ScanMonitor * m_scanMonitor
bool CheckImportedList(const DTVChannelInfoList &channels, uint mpeg_program_num, QString &service_name, QString &callsign, QString &common_status_info)
If we are scanning a dvb-utils import verify channel is in list.
bool TestNextProgramEncryption(void)
bool AddToList(uint mplexid)
bool ScanIPTVChannels(uint sourceid, const fbox_chan_map_t &iptv_channels)
volatile bool m_threadExit
static int CreateChanID(uint sourceid, const QString &chan_num)
Creates a unique channel ID for database use.
static bool CreateChannel(uint db_mplexid, uint db_sourceid, uint new_channel_id, const QString &callsign, const QString &service_name, const QString &chan_num, uint service_id, uint atsc_major_channel, uint atsc_minor_channel, bool use_on_air_guide, ChannelVisibleType visible, const QString &freqid, const QString &icon=QString(), QString format="Default", const QString &xmltvid=QString(), const QString &default_authority=QString(), uint service_type=0, int recpriority=0, int tmOffset=0, int commMethod=-1)
static QString GetUnknownCallsign(void)
static uint FindChannel(uint sourceid, const QString &freqid)
The CAT is used to transmit additional ConditionalAccessDescriptor instances, in addition to the ones...
Definition: mpegtables.h:839
Class providing a generic interface to digital tuning hardware.
Definition: dtvchannel.h:34
virtual bool Tune(const DTVMultiplex &tuning)=0
This performs the actual frequency tuning and in some cases input switching.
virtual std::vector< DTVTunerType > GetTunerTypes(void) const
Returns a vector of supported tuning types.
Definition: dtvchannel.cpp:79
virtual bool TuneMultiplex(uint mplexid, const QString &inputname)
To be used by the channel scanner and possibly the EIT scanner.
Definition: dtvchannel.cpp:397
bool Parse(const QString &_value)
QString toString() const
bool FillFromDeliverySystemDesc(DTVTunerType type, const MPEGDescriptor &desc)
bool ParseTuningParams(DTVTunerType type, const QString &frequency, const QString &inversion, const QString &symbolrate, const QString &fec, const QString &polarity, const QString &hp_code_rate, const QString &lp_code_rate, const QString &ofdm_modulation, const QString &trans_mode, const QString &guard_interval, const QString &hierarchy, const QString &modulation, const QString &bandwidth, const QString &mod_sys, const QString &rolloff)
IPTVTuningData m_iptvTuning
Definition: dtvmultiplex.h:112
DTVModulationSystem m_modSys
Definition: dtvmultiplex.h:106
virtual bool FillFromDB(DTVTunerType type, uint mplexid)
uint64_t m_frequency
Definition: dtvmultiplex.h:94
QString m_sistandard
Definition: dtvmultiplex.h:111
This class is intended to detect the presence of needed tables.
void SetDVBService(uint network_id, uint transport_id, int service_id)
uint GetNetworkID(void) const
void AddFlags(uint64_t _flags) override
uint GetTransportID(void) const
void SetChannel(int major, int minor)
MPEGStreamData * GetStreamData()
Returns the MPEG stream data if it exists.
ScanStreamData * GetScanStreamData()
Returns the scan stream data if it exists.
DVBStreamData * GetDVBStreamData()
Returns the DVB stream data if it exists.
virtual void SetStreamData(MPEGStreamData *data)
Sets the MPEG stream data for DTVSignalMonitor to use, and connects the table signals to the monitor.
bool Parse(const QString &_value)
static const int kTunerTypeASI
static const int kTunerTypeDVBS2
QString toString() const
static const int kTunerTypeDVBT
static const int kTunerTypeUnknown
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
const DiSEqCDevRotor * GetRotor(void) const
Returns rotor object if it exists, nullptr otherwise.
void SetPMT(const ProgramMapTable *pmt)
Tells the Conditional Access Module which streams we wish to decode.
Definition: dvbchannel.cpp:726
DVB Logical Channel Descriptor.
uint ChannelNumber(uint i) const
uint ServiceID(uint i) const
void SetRotorTarget(float target) override
Sets rotor target pos from 0.0 to 1.0.
void GetRotorStatus(bool &was_moving, bool &is_moving) override
DVB HD Simulcast Logical Channel Descriptor.
uint ChannelNumber(uint i) const
sdt_vec_t GetCachedSDTSections(uint tsid, bool current=true) const
bool HasCachedAnySDTs(bool current=true) const
sdt_vec_t GetCachedSDTs(bool current=true) const
bool HasCachedAnyBATs(bool current=true) const
bool HasCachedAllBATs(bool current=true) const
bool HasCachedAnyNIT(bool current=true) const
bat_vec_t GetCachedBATs(bool current=true) const
bool HasCachedAllSDT(uint tsid, bool current=true) const
void ReturnCachedSDTTables(sdt_vec_t &sdts) const
nit_const_ptr_t GetCachedNIT(uint section_num, bool current=true) const
bool HasCachedAllNIT(bool current=true) const
QString DefaultAuthority(void) const
@ terrestrial_delivery_system
@ s2_satellite_delivery_system
Rotor class.
Definition: diseqc.h:303
Freesat Logical Channel Number descriptor.
uint LCNCount(size_t i) const
uint ServiceCount(void) const
uint ServiceID(size_t i) const
uint LogicalChannelNumber(size_t i, size_t j) const
uint RegionID(size_t i, size_t j) const
QString m_nameFormat
uint64_t m_frequencyStep
uint64_t m_frequencyEnd
uint64_t m_frequencyStart
IPTVProtocol GetProtocol(void) const
void GuessProtocol(void)
static const unsigned char * FindExtension(const desc_list_t &parsed, uint desc_tag)
static desc_list_t Parse(const unsigned char *data, uint len)
uint DescriptorTag(void) const
bool IsValid(void) const
static desc_list_t ParseOnlyInclude(const unsigned char *data, uint len, int excluded_descid)
uint DescriptorTagExtension(void) const
static const unsigned char * Find(const desc_list_t &parsed, uint desc_tag)
pmt_vec_t GetCachedPMTs(void) const
void TestDecryption(const ProgramMapTable *pmt)
virtual void ReturnCachedPATTables(pat_vec_t &pats) const
bool HasCachedAnyPMTs(void) const
bool HasCachedAnyPAT(uint tsid) const
bool HasCachedAllPMTs(void) const
pat_vec_t GetCachedPATs(uint tsid) const
bool HasCachedAllPAT(uint tsid) const
virtual void ReturnCachedPMTTables(pmt_vec_t &pmts) const
virtual void AddListeningPID(uint pid, PIDPriority priority=kPIDPriorityNormal)
void ResetDecryptionMonitoringState(void)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838
QVariant value(int i) const
Definition: mythdbcon.h:204
bool isActive(void) const
Definition: mythdbcon.h:215
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:49
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:267
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:284
This table tells the decoder on which PIDs to find other tables, and their sizes and each table's cur...
Definition: atsctables.h:81
QString toString(void) const override
Definition: atsctables.cpp:72
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
This table tells the decoder on which PIDs to find other tables.
Definition: dvbtables.h:34
uint OriginalNetworkID(uint i) const
original_network_id 16 2.0+p
Definition: dvbtables.h:84
QString toString(void) const override
Definition: dvbtables.cpp:32
uint TransportDescriptorsLength(uint i) const
trans_desc_length 12 4.4+p
Definition: dvbtables.h:88
uint NetworkID(void) const
network_id 16 3.0 0x0000
Definition: dvbtables.h:62
const unsigned char * TransportDescriptors(uint i) const
for(j=0;j<N;j++) x 6.0+p { descriptor() }
Definition: dvbtables.h:92
uint TSID(uint i) const
transport_stream_id 16 0.0+p
Definition: dvbtables.h:82
uint TransportStreamCount(void) const
Definition: dvbtables.h:78
@ SCTE_PSIP_PID
Definition: mpegtables.h:237
uint LastSection(void) const
Definition: mpegtables.h:534
uint Section(void) const
Definition: mpegtables.h:531
uint32_t PrivateDataSpecifier(void) const
The Program Association Table lists all the programs in a stream, and is always found on PID 0.
Definition: mpegtables.h:599
QString toString(void) const override
Definition: mpegtables.cpp:844
uint ProgramCount(void) const
Definition: mpegtables.h:619
uint ProgramPID(uint i) const
Definition: mpegtables.h:629
A PMT table maps a program described in the ProgramAssociationTable to various PID's which describe t...
Definition: mpegtables.h:676
bool IsEncrypted(const QString &sistandard) const
Returns true iff PMT contains CA descriptor for a vid/aud stream.
Definition: mpegtables.cpp:557
QString toString(void) const override
Definition: mpegtables.cpp:909
uint ProgramNumber(void) const
Definition: mpegtables.h:712
QString FormatIdentifierString(void) const
uint64_t FrequencykHz(void) const
ChannelInsertInfoList m_channels
Definition: dtvmultiplex.h:138
void ScanPercentComplete(int pct)
void ScanComplete(void)
Definition: scanmonitor.cpp:98
void ScanAppendTextToLog(const QString &status)
void ScanUpdateStatusTitleText(const QString &status)
void ScanUpdateStatusText(const QString &status)
void Reset(void) override
void SetFreesatAdditionalSI(bool freesat_si)
QMap< uint, uint > m_programEncryptionStatus
ScannedChannelInfo()=default
const MasterGuideTable * m_mgt
bool IsEmpty() const
This table tells the decoder on which PIDs to find A/V data.
Definition: dvbtables.h:114
uint TSID() const
transport_stream_id 16 3.0 0x0000
Definition: dvbtables.h:141
QString toString(void) const override
Definition: dvbtables.cpp:129
const unsigned char * ServiceDescriptors(uint i) const
for (j=0;j<N;j++) x 5.0+p { descriptor() }
Definition: dvbtables.h:169
bool HasEITPresentFollowing(uint i) const
Definition: dvbtables.h:158
bool HasEITSchedule(uint i) const
Definition: dvbtables.h:156
ServiceDescriptor * GetServiceDescriptor(uint i) const
Definition: dvbtables.cpp:166
uint ServiceID(uint i) const
service_id 16 0.0+p
Definition: dvbtables.h:153
uint ServiceCount() const
Number of services.
Definition: dvbtables.h:148
uint ServiceDescriptorsLength(uint i) const
desc_loop_length 12 3.4+p
Definition: dvbtables.h:165
uint OriginalNetworkID() const
original_network_id 16 8.0
Definition: dvbtables.h:144
bool IsEncrypted(uint i) const
free_CA_mode 1 3.3+p
Definition: dvbtables.h:163
ServiceRelocatedDescriptor * GetServiceRelocatedDescriptor(uint i) const
Definition: dvbtables.cpp:181
QString ServiceShortName(void) const
uint ServiceType(void) const
bool IsDTV(void) const
QString ServiceName(void) const
bool IsDigitalAudio(void) const
uint ServiceID(uint i) const
uint ServiceCount(void) const
Signal monitoring base class.
Definition: signalmonitor.h:31
static const uint64_t kDTVSigMon_WaitForVCT
void RemoveListener(SignalMonitorListener *listener)
void AddListener(SignalMonitorListener *listener)
static const uint64_t kDTVSigMon_WaitForNIT
static const uint64_t kDVBSigMon_WaitForPos
Wait for rotor to complete turning the antenna.
static const uint64_t kDTVSigMon_WaitForSDT
virtual void Stop()
Stop signal monitoring thread.
int GetSignalStrength(void)
Definition: signalmonitor.h:71
static const uint64_t kDTVSigMon_WaitForMGT
bool HasSignalLock(void) const
Returns true iff scriptStatus.IsGood() and signalLock.IsGood() return true.
Definition: signalmonitor.h:75
virtual void Start()
Start signal monitoring thread.
Sky Logical Channel Number descriptor.
uint RegionID(void) const
uint ServiceCount(void) const
uint LogicalChannelNumber(size_t i) const
uint ServiceID(size_t i) const
static std::vector< uint > GetMplexIDs(uint sourceid)
Definition: sourceutil.cpp:152
@ OpenCableVideo
Always MPEG-2??
Definition: mpegtables.h:120
Class used for doing a list of frequencies / transports.
uint64_t freq_offset(uint i) const
IPTVTuningData m_iptvTuning
DTVMultiplex m_tuning
QString toString() const
Implements tuning for TV cards using the V4L driver API, both versions 1 and 2.
Definition: v4lchannel.h:32
This table contains information about the channels transmitted on this multiplex.
Definition: atsctables.h:195
QString ShortChannelName(uint i) const
Definition: atsctables.h:231
bool IsHiddenInGuide(uint i) const
Definition: atsctables.h:293
bool IsHidden(uint i) const
Definition: atsctables.h:287
uint MajorChannel(uint i) const
Definition: atsctables.h:250
uint ChannelTransportStreamID(uint i) const
Definition: atsctables.h:267
uint TransportStreamID() const
Definition: atsctables.h:223
QString toString(void) const override
Definition: atsctables.cpp:203
uint MinorChannel(uint i) const
Definition: atsctables.h:255
bool IsAccessControlled(uint i) const
Definition: atsctables.h:282
uint ProgramNumber(uint i) const
Definition: atsctables.h:272
QString GetExtendedChannelName(uint idx) const
Definition: atsctables.cpp:509
uint ServiceType(uint i) const
Definition: atsctables.h:299
uint ModulationMode(uint i) const
Definition: atsctables.h:260
uint ChannelCount() const
Definition: atsctables.h:226
transport_scan_items_it_t nextTransport() const
std::list< TransportScanItem >::iterator iter()
unsigned int uint
Definition: compat.h:60
std::vector< DTVChannelInfo > DTVChannelInfoList
Definition: dtvconfparser.h:58
std::vector< DTVTransport > DTVChannelList
Definition: dtvconfparser.h:68
std::vector< ScanDTVTransport > ScanDTVTransportList
Definition: dtvmultiplex.h:143
QMap< uint, sdt_vec_t > sdt_map_t
Definition: dvbstreamdata.h:20
std::vector< const ServiceDescriptionTable * > sdt_vec_t
Definition: dvbstreamdata.h:18
std::vector< const NetworkInformationTable * > nit_vec_t
Definition: dvbstreamdata.h:13
std::vector< const BouquetAssociationTable * > bat_vec_t
Definition: dvbstreamdata.h:24
static const std::array< const uint32_t, 4 > freq
Definition: element.cpp:45
const CHANLISTS_vec gChanLists
bool teardown_frequency_tables(void)
freq_table_list_t get_matching_freq_tables(const QString &format, const QString &modulation, const QString &country)
std::vector< const FrequencyTable * > freq_table_list_t
QMap< QString, IPTVChannelInfo > fbox_chan_map_t
std::vector< const unsigned char * > desc_list_t
QMap< uint, pat_vec_t > pat_map_t
std::vector< const ProgramAssociationTable * > pat_vec_t
@ kEncUnknown
@ kEncEncrypted
@ kEncDecrypted
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
Definition: mythlogging.h:29
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
dictionary info
Definition: azlyrics.py:7
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:86