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