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