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