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