MythTV master
mpegstreamdata.cpp
Go to the documentation of this file.
1// -*- Mode: c++ -*-
2// Copyright (c) 2003-2004, Daniel Thor Kristjansson
3
4#include <algorithm> // for find & max
5
6// POSIX headers
7#include <sys/time.h> // for gettimeofday
8
9// Qt headers
10#include <QString>
11
12// MythTV headers
14#include "mpegstreamdata.h"
15#include "mpegtables.h"
16
17#include "atscstreamdata.h"
18#include "atsctables.h"
19
20//#define DEBUG_MPEG_RADIO // uncomment to strip video streams from TS stream
21#define LOC QString("MPEGStream[%1](0x%2): ").arg(m_cardId).arg((intptr_t)this, QT_POINTER_SIZE, 16, QChar('0'))
22
39MPEGStreamData::MPEGStreamData(int desiredProgram, int cardnum,
40 bool cacheTables)
41 : m_cardId(cardnum),
42 m_cacheTables(cacheTables),
43 // Single program stuff
44 m_desiredProgram(desiredProgram)
45{
48}
49
51{
53 SetPATSingleProgram(nullptr);
54 SetPMTSingleProgram(nullptr);
55
56 // Delete any cached tables that haven't been returned
57 for (auto it = m_cachedSlatedForDeletion.cbegin();
58 it != m_cachedSlatedForDeletion.cend(); ++it)
59 delete it.key();
60
61 QMutexLocker locker(&m_listenerLock);
62 m_mpegListeners.clear();
63 m_mpegSpListeners.clear();
64}
65
67{
68 bool reset = true;
69 uint pid = 0;
70 const ProgramAssociationTable* pat = nullptr;
71 pat_vec_t pats = GetCachedPATs();
72
73 LOG(VB_RECORD, LOG_INFO, LOC + QString("SetDesiredProgram(%2)").arg(p));
74
75 for (uint i = (p) ? 0 : pats.size(); i < pats.size() && !pid; i++)
76 {
77 pat = pats[i];
78 pid = pats[i]->FindPID(p);
79 }
80
81 if (pid)
82 {
83 reset = false;
85 ProcessPAT(pat);
86 pmt_vec_t pmts = GetCachedPMTs();
87 for (auto & pmt : pmts)
88 {
89 if (pmt->ProgramNumber() == (uint)p)
90 ProcessPMT(pmt);
91 }
93 }
94
96
97 if (reset)
98 Reset(p);
99}
100
101void MPEGStreamData::SetRecordingType(const QString &recording_type)
102{
103 m_recordingType = recording_type;
104 uint neededAudio = (m_recordingType == "audio") ? 1 : 0;
106 SetAudioStreamsRequired(neededAudio);
107}
108
110{
111 QMutexLocker locker(&m_listenerLock);
112 m_eitHelper = eit_helper;
113}
114
116{
117 QMutexLocker locker(&m_listenerLock);
118 m_eitRate = rate;
119}
120
121void MPEGStreamData::Reset(int desiredProgram)
122{
123 m_desiredProgram = desiredProgram;
124 m_recordingType = "all";
125 m_stripPmtDescriptors = false;
127
128 m_invalidPatSeen = false;
129
130 SetPATSingleProgram(nullptr);
131 SetPMTSingleProgram(nullptr);
132
134 for (auto it = old.begin(); it != old.end(); ++it)
135 DeletePartialPSIP(it.key());
137
138 m_pidsListening.clear();
139 m_pidsNotListening.clear();
140 m_pidsWriting.clear();
141 m_pidsAudio.clear();
143
145
146 m_patStatus.clear();
147
148 m_pmtStatus.clear();
149
150 {
151 QMutexLocker locker(&m_cacheLock);
152
153 for (const auto & cached : std::as_const(m_cachedPats))
154 DeleteCachedTable(cached);
155 m_cachedPats.clear();
156
157 for (const auto & cached : std::as_const(m_cachedPmts))
158 DeleteCachedTable(cached);
159 m_cachedPmts.clear();
160
161 for (const auto & cached : std::as_const(m_cachedCats))
162 DeleteCachedTable(cached);
163 m_cachedCats.clear();
164 }
165
167
170}
171
173{
174 pid_psip_map_t::iterator it = m_partialPsipPacketCache.find(pid);
175 if (it != m_partialPsipPacketCache.end())
176 {
177 PSIPTable *pkt = *it;
178 m_partialPsipPacketCache.erase(it);
179 delete pkt;
180 }
181}
182
206 bool &moreTablePackets)
207{
208 bool broken = true;
209 moreTablePackets = true;
210
211 PSIPTable* partial = GetPartialPSIP(tspacket->PID());
212
213 // Second and subsequent transport stream packets of PSIP packet
214 if (partial && partial->AddTSPacket(tspacket, m_cardId, broken) && !broken)
215 {
216 // check if it's safe to read pespacket's Length()
217 if ((partial->PSIOffset() + 1 + 3) > partial->TSSizeInBuffer())
218 {
219 LOG(VB_RECORD, LOG_ERR, LOC +
220 QString("Discarding broken PSIP packet. Packet's length at "
221 "position %1 isn't in the buffer of %2 bytes.")
222 .arg(partial->PSIOffset() + 1 + 3)
223 .arg(partial->TSSizeInBuffer()));
224 DeletePartialPSIP(tspacket->PID());
225 return nullptr;
226 }
227
228 // Discard broken packets
229 bool buggy = m_haveCrcBug &&
230 ((TableID::PMT == partial->StreamID()) ||
231 (TableID::PAT == partial->StreamID()));
232 if (!buggy && !partial->IsGood())
233 {
234 LOG(VB_RECORD, LOG_ERR, LOC + QString("Discarding broken PSIP packet on PID 0x%1")
235 .arg(tspacket->PID(),2,16,QChar('0')));
236 DeletePartialPSIP(tspacket->PID());
237 return nullptr;
238 }
239
240 auto* psip = new PSIPTable(*partial);
241
242 // Advance to the next packet
243 // pesdata starts only at PSIOffset()+1
244 uint packetStart = partial->PSIOffset() + 1 + psip->SectionLength();
245 if (packetStart < partial->TSSizeInBuffer())
246 {
247 if (partial->pesdata()[psip->SectionLength()] != 0xff)
248 {
249#if 0 /* This doesn't work, you can't start PSIP packet like this
250 because the PayloadStart() flag won't be set in this TSPacket
251 -- dtk May 4th, 2007
252 */
253
254 // If the next section starts in the new tspacket
255 // create a new partial packet to prevent overflow
256 if ((partial->TSSizeInBuffer() > TSPacket::kSize) &&
257 (packetStart >
259 {
260 // Saving will handle deleting the old one
261 SavePartialPSIP(tspacket->PID(),
262 new PSIPTable(*tspacket));
263 }
264 else
265#endif
266 {
267 partial->SetPSIOffset(partial->PSIOffset() +
268 psip->SectionLength());
269 }
270 return psip;
271 }
272 }
273 // discard incomplete packets
274 if (packetStart > partial->TSSizeInBuffer())
275 {
276 LOG(VB_RECORD, LOG_ERR, LOC +
277 QString("TSPacket pid(0x%3) ").arg(tspacket->PID(),2,16,QChar('0')) +
278 QString("Discarding broken PSIP packet. ") +
279 QString("Packet with %1 bytes doesn't fit into a buffer of %2 bytes.")
280 .arg(packetStart).arg(partial->TSSizeInBuffer()));
281 delete psip;
282 psip = nullptr;
283 }
284
285 moreTablePackets = false;
286 DeletePartialPSIP(tspacket->PID());
287 return psip;
288 }
289 if (partial)
290 {
291 if (broken)
292 {
293 DeletePartialPSIP(tspacket->PID());
294 }
295
296 moreTablePackets = false;
297 return nullptr; // partial packet is not yet complete.
298 }
299
300 // First transport stream packet of PSIP packet after here
301 if (!tspacket->PayloadStart())
302 {
303 // We didn't see this PSIP packet's start, so this must be the
304 // tail end of something we missed. Ignore it.
305 moreTablePackets = false;
306 return nullptr;
307 }
308
309 // table_id (8 bits) and section_length(12), syntax(1), priv(1), res(2)
310 // pointer_field (+8 bits), since payload start is true if we are here.
311 const unsigned int extra_offset = 4;
312 const unsigned int offset = tspacket->AFCOffset() + tspacket->StartOfFieldPointer();
313 const unsigned char* pesdata = tspacket->data() + offset;
314
315 // Get the table length if it is in this packet
316 int pes_length = 0;
317 if (offset + 3 < TSPacket::kSize)
318 {
319 pes_length = (pesdata[2] & 0x0f) << 8 | pesdata[3];
320 }
321
322 // If the table is not completely in this packet we need another packet.
323 if (pes_length == 0 || (pes_length + offset + extra_offset) > TSPacket::kSize)
324 {
325 SavePartialPSIP(tspacket->PID(), new PSIPTable(*tspacket));
326 moreTablePackets = false;
327 return nullptr;
328 }
329
330 // Complete table in one packet after here
331 auto *psip = new PSIPTable(*tspacket);
332
333 // There might be another section after this one in the
334 // current packet. We need room before the end of the
335 // packet, and it must not be packet stuffing.
336 if ((offset + psip->SectionLength() + 1 < TSPacket::kSize) &&
337 (pesdata[psip->SectionLength() + 1] != 0xff))
338 {
339 // This isn't stuffing, so we need to put this
340 // on as a partial packet.
341 auto *pesp = new PSIPTable(*tspacket);
342 pesp->SetPSIOffset(offset + psip->SectionLength());
343 SavePartialPSIP(tspacket->PID(), pesp);
344 return psip;
345 }
346
347 moreTablePackets = false;
348 return psip;
349}
350
352 const ProgramAssociationTable& pat)
353{
354 LOG(VB_RECORD, LOG_DEBUG, LOC + "CreatePATSingleProgram()");
355 LOG(VB_RECORD, LOG_DEBUG, LOC + "PAT in input stream");
356 LOG(VB_RECORD, LOG_DEBUG, LOC + pat.toString());
357 if (m_desiredProgram < 0)
358 {
359 LOG(VB_RECORD, LOG_ERR, LOC + "Desired program not set yet");
360 return false;
361 }
363 LOG(VB_RECORD, LOG_DEBUG, LOC + QString("desired_program(%1) pid(0x%2)").
364 arg(m_desiredProgram).arg(m_pidPmtSingleProgram, 0, 16));
365
367 {
370 {
371 LOG(VB_GENERAL, LOG_ERR, LOC + "No program found in PAT. "
372 "This recording will not play in MythTV.");
373 }
374 LOG(VB_GENERAL, LOG_ERR, LOC +
375 QString("Desired program #%1 not found in PAT."
376 "\n\t\t\tCannot create single program PAT.")
377 .arg(m_desiredProgram));
378 SetPATSingleProgram(nullptr);
379 return false;
380 }
381
383
384 std::vector<uint> pnums;
385 std::vector<uint> pids;
386
387 pnums.push_back(1);
388 pids.push_back(m_pidPmtSingleProgram);
389
390 uint tsid = pat.TableIDExtension();
391 uint ver = pat.Version();
393 ProgramAssociationTable::Create(tsid, ver, pnums, pids);
394
395 if (!pat2)
396 {
397 LOG(VB_GENERAL, LOG_ERR, LOC +
398 "MPEGStreamData::CreatePATSingleProgram: "
399 "Failed to create Program Association Table.");
400 return false;
401 }
402
404
405 LOG(VB_RECORD, LOG_DEBUG, LOC + QString("pmt_pid(0x%1)")
406 .arg(m_pidPmtSingleProgram, 0, 16));
407 LOG(VB_RECORD, LOG_DEBUG, LOC + "PAT for output stream");
408 LOG(VB_RECORD, LOG_DEBUG, LOC + pat2->toString());
409
411
412 return true;
413
414}
415
417 const cvct_vec_t &cvct,
418 uint pnum)
419{
420 desc_list_t desc;
421
422 std::vector<const VirtualChannelTable*> vct;
423
424 for (const auto *i : tvct)
425 vct.push_back(i);
426
427 for (const auto *i : cvct)
428 vct.push_back(i);
429
430 for (size_t i = 0; i < tvct.size(); i++)
431 {
432 for (uint j = 0; j < vct[i]->ChannelCount(); j++)
433 {
434 if (vct[i]->ProgramNumber(j) == pnum)
435 {
437 vct[i]->Descriptors(j), vct[i]->DescriptorsLength(j),
439
440 if (!ldesc.empty())
441 desc.insert(desc.end(), ldesc.begin(), ldesc.end());
442 }
443 }
444
445 if (0 != vct[i]->GlobalDescriptorsLength())
446 {
448 vct[i]->GlobalDescriptors(),
449 vct[i]->GlobalDescriptorsLength(),
451
452 if (!vdesc.empty())
453 desc.insert(desc.end(), vdesc.begin(), vdesc.end());
454 }
455 }
456
457 return desc;
458}
459
461{
462 LOG(VB_RECORD, LOG_DEBUG, LOC + "CreatePMTSingleProgram()");
463 LOG(VB_RECORD, LOG_DEBUG, LOC + "PMT in input stream");
464 LOG(VB_RECORD, LOG_DEBUG, LOC + pmt.toString());
465
466 if (!PATSingleProgram())
467 {
468 LOG(VB_RECORD, LOG_ERR, LOC + "no PAT yet...");
469 return false; // no way to properly rewrite pids without PAT
470 }
471 pmt.Parse();
472
473 uint programNumber = 1; // MPEG Program Number
474
475 ATSCStreamData *sd = nullptr;
476 tvct_vec_t tvct;
477 cvct_vec_t cvct;
478
479 desc_list_t gdesc;
480
482 {
484 pmt.ProgramInfo(), pmt.ProgramInfoLength(),
486
487 // If there is no caption descriptor in PMT, copy any caption
488 // descriptor found in VCT to global descriptors...
489 sd = dynamic_cast<ATSCStreamData*>(this);
491 {
492 tvct = sd->GetCachedTVCTs();
493 cvct = sd->GetCachedCVCTs();
494
496 tvct, cvct, pmt.ProgramNumber());
497
498 if (!vdesc.empty())
499 gdesc.insert(gdesc.end(), vdesc.begin(), vdesc.end());
500 }
501 }
502
503 std::vector<uint> pids;
504 std::vector<uint> types;
505 std::vector<desc_list_t> pdesc;
506
507 std::vector<uint> videoPIDs;
508 std::vector<uint> audioPIDs;
509 std::vector<uint> dataPIDs;
510
511 for (uint i = 0; i < pmt.StreamCount(); i++)
512 {
513 uint pid = pmt.StreamPID(i);
514
516 pmt.StreamInfo(i), pmt.StreamInfoLength(i),
518
520 pmt.StreamType(i), desc, m_siStandard);
521
522 bool is_video = StreamID::IsVideo(type);
523 bool is_audio = StreamID::IsAudio(type);
524
525 if (is_audio)
526 {
527 audioPIDs.push_back(pid);
528 }
529 else if (m_recordingType == "audio" )
530 {
531 // If not an audio PID but we only want audio,
532 // ignore this PID.
533 continue;
534 }
535
536#ifdef DEBUG_MPEG_RADIO
537 if (is_video)
538 continue;
539#endif // DEBUG_MPEG_RADIO
540
541 if (is_video)
542 {
543 videoPIDs.push_back(pid);
544 }
545
547 desc.clear();
548
549 // Filter out streams not used for basic television
550 if (m_recordingType == "tv" && !is_audio && !is_video &&
553 pid != pmt.PCRPID()) // We must not strip the PCR!
554 {
555 continue;
556 }
557
558 if (!is_audio && !is_video) //NOTE: Anything which isn't audio or video is data
559 dataPIDs.push_back(pid);
560
561 pdesc.push_back(desc);
562 pids.push_back(pid);
563 types.push_back(type);
564 }
565
566 if (videoPIDs.size() < m_pmtSingleProgramNumVideo)
567 {
568 LOG(VB_RECORD, LOG_ERR, LOC +
569 QString("Only %1 video streams seen in PMT, but %2 are required.")
570 .arg(videoPIDs.size()).arg(m_pmtSingleProgramNumVideo));
571 return false;
572 }
573
574 if (audioPIDs.size() < m_pmtSingleProgramNumAudio)
575 {
576 LOG(VB_RECORD, LOG_ERR, LOC +
577 QString("Only %1 audio streams seen in PMT, but %2 are required.")
578 .arg(audioPIDs.size()).arg(m_pmtSingleProgramNumAudio));
579 return false;
580 }
581
583 pmt.ProgramInfo(), pmt.ProgramInfoLength(),
585 for (auto & i : cdesc)
586 {
588 if (cad.IsValid())
589 {
590 AddListeningPID(cad.PID());
592 }
593 }
594
595 m_pidsAudio.clear();
596 for (uint pid : audioPIDs)
597 AddAudioPID(pid);
598
599 m_pidsWriting.clear();
600 m_pidVideoSingleProgram = !videoPIDs.empty() ? videoPIDs[0] : 0xffffffff;
601 for (size_t i = 1; i < videoPIDs.size(); i++)
602 AddWritingPID(videoPIDs[i]);
603
604 for (uint pid : dataPIDs)
605 AddWritingPID(pid);
606
607 // Timebase
608 int pcrpidIndex = pmt.FindPID(pmt.PCRPID());
609 if (pcrpidIndex < 0)
610 {
611 // the timecode reference stream is not in the PMT,
612 // add stream to misc record streams
613 AddWritingPID(pmt.PCRPID());
614 }
615
616 // Create the PMT
618 programNumber, m_pidPmtSingleProgram, pmt.PCRPID(),
619 pmt.Version(), gdesc, pids, types, pdesc);
620
621 // Return any TVCT & CVCT tables, once we've copied any descriptors.
622 if (sd)
623 {
624 sd->ReturnCachedTVCTTables(tvct);
625 sd->ReturnCachedCVCTTables(cvct);
626 }
627
628 // Set Continuity Header
629 uint cc_cnt = pmt.tsheader()->ContinuityCounter();
630 pmt2->tsheader()->SetContinuityCounter(cc_cnt);
632
633 LOG(VB_RECORD, LOG_DEBUG, LOC + "PMT for output stream");
634 LOG(VB_RECORD, LOG_DEBUG, LOC + pmt2->toString());
635
636 return true;
637}
638
642bool MPEGStreamData::IsRedundant([[maybe_unused]] uint pid,
643 const PSIPTable &psip) const
644{
645 const int table_id = psip.TableID();
646 const int version = psip.Version();
647
648 if (TableID::PAT == table_id)
649 {
651 }
652
653 if (TableID::CAT == table_id)
654 {
656 }
657
658 if (TableID::PMT == table_id)
659 {
661 }
662
663 return false;
664}
665
670{
671 if (MPEGStreamData::IsRedundant(pid, psip))
672 return true;
673
674 const int version = psip.Version();
675 // If we get this far decode table
676 switch (psip.TableID())
677 {
678 case TableID::PAT:
679 {
680 uint tsid = psip.TableIDExtension();
682
683 ProgramAssociationTable pat(psip);
684
685 if (m_cacheTables)
686 CachePAT(&pat);
687
688 ProcessPAT(&pat);
689
690 return true;
691 }
692 case TableID::CAT:
693 {
694 uint tsid = psip.TableIDExtension();
696
698
699 if (m_cacheTables)
700 CacheCAT(&cat);
701
702 ProcessCAT(&cat);
703
704 return true;
705 }
706 case TableID::PMT:
707 {
708 uint prog_num = psip.TableIDExtension();
709 m_pmtStatus.SetSectionSeen(prog_num, version, psip.Section(), psip.LastSection());
710
711 ProgramMapTable pmt(psip);
712
713 if (m_cacheTables)
714 CachePMT(&pmt);
715
716 ProcessPMT(&pmt);
717
718 return true;
719 }
720 case TableID::SITscte:
721 {
722 SpliceInformationTable sit(psip);
723 sit.setSCTEPID(pid);
724
725 m_listenerLock.lock();
726 for (auto & listener : m_mpegListeners)
727 listener->HandleSplice(&sit);
728 m_listenerLock.unlock();
729
730 return true;
731 }
732 }
733 return false;
734}
735
737{
738 bool foundProgram = pat->FindPID(m_desiredProgram) != 0U;
739
740 m_listenerLock.lock();
741 for (auto & listener : m_mpegListeners)
742 listener->HandlePAT(pat);
743 m_listenerLock.unlock();
744
745 if (m_desiredProgram < 0)
746 return;
747
748 bool send_single_program = false;
749 if (!m_invalidPatSeen && !foundProgram)
750 {
751 m_invalidPatSeen = true;
752 m_invalidPatWarning = false;
754 LOG(VB_RECORD, LOG_WARNING, LOC +
755 "ProcessPAT: PAT is missing program, setting timeout");
756 }
757 else if (m_invalidPatSeen && !foundProgram &&
759 {
760 m_invalidPatWarning = true; // only emit one warning...
761 // After 400ms emit error if we haven't found correct PAT.
762 LOG(VB_GENERAL, LOG_ERR, LOC + "ProcessPAT: Program not found in PAT. "
763 "Rescan your transports.");
764
765 send_single_program = CreatePATSingleProgram(*pat);
766 }
767 else if (foundProgram)
768 {
770 LOG(VB_RECORD, LOG_INFO, LOC +
771 "ProcessPAT: Good PAT seen after a bad PAT");
772
773 m_invalidPatSeen = false;
774
775 send_single_program = CreatePATSingleProgram(*pat);
776 }
777
778 if (send_single_program)
779 {
780 QMutexLocker locker(&m_listenerLock);
782 for (auto & listener : m_mpegSpListeners)
783 listener->HandleSingleProgramPAT(pat_sp, false);
784 }
785}
786
788{
789 m_listenerLock.lock();
790 for (auto & listener : m_mpegListeners)
791 listener->HandleCAT(cat);
792 m_listenerLock.unlock();
793
795 cat->Descriptors(), cat->DescriptorsLength(),
797 for (auto & i : cdesc)
798 {
800 if (cad.IsValid())
801 {
802 AddListeningPID(cad.PID());
804 }
805 }
806}
807
809{
810 m_listenerLock.lock();
811 for (auto & listener : m_mpegListeners)
812 listener->HandlePMT(pmt->ProgramNumber(), pmt);
813 m_listenerLock.unlock();
814
815 bool desired = pmt->ProgramNumber() == (uint) m_desiredProgram;
816 if (desired && CreatePMTSingleProgram(*pmt))
817 {
818 QMutexLocker locker(&m_listenerLock);
820 for (auto & listener : m_mpegSpListeners)
821 listener->HandleSingleProgramPMT(pmt_sp, false);
822 }
823}
824
826{
827 QMutexLocker locker(&m_siTimeLock);
829 return 0.0;
830
831 double avg_offset = 0.0;
832 double mult = 1.0 / m_siTimeOffsetCnt;
833 for (uint i = 0; i < m_siTimeOffsetCnt; i++)
834 avg_offset += m_siTimeOffsets[i] * mult;
835
836 return avg_offset;
837}
838
839void MPEGStreamData::UpdateTimeOffset(uint64_t _si_utc_time)
840{
841 struct timeval tm {};
842 if (gettimeofday(&tm, nullptr) != 0)
843 return;
844
845 double utc_time = tm.tv_sec + (tm.tv_usec * 0.000001);
846 double si_time = _si_utc_time;
847
848 QMutexLocker locker(&m_siTimeLock);
849 m_siTimeOffsets[m_siTimeOffsetIndx] = si_time - utc_time;
850
852
854
855}
856
861{
862 PSIPTable *psip = nullptr;
863 bool morePSIPTables = true;
864 while (morePSIPTables)
865 {
866 // Delete PSIP from previous iteration.
867 delete psip;
868
869 // Assemble PSIP
870 psip = AssemblePSIP(tspacket, morePSIPTables);
871 if (!psip)
872 return;
873
874 // drop stuffing packets
875 if ((TableID::ST == psip->TableID()) ||
876 (TableID::STUFFING == psip->TableID()))
877 {
878 LOG(VB_RECORD, LOG_DEBUG, LOC + "Dropping Stuffing table");
879 continue;
880 }
881
882 // Don't do validation on tables without CRC
883 if (!psip->HasCRC())
884 {
885 HandleTables(tspacket->PID(), *psip);
886 continue;
887 }
888
889 // Validate PSIP
890 // but don't validate PMT/PAT if our driver has the PMT/PAT CRC bug.
891 bool buggy = m_haveCrcBug &&
892 ((TableID::PMT == psip->TableID()) ||
893 (TableID::PAT == psip->TableID()));
894 if (!buggy && !psip->IsGood())
895 {
896 LOG(VB_RECORD, LOG_ERR, LOC +
897 QString("PSIP packet failed CRC check. pid(0x%1) type(0x%2)")
898 .arg(tspacket->PID(),0,16).arg(psip->TableID(),0,16));
899 continue;
900 }
901
902 if (TableID::MGT <= psip->TableID() && psip->TableID() <= TableID::STT &&
903 !psip->IsCurrent())
904 { // we don't cache the next table, for now
905 LOG(VB_RECORD, LOG_DEBUG, LOC + QString("Table not current 0x%1")
906 .arg(psip->TableID(),2,16,QChar('0')));
907 continue;
908 }
909
910 if (tspacket->Scrambled())
911 { // scrambled! ATSC, DVB require tables not to be scrambled
912 LOG(VB_RECORD, LOG_ERR, LOC +
913 "PSIP packet is scrambled, not ATSC/DVB compliant");
914 continue;
915 }
916
917 if (!psip->VerifyPSIP(!m_haveCrcBug))
918 {
919 LOG(VB_RECORD, LOG_ERR, LOC + QString("PSIP table 0x%1 is invalid")
920 .arg(psip->TableID(),2,16,QChar('0')));
921 continue;
922 }
923
924 // Don't decode redundant packets,
925 // but if it is a desired PAT or PMT emit a "heartbeat" signal.
926 if (MPEGStreamData::IsRedundant(tspacket->PID(), *psip))
927 {
928 if (TableID::PAT == psip->TableID())
929 {
930 QMutexLocker locker(&m_listenerLock);
932 for (auto & listener : m_mpegSpListeners)
933 listener->HandleSingleProgramPAT(pat_sp, false);
934 }
935 if (TableID::PMT == psip->TableID() &&
936 tspacket->PID() == m_pidPmtSingleProgram)
937 {
938 QMutexLocker locker(&m_listenerLock);
940 for (auto & listener : m_mpegSpListeners)
941 listener->HandleSingleProgramPMT(pmt_sp, false);
942 }
943 continue; // already parsed this table, toss it.
944 }
945
946 HandleTables(tspacket->PID(), *psip);
947 }
948
949 // Delete PSIP from final iteration.
950 delete psip;
951}
952
953int MPEGStreamData::ProcessData(const unsigned char *buffer, int len)
954{
955 int pos = 0;
956 bool resync = false;
957
958 if (!m_psListeners.empty())
959 {
960
961 for (auto & listener : m_psListeners)
962 listener->FindPSKeyFrames(buffer, len);
963
964 return 0;
965 }
966
967 while (pos + int(TSPacket::kSize) <= len)
968 { // while we have a whole packet left...
969 if (buffer[pos] != SYNC_BYTE || resync)
970 {
971 int newpos = ResyncStream(buffer, pos+1, len);
972 LOG(VB_RECORD, LOG_DEBUG, LOC +
973 QString("Resyncing @ %1+1 w/len %2 -> %3")
974 .arg(pos).arg(len).arg(newpos));
975 if (newpos == -1)
976 return len - pos;
977 if (newpos == -2)
978 return TSPacket::kSize;
979 pos = newpos;
980 }
981
982 const auto *pkt = reinterpret_cast<const TSPacket*>(&buffer[pos]);
983 pos += TSPacket::kSize; // Advance to next TS packet
984 resync = false;
985 if (!ProcessTSPacket(*pkt))
986 {
987 if (pos + int(TSPacket::kSize) > len)
988 continue;
989 if (buffer[pos] != SYNC_BYTE)
990 {
991 // if ProcessTSPacket fails, and we don't appear to be
992 // in sync on the next packet, then resync. Otherwise
993 // just process the next packet normally.
994 pos -= TSPacket::kSize;
995 resync = true;
996 }
997 }
998 }
999
1000 return len - pos;
1001}
1002
1004{
1005 bool ok = !tspacket.TransportError();
1006
1007 if (IsEncryptionTestPID(tspacket.PID()))
1008 {
1009 ProcessEncryptedPacket(tspacket);
1010 }
1011
1012 if (!ok)
1013 return false;
1014
1015 if (tspacket.Scrambled())
1016 return true;
1017
1018 // Discard broken packets with invalid adaptation field length
1019 // See ISO/IEC 13818-1 : 2000 (E). 2.4.3.5 Semantic definition of fields in adaptation field
1020 if (tspacket.HasAdaptationField())
1021 {
1022 size_t afsize = tspacket.AdaptationFieldSize();
1023 bool validsize = (tspacket.HasPayload())
1024 ? afsize <= 182
1025 : afsize == 183;
1026 if (!validsize)
1027 {
1028 LOG(VB_RECORD, LOG_DEBUG, QString("Invalid adaptation field, type %3, size %4")
1029 .arg(tspacket.AdaptationFieldControl()).arg(afsize) + "\n" +
1030 tspacket.toString());
1031 return false;
1032 }
1033 }
1034
1035 if (VERBOSE_LEVEL_CHECK(VB_RECORD, LOG_DEBUG))
1036 {
1037 if (m_pmtSingleProgram && tspacket.PID() ==
1039 {
1040 if (tspacket.HasPCR())
1041 {
1042 LOG(VB_RECORD, LOG_DEBUG, LOC +
1043 QString("PID %1 (0x%2) has PCR %3μs")
1044 .arg(m_pmtSingleProgram->PCRPID())
1045 .arg(m_pmtSingleProgram->PCRPID(), 0, 16)
1046 .arg(duration_cast<std::chrono::microseconds>
1047 (tspacket.GetPCR().time_since_epoch()).count()));
1048 }
1049 }
1050 }
1051
1052 if (IsVideoPID(tspacket.PID()))
1053 {
1054 QMutexLocker locker(&m_listenerLock);
1055
1056 for (auto & listener : m_tsAvListeners)
1057 listener->ProcessVideoTSPacket(tspacket);
1058
1059 return true;
1060 }
1061
1062 if (IsAudioPID(tspacket.PID()))
1063 {
1064 QMutexLocker locker(&m_listenerLock);
1065
1066 for (auto & listener : m_tsAvListeners)
1067 listener->ProcessAudioTSPacket(tspacket);
1068
1069 return true;
1070 }
1071
1072 if (IsWritingPID(tspacket.PID()))
1073 {
1074 QMutexLocker locker(&m_listenerLock);
1075
1076 for (auto & listener : m_tsWritingListeners)
1077 listener->ProcessTSPacket(tspacket);
1078 }
1079
1080 if (tspacket.HasPayload() &&
1081 IsListeningPID(tspacket.PID()) &&
1082 !IsConditionalAccessPID(tspacket.PID()))
1083 {
1084 HandleTSTables(&tspacket); // Table handling starts here....
1085 }
1086
1087 return true;
1088}
1089
1090int MPEGStreamData::ResyncStream(const unsigned char *buffer, int curr_pos,
1091 int len)
1092{
1093 // Search for two sync bytes 188 bytes apart,
1094 int pos = curr_pos;
1095 int nextpos = pos + TSPacket::kSize;
1096 if (nextpos >= len)
1097 return -1; // not enough bytes; caller should try again
1098
1099 while (buffer[pos] != SYNC_BYTE || buffer[nextpos] != SYNC_BYTE)
1100 {
1101 pos++;
1102 nextpos++;
1103 if (nextpos == len)
1104 return -2; // not found
1105 }
1106
1107 return pos;
1108}
1109
1111{
1112 pid_map_t::const_iterator it = m_pidsConditionalAccess.find(pid);
1113 return it != m_pidsConditionalAccess.end();
1114}
1115
1117{
1119 return false;
1120 pid_map_t::const_iterator it = m_pidsListening.find(pid);
1121 return it != m_pidsListening.end();
1122}
1123
1125{
1126 pid_map_t::const_iterator it = m_pidsNotListening.find(pid);
1127 return it != m_pidsNotListening.end();
1128}
1129
1131{
1132 pid_map_t::const_iterator it = m_pidsWriting.find(pid);
1133 return it != m_pidsWriting.end();
1134}
1135
1137{
1138 pid_map_t::const_iterator it = m_pidsAudio.find(pid);
1139 return it != m_pidsAudio.end();
1140}
1141
1143{
1144 uint sz = pids.size();
1145
1146 if (m_pidVideoSingleProgram < 0x1fff)
1148
1149 for (auto it = m_pidsListening.cbegin(); it != m_pidsListening.cend(); ++it)
1150 pids[it.key()] = std::max(pids[it.key()], *it);
1151
1152 for (auto it = m_pidsAudio.cbegin(); it != m_pidsAudio.cend(); ++it)
1153 pids[it.key()] = std::max(pids[it.key()], *it);
1154
1155 for (auto it = m_pidsWriting.cbegin(); it != m_pidsWriting.cend(); ++it)
1156 pids[it.key()] = std::max(pids[it.key()], *it);
1157
1158 return pids.size() - sz;
1159}
1160
1162{
1163 if (m_pidVideoSingleProgram == pid)
1164 return kPIDPriorityHigh;
1165
1166 pid_map_t::const_iterator it;
1167 it = m_pidsListening.find(pid);
1168 if (it != m_pidsListening.end())
1169 return *it;
1170 it = m_pidsNotListening.find(pid);
1171 if (it != m_pidsNotListening.end())
1172 return *it;
1173 it = m_pidsWriting.find(pid);
1174 if (it != m_pidsWriting.end())
1175 return *it;
1176 it = m_pidsAudio.find(pid);
1177 if (it != m_pidsAudio.end())
1178 return *it;
1179
1180 return kPIDPriorityNone;
1181}
1182
1184{
1185 pid_psip_map_t::iterator it = m_partialPsipPacketCache.find(pid);
1186 if (it == m_partialPsipPacketCache.end())
1187 m_partialPsipPacketCache[pid] = packet;
1188 else
1189 {
1190 PSIPTable *old = *it;
1191 m_partialPsipPacketCache.remove(pid);
1192 m_partialPsipPacketCache.insert(pid, packet);
1193 delete old;
1194 }
1195}
1196
1198{
1199 return m_patStatus.HasAllSections(tsid);
1200}
1201
1203{
1204 return m_catStatus.HasAllSections(tsid);
1205}
1206
1208{
1209 return m_pmtStatus.HasAllSections(prog_num);
1210}
1211
1213{
1214 pmt_const_ptr_t pmt = GetCachedPMT(progNum, 0);
1215 bool hasit = pmt;
1216 ReturnCachedTable(pmt);
1217
1218 return hasit;
1219}
1220
1222{
1223 QMutexLocker locker(&m_cacheLock);
1224
1225 pat_cache_t::const_iterator it = m_cachedPats.constFind(tsid << 8);
1226 if (it == m_cachedPats.constEnd())
1227 return false;
1228
1229 uint last_section = (*it)->LastSection();
1230 if (!last_section)
1231 return true;
1232
1233 for (uint i = 1; i <= last_section; i++)
1234 if (m_cachedPats.constFind((tsid << 8) | i) == m_cachedPats.constEnd())
1235 return false;
1236
1237 return true;
1238}
1239
1241{
1242 QMutexLocker locker(&m_cacheLock);
1243
1244 for (uint i = 0; i <= 255; i++)
1245 if (m_cachedPats.contains((tsid << 8) | i))
1246 return true;
1247
1248 return false;
1249}
1250
1252{
1253 QMutexLocker locker(&m_cacheLock);
1254 return !m_cachedPats.empty();
1255}
1256
1258{
1259 QMutexLocker locker(&m_cacheLock);
1260
1261 cat_cache_t::const_iterator it = m_cachedCats.constFind(tsid << 8);
1262 if (it == m_cachedCats.constEnd())
1263 return false;
1264
1265 uint last_section = (*it)->LastSection();
1266 if (!last_section)
1267 return true;
1268
1269 for (uint i = 1; i <= last_section; i++)
1270 if (m_cachedCats.constFind((tsid << 8) | i) == m_cachedCats.constEnd())
1271 return false;
1272
1273 return true;
1274}
1275
1277{
1278 QMutexLocker locker(&m_cacheLock);
1279
1280 for (uint i = 0; i <= 255; i++)
1281 if (m_cachedCats.contains((tsid << 8) | i))
1282 return true;
1283
1284 return false;
1285}
1286
1288{
1289 QMutexLocker locker(&m_cacheLock);
1290 return !m_cachedCats.empty();
1291}
1292
1294{
1295 QMutexLocker locker(&m_cacheLock);
1296
1297 pmt_cache_t::const_iterator it = m_cachedPmts.constFind(pnum << 8);
1298 if (it == m_cachedPmts.constEnd())
1299 return false;
1300
1301 uint last_section = (*it)->LastSection();
1302 if (!last_section)
1303 return true;
1304
1305 for (uint i = 1; i <= last_section; i++)
1306 if (m_cachedPmts.constFind((pnum << 8) | i) == m_cachedPmts.constEnd())
1307 return false;
1308
1309 return true;
1310}
1311
1313{
1314 QMutexLocker locker(&m_cacheLock);
1315
1316 for (uint i = 0; i <= 255; i++)
1317 if (m_cachedPmts.contains((pnum << 8) | i))
1318 return true;
1319
1320 return false;
1321}
1322
1324{
1325 QMutexLocker locker(&m_cacheLock);
1326
1327 if (m_cachedPats.empty())
1328 return false;
1329
1330 for (auto *pat : std::as_const(m_cachedPats))
1331 {
1333 return false;
1334
1335 for (uint i = 0; i < pat->ProgramCount(); i++)
1336 {
1337 uint prognum = pat->ProgramNumber(i);
1338 if (prognum && !HasCachedAllPMT(prognum))
1339 return false;
1340 }
1341 }
1342
1343 return true;
1344}
1345
1347{
1348 QMutexLocker locker(&m_cacheLock);
1349 return !m_cachedPmts.empty();
1350}
1351
1353{
1354 QMutexLocker locker(&m_cacheLock);
1355 ProgramAssociationTable *pat = nullptr;
1356
1357 uint key = (tsid << 8) | section_num;
1358 pat_cache_t::const_iterator it = m_cachedPats.constFind(key);
1359 if (it != m_cachedPats.constEnd())
1360 IncrementRefCnt(pat = *it);
1361
1362 return pat;
1363}
1364
1366{
1367 QMutexLocker locker(&m_cacheLock);
1368 pat_vec_t pats;
1369
1370 for (uint i=0; i < 256; i++)
1371 {
1372 pat_const_ptr_t pat = GetCachedPAT(tsid, i);
1373 if (pat)
1374 pats.push_back(pat);
1375 }
1376
1377 return pats;
1378}
1379
1381{
1382 QMutexLocker locker(&m_cacheLock);
1383 pat_vec_t pats;
1384
1385 for (auto *pat : std::as_const(m_cachedPats))
1386 {
1387 IncrementRefCnt(pat);
1388 pats.push_back(pat);
1389 }
1390
1391 return pats;
1392}
1393
1395{
1396 QMutexLocker locker(&m_cacheLock);
1397 ConditionalAccessTable *cat = nullptr;
1398
1399 uint key = (tsid << 8) | section_num;
1400 cat_cache_t::const_iterator it = m_cachedCats.constFind(key);
1401 if (it != m_cachedCats.constEnd())
1402 IncrementRefCnt(cat = *it);
1403
1404 return cat;
1405}
1406
1408{
1409 QMutexLocker locker(&m_cacheLock);
1410 cat_vec_t cats;
1411
1412 for (uint i=0; i < 256; i++)
1413 {
1414 cat_const_ptr_t cat = GetCachedCAT(tsid, i);
1415 if (cat)
1416 cats.push_back(cat);
1417 }
1418
1419 return cats;
1420}
1421
1423{
1424 QMutexLocker locker(&m_cacheLock);
1425 cat_vec_t cats;
1426
1427 for (auto *cat : std::as_const(m_cachedCats))
1428 {
1430 cats.push_back(cat);
1431 }
1432
1433 return cats;
1434}
1435
1437 uint program_num, uint section_num) const
1438{
1439 QMutexLocker locker(&m_cacheLock);
1440 ProgramMapTable *pmt = nullptr;
1441
1442 uint key = (program_num << 8) | section_num;
1443 pmt_cache_t::const_iterator it = m_cachedPmts.constFind(key);
1444 if (it != m_cachedPmts.constEnd())
1445 IncrementRefCnt(pmt = *it);
1446
1447 return pmt;
1448}
1449
1451{
1452 QMutexLocker locker(&m_cacheLock);
1453 std::vector<const ProgramMapTable*> pmts;
1454
1455 for (auto *pmt : std::as_const(m_cachedPmts))
1456 {
1457 IncrementRefCnt(pmt);
1458 pmts.push_back(pmt);
1459 }
1460
1461 return pmts;
1462}
1463
1465{
1466 QMutexLocker locker(&m_cacheLock);
1467 pmt_map_t pmts;
1468
1469 for (auto *pmt : std::as_const(m_cachedPmts))
1470 {
1471 IncrementRefCnt(pmt);
1472 pmts[pmt->ProgramNumber()].push_back(pmt);
1473 }
1474
1475 return pmts;
1476}
1477
1479{
1480 QMutexLocker locker(&m_cacheLock);
1481
1482 int val = m_cachedRefCnt[psip] - 1;
1483 m_cachedRefCnt[psip] = val;
1484
1485 // if ref <= 0 and table was slated for deletion, delete it.
1486 if (val <= 0)
1487 {
1488 psip_refcnt_map_t::iterator it;
1489 it = m_cachedSlatedForDeletion.find(psip);
1490 if (it != m_cachedSlatedForDeletion.end())
1491 DeleteCachedTable(psip);
1492 }
1493}
1494
1496{
1497 for (auto & pat : pats)
1498 ReturnCachedTable(pat);
1499 pats.clear();
1500}
1501
1503{
1504 // NOLINTNEXTLINE(modernize-loop-convert)
1505 for (pat_map_t::iterator it = pats.begin(); it != pats.end(); ++it)
1507 pats.clear();
1508}
1509
1511{
1512 for (auto & cat : cats)
1514 cats.clear();
1515}
1516
1518{
1519 // NOLINTNEXTLINE(modernize-loop-convert)
1520 for (cat_map_t::iterator it = cats.begin(); it != cats.end(); ++it)
1522 cats.clear();
1523}
1524
1526{
1527 for (auto & pmt : pmts)
1528 ReturnCachedTable(pmt);
1529 pmts.clear();
1530}
1531
1533{
1534 // NOLINTNEXTLINE(modernize-loop-convert)
1535 for (pmt_map_t::iterator it = pmts.begin(); it != pmts.end(); ++it)
1537 pmts.clear();
1538}
1539
1541{
1542 QMutexLocker locker(&m_cacheLock);
1543 m_cachedRefCnt[psip] = m_cachedRefCnt[psip] + 1;
1544}
1545
1547{
1548 if (!psip)
1549 return false;
1550
1551 uint tid = psip->TableIDExtension();
1552
1553 QMutexLocker locker(&m_cacheLock);
1554 if (m_cachedRefCnt[psip] > 0)
1555 {
1556 m_cachedSlatedForDeletion[psip] = 1;
1557 return false;
1558 }
1559 if (TableID::PAT == psip->TableID() &&
1560 (m_cachedPats[(tid << 8) | psip->Section()] == psip))
1561 {
1562 m_cachedPats[(tid << 8) | psip->Section()] = nullptr;
1563 delete psip;
1564 }
1565 else if (TableID::CAT == psip->TableID() &&
1566 (m_cachedCats[(tid << 8) | psip->Section()] == psip))
1567 {
1568 m_cachedCats[(tid << 8) | psip->Section()] = nullptr;
1569 delete psip;
1570 }
1571 else if ((TableID::PMT == psip->TableID()) &&
1572 (m_cachedPmts[(tid << 8) | psip->Section()] == psip))
1573 {
1574 m_cachedPmts[(tid << 8) | psip->Section()] = nullptr;
1575 delete psip;
1576 }
1577 else
1578 {
1579 m_cachedSlatedForDeletion[psip] = 2;
1580 return false;
1581 }
1582 psip_refcnt_map_t::iterator it;
1583 it = m_cachedSlatedForDeletion.find(psip);
1584 if (it != m_cachedSlatedForDeletion.end())
1585 m_cachedSlatedForDeletion.erase(it);
1586
1587 return true;
1588}
1589
1591{
1592 auto *pat = new ProgramAssociationTable(*_pat);
1593 uint key = (_pat->TransportStreamID() << 8) | _pat->Section();
1594
1595 QMutexLocker locker(&m_cacheLock);
1596
1597 pat_cache_t::iterator it = m_cachedPats.find(key);
1598 if (it != m_cachedPats.end())
1599 DeleteCachedTable(*it);
1600
1601 m_cachedPats[key] = pat;
1602}
1603
1605{
1606 auto *cat = new ConditionalAccessTable(*_cat);
1607 uint key = (_cat->TableIDExtension() << 8) | _cat->Section();
1608
1609 QMutexLocker locker(&m_cacheLock);
1610
1611 cat_cache_t::iterator it = m_cachedCats.find(key);
1612 if (it != m_cachedCats.end())
1613 DeleteCachedTable(*it);
1614
1615 m_cachedCats[key] = cat;
1616}
1617
1619{
1620 auto *pmt = new ProgramMapTable(*_pmt);
1621 uint key = (_pmt->ProgramNumber() << 8) | _pmt->Section();
1622
1623 QMutexLocker locker(&m_cacheLock);
1624
1625 pmt_cache_t::iterator it = m_cachedPmts.find(key);
1626 if (it != m_cachedPmts.end())
1627 DeleteCachedTable(*it);
1628
1629 m_cachedPmts[key] = pmt;
1630}
1631
1633{
1634 QMutexLocker locker(&m_listenerLock);
1635
1636 for (auto & listener : m_mpegListeners)
1637 if (((void*)val) == ((void*)listener))
1638 return;
1639
1640 m_mpegListeners.push_back(val);
1641}
1642
1644{
1645 QMutexLocker locker(&m_listenerLock);
1646
1647 for (auto it = m_mpegListeners.begin(); it != m_mpegListeners.end(); ++it)
1648 {
1649 if (((void*)val) == ((void*)*it))
1650 {
1651 m_mpegListeners.erase(it);
1652 return;
1653 }
1654 }
1655}
1656
1658{
1659 QMutexLocker locker(&m_listenerLock);
1660
1661 for (auto & listener : m_tsWritingListeners)
1662 if (((void*)val) == ((void*)listener))
1663 return;
1664
1665 m_tsWritingListeners.push_back(val);
1666}
1667
1669{
1670 QMutexLocker locker(&m_listenerLock);
1671
1672 for (auto it = m_tsWritingListeners.begin(); it != m_tsWritingListeners.end(); ++it)
1673 {
1674 if (((void*)val) == ((void*)*it))
1675 {
1676 m_tsWritingListeners.erase(it);
1677 return;
1678 }
1679 }
1680}
1681
1683{
1684 QMutexLocker locker(&m_listenerLock);
1685
1686 for (auto & listener : m_tsAvListeners)
1687 {
1688 if (((void*)val) == ((void*)listener))
1689 {
1690 LOG(VB_RECORD, LOG_ERR, LOC + QString("AddAVListener 0x%1 already present")
1691 .arg((uint64_t)val, 0, 16));
1692 return;
1693 }
1694 }
1695
1696 m_tsAvListeners.push_back(val);
1697#if 0
1698 LOG(VB_RECORD, LOG_DEBUG, LOC + QString("AddAVListener 0x%1 added")
1699 .arg((uint64_t)val, 0, 16));
1700#endif
1701}
1702
1704{
1705 QMutexLocker locker(&m_listenerLock);
1706
1707 for (auto it = m_tsAvListeners.begin(); it != m_tsAvListeners.end(); ++it)
1708 {
1709 if (((void*)val) == ((void*)*it))
1710 {
1711 m_tsAvListeners.erase(it);
1712#if 0
1713 LOG(VB_RECORD, LOG_DEBUG, LOC + QString("RemoveAVListener 0x%1 found and removed")
1714 .arg((uint64_t)val, 0, 16));
1715#endif
1716 return;
1717 }
1718 }
1719
1720 LOG(VB_RECORD, LOG_ERR, LOC + QString("RemoveAVListener 0x%1 NOT found")
1721 .arg((uint64_t)val, 0, 16));
1722}
1723
1725{
1726 QMutexLocker locker(&m_listenerLock);
1727
1728 for (auto & listener : m_mpegSpListeners)
1729 if (((void*)val) == ((void*)listener))
1730 return;
1731
1732 m_mpegSpListeners.push_back(val);
1733}
1734
1736{
1737 QMutexLocker locker(&m_listenerLock);
1738
1739 for (auto it = m_mpegSpListeners.begin(); it != m_mpegSpListeners.end(); ++it)
1740 {
1741 if (((void*)val) == ((void*)*it))
1742 {
1743 m_mpegSpListeners.erase(it);
1744 return;
1745 }
1746 }
1747}
1748
1750{
1751 QMutexLocker locker(&m_listenerLock);
1752
1753 for (auto & listener : m_psListeners)
1754 if (((void*)val) == ((void*)listener))
1755 return;
1756
1757 m_psListeners.push_back(val);
1758}
1759
1761{
1762 QMutexLocker locker(&m_listenerLock);
1763
1764 for (auto it = m_psListeners.begin(); it != m_psListeners.end(); ++it)
1765 {
1766 if (((void*)val) == ((void*)*it))
1767 {
1768 m_psListeners.erase(it);
1769 return;
1770 }
1771 }
1772}
1773
1775{
1776 QMutexLocker locker(&m_encryptionLock);
1777
1778#if 0
1779 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("AddEncryptionTestPID(%1, 0x%2)")
1780 .arg(pnum) .arg(pid, 0, 16));
1781#endif
1782
1783 AddListeningPID(pid);
1784
1785 m_encryptionPidToInfo[pid] = CryptInfo((isvideo) ? 10000 : 500, 8);
1786
1787 m_encryptionPidToPnums[pid].push_back(pnum);
1788 m_encryptionPnumToPids[pnum].push_back(pid);
1790}
1791
1793{
1794 QMutexLocker locker(&m_encryptionLock);
1795
1796#if 0
1797 LOG(VB_RECORD, LOG_DEBUG, LOC +
1798 QString("Tearing down up decryption monitoring for program %1")
1799 .arg(pnum));
1800#endif
1801
1802 QMap<uint, uint_vec_t>::iterator list;
1803 uint_vec_t::iterator it;
1804
1806 for (uint pid : pids)
1807 {
1808#if 0
1809 LOG(VB_GENERAL, LOG_DEBUG, LOC +
1810 QString("Removing 0x%1 PID Enc monitoring").arg(pid,0,16));
1811#endif
1812
1813 RemoveListeningPID(pid);
1814
1815 list = m_encryptionPidToPnums.find(pid);
1816 if (list != m_encryptionPidToPnums.end())
1817 {
1818 it = find((*list).begin(), (*list).end(), pnum);
1819
1820 if (it != (*list).end())
1821 (*list).erase(it);
1822
1823 if ((*list).empty())
1824 {
1825 m_encryptionPidToPnums.remove(pid);
1826 m_encryptionPidToInfo.remove(pid);
1827 }
1828 }
1829 }
1830
1831 m_encryptionPnumToPids.remove(pnum);
1832}
1833
1835{
1836 QMutexLocker locker(&m_encryptionLock);
1837
1838 QMap<uint, CryptInfo>::const_iterator it =
1839 m_encryptionPidToInfo.find(pid);
1840
1841 return it != m_encryptionPidToInfo.end();
1842}
1843
1845{
1846 QMutexLocker locker(&m_encryptionLock);
1847
1848#if 0
1849 LOG(VB_RECORD, LOG_DEBUG, LOC +
1850 QString("Setting up decryption monitoring for program %1")
1851 .arg(pmt->ProgramNumber()));
1852#endif
1853
1854 bool encrypted = pmt->IsProgramEncrypted();
1855 for (uint i = 0; i < pmt->StreamCount(); i++)
1856 {
1857 if (!encrypted && !pmt->IsStreamEncrypted(i))
1858 continue;
1859
1860 const uint streamType = pmt->StreamType(i);
1861 bool is_vid = StreamID::IsVideo(streamType);
1862 bool is_aud = StreamID::IsAudio(streamType);
1863
1864 if (is_vid || is_aud)
1865 {
1867 pmt->ProgramNumber(), pmt->StreamPID(i), is_vid);
1868 }
1869 }
1870}
1871
1873{
1874 QMutexLocker locker(&m_encryptionLock);
1875
1876 m_encryptionPidToInfo.clear();
1877 m_encryptionPidToPnums.clear();
1878 m_encryptionPnumToPids.clear();
1879}
1880
1882{
1883 QMutexLocker locker(&m_encryptionLock);
1885}
1886
1888{
1889 QMutexLocker locker(&m_encryptionLock);
1891}
1892
1893static QString toString(CryptStatus status)
1894{
1895 if (kEncDecrypted == status)
1896 return "Decrypted";
1897 if (kEncEncrypted == status)
1898 return "Encrypted";
1899 return "Unknown";
1900}
1901
1906{
1907 QMutexLocker encryptionLock(&m_encryptionLock);
1908
1909 std::map<uint,bool> pnumEncrypted;
1910 const uint pid = tspacket.PID();
1912
1913 CryptStatus status = kEncUnknown;
1914
1915 if (tspacket.Scrambled())
1916 {
1917 info.m_decryptedPackets = 0;
1918
1919 // If a fair amount of encrypted packets is passed assume that
1920 // the stream is not decryptable
1921 if (++info.m_encryptedPackets >= info.m_encryptedMin)
1922 status = kEncEncrypted;
1923 }
1924 else
1925 {
1926 info.m_encryptedPackets = 0;
1927 if (++info.m_decryptedPackets > info.m_decryptedMin)
1928 status = kEncDecrypted;
1929 }
1930
1931 if (status == info.m_status)
1932 return; // pid encryption status unchanged
1933
1934 info.m_status = status;
1935
1936 LOG(status != kEncDecrypted ? VB_GENERAL : VB_RECORD, LOG_DEBUG, LOC +
1937 QString("PID 0x%1 status: %2") .arg(pid,0,16).arg(toString(status)));
1938
1939 uint_vec_t pnum_del_list;
1940 const uint_vec_t &pnums = m_encryptionPidToPnums[pid];
1941 for (uint pnum : pnums)
1942 {
1943 status = m_encryptionPnumToStatus[pnum];
1944
1945 const uint_vec_t &pids = m_encryptionPnumToPids[pnum];
1946 if (!pids.empty())
1947 {
1948 std::array<uint,3> enc_cnt { 0, 0, 0 };
1949 for (uint pid2 : pids)
1950 {
1951 CryptStatus stat = m_encryptionPidToInfo[pid2].m_status;
1952 enc_cnt[stat]++;
1953
1954#if 0
1955 LOG(VB_GENERAL, LOG_DEBUG, LOC +
1956 QString("\tpnum %1 PID 0x%2 status: %3")
1957 .arg(pnum).arg(pid2,0,16) .arg(toString(stat)));
1958#endif
1959 }
1960 status = kEncUnknown;
1961
1962 if (enc_cnt[kEncEncrypted])
1963 status = kEncEncrypted;
1964 else if (enc_cnt[kEncDecrypted] >= std::min((size_t) 2, pids.size()))
1965 status = kEncDecrypted;
1966 }
1967
1968 if (status == m_encryptionPnumToStatus[pnum])
1969 continue; // program encryption status unchanged
1970
1971 LOG(VB_RECORD, LOG_DEBUG, LOC + QString("Program %1 status: %2")
1972 .arg(pnum).arg(toString(status)));
1973
1974 m_encryptionPnumToStatus[pnum] = status;
1975
1976 bool encrypted = kEncUnknown == status || kEncEncrypted == status;
1977 pnumEncrypted[pnum] = encrypted;
1978
1979 if (kEncDecrypted == status)
1980 pnum_del_list.push_back(pnum);
1981 }
1982
1983 // Call HandleEncryptionStatus outside the m_encryptionLock
1984 encryptionLock.unlock();
1985 m_listenerLock.lock();
1986 for (auto & pe : pnumEncrypted)
1987 {
1988 for (auto & listener : m_mpegListeners)
1989 {
1990 listener->HandleEncryptionStatus(pe.first, pe.second);
1991 }
1992 }
1993 m_listenerLock.unlock();
1994
1995 for (size_t i = 0; i < pnum_del_list.size(); i++)
1996 RemoveEncryptionTestPIDs(pnums[i]);
1997}
std::vector< const TerrestrialVirtualChannelTable * > tvct_vec_t
std::vector< const CableVirtualChannelTable * > cvct_vec_t
Overall structure.
QMap< uint, pmt_vec_t > pmt_map_t
std::vector< const ProgramMapTable * > pmt_vec_t
Encapsulates data about ATSC stream and emits events for most tables.
cvct_vec_t GetCachedCVCTs(bool current=true) const
void ReturnCachedTVCTTables(tvct_vec_t &tvcts) const
void ReturnCachedCVCTTables(cvct_vec_t &cvcts) const
tvct_vec_t GetCachedTVCTs(bool current=true) const
The CAT is used to transmit additional ConditionalAccessDescriptor instances, in addition to the ones...
Definition: mpegtables.h:839
static desc_list_t ParseAndExclude(const unsigned char *data, uint len, int excluded_descid)
bool IsValid(void) const
static desc_list_t ParseOnlyInclude(const unsigned char *data, uint len, int excluded_descid)
static const unsigned char * Find(const desc_list_t &parsed, uint desc_tag)
QMap< uint, uint_vec_t > m_encryptionPnumToPids
void AddMPEGListener(MPEGStreamListener *val)
void AddPSStreamListener(PSStreamListener *val)
void SetDesiredProgram(int p)
const ProgramMapTable * PMTSingleProgram(void) const
bool HasAllCATSections(uint tsid) const
void ProcessPMT(const ProgramMapTable *pmt)
pmt_vec_t GetCachedPMTs(void) const
std::array< double, 16 > m_siTimeOffsets
virtual bool IsNotListeningPID(uint pid) const
pid_psip_map_t m_partialPsipPacketCache
void CachePAT(const ProgramAssociationTable *pat)
cat_const_ptr_t GetCachedCAT(uint tsid, uint section_num) const
void TestDecryption(const ProgramMapTable *pmt)
QRecursiveMutex m_listenerLock
bool HasCachedAllPMT(uint pnum) const
bool HasCachedAnyPMT(uint pnum) const
psip_refcnt_map_t m_cachedSlatedForDeletion
void CachePMT(const ProgramMapTable *pmt)
QMap< uint, uint_vec_t > m_encryptionPidToPnums
void AddWritingListener(TSPacketListener *val)
void SetAudioStreamsRequired(uint num)
bool IsProgramEncrypted(uint pnum) const
void UpdateTimeOffset(uint64_t si_utc_time)
virtual bool ProcessTSPacket(const TSPacket &tspacket)
virtual void HandleTSTables(const TSPacket *tspacket)
Assembles PSIP packets and processes them.
QString m_siStandard
virtual void ReturnCachedPATTables(pat_vec_t &pats) const
pat_const_ptr_t GetCachedPAT(uint tsid, uint section_num) const
bool HasCachedAnyCAT(void) const
pmt_map_t GetCachedPMTMap(void) const
EITHelper * m_eitHelper
void SetPMTSingleProgram(ProgramMapTable *pmt)
TableStatusMap m_catStatus
QRecursiveMutex m_cacheLock
virtual bool DeleteCachedTable(const PSIPTable *psip) const
MPEGStreamData(int desiredProgram, int cardnum, bool cacheTables)
Initializes MPEGStreamData.
void ProcessCAT(const ConditionalAccessTable *cat)
void ProcessEncryptedPacket(const TSPacket &tspacket)
counts en/decrypted packets to decide if a stream is en/decrypted
PSIPTable * GetPartialPSIP(uint pid)
psip_refcnt_map_t m_cachedRefCnt
void CacheCAT(const ConditionalAccessTable *_cat)
void SetEITHelper(EITHelper *eit_helper) override
pmt_const_ptr_t GetCachedPMT(uint program_num, uint section_num) const
void AddMPEGSPListener(MPEGSingleProgramStreamListener *val)
pid_map_t m_pidsConditionalAccess
bool HasCachedAnyPAT(void) const
ts_av_listener_vec_t m_tsAvListeners
ps_listener_vec_t m_psListeners
pid_map_t m_pidsWriting
TableStatusMap m_pmtStatus
void ProcessPAT(const ProgramAssociationTable *pat)
QRecursiveMutex m_encryptionLock
bool m_normalizeStreamType
void RemoveWritingListener(TSPacketListener *val)
QMap< uint, CryptInfo > m_encryptionPidToInfo
void SavePartialPSIP(uint pid, PSIPTable *packet)
PIDPriority GetPIDPriority(uint pid) const
pmt_cache_t m_cachedPmts
virtual bool IsAudioPID(uint pid) const
bool HasCachedAnyPMTs(void) const
virtual bool IsRedundant(uint pid, const PSIPTable &psip) const
Returns true if table already seen.
virtual void ReturnCachedCATTables(cat_vec_t &cats) const
pid_map_t m_pidsAudio
bool IsProgramDecrypted(uint pnum) const
void SetRecordingType(const QString &recording_type)
bool CreatePMTSingleProgram(const ProgramMapTable &pmt)
ts_listener_vec_t m_tsWritingListeners
void RemoveMPEGListener(MPEGStreamListener *val)
bool IsEncryptionTestPID(uint pid) const
virtual void RemoveListeningPID(uint pid)
void RemovePSStreamListener(PSStreamListener *val)
uint m_pidPmtSingleProgram
mpeg_sp_listener_vec_t m_mpegSpListeners
void AddEncryptionTestPID(uint pnum, uint pid, bool isvideo)
virtual void AddAudioPID(uint pid, PIDPriority priority=kPIDPriorityHigh)
bool HasCachedAllPMTs(void) const
virtual void ReturnCachedTable(const PSIPTable *psip) const
pid_map_t m_pidsNotListening
const ProgramAssociationTable * PATSingleProgram(void) const
static int ResyncStream(const unsigned char *buffer, int curr_pos, int len)
virtual void Reset(void)
double TimeOffset(void) const
Current Offset from computer time to DVB time in seconds.
bool HasProgram(uint progNum) const
TableStatusMap m_patStatus
bool IsVideoPID(uint pid) const
void SetVideoStreamsRequired(uint num)
QString m_recordingType
virtual void AddWritingPID(uint pid, PIDPriority priority=kPIDPriorityHigh)
bool m_stripPmtDescriptors
bool HasAllPATSections(uint tsid) const
ProgramMapTable * m_pmtSingleProgram
virtual int ProcessData(const unsigned char *buffer, int len)
void RemoveEncryptionTestPIDs(uint pnum)
void SetEITRate(float rate) override
QMap< uint, CryptStatus > m_encryptionPnumToStatus
bool HasCachedAllPAT(uint tsid) const
uint m_pidVideoSingleProgram
bool HasCachedAllCAT(uint tsid) const
void DeletePartialPSIP(uint pid)
virtual void ReturnCachedPMTTables(pmt_vec_t &pmts) const
uint m_pmtSingleProgramNumVideo
bool CreatePATSingleProgram(const ProgramAssociationTable &pat)
bool HasAllPMTSections(uint prog_num) const
virtual bool IsListeningPID(uint pid) const
void RemoveAVListener(TSPacketListenerAV *val)
void RemoveMPEGSPListener(MPEGSingleProgramStreamListener *val)
cat_vec_t GetCachedCATs(void) const
virtual void AddListeningPID(uint pid, PIDPriority priority=kPIDPriorityNormal)
void SetPATSingleProgram(ProgramAssociationTable *pat)
uint m_pmtSingleProgramNumAudio
virtual void AddConditionalAccessPID(uint pid, PIDPriority priority=kPIDPriorityNormal)
pat_cache_t m_cachedPats
void ResetDecryptionMonitoringState(void)
MythTimer m_invalidPatTimer
PSIPTable * AssemblePSIP(const TSPacket *tspacket, bool &moreTablePackets)
PSIP packet assembler.
virtual bool IsWritingPID(uint pid) const
mpeg_listener_vec_t m_mpegListeners
pat_vec_t GetCachedPATs(void) const
cat_cache_t m_cachedCats
~MPEGStreamData() override
virtual bool IsConditionalAccessPID(uint pid) const
virtual bool HandleTables(uint pid, const PSIPTable &psip)
Process PSIP packets.
void IncrementRefCnt(const PSIPTable *psip) const
uint GetPIDs(pid_map_t &pids) const
pid_map_t m_pidsListening
void AddAVListener(TSPacketListenerAV *val)
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:91
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
bool AddTSPacket(const TSPacket *tspacket, int cardid, bool &broken)
Definition: pespacket.cpp:20
const TSHeader * tsheader() const
Definition: pespacket.h:90
bool IsGood() const
Definition: pespacket.h:88
const unsigned char * pesdata() const
Definition: pespacket.h:164
uint PSIOffset() const
Definition: pespacket.h:162
uint StreamID() const
Definition: pespacket.h:98
void SetPSIOffset(uint offset)
Definition: pespacket.h:183
uint TSSizeInBuffer() const
Definition: pespacket.h:161
@ MPEG_PAT_PID
Definition: mpegtables.h:211
@ MPEG_CAT_PID
Definition: mpegtables.h:212
A PSIP table is a variant of a PES packet containing an MPEG, ATSC or DVB table.
Definition: mpegtables.h:410
uint LastSection(void) const
Definition: mpegtables.h:534
uint Version(void) const
Definition: mpegtables.h:522
bool HasCRC(void) const override
1 bit Cyclic Redundancy Check present
Definition: mpegtables.cpp:100
bool IsCurrent(void) const
Definition: mpegtables.h:528
bool VerifyPSIP(bool verify_crc) const
Definition: mpegtables.cpp:242
uint TableID(void) const
Definition: mpegtables.h:496
uint Section(void) const
Definition: mpegtables.h:531
uint TableIDExtension(void) const
Definition: mpegtables.h:515
The Program Association Table lists all the programs in a stream, and is always found on PID 0.
Definition: mpegtables.h:599
uint FindAnyPID(void) const
Definition: mpegtables.h:645
QString toString(void) const override
Definition: mpegtables.cpp:841
uint ProgramCount(void) const
Definition: mpegtables.h:619
uint TransportStreamID(void) const
Definition: mpegtables.h:617
uint FindPID(uint progNum) const
Definition: mpegtables.h:638
static ProgramAssociationTable * Create(uint tsid, uint version, const std::vector< uint > &pnum, const std::vector< uint > &pid)
Definition: mpegtables.cpp:351
uint ProgramNumber(uint i) const
Definition: mpegtables.h:626
A PMT table maps a program described in the ProgramAssociationTable to various PID's which describe t...
Definition: mpegtables.h:676
bool IsProgramEncrypted(void) const
Returns true iff PMT's ProgramInfo contains CA descriptor.
Definition: mpegtables.cpp:572
uint StreamCount(void) const
Definition: mpegtables.h:733
uint PCRPID(void) const
stream that contains program clock reference.
Definition: mpegtables.h:709
uint StreamPID(uint i) const
Definition: mpegtables.h:724
QString toString(void) const override
Definition: mpegtables.cpp:906
uint StreamType(uint i) const
Definition: mpegtables.h:721
int FindPID(uint pid) const
Locates stream index of pid.
Definition: mpegtables.h:780
const unsigned char * ProgramInfo(void) const
Definition: mpegtables.h:718
const unsigned char * StreamInfo(uint i) const
Definition: mpegtables.h:730
uint ProgramNumber(void) const
Definition: mpegtables.h:712
uint ProgramInfoLength(void) const
Definition: mpegtables.h:715
uint StreamInfoLength(uint i) const
Definition: mpegtables.h:727
bool IsStreamEncrypted(uint pid) const
Returns true iff PMT contains CA descriptor.
Definition: mpegtables.cpp:600
void Parse(void) const
Definition: mpegtables.cpp:473
static ProgramMapTable * Create(uint programNumber, uint basepid, uint pcrpid, uint version, std::vector< uint > pids, std::vector< uint > types)
Definition: mpegtables.cpp:411
void setSCTEPID(int ts_pid)
Definition: mpegtables.h:1027
static uint Normalize(uint stream_id, const desc_list_t &desc, const QString &sistandard)
Definition: mpegtables.cpp:52
static bool IsAudio(uint type)
Returns true iff audio is MPEG1/2, AAC, AC3 or DTS audio stream.
Definition: mpegtables.h:179
static bool IsVideo(uint type)
Returns true iff video is an MPEG1/2/3, H264 or open cable video stream.
Definition: mpegtables.h:168
bool HasAdaptationField(void) const
Definition: tspacket.h:113
unsigned int ContinuityCounter(void) const
Definition: tspacket.h:109
bool HasPCR(void) const
Definition: tspacket.h:121
unsigned int PID(void) const
Definition: tspacket.h:93
bool HasPayload(void) const
Definition: tspacket.h:116
bool PayloadStart(void) const
Definition: tspacket.h:89
unsigned int AdaptationFieldControl(void) const
Definition: tspacket.h:103
TimePoint GetPCR(void) const
Definition: tspacket.h:148
bool TransportError(void) const
Definition: tspacket.h:86
bool Scrambled(void) const
Definition: tspacket.h:112
void SetContinuityCounter(unsigned int cc)
Definition: tspacket.h:170
const unsigned char * data(void) const
Definition: tspacket.h:174
size_t AdaptationFieldSize(void) const
Definition: tspacket.h:114
Used to access the data of a Transport Stream packet.
Definition: tspacket.h:208
static constexpr unsigned int kPayloadSize
Definition: tspacket.h:262
unsigned int AFCOffset(void) const
Definition: tspacket.h:249
QString toString(void) const
Definition: tspacket.cpp:28
unsigned int StartOfFieldPointer(void) const
Definition: tspacket.h:254
static constexpr unsigned int kSize
Definition: tspacket.h:261
Contains listing of Table ID's for various tables (PAT=0,PMT=2,etc).
Definition: mpegtables.h:260
@ STUFFING
Definition: mpegtables.h:384
void SetSectionSeen(uint32_t key, int32_t version, uint32_t section, uint32_t last_section, uint32_t segment_last_section=0xffff)
Definition: tablestatus.cpp:65
bool IsSectionSeen(uint32_t key, int32_t version, uint32_t section) const
Definition: tablestatus.cpp:73
bool HasAllSections(uint32_t key) const
Definition: tablestatus.cpp:81
unsigned int uint
Definition: compat.h:68
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
static const struct wl_interface * types[]
std::vector< const unsigned char * > desc_list_t
#define LOC
static desc_list_t extract_atsc_desc(const tvct_vec_t &tvct, const cvct_vec_t &cvct, uint pnum)
static QString toString(CryptStatus status)
QMap< uint, pat_vec_t > pat_map_t
std::vector< uint > uint_vec_t
QMap< unsigned int, PSIPTable * > pid_psip_map_t
QMap< uint, PIDPriority > pid_map_t
QMap< uint, cat_vec_t > cat_map_t
std::vector< const ProgramAssociationTable * > pat_vec_t
std::vector< const ConditionalAccessTable * > cat_vec_t
ProgramMapTable const * pmt_const_ptr_t
PIDPriority
@ kPIDPriorityNone
@ kPIDPriorityHigh
CryptStatus
@ 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
string version
Definition: giantbomb.py:185
static constexpr uint8_t SYNC_BYTE
Definition: tspacket.h:21