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 vct.reserve(tvct.size() + cvct.size());
424
425 for (const auto *i : tvct)
426 vct.push_back(i);
427
428 for (const auto *i : cvct)
429 vct.push_back(i);
430
431 for (size_t i = 0; i < tvct.size(); i++)
432 {
433 for (uint j = 0; j < vct[i]->ChannelCount(); j++)
434 {
435 if (vct[i]->ProgramNumber(j) == pnum)
436 {
438 vct[i]->Descriptors(j), vct[i]->DescriptorsLength(j),
440
441 if (!ldesc.empty())
442 desc.insert(desc.end(), ldesc.begin(), ldesc.end());
443 }
444 }
445
446 if (0 != vct[i]->GlobalDescriptorsLength())
447 {
449 vct[i]->GlobalDescriptors(),
450 vct[i]->GlobalDescriptorsLength(),
452
453 if (!vdesc.empty())
454 desc.insert(desc.end(), vdesc.begin(), vdesc.end());
455 }
456 }
457
458 return desc;
459}
460
462{
463 LOG(VB_RECORD, LOG_DEBUG, LOC + "CreatePMTSingleProgram()");
464 LOG(VB_RECORD, LOG_DEBUG, LOC + "PMT in input stream");
465 LOG(VB_RECORD, LOG_DEBUG, LOC + pmt.toString());
466
467 if (!PATSingleProgram())
468 {
469 LOG(VB_RECORD, LOG_ERR, LOC + "no PAT yet...");
470 return false; // no way to properly rewrite pids without PAT
471 }
472 pmt.Parse();
473
474 uint programNumber = 1; // MPEG Program Number
475
476 ATSCStreamData *sd = nullptr;
477 tvct_vec_t tvct;
478 cvct_vec_t cvct;
479
480 desc_list_t gdesc;
481
483 {
485 pmt.ProgramInfo(), pmt.ProgramInfoLength(),
487
488 // If there is no caption descriptor in PMT, copy any caption
489 // descriptor found in VCT to global descriptors...
490 sd = dynamic_cast<ATSCStreamData*>(this);
492 {
493 tvct = sd->GetCachedTVCTs();
494 cvct = sd->GetCachedCVCTs();
495
497 tvct, cvct, pmt.ProgramNumber());
498
499 if (!vdesc.empty())
500 gdesc.insert(gdesc.end(), vdesc.begin(), vdesc.end());
501 }
502 }
503
504 std::vector<uint> pids;
505 std::vector<uint> types;
506 std::vector<desc_list_t> pdesc;
507 pids.reserve(pmt.StreamCount());
508 types.reserve(pmt.StreamCount());
509 pdesc.reserve(pmt.StreamCount());
510
511 std::vector<uint> videoPIDs;
512 std::vector<uint> audioPIDs;
513 std::vector<uint> dataPIDs;
514 // Guessing two audio streams per video stream. Hopefully
515 // slightly oversized so only one memory allocation occurs.
516 videoPIDs.reserve(pmt.StreamCount()/3);
517 audioPIDs.reserve(pmt.StreamCount()/2);
518 dataPIDs.reserve(pmt.StreamCount()/3);
519
520 for (uint i = 0; i < pmt.StreamCount(); i++)
521 {
522 uint pid = pmt.StreamPID(i);
523
525 pmt.StreamInfo(i), pmt.StreamInfoLength(i),
527
529 pmt.StreamType(i), desc, m_siStandard);
530
531 bool is_video = StreamID::IsVideo(type);
532 bool is_audio = StreamID::IsAudio(type);
533
534 if (is_audio)
535 {
536 audioPIDs.push_back(pid);
537 }
538 else if (m_recordingType == "audio" )
539 {
540 // If not an audio PID but we only want audio,
541 // ignore this PID.
542 continue;
543 }
544
545#ifdef DEBUG_MPEG_RADIO
546 if (is_video)
547 continue;
548#endif // DEBUG_MPEG_RADIO
549
550 if (is_video)
551 {
552 videoPIDs.push_back(pid);
553 }
554
556 desc.clear();
557
558 // Filter out streams not used for basic television
559 if (m_recordingType == "tv" && !is_audio && !is_video &&
562 pid != pmt.PCRPID()) // We must not strip the PCR!
563 {
564 continue;
565 }
566
567 if (!is_audio && !is_video) //NOTE: Anything which isn't audio or video is data
568 dataPIDs.push_back(pid);
569
570 pdesc.push_back(desc);
571 pids.push_back(pid);
572 types.push_back(type);
573 }
574
575 if (videoPIDs.size() < m_pmtSingleProgramNumVideo)
576 {
577 LOG(VB_RECORD, LOG_ERR, LOC +
578 QString("Only %1 video streams seen in PMT, but %2 are required.")
579 .arg(videoPIDs.size()).arg(m_pmtSingleProgramNumVideo));
580 return false;
581 }
582
583 if (audioPIDs.size() < m_pmtSingleProgramNumAudio)
584 {
585 LOG(VB_RECORD, LOG_ERR, LOC +
586 QString("Only %1 audio streams seen in PMT, but %2 are required.")
587 .arg(audioPIDs.size()).arg(m_pmtSingleProgramNumAudio));
588 return false;
589 }
590
592 pmt.ProgramInfo(), pmt.ProgramInfoLength(),
594 for (auto & i : cdesc)
595 {
597 if (cad.IsValid())
598 {
599 AddListeningPID(cad.PID());
601 }
602 }
603
604 m_pidsAudio.clear();
605 for (uint pid : audioPIDs)
606 AddAudioPID(pid);
607
608 m_pidsWriting.clear();
609 m_pidVideoSingleProgram = !videoPIDs.empty() ? videoPIDs[0] : 0xffffffff;
610 for (size_t i = 1; i < videoPIDs.size(); i++)
611 AddWritingPID(videoPIDs[i]);
612
613 for (uint pid : dataPIDs)
614 AddWritingPID(pid);
615
616 // Timebase
617 int pcrpidIndex = pmt.FindPID(pmt.PCRPID());
618 if (pcrpidIndex < 0)
619 {
620 // the timecode reference stream is not in the PMT,
621 // add stream to misc record streams
622 AddWritingPID(pmt.PCRPID());
623 }
624
625 // Create the PMT
627 programNumber, m_pidPmtSingleProgram, pmt.PCRPID(),
628 pmt.Version(), gdesc, pids, types, pdesc);
629
630 // Return any TVCT & CVCT tables, once we've copied any descriptors.
631 if (sd)
632 {
633 sd->ReturnCachedTVCTTables(tvct);
634 sd->ReturnCachedCVCTTables(cvct);
635 }
636
637 // Set Continuity Header
638 uint cc_cnt = pmt.tsheader()->ContinuityCounter();
639 pmt2->tsheader()->SetContinuityCounter(cc_cnt);
641
642 LOG(VB_RECORD, LOG_DEBUG, LOC + "PMT for output stream");
643 LOG(VB_RECORD, LOG_DEBUG, LOC + pmt2->toString());
644
645 return true;
646}
647
651bool MPEGStreamData::IsRedundant([[maybe_unused]] uint pid,
652 const PSIPTable &psip) const
653{
654 const int table_id = psip.TableID();
655 const int version = psip.Version();
656
657 if (TableID::PAT == table_id)
658 {
660 }
661
662 if (TableID::CAT == table_id)
663 {
665 }
666
667 if (TableID::PMT == table_id)
668 {
670 }
671
672 return false;
673}
674
679{
680 if (MPEGStreamData::IsRedundant(pid, psip))
681 return true;
682
683 const int version = psip.Version();
684 // If we get this far decode table
685 switch (psip.TableID())
686 {
687 case TableID::PAT:
688 {
689 uint tsid = psip.TableIDExtension();
691
692 ProgramAssociationTable pat(psip);
693
694 if (m_cacheTables)
695 CachePAT(&pat);
696
697 ProcessPAT(&pat);
698
699 return true;
700 }
701 case TableID::CAT:
702 {
703 uint tsid = psip.TableIDExtension();
705
707
708 if (m_cacheTables)
709 CacheCAT(&cat);
710
711 ProcessCAT(&cat);
712
713 return true;
714 }
715 case TableID::PMT:
716 {
717 uint prog_num = psip.TableIDExtension();
718 m_pmtStatus.SetSectionSeen(prog_num, version, psip.Section(), psip.LastSection());
719
720 ProgramMapTable pmt(psip);
721
722 if (m_cacheTables)
723 CachePMT(&pmt);
724
725 ProcessPMT(&pmt);
726
727 return true;
728 }
729 case TableID::SITscte:
730 {
731 SpliceInformationTable sit(psip);
732 sit.setSCTEPID(pid);
733
734 m_listenerLock.lock();
735 for (auto & listener : m_mpegListeners)
736 listener->HandleSplice(&sit);
737 m_listenerLock.unlock();
738
739 return true;
740 }
741 }
742 return false;
743}
744
746{
747 bool foundProgram = pat->FindPID(m_desiredProgram) != 0U;
748
749 m_listenerLock.lock();
750 for (auto & listener : m_mpegListeners)
751 listener->HandlePAT(pat);
752 m_listenerLock.unlock();
753
754 if (m_desiredProgram < 0)
755 return;
756
757 bool send_single_program = false;
758 if (!m_invalidPatSeen && !foundProgram)
759 {
760 m_invalidPatSeen = true;
761 m_invalidPatWarning = false;
763 LOG(VB_RECORD, LOG_WARNING, LOC +
764 "ProcessPAT: PAT is missing program, setting timeout");
765 }
766 else if (m_invalidPatSeen && !foundProgram &&
768 {
769 m_invalidPatWarning = true; // only emit one warning...
770 // After 400ms emit error if we haven't found correct PAT.
771 LOG(VB_GENERAL, LOG_ERR, LOC + "ProcessPAT: Program not found in PAT. "
772 "Rescan your transports.");
773
774 send_single_program = CreatePATSingleProgram(*pat);
775 }
776 else if (foundProgram)
777 {
779 LOG(VB_RECORD, LOG_INFO, LOC +
780 "ProcessPAT: Good PAT seen after a bad PAT");
781
782 m_invalidPatSeen = false;
783
784 send_single_program = CreatePATSingleProgram(*pat);
785 }
786
787 if (send_single_program)
788 {
789 QMutexLocker locker(&m_listenerLock);
791 for (auto & listener : m_mpegSpListeners)
792 listener->HandleSingleProgramPAT(pat_sp, false);
793 }
794}
795
797{
798 m_listenerLock.lock();
799 for (auto & listener : m_mpegListeners)
800 listener->HandleCAT(cat);
801 m_listenerLock.unlock();
802
804 cat->Descriptors(), cat->DescriptorsLength(),
806 for (auto & i : cdesc)
807 {
809 if (cad.IsValid())
810 {
811 AddListeningPID(cad.PID());
813 }
814 }
815}
816
818{
819 m_listenerLock.lock();
820 for (auto & listener : m_mpegListeners)
821 listener->HandlePMT(pmt->ProgramNumber(), pmt);
822 m_listenerLock.unlock();
823
824 bool desired = pmt->ProgramNumber() == (uint) m_desiredProgram;
825 if (desired && CreatePMTSingleProgram(*pmt))
826 {
827 QMutexLocker locker(&m_listenerLock);
829 for (auto & listener : m_mpegSpListeners)
830 listener->HandleSingleProgramPMT(pmt_sp, false);
831 }
832}
833
835{
836 QMutexLocker locker(&m_siTimeLock);
838 return 0.0;
839
840 double avg_offset = 0.0;
841 double mult = 1.0 / m_siTimeOffsetCnt;
842 for (uint i = 0; i < m_siTimeOffsetCnt; i++)
843 avg_offset += m_siTimeOffsets[i] * mult;
844
845 return avg_offset;
846}
847
848void MPEGStreamData::UpdateTimeOffset(uint64_t _si_utc_time)
849{
850 struct timeval tm {};
851 if (gettimeofday(&tm, nullptr) != 0)
852 return;
853
854 double utc_time = tm.tv_sec + (tm.tv_usec * 0.000001);
855 double si_time = _si_utc_time;
856
857 QMutexLocker locker(&m_siTimeLock);
858 m_siTimeOffsets[m_siTimeOffsetIndx] = si_time - utc_time;
859
861
863
864}
865
870{
871 PSIPTable *psip = nullptr;
872 bool morePSIPTables = true;
873 while (morePSIPTables)
874 {
875 // Delete PSIP from previous iteration.
876 delete psip;
877
878 // Assemble PSIP
879 psip = AssemblePSIP(tspacket, morePSIPTables);
880 if (!psip)
881 return;
882
883 // drop stuffing packets
884 if ((TableID::ST == psip->TableID()) ||
885 (TableID::STUFFING == psip->TableID()))
886 {
887 LOG(VB_RECORD, LOG_DEBUG, LOC + "Dropping Stuffing table");
888 continue;
889 }
890
891 // Don't do validation on tables without CRC
892 if (!psip->HasCRC())
893 {
894 HandleTables(tspacket->PID(), *psip);
895 continue;
896 }
897
898 // Validate PSIP
899 // but don't validate PMT/PAT if our driver has the PMT/PAT CRC bug.
900 bool buggy = m_haveCrcBug &&
901 ((TableID::PMT == psip->TableID()) ||
902 (TableID::PAT == psip->TableID()));
903 if (!buggy && !psip->IsGood())
904 {
905 LOG(VB_RECORD, LOG_ERR, LOC +
906 QString("PSIP packet failed CRC check. pid(0x%1) type(0x%2)")
907 .arg(tspacket->PID(),0,16).arg(psip->TableID(),0,16));
908 continue;
909 }
910
911 if (TableID::MGT <= psip->TableID() && psip->TableID() <= TableID::STT &&
912 !psip->IsCurrent())
913 { // we don't cache the next table, for now
914 LOG(VB_RECORD, LOG_DEBUG, LOC + QString("Table not current 0x%1")
915 .arg(psip->TableID(),2,16,QChar('0')));
916 continue;
917 }
918
919 if (tspacket->Scrambled())
920 { // scrambled! ATSC, DVB require tables not to be scrambled
921 LOG(VB_RECORD, LOG_ERR, LOC +
922 "PSIP packet is scrambled, not ATSC/DVB compliant");
923 continue;
924 }
925
926 if (!psip->VerifyPSIP(!m_haveCrcBug))
927 {
928 LOG(VB_RECORD, LOG_ERR, LOC + QString("PSIP table 0x%1 is invalid")
929 .arg(psip->TableID(),2,16,QChar('0')));
930 continue;
931 }
932
933 // Don't decode redundant packets,
934 // but if it is a desired PAT or PMT emit a "heartbeat" signal.
935 if (MPEGStreamData::IsRedundant(tspacket->PID(), *psip))
936 {
937 if (TableID::PAT == psip->TableID())
938 {
939 QMutexLocker locker(&m_listenerLock);
941 for (auto & listener : m_mpegSpListeners)
942 listener->HandleSingleProgramPAT(pat_sp, false);
943 }
944 if (TableID::PMT == psip->TableID() &&
945 tspacket->PID() == m_pidPmtSingleProgram)
946 {
947 QMutexLocker locker(&m_listenerLock);
949 for (auto & listener : m_mpegSpListeners)
950 listener->HandleSingleProgramPMT(pmt_sp, false);
951 }
952 continue; // already parsed this table, toss it.
953 }
954
955 HandleTables(tspacket->PID(), *psip);
956 }
957
958 // Delete PSIP from final iteration.
959 delete psip;
960}
961
962int MPEGStreamData::ProcessData(const unsigned char *buffer, int len)
963{
964 int pos = 0;
965 bool resync = false;
966
967 if (!m_psListeners.empty())
968 {
969
970 for (auto & listener : m_psListeners)
971 listener->FindPSKeyFrames(buffer, len);
972
973 return 0;
974 }
975
976 while (pos + int(TSPacket::kSize) <= len)
977 { // while we have a whole packet left...
978 if (buffer[pos] != SYNC_BYTE || resync)
979 {
980 int newpos = ResyncStream(buffer, pos+1, len);
981 LOG(VB_RECORD, LOG_DEBUG, LOC +
982 QString("Resyncing @ %1+1 w/len %2 -> %3")
983 .arg(pos).arg(len).arg(newpos));
984 if (newpos == -1)
985 return len - pos;
986 if (newpos == -2)
987 return TSPacket::kSize;
988 pos = newpos;
989 }
990
991 const auto *pkt = reinterpret_cast<const TSPacket*>(&buffer[pos]);
992 pos += TSPacket::kSize; // Advance to next TS packet
993 resync = false;
994 if (!ProcessTSPacket(*pkt))
995 {
996 if (pos + int(TSPacket::kSize) > len)
997 continue;
998 if (buffer[pos] != SYNC_BYTE)
999 {
1000 // if ProcessTSPacket fails, and we don't appear to be
1001 // in sync on the next packet, then resync. Otherwise
1002 // just process the next packet normally.
1003 pos -= TSPacket::kSize;
1004 resync = true;
1005 }
1006 }
1007 }
1008
1009 return len - pos;
1010}
1011
1013{
1014 bool ok = !tspacket.TransportError();
1015
1016 if (IsEncryptionTestPID(tspacket.PID()))
1017 {
1018 ProcessEncryptedPacket(tspacket);
1019 }
1020
1021 if (!ok)
1022 return false;
1023
1024 if (tspacket.Scrambled())
1025 return true;
1026
1027 // Discard broken packets with invalid adaptation field length
1028 // See ISO/IEC 13818-1 : 2000 (E). 2.4.3.5 Semantic definition of fields in adaptation field
1029 if (tspacket.HasAdaptationField())
1030 {
1031 size_t afsize = tspacket.AdaptationFieldSize();
1032 bool validsize = (tspacket.HasPayload())
1033 ? afsize <= 182
1034 : afsize == 183;
1035 if (!validsize)
1036 {
1037 LOG(VB_RECORD, LOG_DEBUG, QString("Invalid adaptation field, type %3, size %4")
1038 .arg(tspacket.AdaptationFieldControl()).arg(afsize) + "\n" +
1039 tspacket.toString());
1040 return false;
1041 }
1042 }
1043
1044 if (VERBOSE_LEVEL_CHECK(VB_RECORD, LOG_DEBUG))
1045 {
1046 if (m_pmtSingleProgram && tspacket.PID() ==
1048 {
1049 if (tspacket.HasPCR())
1050 {
1051 LOG(VB_RECORD, LOG_DEBUG, LOC +
1052 QString("PID %1 (0x%2) has PCR %3μs")
1053 .arg(m_pmtSingleProgram->PCRPID())
1054 .arg(m_pmtSingleProgram->PCRPID(), 0, 16)
1055 .arg(duration_cast<std::chrono::microseconds>
1056 (tspacket.GetPCR().time_since_epoch()).count()));
1057 }
1058 }
1059 }
1060
1061 if (IsVideoPID(tspacket.PID()))
1062 {
1063 QMutexLocker locker(&m_listenerLock);
1064
1065 for (auto & listener : m_tsAvListeners)
1066 listener->ProcessVideoTSPacket(tspacket);
1067
1068 return true;
1069 }
1070
1071 if (IsAudioPID(tspacket.PID()))
1072 {
1073 QMutexLocker locker(&m_listenerLock);
1074
1075 for (auto & listener : m_tsAvListeners)
1076 listener->ProcessAudioTSPacket(tspacket);
1077
1078 return true;
1079 }
1080
1081 if (IsWritingPID(tspacket.PID()))
1082 {
1083 QMutexLocker locker(&m_listenerLock);
1084
1085 for (auto & listener : m_tsWritingListeners)
1086 listener->ProcessTSPacket(tspacket);
1087 }
1088
1089 if (tspacket.HasPayload() &&
1090 IsListeningPID(tspacket.PID()) &&
1091 !IsConditionalAccessPID(tspacket.PID()))
1092 {
1093 HandleTSTables(&tspacket); // Table handling starts here....
1094 }
1095
1096 return true;
1097}
1098
1099int MPEGStreamData::ResyncStream(const unsigned char *buffer, int curr_pos,
1100 int len)
1101{
1102 // Search for two sync bytes 188 bytes apart,
1103 int pos = curr_pos;
1104 int nextpos = pos + TSPacket::kSize;
1105 if (nextpos >= len)
1106 return -1; // not enough bytes; caller should try again
1107
1108 while (buffer[pos] != SYNC_BYTE || buffer[nextpos] != SYNC_BYTE)
1109 {
1110 pos++;
1111 nextpos++;
1112 if (nextpos == len)
1113 return -2; // not found
1114 }
1115
1116 return pos;
1117}
1118
1120{
1121 pid_map_t::const_iterator it = m_pidsConditionalAccess.find(pid);
1122 return it != m_pidsConditionalAccess.end();
1123}
1124
1126{
1128 return false;
1129 pid_map_t::const_iterator it = m_pidsListening.find(pid);
1130 return it != m_pidsListening.end();
1131}
1132
1134{
1135 pid_map_t::const_iterator it = m_pidsNotListening.find(pid);
1136 return it != m_pidsNotListening.end();
1137}
1138
1140{
1141 pid_map_t::const_iterator it = m_pidsWriting.find(pid);
1142 return it != m_pidsWriting.end();
1143}
1144
1146{
1147 pid_map_t::const_iterator it = m_pidsAudio.find(pid);
1148 return it != m_pidsAudio.end();
1149}
1150
1152{
1153 uint sz = pids.size();
1154
1155 if (m_pidVideoSingleProgram < 0x1fff)
1157
1158 for (auto it = m_pidsListening.cbegin(); it != m_pidsListening.cend(); ++it)
1159 pids[it.key()] = std::max(pids[it.key()], *it);
1160
1161 for (auto it = m_pidsAudio.cbegin(); it != m_pidsAudio.cend(); ++it)
1162 pids[it.key()] = std::max(pids[it.key()], *it);
1163
1164 for (auto it = m_pidsWriting.cbegin(); it != m_pidsWriting.cend(); ++it)
1165 pids[it.key()] = std::max(pids[it.key()], *it);
1166
1167 return pids.size() - sz;
1168}
1169
1171{
1172 if (m_pidVideoSingleProgram == pid)
1173 return kPIDPriorityHigh;
1174
1175 pid_map_t::const_iterator it;
1176 it = m_pidsListening.find(pid);
1177 if (it != m_pidsListening.end())
1178 return *it;
1179 it = m_pidsNotListening.find(pid);
1180 if (it != m_pidsNotListening.end())
1181 return *it;
1182 it = m_pidsWriting.find(pid);
1183 if (it != m_pidsWriting.end())
1184 return *it;
1185 it = m_pidsAudio.find(pid);
1186 if (it != m_pidsAudio.end())
1187 return *it;
1188
1189 return kPIDPriorityNone;
1190}
1191
1193{
1194 pid_psip_map_t::iterator it = m_partialPsipPacketCache.find(pid);
1195 if (it == m_partialPsipPacketCache.end())
1196 {
1197 m_partialPsipPacketCache[pid] = packet;
1198 }
1199 else
1200 {
1201 PSIPTable *old = *it;
1202 m_partialPsipPacketCache.remove(pid);
1203 m_partialPsipPacketCache.insert(pid, packet);
1204 delete old;
1205 }
1206}
1207
1209{
1210 return m_patStatus.HasAllSections(tsid);
1211}
1212
1214{
1215 return m_catStatus.HasAllSections(tsid);
1216}
1217
1219{
1220 return m_pmtStatus.HasAllSections(prog_num);
1221}
1222
1224{
1225 pmt_const_ptr_t pmt = GetCachedPMT(progNum, 0);
1226 bool hasit = pmt;
1227 ReturnCachedTable(pmt);
1228
1229 return hasit;
1230}
1231
1233{
1234 QMutexLocker locker(&m_cacheLock);
1235
1236 pat_cache_t::const_iterator it = m_cachedPats.constFind(tsid << 8);
1237 if (it == m_cachedPats.constEnd())
1238 return false;
1239
1240 uint last_section = (*it)->LastSection();
1241 if (!last_section)
1242 return true;
1243
1244 for (uint i = 1; i <= last_section; i++)
1245 if (m_cachedPats.constFind((tsid << 8) | i) == m_cachedPats.constEnd())
1246 return false;
1247
1248 return true;
1249}
1250
1252{
1253 QMutexLocker locker(&m_cacheLock);
1254
1255 for (uint i = 0; i <= 255; i++)
1256 if (m_cachedPats.contains((tsid << 8) | i))
1257 return true;
1258
1259 return false;
1260}
1261
1263{
1264 QMutexLocker locker(&m_cacheLock);
1265 return !m_cachedPats.empty();
1266}
1267
1269{
1270 QMutexLocker locker(&m_cacheLock);
1271
1272 cat_cache_t::const_iterator it = m_cachedCats.constFind(tsid << 8);
1273 if (it == m_cachedCats.constEnd())
1274 return false;
1275
1276 uint last_section = (*it)->LastSection();
1277 if (!last_section)
1278 return true;
1279
1280 for (uint i = 1; i <= last_section; i++)
1281 if (m_cachedCats.constFind((tsid << 8) | i) == m_cachedCats.constEnd())
1282 return false;
1283
1284 return true;
1285}
1286
1288{
1289 QMutexLocker locker(&m_cacheLock);
1290
1291 for (uint i = 0; i <= 255; i++)
1292 if (m_cachedCats.contains((tsid << 8) | i))
1293 return true;
1294
1295 return false;
1296}
1297
1299{
1300 QMutexLocker locker(&m_cacheLock);
1301 return !m_cachedCats.empty();
1302}
1303
1305{
1306 QMutexLocker locker(&m_cacheLock);
1307
1308 pmt_cache_t::const_iterator it = m_cachedPmts.constFind(pnum << 8);
1309 if (it == m_cachedPmts.constEnd())
1310 return false;
1311
1312 uint last_section = (*it)->LastSection();
1313 if (!last_section)
1314 return true;
1315
1316 for (uint i = 1; i <= last_section; i++)
1317 if (m_cachedPmts.constFind((pnum << 8) | i) == m_cachedPmts.constEnd())
1318 return false;
1319
1320 return true;
1321}
1322
1324{
1325 QMutexLocker locker(&m_cacheLock);
1326
1327 for (uint i = 0; i <= 255; i++)
1328 if (m_cachedPmts.contains((pnum << 8) | i))
1329 return true;
1330
1331 return false;
1332}
1333
1335{
1336 QMutexLocker locker(&m_cacheLock);
1337
1338 if (m_cachedPats.empty())
1339 return false;
1340
1341 for (auto *pat : std::as_const(m_cachedPats))
1342 {
1344 return false;
1345
1346 for (uint i = 0; i < pat->ProgramCount(); i++)
1347 {
1348 uint prognum = pat->ProgramNumber(i);
1349 if (prognum && !HasCachedAllPMT(prognum))
1350 return false;
1351 }
1352 }
1353
1354 return true;
1355}
1356
1358{
1359 QMutexLocker locker(&m_cacheLock);
1360 return !m_cachedPmts.empty();
1361}
1362
1364{
1365 QMutexLocker locker(&m_cacheLock);
1366 ProgramAssociationTable *pat = nullptr;
1367
1368 uint key = (tsid << 8) | section_num;
1369 pat_cache_t::const_iterator it = m_cachedPats.constFind(key);
1370 if (it != m_cachedPats.constEnd())
1371 IncrementRefCnt(pat = *it);
1372
1373 return pat;
1374}
1375
1377{
1378 QMutexLocker locker(&m_cacheLock);
1379 pat_vec_t pats;
1380
1381 pats.reserve(256);
1382 for (uint i=0; i < 256; i++)
1383 {
1384 pat_const_ptr_t pat = GetCachedPAT(tsid, i);
1385 if (pat)
1386 pats.push_back(pat);
1387 }
1388
1389 return pats;
1390}
1391
1393{
1394 QMutexLocker locker(&m_cacheLock);
1395 pat_vec_t pats;
1396
1397 pats.reserve(m_cachedPats.size());
1398 for (auto *pat : std::as_const(m_cachedPats))
1399 {
1400 IncrementRefCnt(pat);
1401 pats.push_back(pat);
1402 }
1403
1404 return pats;
1405}
1406
1408{
1409 QMutexLocker locker(&m_cacheLock);
1410 ConditionalAccessTable *cat = nullptr;
1411
1412 uint key = (tsid << 8) | section_num;
1413 cat_cache_t::const_iterator it = m_cachedCats.constFind(key);
1414 if (it != m_cachedCats.constEnd())
1415 IncrementRefCnt(cat = *it);
1416
1417 return cat;
1418}
1419
1421{
1422 QMutexLocker locker(&m_cacheLock);
1423 cat_vec_t cats;
1424
1425 cats.reserve(256);
1426 for (uint i=0; i < 256; i++)
1427 {
1428 cat_const_ptr_t cat = GetCachedCAT(tsid, i);
1429 if (cat)
1430 cats.push_back(cat);
1431 }
1432
1433 return cats;
1434}
1435
1437{
1438 QMutexLocker locker(&m_cacheLock);
1439 cat_vec_t cats;
1440
1441 cats.reserve(m_cachedCats.size());
1442 for (auto *cat : std::as_const(m_cachedCats))
1443 {
1445 cats.push_back(cat);
1446 }
1447
1448 return cats;
1449}
1450
1452 uint program_num, uint section_num) const
1453{
1454 QMutexLocker locker(&m_cacheLock);
1455 ProgramMapTable *pmt = nullptr;
1456
1457 uint key = (program_num << 8) | section_num;
1458 pmt_cache_t::const_iterator it = m_cachedPmts.constFind(key);
1459 if (it != m_cachedPmts.constEnd())
1460 IncrementRefCnt(pmt = *it);
1461
1462 return pmt;
1463}
1464
1466{
1467 QMutexLocker locker(&m_cacheLock);
1468 std::vector<const ProgramMapTable*> pmts;
1469
1470 pmts.reserve(m_cachedPmts.size());
1471 for (auto *pmt : std::as_const(m_cachedPmts))
1472 {
1473 IncrementRefCnt(pmt);
1474 pmts.push_back(pmt);
1475 }
1476
1477 return pmts;
1478}
1479
1481{
1482 QMutexLocker locker(&m_cacheLock);
1483 pmt_map_t pmts;
1484
1485 for (auto *pmt : std::as_const(m_cachedPmts))
1486 {
1487 IncrementRefCnt(pmt);
1488 pmts[pmt->ProgramNumber()].push_back(pmt);
1489 }
1490
1491 return pmts;
1492}
1493
1495{
1496 QMutexLocker locker(&m_cacheLock);
1497
1498 int val = m_cachedRefCnt[psip] - 1;
1499 m_cachedRefCnt[psip] = val;
1500
1501 // if ref <= 0 and table was slated for deletion, delete it.
1502 if (val <= 0)
1503 {
1504 psip_refcnt_map_t::iterator it;
1505 it = m_cachedSlatedForDeletion.find(psip);
1506 if (it != m_cachedSlatedForDeletion.end())
1507 DeleteCachedTable(psip);
1508 }
1509}
1510
1512{
1513 for (auto & pat : pats)
1514 ReturnCachedTable(pat);
1515 pats.clear();
1516}
1517
1519{
1520 // NOLINTNEXTLINE(modernize-loop-convert)
1521 for (pat_map_t::iterator it = pats.begin(); it != pats.end(); ++it)
1523 pats.clear();
1524}
1525
1527{
1528 for (auto & cat : cats)
1530 cats.clear();
1531}
1532
1534{
1535 // NOLINTNEXTLINE(modernize-loop-convert)
1536 for (cat_map_t::iterator it = cats.begin(); it != cats.end(); ++it)
1538 cats.clear();
1539}
1540
1542{
1543 for (auto & pmt : pmts)
1544 ReturnCachedTable(pmt);
1545 pmts.clear();
1546}
1547
1549{
1550 // NOLINTNEXTLINE(modernize-loop-convert)
1551 for (pmt_map_t::iterator it = pmts.begin(); it != pmts.end(); ++it)
1553 pmts.clear();
1554}
1555
1557{
1558 QMutexLocker locker(&m_cacheLock);
1559 m_cachedRefCnt[psip] = m_cachedRefCnt[psip] + 1;
1560}
1561
1563{
1564 if (!psip)
1565 return false;
1566
1567 uint tid = psip->TableIDExtension();
1568
1569 QMutexLocker locker(&m_cacheLock);
1570 if (m_cachedRefCnt[psip] > 0)
1571 {
1572 m_cachedSlatedForDeletion[psip] = 1;
1573 return false;
1574 }
1575 if (TableID::PAT == psip->TableID() &&
1576 (m_cachedPats[(tid << 8) | psip->Section()] == psip))
1577 {
1578 m_cachedPats[(tid << 8) | psip->Section()] = nullptr;
1579 delete psip;
1580 }
1581 else if (TableID::CAT == psip->TableID() &&
1582 (m_cachedCats[(tid << 8) | psip->Section()] == psip))
1583 {
1584 m_cachedCats[(tid << 8) | psip->Section()] = nullptr;
1585 delete psip;
1586 }
1587 else if ((TableID::PMT == psip->TableID()) &&
1588 (m_cachedPmts[(tid << 8) | psip->Section()] == psip))
1589 {
1590 m_cachedPmts[(tid << 8) | psip->Section()] = nullptr;
1591 delete psip;
1592 }
1593 else
1594 {
1595 m_cachedSlatedForDeletion[psip] = 2;
1596 return false;
1597 }
1598 psip_refcnt_map_t::iterator it;
1599 it = m_cachedSlatedForDeletion.find(psip);
1600 if (it != m_cachedSlatedForDeletion.end())
1601 m_cachedSlatedForDeletion.erase(it);
1602
1603 return true;
1604}
1605
1607{
1608 auto *pat = new ProgramAssociationTable(*_pat);
1609 uint key = (_pat->TransportStreamID() << 8) | _pat->Section();
1610
1611 QMutexLocker locker(&m_cacheLock);
1612
1613 pat_cache_t::iterator it = m_cachedPats.find(key);
1614 if (it != m_cachedPats.end())
1615 DeleteCachedTable(*it);
1616
1617 m_cachedPats[key] = pat;
1618}
1619
1621{
1622 auto *cat = new ConditionalAccessTable(*_cat);
1623 uint key = (_cat->TableIDExtension() << 8) | _cat->Section();
1624
1625 QMutexLocker locker(&m_cacheLock);
1626
1627 cat_cache_t::iterator it = m_cachedCats.find(key);
1628 if (it != m_cachedCats.end())
1629 DeleteCachedTable(*it);
1630
1631 m_cachedCats[key] = cat;
1632}
1633
1635{
1636 auto *pmt = new ProgramMapTable(*_pmt);
1637 uint key = (_pmt->ProgramNumber() << 8) | _pmt->Section();
1638
1639 QMutexLocker locker(&m_cacheLock);
1640
1641 pmt_cache_t::iterator it = m_cachedPmts.find(key);
1642 if (it != m_cachedPmts.end())
1643 DeleteCachedTable(*it);
1644
1645 m_cachedPmts[key] = pmt;
1646}
1647
1649{
1650 QMutexLocker locker(&m_listenerLock);
1651
1652 for (auto & listener : m_mpegListeners)
1653 if (((void*)val) == ((void*)listener))
1654 return;
1655
1656 m_mpegListeners.push_back(val);
1657}
1658
1660{
1661 QMutexLocker locker(&m_listenerLock);
1662
1663 for (auto it = m_mpegListeners.begin(); it != m_mpegListeners.end(); ++it)
1664 {
1665 if (((void*)val) == ((void*)*it))
1666 {
1667 m_mpegListeners.erase(it);
1668 return;
1669 }
1670 }
1671}
1672
1674{
1675 QMutexLocker locker(&m_listenerLock);
1676
1677 for (auto & listener : m_tsWritingListeners)
1678 if (((void*)val) == ((void*)listener))
1679 return;
1680
1681 m_tsWritingListeners.push_back(val);
1682}
1683
1685{
1686 QMutexLocker locker(&m_listenerLock);
1687
1688 for (auto it = m_tsWritingListeners.begin(); it != m_tsWritingListeners.end(); ++it)
1689 {
1690 if (((void*)val) == ((void*)*it))
1691 {
1692 m_tsWritingListeners.erase(it);
1693 return;
1694 }
1695 }
1696}
1697
1699{
1700 QMutexLocker locker(&m_listenerLock);
1701
1702 for (auto & listener : m_tsAvListeners)
1703 {
1704 if (((void*)val) == ((void*)listener))
1705 {
1706 LOG(VB_RECORD, LOG_ERR, LOC + QString("AddAVListener 0x%1 already present")
1707 .arg((uint64_t)val, 0, 16));
1708 return;
1709 }
1710 }
1711
1712 m_tsAvListeners.push_back(val);
1713#if 0
1714 LOG(VB_RECORD, LOG_DEBUG, LOC + QString("AddAVListener 0x%1 added")
1715 .arg((uint64_t)val, 0, 16));
1716#endif
1717}
1718
1720{
1721 QMutexLocker locker(&m_listenerLock);
1722
1723 for (auto it = m_tsAvListeners.begin(); it != m_tsAvListeners.end(); ++it)
1724 {
1725 if (((void*)val) == ((void*)*it))
1726 {
1727 m_tsAvListeners.erase(it);
1728#if 0
1729 LOG(VB_RECORD, LOG_DEBUG, LOC + QString("RemoveAVListener 0x%1 found and removed")
1730 .arg((uint64_t)val, 0, 16));
1731#endif
1732 return;
1733 }
1734 }
1735
1736 LOG(VB_RECORD, LOG_ERR, LOC + QString("RemoveAVListener 0x%1 NOT found")
1737 .arg((uint64_t)val, 0, 16));
1738}
1739
1741{
1742 QMutexLocker locker(&m_listenerLock);
1743
1744 for (auto & listener : m_mpegSpListeners)
1745 if (((void*)val) == ((void*)listener))
1746 return;
1747
1748 m_mpegSpListeners.push_back(val);
1749}
1750
1752{
1753 QMutexLocker locker(&m_listenerLock);
1754
1755 for (auto it = m_mpegSpListeners.begin(); it != m_mpegSpListeners.end(); ++it)
1756 {
1757 if (((void*)val) == ((void*)*it))
1758 {
1759 m_mpegSpListeners.erase(it);
1760 return;
1761 }
1762 }
1763}
1764
1766{
1767 QMutexLocker locker(&m_listenerLock);
1768
1769 for (auto & listener : m_psListeners)
1770 if (((void*)val) == ((void*)listener))
1771 return;
1772
1773 m_psListeners.push_back(val);
1774}
1775
1777{
1778 QMutexLocker locker(&m_listenerLock);
1779
1780 for (auto it = m_psListeners.begin(); it != m_psListeners.end(); ++it)
1781 {
1782 if (((void*)val) == ((void*)*it))
1783 {
1784 m_psListeners.erase(it);
1785 return;
1786 }
1787 }
1788}
1789
1791{
1792 QMutexLocker locker(&m_encryptionLock);
1793
1794#if 0
1795 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("AddEncryptionTestPID(%1, 0x%2)")
1796 .arg(pnum) .arg(pid, 0, 16));
1797#endif
1798
1799 AddListeningPID(pid);
1800
1801 m_encryptionPidToInfo[pid] = CryptInfo(isvideo ? 10000 : 500, 8);
1802
1803 m_encryptionPidToPnums[pid].push_back(pnum);
1804 m_encryptionPnumToPids[pnum].push_back(pid);
1806}
1807
1809{
1810 QMutexLocker locker(&m_encryptionLock);
1811
1812#if 0
1813 LOG(VB_RECORD, LOG_DEBUG, LOC +
1814 QString("Tearing down up decryption monitoring for program %1")
1815 .arg(pnum));
1816#endif
1817
1818 QMap<uint, uint_vec_t>::iterator list;
1819 uint_vec_t::iterator it;
1820
1822 for (uint pid : pids)
1823 {
1824#if 0
1825 LOG(VB_GENERAL, LOG_DEBUG, LOC +
1826 QString("Removing 0x%1 PID Enc monitoring").arg(pid,0,16));
1827#endif
1828
1829 RemoveListeningPID(pid);
1830
1831 list = m_encryptionPidToPnums.find(pid);
1832 if (list != m_encryptionPidToPnums.end())
1833 {
1834 it = find((*list).begin(), (*list).end(), pnum);
1835
1836 if (it != (*list).end())
1837 (*list).erase(it);
1838
1839 if ((*list).empty())
1840 {
1841 m_encryptionPidToPnums.remove(pid);
1842 m_encryptionPidToInfo.remove(pid);
1843 }
1844 }
1845 }
1846
1847 m_encryptionPnumToPids.remove(pnum);
1848}
1849
1851{
1852 QMutexLocker locker(&m_encryptionLock);
1853
1854 QMap<uint, CryptInfo>::const_iterator it =
1855 m_encryptionPidToInfo.find(pid);
1856
1857 return it != m_encryptionPidToInfo.end();
1858}
1859
1861{
1862 QMutexLocker locker(&m_encryptionLock);
1863
1864#if 0
1865 LOG(VB_RECORD, LOG_DEBUG, LOC +
1866 QString("Setting up decryption monitoring for program %1")
1867 .arg(pmt->ProgramNumber()));
1868#endif
1869
1870 bool encrypted = pmt->IsProgramEncrypted();
1871 for (uint i = 0; i < pmt->StreamCount(); i++)
1872 {
1873 if (!encrypted && !pmt->IsStreamEncrypted(i))
1874 continue;
1875
1876 const uint streamType = pmt->StreamType(i);
1877 bool is_vid = StreamID::IsVideo(streamType);
1878 bool is_aud = StreamID::IsAudio(streamType);
1879
1880 if (is_vid || is_aud)
1881 {
1883 pmt->ProgramNumber(), pmt->StreamPID(i), is_vid);
1884 }
1885 }
1886}
1887
1889{
1890 QMutexLocker locker(&m_encryptionLock);
1891
1892 m_encryptionPidToInfo.clear();
1893 m_encryptionPidToPnums.clear();
1894 m_encryptionPnumToPids.clear();
1895}
1896
1898{
1899 QMutexLocker locker(&m_encryptionLock);
1901}
1902
1904{
1905 QMutexLocker locker(&m_encryptionLock);
1907}
1908
1909static QString toString(CryptStatus status)
1910{
1911 if (kEncDecrypted == status)
1912 return "Decrypted";
1913 if (kEncEncrypted == status)
1914 return "Encrypted";
1915 return "Unknown";
1916}
1917
1922{
1923 QMutexLocker encryptionLock(&m_encryptionLock);
1924
1925 std::map<uint,bool> pnumEncrypted;
1926 const uint pid = tspacket.PID();
1928
1929 CryptStatus status = kEncUnknown;
1930
1931 if (tspacket.Scrambled())
1932 {
1933 info.m_decryptedPackets = 0;
1934
1935 // If a fair amount of encrypted packets is passed assume that
1936 // the stream is not decryptable
1937 if (++info.m_encryptedPackets >= info.m_encryptedMin)
1938 status = kEncEncrypted;
1939 }
1940 else
1941 {
1942 info.m_encryptedPackets = 0;
1943 if (++info.m_decryptedPackets > info.m_decryptedMin)
1944 status = kEncDecrypted;
1945 }
1946
1947 if (status == info.m_status)
1948 return; // pid encryption status unchanged
1949
1950 info.m_status = status;
1951
1952 LOG(status != kEncDecrypted ? VB_GENERAL : VB_RECORD, LOG_DEBUG, LOC +
1953 QString("PID 0x%1 status: %2") .arg(pid,0,16).arg(toString(status)));
1954
1955 uint_vec_t pnum_del_list;
1956 const uint_vec_t &pnums = m_encryptionPidToPnums[pid];
1957 for (uint pnum : pnums)
1958 {
1959 status = m_encryptionPnumToStatus[pnum];
1960
1961 const uint_vec_t &pids = m_encryptionPnumToPids[pnum];
1962 if (!pids.empty())
1963 {
1964 std::array<uint,3> enc_cnt { 0, 0, 0 };
1965 for (uint pid2 : pids)
1966 {
1967 CryptStatus stat = m_encryptionPidToInfo[pid2].m_status;
1968 enc_cnt[stat]++;
1969
1970#if 0
1971 LOG(VB_GENERAL, LOG_DEBUG, LOC +
1972 QString("\tpnum %1 PID 0x%2 status: %3")
1973 .arg(pnum).arg(pid2,0,16) .arg(toString(stat)));
1974#endif
1975 }
1976 status = kEncUnknown;
1977
1978 if (enc_cnt[kEncEncrypted])
1979 status = kEncEncrypted;
1980 else if (enc_cnt[kEncDecrypted] >= std::min((size_t) 2, pids.size()))
1981 status = kEncDecrypted;
1982 }
1983
1984 if (status == m_encryptionPnumToStatus[pnum])
1985 continue; // program encryption status unchanged
1986
1987 LOG(VB_RECORD, LOG_DEBUG, LOC + QString("Program %1 status: %2")
1988 .arg(pnum).arg(toString(status)));
1989
1990 m_encryptionPnumToStatus[pnum] = status;
1991
1992 bool encrypted = kEncUnknown == status || kEncEncrypted == status;
1993 pnumEncrypted[pnum] = encrypted;
1994
1995 if (kEncDecrypted == status)
1996 pnum_del_list.push_back(pnum);
1997 }
1998
1999 // Call HandleEncryptionStatus outside the m_encryptionLock
2000 encryptionLock.unlock();
2001 m_listenerLock.lock();
2002 for (auto & pe : pnumEncrypted)
2003 {
2004 for (auto & listener : m_mpegListeners)
2005 {
2006 listener->HandleEncryptionStatus(pe.first, pe.second);
2007 }
2008 }
2009 m_listenerLock.unlock();
2010
2011 for (size_t i = 0; i < pnum_del_list.size(); i++)
2012 RemoveEncryptionTestPIDs(pnums[i]);
2013}
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:22
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:101
bool IsCurrent(void) const
Definition: mpegtables.h:528
bool VerifyPSIP(bool verify_crc) const
Definition: mpegtables.cpp:243
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:844
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:352
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:573
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:909
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:601
void Parse(void) const
Definition: mpegtables.cpp:474
static ProgramMapTable * Create(uint programNumber, uint basepid, uint pcrpid, uint version, std::vector< uint > pids, std::vector< uint > types)
Definition: mpegtables.cpp:412
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:53
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:60
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