MythTV  master
dtvrecorder.cpp
Go to the documentation of this file.
1 
21 #include "atscstreamdata.h"
22 #include "mpegstreamdata.h"
23 #include "dvbstreamdata.h"
24 #include "dtvrecorder.h"
25 #include "programinfo.h"
26 #include "mythlogging.h"
27 #include "mpegtables.h"
28 #include "io/mythmediabuffer.h"
29 #include "tv_rec.h"
30 #include "mythsystemevent.h"
31 
32 #include "AVCParser.h"
33 #include "HEVCParser.h"
34 
35 #define LOC ((m_tvrec) ? \
36  QString("DTVRec[%1]: ").arg(m_tvrec->GetInputId()) : \
37  QString("DTVRec(0x%1): ").arg(intptr_t(this),0,16))
38 
40 
50  RecorderBase(rec),
51  m_h2645Parser(reinterpret_cast<H2645Parser *>(new AVCParser))
52 {
54  m_payloadBuffer.reserve(TSPacket::kSize * (50 + 1));
55 
57 
59  gCoreContext->GetNumSetting("MinimumRecordingQuality", 95);
60 
62 }
63 
65 {
66  StopRecording();
67 
69 
70  if (m_inputPat)
71  {
72  delete m_inputPat;
73  m_inputPat = nullptr;
74  }
75 
76  if (m_inputPmt)
77  {
78  delete m_inputPmt;
79  m_inputPmt = nullptr;
80  }
81 
82  if (m_h2645Parser)
83  {
84  delete m_h2645Parser;
85  m_h2645Parser = nullptr;
86  }
87 }
88 
89 void DTVRecorder::SetOption(const QString &name, const QString &value)
90 {
91  if (name == "recordingtype")
92  m_recordingType = value;
93  else
94  RecorderBase::SetOption(name, value);
95 }
96 
100 void DTVRecorder::SetOption(const QString &name, int value)
101 {
102  if (name == "wait_for_seqstart")
103  m_waitForKeyframeOption = (value == 1);
104  else if (name == "recordmpts")
105  m_recordMpts = (value != 0);
106  else
107  RecorderBase::SetOption(name, value);
108 }
109 
111  const QString &videodev,
112  const QString& /*audiodev*/, const QString& /*vbidev*/)
113 {
114  SetOption("videodevice", videodev);
115  DTVRecorder::SetOption("tvformat", gCoreContext->GetSetting("TVFormat"));
116  SetStrOption(profile, "recordingtype");
117  SetIntOption(profile, "recordmpts");
118 }
119 
125 {
126  if (m_ringBuffer)
128 
129  if (m_curRecording)
130  {
133  }
134 
136 }
137 
139 {
140  LOG(VB_RECORD, LOG_INFO, LOC + "ResetForNewFile(void)");
141  QMutexLocker locker(&m_positionMapLock);
142 
143  // m_seen_psp and m_h2645_parser should
144  // not be reset here. This will only be called just as
145  // we're seeing the first packet of a new keyframe for
146  // writing to the new file and anything that makes the
147  // recorder think we're waiting on another keyframe will
148  // send significant amounts of good data to /dev/null.
149  // -- Daniel Kristjansson 2011-02-26
150 
151  m_startCode = 0xffffffff;
152  m_firstKeyframe = -1;
154  m_lastKeyframeSeen = 0;
155  m_lastGopSeen = 0;
156  m_lastSeqSeen = 0;
160  //_recording
161  m_error = QString();
162 
164  m_repeatPict = 0;
165 
166  //m_pes_synced
167  //m_seen_sps
168  m_positionMap.clear();
169  m_positionMapDelta.clear();
170  m_durationMap.clear();
171  m_durationMapDelta.clear();
172 
173  m_primaryVideoCodec = AV_CODEC_ID_NONE;
174  m_primaryAudioCodec = AV_CODEC_ID_NONE;
175 
176  m_continuityCounter.fill(0xff);
177 
178  locker.unlock();
180 }
181 
183 {
185 
186  m_tsCount.fill(0);
187  m_tsLast.fill(-1LL);
188  m_tsFirst.fill(-1LL);
189  //m_tsFirst_dt -- doesn't need to be cleared only used if m_tsFirst>=0
190  m_packetCount.fetchAndStoreRelaxed(0);
191  m_continuityErrorCount.fetchAndStoreRelaxed(0);
192  m_framesSeenCount = 0;
194  m_totalDuration = 0;
195  m_tdBase = 0;
196  m_tdTickCount = 0;
198 
199 }
200 
201 // documented in recorderbase.h
203 {
204  LOG(VB_RECORD, LOG_INFO, LOC + "Reset(void)");
205  ResetForNewFile();
206 
207  m_startCode = 0xffffffff;
208 
209  if (m_curRecording)
210  {
213  }
214 }
215 
217 {
218  if (data == m_streamData)
219  return;
220 
221  MPEGStreamData *old_data = m_streamData;
222  m_streamData = data;
223  delete old_data;
224 
225  if (m_streamData)
226  InitStreamData();
227 }
228 
230 {
231  if (m_streamData == nullptr)
232  return;
233 
236 
237  auto *dvb = dynamic_cast<DVBStreamData*>(m_streamData);
238  if (dvb)
239  dvb->AddDVBMainListener(this);
240 
241  auto *atsc = dynamic_cast<ATSCStreamData*>(m_streamData);
242 
243  if (atsc && atsc->DesiredMinorChannel())
244  {
245  atsc->SetDesiredChannel(atsc->DesiredMajorChannel(),
246  atsc->DesiredMinorChannel());
247  }
248  // clang-tidy assumes that if the result of dynamic_cast is
249  // nullptr that the thing being casted must also be a nullptr.
250  // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage)
251  else if (m_streamData->DesiredProgram() >= 0)
252  {
254  }
255 }
256 
257 void DTVRecorder::BufferedWrite(const TSPacket &tspacket, bool insert)
258 {
259  if (!insert) // PAT/PMT may need inserted in front of any buffered data
260  {
261  // delay until first GOP to avoid decoder crash on res change
263  m_firstKeyframe < 0)
264  return;
265 
266  if (m_curRecording && m_timeOfFirstDataIsSet.testAndSetRelaxed(0,1))
267  {
268  QMutexLocker locker(&m_statisticsLock);
272  }
273 
274  int val = m_timeOfLatestDataCount.fetchAndAddRelaxed(1);
275  int thresh = m_timeOfLatestDataPacketInterval.fetchAndAddRelaxed(0);
276  if (val > thresh)
277  {
278  QMutexLocker locker(&m_statisticsLock);
279  std::chrono::milliseconds elapsed = m_timeOfLatestDataTimer.restart();
280  int interval = thresh;
281  if (elapsed > kTimeOfLatestDataIntervalTarget + 250ms)
282  {
284  .fetchAndStoreRelaxed(thresh * 4 / 5);
285  }
286  else if (elapsed + 250ms < kTimeOfLatestDataIntervalTarget)
287  {
289  .fetchAndStoreRelaxed(thresh * 9 / 8);
290  }
291 
292  m_timeOfLatestDataCount.fetchAndStoreRelaxed(1);
294 
295  LOG(VB_RECORD, LOG_DEBUG, LOC +
296  QString("Updating timeOfLatestData elapsed(%1) interval(%2)")
297  .arg(elapsed.count()).arg(interval));
298  }
299 
300  // Do we have to buffer the packet for exact keyframe detection?
301  if (m_bufferPackets)
302  {
303  int idx = m_payloadBuffer.size();
304  m_payloadBuffer.resize(idx + TSPacket::kSize);
305  memcpy(&m_payloadBuffer[idx], tspacket.data(), TSPacket::kSize);
306  return;
307  }
308 
309  // We are free to write the packet, but if we have buffered packet[s]
310  // we have to write them first...
311  if (!m_payloadBuffer.empty())
312  {
313  if (m_ringBuffer)
315  m_payloadBuffer.clear();
316  }
317  }
318 
319  if (m_ringBuffer && m_ringBuffer->Write(tspacket.data(), TSPacket::kSize) < 0 &&
321  {
322  LOG(VB_GENERAL, LOG_INFO, LOC +
323  QString("BufferedWrite: Writes are failing, "
324  "setting status to %1")
326  SetRecordingStatus(RecStatus::Failing, __FILE__, __LINE__);
327  }
328 }
329 
331 static int64_t extract_timestamp(
332  const uint8_t *bufptr, int bytes_left, int pts_or_dts)
333 {
334  if (bytes_left < 4)
335  return -1LL;
336 
337  bool has_pts = (bufptr[3] & 0x80) != 0;
338  int offset = 5;
339  if (((kExtractPTS == pts_or_dts) && !has_pts) || (offset + 5 > bytes_left))
340  return -1LL;
341 
342  bool has_dts = (bufptr[3] & 0x40) != 0;
343  if (kExtractDTS == pts_or_dts)
344  {
345  if (!has_dts)
346  return -1LL;
347  offset += has_pts ? 5 : 0;
348  if (offset + 5 > bytes_left)
349  return -1LL;
350  }
351 
352  return ((uint64_t(bufptr[offset+0] & 0x0e) << 29) |
353  (uint64_t(bufptr[offset+1] ) << 22) |
354  (uint64_t(bufptr[offset+2] & 0xfe) << 14) |
355  (uint64_t(bufptr[offset+3] ) << 7) |
356  (uint64_t(bufptr[offset+4] & 0xfe) >> 1));
357 }
358 
359 static QDateTime ts_to_qdatetime(
360  uint64_t pts, uint64_t pts_first, const QDateTime &pts_first_dt)
361 {
362  if (pts < pts_first)
363  pts += 0x1FFFFFFFFLL;
364  const QDateTime& dt = pts_first_dt;
365  return dt.addMSecs((pts - pts_first)/90);
366 }
367 
368 static const std::array<const FrameRate,16> frameRateMap = {
369  FrameRate(0), FrameRate(24000, 1001), FrameRate(24),
370  FrameRate(25), FrameRate(30000, 1001), FrameRate(30),
371  FrameRate(50), FrameRate(60000, 1001), FrameRate(60),
372  FrameRate(0), FrameRate(0), FrameRate(0),
373  FrameRate(0), FrameRate(0), FrameRate(0),
374  FrameRate(0)
375 };
376 
404 {
405  if (!tspacket->HasPayload()) // no payload to scan
406  return m_firstKeyframe >= 0;
407 
408  if (!m_ringBuffer)
409  return m_firstKeyframe >= 0;
410 
411  // if packet contains start of PES packet, start
412  // looking for first byte of MPEG start code (3 bytes 0 0 1)
413  // otherwise, pick up search where we left off.
414  const bool payloadStart = tspacket->PayloadStart();
415  m_startCode = (payloadStart) ? 0xffffffff : m_startCode;
416 
417  // Just make these local for efficiency reasons (gcc not so smart..)
418  const uint maxKFD = kMaxKeyFrameDistance;
419  bool hasFrame = false;
420  bool hasKeyFrame = false;
421 
422  uint aspectRatio = 0;
423  uint height = 0;
424  uint width = 0;
425  FrameRate frameRate(0);
426 
427  // Scan for PES header codes; specifically picture_start
428  // sequence_start (SEQ) and group_start (GOP).
429  // 00 00 01 00: picture_start_code
430  // 00 00 01 B8: group_start_code
431  // 00 00 01 B3: seq_start_code
432  // 00 00 01 B5: ext_start_code
433  // (there are others that we don't care about)
434  const uint8_t *bufptr = tspacket->data() + tspacket->AFCOffset();
435  const uint8_t *bufend = tspacket->data() + TSPacket::kSize;
436  m_repeatPict = 0;
437 
438  while (bufptr < bufend)
439  {
440  bufptr = avpriv_find_start_code(bufptr, bufend, &m_startCode);
441  int bytes_left = bufend - bufptr;
442  if ((m_startCode & 0xffffff00) == 0x00000100)
443  {
444  // At this point we have seen the start code 0 0 1
445  // the next byte will be the PES packet stream id.
446  const int stream_id = m_startCode & 0x000000ff;
447  if (PESStreamID::PictureStartCode == stream_id)
448  hasFrame = true;
449  else if (PESStreamID::GOPStartCode == stream_id)
450  {
452  hasKeyFrame = true;
453  }
454  else if (PESStreamID::SequenceStartCode == stream_id)
455  {
457  hasKeyFrame |= (m_lastGopSeen + maxKFD)<m_framesSeenCount;
458 
459  // Look for aspectRatio changes and store them in the database
460  aspectRatio = (bufptr[3] >> 4);
461 
462  // Get resolution
463  height = ((bufptr[1] & 0xf) << 8) | bufptr[2];
464  width = (bufptr[0] <<4) | (bufptr[1]>>4);
465 
466  frameRate = frameRateMap[(bufptr[3] & 0x0000000f)];
467  }
468  else if (PESStreamID::MPEG2ExtensionStartCode == stream_id)
469  {
470  if (bytes_left >= 1)
471  {
472  int ext_type = (bufptr[0] >> 4);
473  switch(ext_type)
474  {
475  case 0x1: /* sequence extension */
476  if (bytes_left >= 6)
477  {
478  m_progressiveSequence = bufptr[1] & (1 << 3);
479  }
480  break;
481  case 0x8: /* picture coding extension */
482  if (bytes_left >= 5)
483  {
484  //int picture_structure = bufptr[2]&3;
485  int top_field_first = bufptr[3] & (1 << 7);
486  int repeat_first_field = bufptr[3] & (1 << 1);
487  int progressive_frame = bufptr[4] & (1 << 7);
488 
489  /* check if we must repeat the frame */
490  m_repeatPict = 1;
491  if (repeat_first_field)
492  {
494  {
495  if (top_field_first)
496  m_repeatPict = 5;
497  else
498  m_repeatPict = 3;
499  }
500  else if (progressive_frame)
501  {
502  m_repeatPict = 2;
503  }
504  }
505  // The m_repeatPict code above matches
506  // mpegvideo_extract_headers(), but the
507  // code in mpeg_field_start() computes a
508  // value one less, which seems correct.
509  --m_repeatPict;
510  }
511  break;
512  }
513  }
514  }
515  if ((stream_id >= PESStreamID::MPEGVideoStreamBegin) &&
516  (stream_id <= PESStreamID::MPEGVideoStreamEnd))
517  {
518  int64_t pts = extract_timestamp(
519  bufptr, bytes_left, kExtractPTS);
520  int64_t dts = extract_timestamp(
521  bufptr, bytes_left, kExtractPTS);
522  HandleTimestamps(stream_id, pts, dts);
523  // Detect music choice program (very slow frame rate and audio)
524  if (m_firstKeyframe < 0
525  && m_tsLast[stream_id] - m_tsFirst[stream_id] > 3*90000)
526  {
527  hasKeyFrame = true;
528  m_musicChoice = true;
529  LOG(VB_GENERAL, LOG_INFO, LOC + "Music Choice program detected");
530  }
531  }
532  }
533  }
534 
535  if (hasFrame && !hasKeyFrame)
536  {
537  // If we have seen kMaxKeyFrameDistance frames since the
538  // last GOP or SEQ stream_id, then pretend this picture
539  // is a keyframe. We may get artifacts but at least
540  // we will be able to skip frames.
541  hasKeyFrame = ((m_framesSeenCount & 0xf) == 0U);
542  hasKeyFrame &= (m_lastGopSeen + maxKFD) < m_framesSeenCount;
543  hasKeyFrame &= (m_lastSeqSeen + maxKFD) < m_framesSeenCount;
544  }
545 
546  // m_bufferPackets will only be true if a payload start has been seen
547  if (hasKeyFrame && (m_bufferPackets || m_firstKeyframe >= 0))
548  {
549  LOG(VB_RECORD, LOG_DEBUG, LOC + QString
550  ("Keyframe @ %1 + %2 = %3")
552  .arg(m_payloadBuffer.size())
553  .arg(m_ringBuffer->GetWritePosition() + m_payloadBuffer.size()));
554 
556  HandleKeyframe(0);
557  }
558 
559  if (hasFrame)
560  {
561  LOG(VB_RECORD, LOG_DEBUG, LOC + QString
562  ("Frame @ %1 + %2 = %3")
564  .arg(m_payloadBuffer.size())
565  .arg(m_ringBuffer->GetWritePosition() + m_payloadBuffer.size()));
566 
567  m_bufferPackets = false; // We now know if it is a keyframe, or not
571  else
572  {
573  /* Found a frame that is not a keyframe, and we want to
574  * start on a keyframe */
575  m_payloadBuffer.clear();
576  }
577  }
578 
579  if ((aspectRatio > 0) && (aspectRatio != m_videoAspect))
580  {
581  m_videoAspect = aspectRatio;
583  }
584 
585  if (height && width && (height != m_videoHeight || m_videoWidth != width))
586  {
587  m_videoHeight = height;
588  m_videoWidth = width;
589  ResolutionChange(width, height, m_framesWrittenCount);
590  }
591 
592  if (frameRate.isNonzero() && frameRate != m_frameRate)
593  {
594  m_frameRate = frameRate;
595  LOG(VB_RECORD, LOG_INFO, LOC +
596  QString("FindMPEG2Keyframes: frame rate = %1")
597  .arg(frameRate.toDouble() * 1000));
598  FrameRateChange(frameRate.toDouble() * 1000, m_framesWrittenCount);
599  }
600 
601  return m_firstKeyframe >= 0;
602 }
603 
604 void DTVRecorder::HandleTimestamps(int stream_id, int64_t pts, int64_t dts)
605 {
606  if (pts < 0)
607  {
608  m_tsLast[stream_id] = -1;
609  return;
610  }
611 
612  if ((dts < 0) && !m_usePts)
613  {
614  m_tsLast[stream_id] = -1;
615  m_usePts = true;
616  LOG(VB_RECORD, LOG_DEBUG,
617  "Switching from dts tracking to pts tracking." +
618  QString("TS count is %1").arg(m_tsCount[stream_id]));
619  }
620 
621  int64_t ts = dts;
622  int64_t gap_threshold = 90000; // 1 second
623  if (m_usePts)
624  {
625  ts = dts;
626  gap_threshold = 2*90000; // two seconds, compensate for GOP ordering
627  }
628 
629  if (m_musicChoice)
630  gap_threshold = 8*90000; // music choice uses frames every 6 seconds
631 
632  if (m_tsLast[stream_id] >= 0)
633  {
634  int64_t diff = ts - m_tsLast[stream_id];
635 
636  // time jumped back more then 10 seconds, handle it as 33bit overflow
637  if (diff < (10 * -90000))
638  // MAX_PTS is 33bits all 1
639  diff += 0x1ffffffffLL;
640 
641  // FIXME why do we handle negative gaps (aka overlap) like a gap?
642  if (diff < 0)
643  diff = -diff;
644 
645  if (diff > gap_threshold && m_firstKeyframe >= 0)
646  {
647  QMutexLocker locker(&m_statisticsLock);
648 
649  m_recordingGaps.push_back(
650  RecordingGap(
652  m_tsLast[stream_id], m_tsFirst[stream_id],
653  m_tsFirstDt[stream_id]),
655  ts, m_tsFirst[stream_id], m_tsFirstDt[stream_id])));
656  LOG(VB_RECORD, LOG_DEBUG, LOC + QString("Inserted gap %1 dur %2")
657  .arg(m_recordingGaps.back().toString()).arg(diff/90000.0));
658 
660  {
662  if (recq.IsDamaged())
663  {
664  LOG(VB_GENERAL, LOG_INFO, LOC +
665  QString("HandleTimestamps: too much damage, "
666  "setting status to %1")
668  SetRecordingStatus(RecStatus::Failing, __FILE__, __LINE__);
669  }
670  }
671  }
672  }
673 
674  m_tsLast[stream_id] = ts;
675 
676  if (m_tsCount[stream_id] < 30)
677  {
678  if (!m_tsCount[stream_id] || (ts < m_tsFirst[stream_id]))
679  {
680  m_tsFirst[stream_id] = ts;
681  m_tsFirstDt[stream_id] = MythDate::current();
682  }
683  }
684 
685  m_tsCount[stream_id]++;
686 }
687 
689 {
694  {
696  m_tdTickCount = 0;
698  }
699  m_tdTickCount += (2 + m_repeatPict);
701  {
702  m_totalDuration = m_tdBase + (int64_t) 500 * m_tdTickCount *
704  }
705 
706  if (m_framesWrittenCount < 2000 || m_framesWrittenCount % 1000 == 0)
707  {
708  LOG(VB_RECORD, LOG_DEBUG,
709  QString("count=%1 m_frameRate=%2 tick_frameRate=%3 "
710  "tick_cnt=%4 tick_base=%5 _total_dur=%6")
712  .arg(m_frameRate.toString(),
714  .arg(m_tdTickCount)
715  .arg(m_tdBase)
716  .arg(m_totalDuration));
717  }
718 }
719 
720 bool DTVRecorder::FindAudioKeyframes(const TSPacket* /*tspacket*/)
721 {
722  bool hasKeyFrame = false;
723  if (!m_ringBuffer || (GetStreamData()->VideoPIDSingleProgram() <= 0x1fff))
724  return hasKeyFrame;
725 
726  static constexpr uint64_t kMsecPerDay = 24 * 60 * 60 * 1000ULL;
727  const double frame_interval = (1000.0 / m_videoFrameRate);
728  uint64_t elapsed = 0;
729  if (m_audioTimer.isValid())
730  elapsed = m_audioTimer.elapsed();
731  auto expected_frame = (uint64_t) ((double)elapsed / frame_interval);
732 
733  while (m_framesSeenCount > expected_frame + 10000)
734  expected_frame += (uint64_t) ((double)kMsecPerDay / frame_interval);
735 
736  if (!m_framesSeenCount || (m_framesSeenCount < expected_frame))
737  {
738  if (!m_framesSeenCount)
739  m_audioTimer.start();
740 
741  m_bufferPackets = false;
743 
744  if (1 == (m_framesSeenCount & 0x7))
745  {
748  hasKeyFrame = true;
749  }
750 
753  }
754 
755  return hasKeyFrame;
756 }
757 
760 bool DTVRecorder::FindOtherKeyframes(const TSPacket */*tspacket*/)
761 {
762  if (!m_ringBuffer || (GetStreamData()->VideoPIDSingleProgram() <= 0x1fff))
763  return true;
764 
766  return true;
767 
768  LOG(VB_RECORD, LOG_INFO, LOC + "DSMCC - FindOtherKeyframes() - "
769  "generating initial key-frame");
770 
774 
776 
778 
779  return true;
780 }
781 
786 void DTVRecorder::HandleKeyframe(int64_t extra)
787 {
788  if (!m_ringBuffer)
789  return;
790 
791  // Perform ringbuffer switch if needed.
793 
794  uint64_t frameNum = m_framesWrittenCount;
795  if (m_firstKeyframe < 0)
796  {
797  m_firstKeyframe = frameNum;
798  SendMythSystemRecEvent("REC_STARTED_WRITING", m_curRecording);
799  }
800 
801  // Add key frame to position map
802  m_positionMapLock.lock();
803  if (!m_positionMap.contains(frameNum))
804  {
805  int64_t startpos = m_ringBuffer->GetWritePosition() + extra;
806 
807  // Don't put negative offsets into the database, they get munged into
808  // MAX_INT64 - offset, which is an exceedingly large number, and
809  // certainly not valid.
810  if (startpos >= 0)
811  {
812  m_positionMapDelta[frameNum] = startpos;
813  m_positionMap[frameNum] = startpos;
814  m_durationMap[frameNum] = llround(m_totalDuration);
815  m_durationMapDelta[frameNum] = llround(m_totalDuration);
816  }
817  }
818  m_positionMapLock.unlock();
819 }
820 
827 {
828  if (!tspacket->HasPayload()) // no payload to scan
829  return m_firstKeyframe >= 0;
830 
831  if (!m_ringBuffer)
832  {
833  LOG(VB_GENERAL, LOG_ERR, LOC + "FindH2645Keyframes: No ringbuffer");
834  return m_firstKeyframe >= 0;
835  }
836 
837  if (!m_h2645Parser)
838  {
839  LOG(VB_GENERAL, LOG_ERR, LOC + "FindH2645Keyframes: m_h2645Parser not present");
840  return m_firstKeyframe >= 0;
841  }
842 
843  const bool payloadStart = tspacket->PayloadStart();
844  if (payloadStart)
845  {
846  // reset PES sync state
847  m_pesSynced = false;
848  m_startCode = 0xffffffff;
849  }
850 
851  uint aspectRatio = 0;
852  uint height = 0;
853  uint width = 0;
854  FrameRate frameRate(0);
855  SCAN_t scantype(SCAN_t::UNKNOWN_SCAN);
856 
857  bool hasFrame = false;
858  bool hasKeyFrame = false;
859 
860  // scan for PES packets and H.264 NAL units
861  uint i = tspacket->AFCOffset();
862  for (; i < TSPacket::kSize; ++i)
863  {
864  // special handling required when a new PES packet begins
865  if (payloadStart && !m_pesSynced)
866  {
867  // bounds check
868  if (i + 2 >= TSPacket::kSize)
869  {
870  LOG(VB_GENERAL, LOG_ERR, LOC +
871  "PES packet start code may overflow to next TS packet, "
872  "aborting keyframe search");
873  break;
874  }
875 
876  // must find the PES start code
877  if (tspacket->data()[i++] != 0x00 ||
878  tspacket->data()[i++] != 0x00 ||
879  tspacket->data()[i++] != 0x01)
880  {
881  if (!m_pesTimer.isRunning() || m_pesTimer.elapsed() > 20000ms)
882  {
884  LOG(VB_GENERAL, LOG_ERR, LOC +
885  "PES start code not found in TS packet with PUSI set");
886  }
887  break;
888  }
889 
890  m_pesTimer.stop();
891 
892  // bounds check
893  if (i + 5 >= TSPacket::kSize)
894  {
895  LOG(VB_GENERAL, LOG_ERR, LOC +
896  "PES packet headers overflow to next TS packet, "
897  "aborting keyframe search");
898  break;
899  }
900 
901  // now we need to compute where the PES payload begins
902  // skip past the stream_id (+1)
903  // the next two bytes are the PES packet length (+2)
904  // after that, one byte of PES packet control bits (+1)
905  // after that, one byte of PES header flags bits (+1)
906  // and finally, one byte for the PES header length
907  const unsigned char pes_header_length = tspacket->data()[i + 5];
908 
909  // bounds check
910  if ((i + 6 + pes_header_length) >= TSPacket::kSize)
911  {
912  LOG(VB_GENERAL, LOG_ERR, LOC +
913  "PES packet headers overflow to next TS packet, "
914  "aborting keyframe search");
915  break;
916  }
917 
918  // we now know where the PES payload is
919  // normally, we should have used 6, but use 5 because the for
920  // loop will bump i
921  i += 5 + pes_header_length;
922  m_pesSynced = true;
923 
924 #if 0
925  LOG(VB_RECORD, LOG_DEBUG, LOC + "PES synced");
926 #endif
927  continue;
928  }
929 
930  if (!m_pesSynced)
931  break;
932 
933  // scan the NAL units
934  uint32_t bytes_used = m_h2645Parser->addBytes
935  (tspacket->data() + i, TSPacket::kSize - i,
937  i += (bytes_used - 1);
938 
940  {
941  if (m_h2645Parser->onFrameStart() &&
943  {
944  hasKeyFrame = m_h2645Parser->onKeyFrameStart();
945  hasFrame = true;
946  m_seenSps |= hasKeyFrame;
947 
948  width = m_h2645Parser->pictureWidth();
949  height = m_h2645Parser->pictureHeight();
950  aspectRatio = m_h2645Parser->aspectRatio();
951  scantype = m_h2645Parser->GetScanType();
952  m_h2645Parser->getFrameRate(frameRate);
953  }
954  }
955  } // for (; i < TSPacket::kSize; ++i)
956 
957  // If it has been more than 511 frames since the last keyframe,
958  // pretend we have one.
959  if (hasFrame && !hasKeyFrame &&
961  {
962  hasKeyFrame = true;
963  LOG(VB_RECORD, LOG_WARNING, LOC +
964  QString("FindH2645Keyframes: %1 frames without a keyframe.")
966  }
967 
968  // m_bufferPackets will only be true if a payload start has been seen
969  if (hasKeyFrame && (m_bufferPackets || m_firstKeyframe >= 0))
970  {
971  LOG(VB_RECORD, LOG_DEBUG, LOC + QString
972  ("Keyframe @ %1 + %2 = %3 AU %4")
974  .arg(m_payloadBuffer.size())
977 
980  }
981 
982  if (hasFrame)
983  {
984  LOG(VB_RECORD, LOG_DEBUG, LOC + QString
985  ("Frame @ %1 + %2 = %3 AU %4")
987  .arg(m_payloadBuffer.size())
990 
991  m_bufferPackets = false; // We now know if this is a keyframe
995  else
996  {
997  /* Found a frame that is not a keyframe, and we want to
998  * start on a keyframe */
999  m_payloadBuffer.clear();
1000  }
1001  }
1002 
1003  if ((aspectRatio > 0) && (aspectRatio != m_videoAspect))
1004  {
1005  m_videoAspect = aspectRatio;
1007  }
1008 
1009  if (height && width && (height != m_videoHeight || m_videoWidth != width))
1010  {
1011  m_videoHeight = height;
1012  m_videoWidth = width;
1013  ResolutionChange(width, height, m_framesWrittenCount);
1014  }
1015 
1016  if (frameRate.isNonzero() && frameRate != m_frameRate)
1017  {
1018  LOG(VB_RECORD, LOG_INFO, LOC +
1019  QString("FindH2645Keyframes: timescale: %1, tick: %2, framerate: %3")
1020  .arg( m_h2645Parser->GetTimeScale() )
1021  .arg( m_h2645Parser->GetUnitsInTick() )
1022  .arg( frameRate.toDouble() * 1000 ) );
1023  m_frameRate = frameRate;
1024  FrameRateChange(frameRate.toDouble() * 1000, m_framesWrittenCount);
1025  }
1026 
1027  if (scantype != SCAN_t::UNKNOWN_SCAN && scantype != m_scanType)
1028  {
1029  LOG(VB_RECORD, LOG_INFO, LOC +
1030  QString("FindH2645Keyframes: scan type: %1")
1031  .arg(scantype == SCAN_t::INTERLACED ?
1032  "Interlaced" : "Progressive"));
1033  m_scanType = scantype;
1035  }
1036 
1037  return m_seenSps;
1038 }
1039 
1045 {
1046  // Perform ringbuffer switch if needed.
1048 
1049  uint64_t startpos = 0;
1050  uint64_t frameNum = m_framesWrittenCount;
1051 
1052  if (m_firstKeyframe < 0)
1053  {
1054  m_firstKeyframe = frameNum;
1055  startpos = 0;
1056  SendMythSystemRecEvent("REC_STARTED_WRITING", m_curRecording);
1057  }
1058  else
1059  startpos = m_h2645Parser->keyframeAUstreamOffset();
1060 
1061  // Add key frame to position map
1062  m_positionMapLock.lock();
1063  if (!m_positionMap.contains(frameNum))
1064  {
1065  m_positionMapDelta[frameNum] = startpos;
1066  m_positionMap[frameNum] = startpos;
1067  m_durationMap[frameNum] = llround(m_totalDuration);
1068  m_durationMapDelta[frameNum] = llround(m_totalDuration);
1069  }
1070  m_positionMapLock.unlock();
1071 }
1072 
1073 void DTVRecorder::FindPSKeyFrames(const uint8_t *buffer, uint len)
1074 {
1075  const uint maxKFD = kMaxKeyFrameDistance;
1076 
1077  const uint8_t *bufstart = buffer;
1078  const uint8_t *bufptr = buffer;
1079  const uint8_t *bufend = buffer + len;
1080 
1081  uint aspectRatio = 0;
1082  uint height = 0;
1083  uint width = 0;
1084  FrameRate frameRate(0);
1085 
1087  while (bufptr + skip < bufend)
1088  {
1089  bool hasFrame = false;
1090  bool hasKeyFrame = false;
1091 
1092  const uint8_t *tmp = bufptr;
1093  bufptr =
1094  avpriv_find_start_code(bufptr + skip, bufend, &m_startCode);
1097  m_videoBytesRemaining -= std::min(
1098  (uint)(bufptr - tmp), m_videoBytesRemaining);
1099 
1100  if ((m_startCode & 0xffffff00) != 0x00000100)
1101  continue;
1102 
1103  // NOTE: Length may be zero for packets that only contain bytes from
1104  // video elementary streams in TS packets. 13818-1:2000 2.4.3.7
1105  int pes_packet_length = -1;
1106  if ((bufend - bufptr) >= 2)
1107  pes_packet_length = ((bufptr[0]<<8) | bufptr[1]) + 2 + 6;
1108 
1109  const int stream_id = m_startCode & 0x000000ff;
1111  {
1112  if (PESStreamID::PictureStartCode == stream_id)
1113  { // pes_packet_length is meaningless
1114  pes_packet_length = -1;
1115  if (bufend-bufptr >= 4)
1116  {
1117  uint frmtypei = (bufptr[1]>>3) & 0x7;
1118  if ((1 <= frmtypei) && (frmtypei <= 5))
1119  hasFrame = true;
1120  }
1121  else
1122  {
1123  hasFrame = true;
1124  }
1125  }
1126  else if (PESStreamID::GOPStartCode == stream_id)
1127  { // pes_packet_length is meaningless
1128  pes_packet_length = -1;
1130  hasKeyFrame = true;
1131  }
1132  else if (PESStreamID::SequenceStartCode == stream_id)
1133  { // pes_packet_length is meaningless
1134  pes_packet_length = -1;
1136  hasKeyFrame |= (m_lastGopSeen + maxKFD)<m_framesSeenCount;
1137 
1138  // Look for aspectRatio changes and store them in the database
1139  aspectRatio = (bufptr[3] >> 4);
1140 
1141  // Get resolution
1142  height = ((bufptr[1] & 0xf) << 8) | bufptr[2];
1143  width = (bufptr[0] <<4) | (bufptr[1]>>4);
1144 
1145  frameRate = frameRateMap[(bufptr[3] & 0x0000000f)];
1146  }
1147  }
1148  else if (!m_audioBytesRemaining)
1149  {
1150  if ((stream_id >= PESStreamID::MPEGVideoStreamBegin) &&
1151  (stream_id <= PESStreamID::MPEGVideoStreamEnd))
1152  { // ok-dvdinfo
1153  m_videoBytesRemaining = std::max(0, pes_packet_length);
1154  }
1155  else if ((stream_id >= PESStreamID::MPEGAudioStreamBegin) &&
1156  (stream_id <= PESStreamID::MPEGAudioStreamEnd))
1157  { // ok-dvdinfo
1158  m_audioBytesRemaining = std::max(0, pes_packet_length);
1159  }
1160  }
1161 
1162  if (PESStreamID::PaddingStream == stream_id)
1163  { // ok-dvdinfo
1164  m_otherBytesRemaining = std::max(0, pes_packet_length);
1165  }
1166 
1167  m_startCode = 0xffffffff; // reset start code
1168 
1169  if (hasFrame && !hasKeyFrame)
1170  {
1171  // If we have seen kMaxKeyFrameDistance frames since the
1172  // last GOP or SEQ stream_id, then pretend this picture
1173  // is a keyframe. We may get artifacts but at least
1174  // we will be able to skip frames.
1175  hasKeyFrame = ((m_framesSeenCount & 0xf) == 0U);
1176  hasKeyFrame &= (m_lastGopSeen + maxKFD) < m_framesSeenCount;
1177  hasKeyFrame &= (m_lastSeqSeen + maxKFD) < m_framesSeenCount;
1178  }
1179 
1180  if (hasFrame)
1181  {
1185  }
1186 
1187  if (hasKeyFrame)
1188  {
1190  HandleKeyframe((int64_t)m_payloadBuffer.size() - (bufptr - bufstart));
1191  }
1192 
1193  if ((aspectRatio > 0) && (aspectRatio != m_videoAspect))
1194  {
1195  m_videoAspect = aspectRatio;
1197  }
1198 
1199  if (height && width &&
1200  (height != m_videoHeight || m_videoWidth != width))
1201  {
1202  m_videoHeight = height;
1203  m_videoWidth = width;
1204  ResolutionChange(width, height, m_framesWrittenCount);
1205  }
1206 
1207  if (frameRate.isNonzero() && frameRate != m_frameRate)
1208  {
1209  m_frameRate = frameRate;
1210  LOG(VB_RECORD, LOG_INFO, LOC +
1211  QString("FindPSKeyFrames: frame rate = %1")
1212  .arg(frameRate.toDouble() * 1000));
1213  FrameRateChange(frameRate.toDouble() * 1000, m_framesWrittenCount);
1214  }
1215 
1216  if (hasKeyFrame || hasFrame)
1217  {
1218  // We are free to write the packet, but if we have
1219  // buffered packet[s] we have to write them first...
1220  if (!m_payloadBuffer.empty())
1221  {
1222  if (m_ringBuffer)
1223  {
1225  &m_payloadBuffer[0], m_payloadBuffer.size());
1226  }
1227  m_payloadBuffer.clear();
1228  }
1229 
1230  if (m_ringBuffer)
1231  m_ringBuffer->Write(bufstart, (bufptr - bufstart));
1232 
1233  bufstart = bufptr;
1234  }
1235 
1236  skip = std::max(m_audioBytesRemaining, m_otherBytesRemaining);
1237  }
1238 
1239  int bytes_skipped = bufend - bufptr;
1240  if (bytes_skipped > 0)
1241  {
1242  m_audioBytesRemaining -= std::min(
1243  (uint)bytes_skipped, m_audioBytesRemaining);
1244  m_videoBytesRemaining -= std::min(
1245  (uint)bytes_skipped, m_videoBytesRemaining);
1246  m_otherBytesRemaining -= std::min(
1247  (uint)bytes_skipped, m_otherBytesRemaining);
1248  }
1249 
1250  uint64_t idx = m_payloadBuffer.size();
1251  uint64_t rem = (bufend - bufstart);
1252  m_payloadBuffer.resize(idx + rem);
1253  memcpy(&m_payloadBuffer[idx], bufstart, rem);
1254 #if 0
1255  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1256  QString("idx: %1, rem: %2").arg(idx).arg(rem));
1257 #endif
1258 }
1259 
1261 {
1262  if (!_pat)
1263  {
1264  LOG(VB_RECORD, LOG_ERR, LOC + "SetPAT(NULL)");
1265  return;
1266  }
1267 
1268  QMutexLocker change_lock(&m_pidLock);
1269 
1270  int progNum = m_streamData->DesiredProgram();
1271  uint pmtpid = _pat->FindPID(progNum);
1272 
1273  // If we have not found the desired program in the PAT and this happens to be
1274  // an SPTS then update the desired program to the one that is present in the PAT.
1275  if (!pmtpid)
1276  {
1277  if (_pat->ProgramCount() == 1)
1278  {
1279  int oldProgNum = progNum;
1280  progNum = _pat->ProgramNumber(0);
1281  LOG(VB_GENERAL, LOG_INFO, LOC +
1282  QString("Update desired program found in SPTS PAT from %1 to %2")
1283  .arg(oldProgNum).arg(progNum));
1284  GetStreamData()->SetDesiredProgram(progNum);
1285  pmtpid = _pat->FindPID(progNum);
1286  }
1287  }
1288  if (!pmtpid)
1289  {
1290  LOG(VB_RECORD, LOG_ERR, LOC +
1291  QString("SetPAT(): Ignoring PAT not containing our desired "
1292  "program (%1)...").arg(progNum));
1293  return;
1294  }
1295 
1296  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPAT(%1 on pid 0x%2)")
1297  .arg(progNum).arg(pmtpid,0,16));
1298 
1300  m_inputPat = new ProgramAssociationTable(*_pat);
1301  delete oldpat;
1302 
1303  // Listen for the other PMTs for faster channel switching
1304  for (uint i = 0; m_inputPat && (i < m_inputPat->ProgramCount()); ++i)
1305  {
1306  uint pmt_pid = m_inputPat->ProgramPID(i);
1307  if (!m_streamData->IsListeningPID(pmt_pid))
1309  }
1310 }
1311 
1312 void DTVRecorder::HandlePMT(uint progNum, const ProgramMapTable *_pmt)
1313 {
1314  QMutexLocker change_lock(&m_pidLock);
1315 
1316  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPMT(%1, %2)").arg(progNum)
1317  .arg(_pmt == nullptr ? "NULL" : "valid"));
1318 
1319 
1320  if ((int)progNum == m_streamData->DesiredProgram())
1321  {
1322  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPMT(%1)").arg(progNum));
1323  ProgramMapTable *oldpmt = m_inputPmt;
1324  m_inputPmt = new ProgramMapTable(*_pmt);
1325 
1326  QString sistandard = GetSIStandard();
1327 
1328  bool has_no_av = true;
1329  for (uint i = 0; i < m_inputPmt->StreamCount() && has_no_av; ++i)
1330  {
1331  has_no_av &= !m_inputPmt->IsVideo(i, sistandard);
1332  has_no_av &= !m_inputPmt->IsAudio(i, sistandard);
1333  }
1334  m_hasNoAV = has_no_av;
1335 
1337  delete oldpmt;
1338  }
1339 }
1340 
1342  bool insert)
1343 {
1344  if (!pat)
1345  {
1346  LOG(VB_RECORD, LOG_ERR, LOC + "HandleSingleProgramPAT(NULL)");
1347  return;
1348  }
1349 
1350  if (!m_ringBuffer)
1351  return;
1352 
1353  uint next_cc = (pat->tsheader()->ContinuityCounter()+1)&0xf;
1354  pat->tsheader()->SetContinuityCounter(next_cc);
1355  pat->GetAsTSPackets(m_scratch, next_cc);
1356 
1357  for (const auto & tspacket : m_scratch)
1358  DTVRecorder::BufferedWrite(tspacket, insert);
1359 }
1360 
1362 {
1363  if (!pmt)
1364  {
1365  LOG(VB_RECORD, LOG_ERR, LOC + "HandleSingleProgramPMT(NULL)");
1366  return;
1367  }
1368 
1369  // We only want to do these checks once per recording
1370  bool seenVideo = (m_primaryVideoCodec != AV_CODEC_ID_NONE);
1371  bool seenAudio = (m_primaryAudioCodec != AV_CODEC_ID_NONE);
1372  uint bestAudioCodec = 0;
1373  // collect stream types for H.264 (MPEG-4 AVC) keyframe detection
1374  for (uint i = 0; i < pmt->StreamCount(); ++i)
1375  {
1376  // We only care about the first identifiable video stream
1377  if (!seenVideo && (m_primaryVideoCodec == AV_CODEC_ID_NONE) &&
1378  StreamID::IsVideo(pmt->StreamType(i)))
1379  {
1380  seenVideo = true; // Ignore other video streams
1381  switch (pmt->StreamType(i))
1382  {
1383  case StreamID::MPEG1Video:
1384  m_primaryVideoCodec = AV_CODEC_ID_MPEG1VIDEO;
1385  break;
1386  case StreamID::MPEG2Video:
1387  m_primaryVideoCodec = AV_CODEC_ID_MPEG2VIDEO;
1388  break;
1389  case StreamID::MPEG4Video:
1390  m_primaryVideoCodec = AV_CODEC_ID_MPEG4;
1391  break;
1392  case StreamID::H264Video:
1393  m_primaryVideoCodec = AV_CODEC_ID_H264;
1394  if (dynamic_cast<AVCParser *>(m_h2645Parser) == nullptr)
1395  {
1396  delete m_h2645Parser;
1397  m_h2645Parser = nullptr;
1398  }
1399  if (m_h2645Parser == nullptr)
1400  {
1401  auto * avcParser = new AVCParser;
1402  if (avcParser != nullptr)
1403  {
1404  m_h2645Parser = reinterpret_cast<H2645Parser *>
1405  (avcParser);
1406  avcParser->use_I_forKeyframes(m_useIForKeyframe);
1407  }
1408  }
1409  break;
1410  case StreamID::H265Video:
1411  LOG(VB_GENERAL, LOG_INFO, LOC + "HEVC detected");
1412  m_primaryVideoCodec = AV_CODEC_ID_H265;
1413  if (dynamic_cast<HEVCParser *>(m_h2645Parser) == nullptr)
1414  {
1415  delete m_h2645Parser;
1416  m_h2645Parser = nullptr;
1417  }
1418  if (m_h2645Parser == nullptr)
1419  {
1420  m_h2645Parser = reinterpret_cast<H2645Parser *>
1421  (new HEVCParser);
1422  }
1423  break;
1425  m_primaryVideoCodec = AV_CODEC_ID_MPEG2VIDEO; // TODO Will it always be MPEG2?
1426  break;
1427  case StreamID::VC1Video:
1428  m_primaryVideoCodec = AV_CODEC_ID_VC1;
1429  break;
1430  default:
1431  break;
1432  }
1433 
1434  if (m_primaryVideoCodec != AV_CODEC_ID_NONE)
1436  }
1437 
1438  // We want the 'best' identifiable audio stream, where 'best' is
1439  // subjective and no-one will likely agree.
1440  // For now it's the 'best' codec, assuming mpeg stream types range
1441  // from worst to best, which it does
1442  if (!seenAudio && StreamID::IsAudio(pmt->StreamType(i)) &&
1443  pmt->StreamType(i) > bestAudioCodec)
1444  {
1445  bestAudioCodec = pmt->StreamType(i);
1446  switch (pmt->StreamType(i))
1447  {
1448  case StreamID::MPEG1Audio: // MPEG-1 Layer 2 (MP2)
1449  case StreamID::MPEG2Audio: // MPEG-2 Part 3 (MP2 Multichannel)
1450  m_primaryAudioCodec = AV_CODEC_ID_MP2;
1451  break;
1453  m_primaryAudioCodec = AV_CODEC_ID_AAC;
1454  break;
1456  m_primaryAudioCodec = AV_CODEC_ID_AAC_LATM;
1457  break;
1458  case StreamID::AC3Audio:
1459  m_primaryAudioCodec = AV_CODEC_ID_AC3;
1460  break;
1461  case StreamID::EAC3Audio:
1462  m_primaryAudioCodec = AV_CODEC_ID_EAC3;
1463  break;
1464  case StreamID::DTSAudio:
1465  m_primaryAudioCodec = AV_CODEC_ID_DTS;
1466  break;
1467  default:
1468  break;
1469  }
1470 
1471  if (m_primaryAudioCodec != AV_CODEC_ID_NONE)
1473  }
1474 
1475 // LOG(VB_GENERAL, LOG_DEBUG, QString("Recording(%1): Stream #%2: %3 ")
1476 // .arg(m_curRecording ? QString::number(m_curRecording->GetRecordingID()) : "")
1477 // .arg(i)
1478 // .arg(StreamID::GetDescription(pmt->StreamType(i))));
1479  m_streamId[pmt->StreamPID(i)] = pmt->StreamType(i);
1480  }
1481 
1482  // If the PCRPID is valid and the PCR is not contained
1483  // in another stream, make sure the PCR stream is not
1484  // discarded (use PrivSec type as dummy 'valid' value)
1485  if (pmt->PCRPID() != 0x1fff && pmt->FindPID(pmt->PCRPID()) == -1)
1487 
1488  if (!m_ringBuffer)
1489  return;
1490 
1491  uint next_cc = (pmt->tsheader()->ContinuityCounter()+1)&0xf;
1492  pmt->tsheader()->SetContinuityCounter(next_cc);
1493  pmt->GetAsTSPackets(m_scratch, next_cc);
1494 
1495  for (const auto & tspacket : m_scratch)
1496  DTVRecorder::BufferedWrite(tspacket, insert);
1497 }
1498 
1500 {
1501  const uint pid = tspacket.PID();
1502 
1503  if (pid != 0x1fff)
1504  m_packetCount.fetchAndAddAcquire(1);
1505 
1506  // Check continuity counter
1507  uint old_cnt = m_continuityCounter[pid];
1508  if ((pid != 0x1fff) && !CheckCC(pid, tspacket.ContinuityCounter()))
1509  {
1510  int v = m_continuityErrorCount.fetchAndAddRelaxed(1) + 1;
1511  double erate = v * 100.0 / m_packetCount.fetchAndAddRelaxed(0);
1512  LOG(VB_RECORD, LOG_WARNING, LOC +
1513  QString("PID 0x%1 discontinuity detected ((%2+1)%16!=%3) %4%")
1514  .arg(pid,0,16).arg(old_cnt,2)
1515  .arg(tspacket.ContinuityCounter(),2)
1516  .arg(erate));
1517  }
1518 
1519  // Only create fake keyframe[s] if there are no audio/video streams
1520  if (m_inputPmt && m_hasNoAV)
1521  {
1522  FindOtherKeyframes(&tspacket);
1523  m_bufferPackets = false;
1524  }
1525  else if (m_recordMptsOnly)
1526  {
1527  /* When recording the full, unfiltered, MPTS, trigger a write
1528  * every 0.5 seconds. Since the packets are unfiltered and
1529  * unprocessed we cannot wait for a keyframe to trigger the
1530  * writes. */
1531 
1532  if (m_framesSeenCount++ == 0)
1534 
1535  if (m_recordMptsTimer.elapsed() > 0.5s)
1536  {
1540  m_recordMptsTimer.addMSecs(-500ms);
1541  }
1542  }
1543  else if (m_streamId[pid] == 0)
1544  {
1545  // Ignore this packet if the PID should be stripped
1546  return true;
1547  }
1548  else
1549  {
1550  // There are audio/video streams. Only write the packet
1551  // if audio/video key-frames have been found
1553  return true;
1554  }
1555 
1556  BufferedWrite(tspacket);
1557 
1558  return true;
1559 }
1560 
1562 {
1563  if (!m_ringBuffer)
1564  return true;
1565 
1566  uint streamType = m_streamId[tspacket.PID()];
1567 
1568  if (tspacket.HasPayload() && tspacket.PayloadStart())
1569  {
1570  if (m_bufferPackets && m_firstKeyframe >= 0 && !m_payloadBuffer.empty())
1571  {
1572  // Flush the buffer
1573  if (m_ringBuffer)
1575  m_payloadBuffer.clear();
1576  }
1577 
1578  // buffer packets until we know if this is a keyframe
1579  m_bufferPackets = true;
1580  }
1581 
1582  // Check for keyframes and count frames
1583  if (streamType == StreamID::H264Video ||
1584  streamType == StreamID::H265Video)
1585  FindH2645Keyframes(&tspacket);
1586  else if (streamType != 0)
1587  FindMPEG2Keyframes(&tspacket);
1588  else
1589  LOG(VB_RECORD, LOG_ERR, LOC +
1590  "ProcessVideoTSPacket: unknown stream type!");
1591 
1592  return ProcessAVTSPacket(tspacket);
1593 }
1594 
1596 {
1597  if (!m_ringBuffer)
1598  return true;
1599 
1600  if (tspacket.HasPayload() && tspacket.PayloadStart())
1601  {
1602  if (m_bufferPackets && m_firstKeyframe >= 0 && !m_payloadBuffer.empty())
1603  {
1604  // Flush the buffer
1605  if (m_ringBuffer)
1607  m_payloadBuffer.clear();
1608  }
1609 
1610  // buffer packets until we know if this is a keyframe
1611  m_bufferPackets = true;
1612  }
1613 
1614  FindAudioKeyframes(&tspacket);
1615  return ProcessAVTSPacket(tspacket);
1616 }
1617 
1620 {
1621  // Sync recording start to first keyframe
1623  {
1624  if (m_bufferPackets)
1625  BufferedWrite(tspacket);
1626  return true;
1627  }
1628 
1629  const uint pid = tspacket.PID();
1630 
1631  if (pid != 0x1fff)
1632  m_packetCount.fetchAndAddAcquire(1);
1633 
1634  // Check continuity counter
1635  uint old_cnt = m_continuityCounter[pid];
1636  if ((pid != 0x1fff) && !CheckCC(pid, tspacket.ContinuityCounter()))
1637  {
1638  int v = m_continuityErrorCount.fetchAndAddRelaxed(1) + 1;
1639  double erate = v * 100.0 / m_packetCount.fetchAndAddRelaxed(0);
1640  LOG(VB_RECORD, LOG_WARNING, LOC +
1641  QString("A/V PID 0x%1 discontinuity detected ((%2+1)%16!=%3) %4%")
1642  .arg(pid,0,16).arg(old_cnt).arg(tspacket.ContinuityCounter())
1643  .arg(erate,5,'f',2));
1644  }
1645 
1646  if (!(m_pidStatus[pid] & kPayloadStartSeen))
1647  {
1649  LOG(VB_RECORD, LOG_INFO, LOC +
1650  QString("PID 0x%1 Found Payload Start").arg(pid,0,16));
1651  }
1652 
1653  BufferedWrite(tspacket);
1654 
1655  return true;
1656 }
1657 
1659 {
1661  recq->AddTSStatistics(
1662  m_continuityErrorCount.fetchAndAddRelaxed(0),
1663  m_packetCount.fetchAndAddRelaxed(0));
1664  return recq;
1665 }
1666 
1667 /* vim: set expandtab tabstop=4 shiftwidth=4: */
StreamID::VC1Video
@ VC1Video
SMPTE 421M video codec (aka VC1) in Blu-Ray.
Definition: mpegtables.h:121
DTVRecorder::FindPSKeyFrames
void FindPSKeyFrames(const uint8_t *buffer, uint len) override
Definition: dtvrecorder.cpp:1073
DTVRecorder::m_pesSynced
bool m_pesSynced
Definition: dtvrecorder.h:149
DTVRecorder::HandleSingleProgramPMT
void HandleSingleProgramPMT(ProgramMapTable *pmt, bool insert) override
Definition: dtvrecorder.cpp:1361
DTVRecorder::m_pidStatus
std::array< uint8_t, 0x1fff+1 > m_pidStatus
Definition: dtvrecorder.h:185
AVCParser
Definition: AVCParser.h:29
RecorderBase::m_timeOfLatestDataTimer
MythTimer m_timeOfLatestDataTimer
Definition: recorderbase.h:380
ProgramAssociationTable::ProgramPID
uint ProgramPID(uint i) const
Definition: mpegtables.h:646
MythTimer::elapsed
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:91
TSHeader::PayloadStart
bool PayloadStart(void) const
Definition: tspacket.h:70
RecorderBase::SetPositionMapType
void SetPositionMapType(MarkTypes type)
Set seektable type.
Definition: recorderbase.h:278
PESStreamID::MPEGVideoStreamBegin
@ MPEGVideoStreamBegin
First MPEG-1/2 video stream (w/ext hdr)
Definition: mpegtables.h:80
RecorderBase::m_timeOfLatestData
QDateTime m_timeOfLatestData
Definition: recorderbase.h:379
DTVRecorder::m_inputPmt
ProgramMapTable * m_inputPmt
PMT on input side.
Definition: dtvrecorder.h:177
RecorderBase::VideoCodecChange
void VideoCodecChange(AVCodecID vCodec)
Note a change in video codec.
Definition: recorderbase.cpp:820
dtvrecorder.h
FrameRate::toDouble
double toDouble(void) const
Definition: recorderbase.h:40
DTVRecorder::CheckCC
bool CheckCC(uint pid, uint new_cnt)
Definition: dtvrecorder.h:222
ProgramMapTable::StreamCount
uint StreamCount(void) const
Definition: mpegtables.h:750
MPEGStreamData::SetDesiredProgram
void SetDesiredProgram(int p)
Definition: mpegstreamdata.cpp:66
DTVRecorder::m_bufferPackets
bool m_bufferPackets
Definition: dtvrecorder.h:165
DTVRecorder::m_framesWrittenCount
unsigned long long m_framesWrittenCount
Definition: dtvrecorder.h:199
DTVRecorder::m_payloadBuffer
std::vector< unsigned char > m_payloadBuffer
Definition: dtvrecorder.h:166
MPEGStreamData::AddMPEGSPListener
void AddMPEGSPListener(MPEGSingleProgramStreamListener *val)
Definition: mpegstreamdata.cpp:1687
HEVCParser
Definition: HEVCParser.h:29
RecorderBase::m_statisticsLock
QMutex m_statisticsLock
Definition: recorderbase.h:374
StreamID::DTSAudio
@ DTSAudio
Definition: mpegtables.h:130
H2645Parser::getFrameRate
virtual void getFrameRate(FrameRate &result) const =0
DTVRecorder::kMaxKeyFrameDistance
static const uint kMaxKeyFrameDistance
If the number of regular frames detected since the last detected keyframe exceeds this value,...
Definition: dtvrecorder.h:218
StreamID::MPEG2AudioAmd1
@ MPEG2AudioAmd1
ISO 13818-3/AMD-1 Audio using LATM syntax.
Definition: mpegtables.h:127
FrameRate::getNum
uint getNum(void) const
Definition: recorderbase.h:42
DTVRecorder::ResetForNewFile
void ResetForNewFile(void) override
Definition: dtvrecorder.cpp:138
DTVRecorder::m_audioTimer
QElapsedTimer m_audioTimer
Definition: dtvrecorder.h:134
RecordingInfo
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:35
DTVRecorder::m_inputPat
ProgramAssociationTable * m_inputPat
PAT on input side.
Definition: dtvrecorder.h:175
DTVRecorder::m_startCode
uint32_t m_startCode
Definition: dtvrecorder.h:135
MythMediaBuffer::GetWritePosition
long long GetWritePosition(void) const
Returns how far into a ThreadedFileWriter file we have written.
Definition: mythmediabuffer.cpp:1786
DTVRecorder::m_scanType
SCAN_t m_scanType
Definition: dtvrecorder.h:206
ProgramMapTable
A PMT table maps a program described in the ProgramAssociationTable to various PID's which describe t...
Definition: mpegtables.h:692
DTVRecorder::ProcessVideoTSPacket
bool ProcessVideoTSPacket(const TSPacket &tspacket) override
Definition: dtvrecorder.cpp:1561
DTVRecorder::m_packetCount
QAtomicInt m_packetCount
Definition: dtvrecorder.h:196
PESPacket::tsheader
const TSHeader * tsheader() const
Definition: pespacket.h:91
TSHeader::PID
unsigned int PID(void) const
Definition: tspacket.h:74
H2645Parser::FIELD_BOTTOM
@ FIELD_BOTTOM
Definition: H2645Parser.h:83
DTVRecorder::FindOtherKeyframes
bool FindOtherKeyframes(const TSPacket *tspacket)
Non-Audio/Video data.
Definition: dtvrecorder.cpp:760
DTVRecorder::m_videoBytesRemaining
unsigned int m_videoBytesRemaining
Definition: dtvrecorder.h:141
ProgramMapTable::FindPID
int FindPID(uint pid) const
Locates stream index of pid.
Definition: mpegtables.h:797
DTVRecorder::SetOptionsFromProfile
void SetOptionsFromProfile(RecordingProfile *profile, const QString &videodev, const QString &audiodev, const QString &vbidev) override
Sets basic recorder options.
Definition: dtvrecorder.cpp:110
DTVRecorder::Reset
void Reset(void) override
Reset the recorder to the startup state.
Definition: dtvrecorder.cpp:202
ProgramMapTable::PCRPID
uint PCRPID(void) const
stream that contains program clock reference.
Definition: mpegtables.h:726
DTVRecorder::HandlePAT
void HandlePAT(const ProgramAssociationTable *_pat) override
Definition: dtvrecorder.cpp:1260
DTVRecorder::m_lastGopSeen
unsigned long long m_lastGopSeen
Definition: dtvrecorder.h:137
MythTimer::stop
void stop(void)
Stops timer, next call to isRunning() will return false and any calls to elapsed() or restart() will ...
Definition: mythtimer.cpp:78
TSPacket::AFCOffset
unsigned int AFCOffset(void) const
Definition: tspacket.h:211
H2645Parser::pictureWidth
uint pictureWidth(void) const
Definition: H2645Parser.h:102
MythTimer::isRunning
bool isRunning(void) const
Returns true if start() or restart() has been called at least once since construction and since any c...
Definition: mythtimer.cpp:135
DTVRecorder::SetStreamData
virtual void SetStreamData(MPEGStreamData *data)
Definition: dtvrecorder.cpp:216
DTVRecorder::BufferedWrite
void BufferedWrite(const TSPacket &tspacket, bool insert=false)
Definition: dtvrecorder.cpp:257
RecorderBase::m_videoAspect
uint m_videoAspect
Definition: recorderbase.h:328
ProgramMapTable::IsVideo
bool IsVideo(uint i, const QString &sistandard) const
Returns true iff the stream at index i is a video stream.
Definition: mpegtables.cpp:513
PESStreamID::MPEGAudioStreamEnd
@ MPEGAudioStreamEnd
Last MPEG-1/2 audio stream (w/ext hdr)
Definition: mpegtables.h:78
MythMediaBuffer::Write
int Write(const void *Buffer, uint Count)
Writes buffer to ThreadedFileWriter::Write(const void*,uint)
Definition: mythmediabuffer.cpp:1624
atscstreamdata.h
DTVRecorder::m_tsCount
std::array< uint64_t, 256 > m_tsCount
Definition: dtvrecorder.h:192
RecorderBase::m_videoWidth
uint m_videoWidth
Definition: recorderbase.h:331
H2645Parser::GetUnitsInTick
uint32_t GetUnitsInTick(void) const
Definition: H2645Parser.h:118
MythTimer::start
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
StreamID::MPEG2Video
@ MPEG2Video
ISO 13818-2 & ITU H.262 (aka MPEG-2)
Definition: mpegtables.h:116
DTVRecorder::m_framesSeenCount
unsigned long long m_framesSeenCount
Definition: dtvrecorder.h:198
kExtractDTS
@ kExtractDTS
Definition: dtvrecorder.cpp:330
RecordingQuality::IsDamaged
bool IsDamaged(void) const
Definition: recordingquality.cpp:107
mythsystemevent.h
ProgramAssociationTable::ProgramCount
uint ProgramCount(void) const
Definition: mpegtables.h:636
DTVRecorder::GetSIStandard
virtual QString GetSIStandard(void) const
Definition: dtvrecorder.h:123
DTVRecorder::FinishRecording
void FinishRecording(void) override
Flushes the ringbuffer, and if this is not a live LiveTV recording saves the position map and filesiz...
Definition: dtvrecorder.cpp:124
DTVRecorder::SetCAMPMT
virtual void SetCAMPMT(const ProgramMapTable *)
Definition: dtvrecorder.h:124
DTVRecorder::m_tsFirst
std::array< int64_t, 256 > m_tsFirst
Definition: dtvrecorder.h:194
DTVRecorder::m_recordingType
QString m_recordingType
Definition: dtvrecorder.h:130
RecorderBase::m_timeOfLatestDataPacketInterval
QAtomicInt m_timeOfLatestDataPacketInterval
Definition: recorderbase.h:378
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
StreamID::IsVideo
static bool IsVideo(uint type)
Returns true iff video is an MPEG1/2/3, H264 or open cable video stream.
Definition: mpegtables.h:168
tmp
static guint32 * tmp
Definition: goom_core.cpp:31
StreamID::H265Video
@ H265Video
ISO 23008-2 & ITU H.265 (aka HEVC, Ultra HD)
Definition: mpegtables.h:119
DTVRecorder::m_continuityCounter
std::array< uint8_t, 0x1fff+1 > m_continuityCounter
Definition: dtvrecorder.h:186
TSHeader::ContinuityCounter
unsigned int ContinuityCounter(void) const
Definition: tspacket.h:90
TSHeader::SetContinuityCounter
void SetContinuityCounter(unsigned int cc)
Definition: tspacket.h:151
FrameRate::isNonzero
bool isNonzero(void) const
Definition: recorderbase.h:41
DTVRecorder::kPayloadStartSeen
static const unsigned char kPayloadStartSeen
Definition: dtvrecorder.h:219
StreamID::MPEG1Video
@ MPEG1Video
ISO 11172-2 (aka MPEG-1)
Definition: mpegtables.h:115
MPEGStreamData::IsListeningPID
virtual bool IsListeningPID(uint pid) const
Definition: mpegstreamdata.cpp:1096
StreamID::EAC3Audio
@ EAC3Audio
A/53 Part 3:2009 6.7.3.
Definition: mpegtables.h:129
RecorderBase::m_curRecording
RecordingInfo * m_curRecording
Definition: recorderbase.h:334
DTVRecorder::m_tdTickFramerate
FrameRate m_tdTickFramerate
Definition: dtvrecorder.h:205
RecorderBase::m_durationMapDelta
frm_pos_map_t m_durationMapDelta
Definition: recorderbase.h:361
DTVRecorder::m_hasNoAV
bool m_hasNoAV
Definition: dtvrecorder.h:178
RecorderBase::SetStrOption
void SetStrOption(RecordingProfile *profile, const QString &name)
Convenience function used to set QString options from a profile.
Definition: recorderbase.cpp:208
DTVRecorder::m_totalDuration
double m_totalDuration
Definition: dtvrecorder.h:200
DTVRecorder::HandleSingleProgramPAT
void HandleSingleProgramPAT(ProgramAssociationTable *pat, bool insert) override
Definition: dtvrecorder.cpp:1341
programinfo.h
ATSCStreamData::SetDesiredChannel
void SetDesiredChannel(int major, int minor)
Definition: atscstreamdata.cpp:57
mythlogging.h
ProgramInfo::GetRecordingStatus
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:446
DTVRecorder::ProcessAVTSPacket
bool ProcessAVTSPacket(const TSPacket &tspacket)
Common code for processing either audio or video packets.
Definition: dtvrecorder.cpp:1619
ProgramAssociationTable::ProgramNumber
uint ProgramNumber(uint i) const
Definition: mpegtables.h:643
hardwareprofile.scan.profile
profile
Definition: scan.py:99
DTVRecorder::m_recordMptsOnly
bool m_recordMptsOnly
Definition: dtvrecorder.h:182
PESStreamID::PaddingStream
@ PaddingStream
Definition: mpegtables.h:72
DTVRecorder::m_lastKeyframeSeen
unsigned long long m_lastKeyframeSeen
Definition: dtvrecorder.h:139
RecorderBase::AspectChange
void AspectChange(uint aspect, long long frame)
Note a change in aspect ratio in the recordedmark table.
Definition: recorderbase.cpp:732
MARK_DURATION_MS
@ MARK_DURATION_MS
Definition: programtypes.h:75
DTVRecorder::m_progressiveSequence
int m_progressiveSequence
Definition: dtvrecorder.h:145
H2645Parser::keyframeAUstreamOffset
uint64_t keyframeAUstreamOffset(void) const
Definition: H2645Parser.h:114
DTVRecorder::HandleKeyframe
void HandleKeyframe(int64_t extra)
This save the current frame to the position maps and handles ringbuffer switching.
Definition: dtvrecorder.cpp:786
DTVRecorder::m_tdTickCount
uint64_t m_tdTickCount
Definition: dtvrecorder.h:204
H2645Parser::GetScanType
SCAN_t GetScanType(void) const
Definition: H2645Parser.h:119
StreamID::IsAudio
static bool IsAudio(uint type)
Returns true iff audio is MPEG1/2, AAC, AC3 or DTS audio stream.
Definition: mpegtables.h:179
RecorderBase::m_recordingGaps
RecordingGaps m_recordingGaps
Definition: recorderbase.h:381
RecorderBase::ClearStatistics
virtual void ClearStatistics(void)
Definition: recorderbase.cpp:425
TSPacket
Used to access the data of a Transport Stream packet.
Definition: tspacket.h:169
ATSCStreamData
Encapsulates data about ATSC stream and emits events for most tables.
Definition: atscstreamdata.h:28
DTVRecorder::m_firstKeyframe
int m_firstKeyframe
Definition: dtvrecorder.h:136
ProgramMapTable::StreamType
uint StreamType(uint i) const
Definition: mpegtables.h:738
StreamID::MPEG2Audio
@ MPEG2Audio
ISO 13818-3.
Definition: mpegtables.h:125
LOC
#define LOC
DTVRecorder – base class for Digital Televison recorders Copyright 2003-2004 by Brandon Beattie,...
Definition: dtvrecorder.cpp:35
RecorderBase::m_videoHeight
uint m_videoHeight
Definition: recorderbase.h:330
kPIDPriorityLow
@ kPIDPriorityLow
Definition: mpegstreamdata.h:78
RecorderBase::GetRecordingQuality
virtual RecordingQuality * GetRecordingQuality(const RecordingInfo *ri) const
Returns a report about the current recordings quality.
Definition: recorderbase.cpp:505
DTVRecorder::DTVRecorder
DTVRecorder(TVRec *rec)
Definition: dtvrecorder.cpp:49
RecStatus::Failing
@ Failing
Definition: recStatus.h:18
MPEGStreamData
Encapsulates data about MPEG stream and emits events for each table.
Definition: mpegstreamdata.h:84
MythTimer::restart
std::chrono::milliseconds restart(void)
Returns milliseconds elapsed since last start() or restart() and resets the count.
Definition: mythtimer.cpp:62
H2645Parser::onFrameStart
bool onFrameStart(void) const
Definition: H2645Parser.h:99
RecorderBase::ResolutionChange
void ResolutionChange(uint width, uint height, long long frame)
Note a change in video size in the recordedmark table.
Definition: recorderbase.cpp:783
DTVRecorder::FindAudioKeyframes
bool FindAudioKeyframes(const TSPacket *tspacket)
Definition: dtvrecorder.cpp:720
FrameRate
Definition: recorderbase.h:36
StreamID::MPEG1Audio
@ MPEG1Audio
ISO 11172-3.
Definition: mpegtables.h:124
DVBStreamData
Definition: dvbstreamdata.h:32
RecorderBase::AudioCodecChange
void AudioCodecChange(AVCodecID aCodec)
Note a change in audio codec.
Definition: recorderbase.cpp:829
DTVRecorder::m_h2645Parser
H2645Parser * m_h2645Parser
Definition: dtvrecorder.h:151
DTVRecorder::HandleH2645Keyframe
void HandleH2645Keyframe(void)
This save the current frame to the position maps and handles ringbuffer switching.
Definition: dtvrecorder.cpp:1044
frameRateMap
static const std::array< const FrameRate, 16 > frameRateMap
Definition: dtvrecorder.cpp:368
DTVRecorder::m_seenSps
bool m_seenSps
Definition: dtvrecorder.h:150
RecorderBase::m_videoFrameRate
double m_videoFrameRate
Definition: recorderbase.h:326
DTVRecorder::GetStreamData
MPEGStreamData * GetStreamData(void) const
Definition: dtvrecorder.h:57
RecorderBase::m_durationMap
frm_pos_map_t m_durationMap
Definition: recorderbase.h:360
mpegtables.h
DTVRecorder::m_streamId
std::array< uint8_t, 0x1fff+1 > m_streamId
Definition: dtvrecorder.h:184
RecorderBase::SetTotalFrames
void SetTotalFrames(uint64_t total_frames)
Note the total frames in the recordedmark table.
Definition: recorderbase.cpp:844
RecordingQuality::AddTSStatistics
void AddTSStatistics(int continuity_error_count, int packet_count)
Definition: recordingquality.cpp:87
HEVCParser.h
StreamID::AC3Audio
@ AC3Audio
A/53 Part 3:2009 6.7.1.
Definition: mpegtables.h:128
DTVRecorder::ClearStatistics
void ClearStatistics(void) override
Definition: dtvrecorder.cpp:182
DTVRecorder::ProcessTSPacket
bool ProcessTSPacket(const TSPacket &tspacket) override
Definition: dtvrecorder.cpp:1499
StreamID::MPEG4Video
@ MPEG4Video
ISO 14492-2 (aka MPEG-4)
Definition: mpegtables.h:117
DTVRecorder::SetOption
void SetOption(const QString &name, const QString &value) override
Set an specific option.
Definition: dtvrecorder.cpp:89
RecordingQuality
Definition: recordingquality.h:34
DTVRecorder::m_recordMptsTimer
MythTimer m_recordMptsTimer
Definition: dtvrecorder.h:183
StreamID::H264Video
@ H264Video
ISO 14492-10 & ITU H.264 (aka MPEG-4-AVC)
Definition: mpegtables.h:118
uint
unsigned int uint
Definition: compat.h:144
DTVRecorder::m_tdBase
double m_tdBase
Definition: dtvrecorder.h:203
RecorderBase::m_positionMap
frm_pos_map_t m_positionMap
Definition: recorderbase.h:358
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:60
RecorderBase::m_containerFormat
AVContainer m_containerFormat
Definition: recorderbase.h:318
RecorderBase::SetDuration
void SetDuration(std::chrono::milliseconds duration)
Note the total duration in the recordedmark table.
Definition: recorderbase.cpp:838
ProgramAssociationTable
The Program Association Table lists all the programs in a stream, and is always found on PID 0.
Definition: mpegtables.h:615
RecorderBase::FrameRateChange
void FrameRateChange(uint framerate, uint64_t frame)
Note a change in video frame rate in the recordedmark table.
Definition: recorderbase.cpp:799
DTVRecorder::m_lastSeqSeen
unsigned long long m_lastSeqSeen
Definition: dtvrecorder.h:138
DTVRecorder::~DTVRecorder
~DTVRecorder() override
Definition: dtvrecorder.cpp:64
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:936
ProgramMapTable::IsAudio
bool IsAudio(uint i, const QString &sistandard) const
Returns true iff the stream at index i is an audio stream.
Definition: mpegtables.cpp:535
mythmediabuffer.h
H2645Parser::addBytes
virtual uint32_t addBytes(const uint8_t *bytes, uint32_t byte_count, uint64_t stream_offset)=0
mpegstreamdata.h
RecorderBase::FinishRecording
virtual void FinishRecording(void)
Definition: recorderbase.cpp:436
AVCParser.h
RecorderBase::m_positionMapDelta
frm_pos_map_t m_positionMapDelta
Definition: recorderbase.h:359
DTVRecorder::m_repeatPict
int m_repeatPict
Definition: dtvrecorder.h:146
DTVRecorder::m_audioBytesRemaining
unsigned int m_audioBytesRemaining
Definition: dtvrecorder.h:140
RecorderBase::StopRecording
virtual void StopRecording(void)
StopRecording() signals to the recorder that it should stop recording and exit cleanly.
Definition: recorderbase.cpp:223
DTVRecorder::m_hasWrittenOtherKeyframe
bool m_hasWrittenOtherKeyframe
Definition: dtvrecorder.h:156
MPEGStreamData::AddMPEGListener
void AddMPEGListener(MPEGStreamListener *val)
Definition: mpegstreamdata.cpp:1612
dvbstreamdata.h
DTVRecorder::m_otherBytesRemaining
unsigned int m_otherBytesRemaining
Definition: dtvrecorder.h:142
MythMediaBuffer::WriterFlush
void WriterFlush(void)
Calls ThreadedFileWriter::Flush(void)
Definition: mythmediabuffer.cpp:1697
RecorderBase::m_ringBuffer
MythMediaBuffer * m_ringBuffer
Definition: recorderbase.h:315
DTVRecorder::ProcessAudioTSPacket
bool ProcessAudioTSPacket(const TSPacket &tspacket) override
Definition: dtvrecorder.cpp:1595
DVBStreamData::AddDVBMainListener
void AddDVBMainListener(DVBMainStreamListener *val)
Definition: dvbstreamdata.cpp:1017
DTVRecorder::FindMPEG2Keyframes
bool FindMPEG2Keyframes(const TSPacket *tspacket)
Locates the keyframes and saves them to the position map.
Definition: dtvrecorder.cpp:403
SCAN_t::INTERLACED
@ INTERLACED
DTVRecorder::UpdateFramesWritten
void UpdateFramesWritten(void)
Definition: dtvrecorder.cpp:688
DTVRecorder::m_waitForKeyframeOption
bool m_waitForKeyframeOption
Wait for the a GOP/SEQ-start before sending data.
Definition: dtvrecorder.h:154
RecordingGap
Definition: recordingquality.h:14
RecorderBase::SetRecordingStatus
virtual void SetRecordingStatus(RecStatus::Type status, const QString &file, int line)
Definition: recorderbase.cpp:395
DTVRecorder::m_streamData
MPEGStreamData * m_streamData
Definition: dtvrecorder.h:162
RecorderBase::m_timeOfFirstData
QDateTime m_timeOfFirstData
Definition: recorderbase.h:376
extract_timestamp
static int64_t extract_timestamp(const uint8_t *bufptr, int bytes_left, int pts_or_dts)
Definition: dtvrecorder.cpp:331
MPEGStreamData::DesiredProgram
int DesiredProgram(void) const
Definition: mpegstreamdata.h:259
MARK_GOP_BYFRAME
@ MARK_GOP_BYFRAME
Definition: programtypes.h:65
FrameRate::getDen
uint getDen(void) const
Definition: recorderbase.h:43
DTVRecorder::m_error
QString m_error
non-empty iff irrecoverable recording error detected
Definition: dtvrecorder.h:160
StreamID::MPEG2AACAudio
@ MPEG2AACAudio
ISO 13818-7 Audio w/ADTS syntax.
Definition: mpegtables.h:126
DTVRecorder::m_useIForKeyframe
bool m_useIForKeyframe
Definition: dtvrecorder.h:212
ProgramInfo::ClearPositionMap
void ClearPositionMap(MarkTypes type) const
Definition: programinfo.cpp:3736
H2645Parser::getFieldType
virtual field_type getFieldType(void) const =0
RecorderBase::m_timeOfFirstDataIsSet
QAtomicInt m_timeOfFirstDataIsSet
Definition: recorderbase.h:375
DTVRecorder::m_recordMpts
bool m_recordMpts
Definition: dtvrecorder.h:181
RecorderBase::m_positionMapLock
QMutex m_positionMapLock
Definition: recorderbase.h:357
MythTimer::addMSecs
void addMSecs(std::chrono::milliseconds ms)
Adds an offset to the last call to start() or restart().
Definition: mythtimer.cpp:146
kExtractPTS
@ kExtractPTS
Definition: dtvrecorder.cpp:330
RecorderBase
This is the abstract base class for supporting recorder hardware.
Definition: recorderbase.h:73
PESStreamID::MPEG2ExtensionStartCode
@ MPEG2ExtensionStartCode
Followed by an extension byte, not documented here.
Definition: mpegtables.h:59
DTVRecorder::HandlePMT
void HandlePMT(uint progNum, const ProgramMapTable *_pmt) override
Definition: dtvrecorder.cpp:1312
RecorderBase::AspectRatio
AspectRatio
Definition: recorderbase.h:243
kSingleRecord
@ kSingleRecord
Definition: recordingtypes.h:22
TVRec
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:145
TSHeader::HasPayload
bool HasPayload(void) const
Definition: tspacket.h:97
PESStreamID::GOPStartCode
@ GOPStartCode
Group of Pictures (GOP) start code.
Definition: mpegtables.h:65
DTVRecorder::m_continuityErrorCount
QAtomicInt m_continuityErrorCount
Definition: dtvrecorder.h:197
H2645Parser::onKeyFrameStart
bool onKeyFrameStart(void) const
Definition: H2645Parser.h:100
RecorderBase::m_primaryAudioCodec
AVCodecID m_primaryAudioCodec
Definition: recorderbase.h:320
PESStreamID::SequenceStartCode
@ SequenceStartCode
Sequence (SEQ) start code contains frame size, aspect ratio and fps.
Definition: mpegtables.h:56
tv_rec.h
H2645Parser::aspectRatio
uint aspectRatio(void) const
Computes aspect ratio from picture size and sample aspect ratio.
Definition: H2645Parser.cpp:400
ts_to_qdatetime
static QDateTime ts_to_qdatetime(uint64_t pts, uint64_t pts_first, const QDateTime &pts_first_dt)
Definition: dtvrecorder.cpp:359
PESStreamID::PictureStartCode
@ PictureStartCode
Definition: mpegtables.h:49
SCAN_t
SCAN_t
Definition: recorderbase.h:54
SCAN_t::UNKNOWN_SCAN
@ UNKNOWN_SCAN
RecorderBase::SetOption
virtual void SetOption(const QString &name, const QString &value)
Set an specific option.
Definition: recorderbase.cpp:152
mpeg::chrono::pts
std::chrono::duration< CHRONO_TYPE, std::ratio< 1, 90000 > > pts
Definition: mythchrono.h:55
RecorderBase::m_primaryVideoCodec
AVCodecID m_primaryVideoCodec
Definition: recorderbase.h:319
DTVRecorder::m_usePts
bool m_usePts
Definition: dtvrecorder.h:191
DTVRecorder::m_scratch
std::vector< TSPacket > m_scratch
Definition: dtvrecorder.h:187
DTVRecorder::m_pesTimer
MythTimer m_pesTimer
Definition: dtvrecorder.h:133
FrameRate::toString
QString toString(void) const
Definition: recorderbase.h:44
PESPacket::GetAsTSPackets
void GetAsTSPackets(std::vector< TSPacket > &output, uint cc) const
Returns payload only PESPacket as series of TSPackets.
Definition: pespacket.cpp:121
SendMythSystemRecEvent
void SendMythSystemRecEvent(const QString &msg, const RecordingInfo *pginfo)
Definition: mythsystemevent.cpp:332
MPEGStreamData::AddListeningPID
virtual void AddListeningPID(uint pid, PIDPriority priority=kPIDPriorityNormal)
Definition: mpegstreamdata.h:119
StreamID::OpenCableVideo
@ OpenCableVideo
Always MPEG-2??
Definition: mpegtables.h:120
ProgramAssociationTable::FindPID
uint FindPID(uint progNum) const
Definition: mpegtables.h:655
H2645Parser
Definition: H2645Parser.h:74
H2645Parser::GetTimeScale
uint32_t GetTimeScale(void) const
Definition: H2645Parser.h:117
DTVRecorder::m_minimumRecordingQuality
int m_minimumRecordingQuality
Definition: dtvrecorder.h:190
RecordingProfile
Definition: recordingprofile.h:39
RecorderBase::m_frameRate
FrameRate m_frameRate
Definition: recorderbase.h:332
RecStatus::toString
static QString toString(Type recstatus, uint id)
Converts "recstatus" into a short (unreadable) string.
Definition: recStatus.cpp:39
RecorderBase::kTimeOfLatestDataIntervalTarget
static constexpr std::chrono::milliseconds kTimeOfLatestDataIntervalTarget
timeOfLatest update interval target in milliseconds.
Definition: recorderbase.h:383
DTVRecorder::m_pidLock
QRecursiveMutex m_pidLock
Definition: dtvrecorder.h:172
DTVRecorder::FindH2645Keyframes
bool FindH2645Keyframes(const TSPacket *tspacket)
Definition: dtvrecorder.cpp:826
DTVRecorder::m_musicChoice
bool m_musicChoice
Definition: dtvrecorder.h:210
H2645Parser::pictureHeight
uint pictureHeight(void) const
Definition: H2645Parser.h:103
DTVRecorder::HandleTimestamps
void HandleTimestamps(int stream_id, int64_t pts, int64_t dts)
Definition: dtvrecorder.cpp:604
RecorderBase::CheckForRingBufferSwitch
virtual bool CheckForRingBufferSwitch(void)
If requested, switch to new RingBuffer/ProgramInfo objects.
Definition: recorderbase.cpp:351
PESStreamID::MPEGVideoStreamEnd
@ MPEGVideoStreamEnd
Last MPEG-1/2 video stream (w/ext hdr)
Definition: mpegtables.h:82
ProgramMapTable::StreamPID
uint StreamPID(uint i) const
Definition: mpegtables.h:741
DTVRecorder::GetRecordingQuality
RecordingQuality * GetRecordingQuality(const RecordingInfo *r) const override
Returns a report about the current recordings quality.
Definition: dtvrecorder.cpp:1658
TSHeader::data
const unsigned char * data(void) const
Definition: tspacket.h:155
RecorderBase::m_timeOfLatestDataCount
QAtomicInt m_timeOfLatestDataCount
Definition: recorderbase.h:377
StreamID::PrivSec
@ PrivSec
ISO 13818-1 private tables & ITU H.222.0.
Definition: mpegtables.h:146
DTVRecorder::InitStreamData
virtual void InitStreamData(void)
Definition: dtvrecorder.cpp:229
PESStreamID::MPEGAudioStreamBegin
@ MPEGAudioStreamBegin
First MPEG-1/2 audio stream (w/ext hdr)
Definition: mpegtables.h:76
RecorderBase::VideoScanChange
void VideoScanChange(SCAN_t scan, uint64_t frame)
Note a change in video scan type in the recordedmark table.
Definition: recorderbase.cpp:814
DTVRecorder::m_tsLast
std::array< int64_t, 256 > m_tsLast
Definition: dtvrecorder.h:193
DTVRecorder::m_tsFirstDt
std::array< QDateTime, 256 > m_tsFirstDt
Definition: dtvrecorder.h:195
formatMPEG2_TS
@ formatMPEG2_TS
Definition: recordingfile.h:16
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:922
TSPacket::kSize
static constexpr unsigned int kSize
Definition: tspacket.h:223
millisecondsFromFloat
std::enable_if_t< std::is_floating_point_v< T >, std::chrono::milliseconds > millisecondsFromFloat(T value)
Helper function for convert a floating point number to a duration.
Definition: mythchrono.h:91
H2645Parser::stateChanged
bool stateChanged(void) const
Definition: H2645Parser.h:97
RecorderBase::SetIntOption
void SetIntOption(RecordingProfile *profile, const QString &name)
Convenience function used to set integer options from a profile.
Definition: recorderbase.cpp:198