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