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")
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")
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  }
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 (!pmtpid)
1274  {
1275  LOG(VB_RECORD, LOG_ERR, LOC +
1276  QString("SetPAT(): Ignoring PAT not containing our desired "
1277  "program (%1)...").arg(progNum));
1278  return;
1279  }
1280 
1281  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPAT(%1 on pid 0x%2)")
1282  .arg(progNum).arg(pmtpid,0,16));
1283 
1285  m_inputPat = new ProgramAssociationTable(*_pat);
1286  delete oldpat;
1287 
1288  // Listen for the other PMTs for faster channel switching
1289  for (uint i = 0; m_inputPat && (i < m_inputPat->ProgramCount()); ++i)
1290  {
1291  uint pmt_pid = m_inputPat->ProgramPID(i);
1292  if (!m_streamData->IsListeningPID(pmt_pid))
1294  }
1295 }
1296 
1297 void DTVRecorder::HandlePMT(uint progNum, const ProgramMapTable *_pmt)
1298 {
1299  QMutexLocker change_lock(&m_pidLock);
1300 
1301  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPMT(%1, %2)").arg(progNum)
1302  .arg(_pmt == nullptr ? "NULL" : "valid"));
1303 
1304 
1305  if ((int)progNum == m_streamData->DesiredProgram())
1306  {
1307  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPMT(%1)").arg(progNum));
1308  ProgramMapTable *oldpmt = m_inputPmt;
1309  m_inputPmt = new ProgramMapTable(*_pmt);
1310 
1311  QString sistandard = GetSIStandard();
1312 
1313  bool has_no_av = true;
1314  for (uint i = 0; i < m_inputPmt->StreamCount() && has_no_av; ++i)
1315  {
1316  has_no_av &= !m_inputPmt->IsVideo(i, sistandard);
1317  has_no_av &= !m_inputPmt->IsAudio(i, sistandard);
1318  }
1319  m_hasNoAV = has_no_av;
1320 
1322  delete oldpmt;
1323  }
1324 }
1325 
1327  bool insert)
1328 {
1329  if (!pat)
1330  {
1331  LOG(VB_RECORD, LOG_ERR, LOC + "HandleSingleProgramPAT(NULL)");
1332  return;
1333  }
1334 
1335  if (!m_ringBuffer)
1336  return;
1337 
1338  uint next_cc = (pat->tsheader()->ContinuityCounter()+1)&0xf;
1339  pat->tsheader()->SetContinuityCounter(next_cc);
1340  pat->GetAsTSPackets(m_scratch, next_cc);
1341 
1342  for (const auto & tspacket : m_scratch)
1343  DTVRecorder::BufferedWrite(tspacket, insert);
1344 }
1345 
1347 {
1348  if (!pmt)
1349  {
1350  LOG(VB_RECORD, LOG_ERR, LOC + "HandleSingleProgramPMT(NULL)");
1351  return;
1352  }
1353 
1354  // We only want to do these checks once per recording
1355  bool seenVideo = (m_primaryVideoCodec != AV_CODEC_ID_NONE);
1356  bool seenAudio = (m_primaryAudioCodec != AV_CODEC_ID_NONE);
1357  uint bestAudioCodec = 0;
1358  // collect stream types for H.264 (MPEG-4 AVC) keyframe detection
1359  for (uint i = 0; i < pmt->StreamCount(); ++i)
1360  {
1361  // We only care about the first identifiable video stream
1362  if (!seenVideo && (m_primaryVideoCodec == AV_CODEC_ID_NONE) &&
1363  StreamID::IsVideo(pmt->StreamType(i)))
1364  {
1365  seenVideo = true; // Ignore other video streams
1366  switch (pmt->StreamType(i))
1367  {
1368  case StreamID::MPEG1Video:
1369  m_primaryVideoCodec = AV_CODEC_ID_MPEG1VIDEO;
1370  break;
1371  case StreamID::MPEG2Video:
1372  m_primaryVideoCodec = AV_CODEC_ID_MPEG2VIDEO;
1373  break;
1374  case StreamID::MPEG4Video:
1375  m_primaryVideoCodec = AV_CODEC_ID_MPEG4;
1376  break;
1377  case StreamID::H264Video:
1378  m_primaryVideoCodec = AV_CODEC_ID_H264;
1379  if (dynamic_cast<AVCParser *>(m_h2645Parser) == nullptr)
1380  {
1381  delete m_h2645Parser;
1382  m_h2645Parser = nullptr;
1383  }
1384  if (m_h2645Parser == nullptr)
1385  {
1386  m_h2645Parser = reinterpret_cast<H2645Parser *>
1387  (new AVCParser);
1388  dynamic_cast<AVCParser *>(m_h2645Parser)->
1389  use_I_forKeyframes(m_useIForKeyframe);
1390  }
1391  break;
1392  case StreamID::H265Video:
1393  LOG(VB_GENERAL, LOG_INFO, LOC + "HEVC detected");
1394  m_primaryVideoCodec = AV_CODEC_ID_H265;
1395  if (dynamic_cast<HEVCParser *>(m_h2645Parser) == nullptr)
1396  {
1397  delete m_h2645Parser;
1398  m_h2645Parser = nullptr;
1399  }
1400  if (m_h2645Parser == nullptr)
1401  {
1402  m_h2645Parser = reinterpret_cast<H2645Parser *>
1403  (new HEVCParser);
1404  }
1405  break;
1407  m_primaryVideoCodec = AV_CODEC_ID_MPEG2VIDEO; // TODO Will it always be MPEG2?
1408  break;
1409  case StreamID::VC1Video:
1410  m_primaryVideoCodec = AV_CODEC_ID_VC1;
1411  break;
1412  default:
1413  break;
1414  }
1415 
1416  if (m_primaryVideoCodec != AV_CODEC_ID_NONE)
1418  }
1419 
1420  // We want the 'best' identifiable audio stream, where 'best' is
1421  // subjective and no-one will likely agree.
1422  // For now it's the 'best' codec, assuming mpeg stream types range
1423  // from worst to best, which it does
1424  if (!seenAudio && StreamID::IsAudio(pmt->StreamType(i)) &&
1425  pmt->StreamType(i) > bestAudioCodec)
1426  {
1427  bestAudioCodec = pmt->StreamType(i);
1428  switch (pmt->StreamType(i))
1429  {
1430  case StreamID::MPEG1Audio: // MPEG-1 Layer 2 (MP2)
1431  case StreamID::MPEG2Audio: // MPEG-2 Part 3 (MP2 Multichannel)
1432  m_primaryAudioCodec = AV_CODEC_ID_MP2;
1433  break;
1435  m_primaryAudioCodec = AV_CODEC_ID_AAC;
1436  break;
1438  m_primaryAudioCodec = AV_CODEC_ID_AAC_LATM;
1439  break;
1440  case StreamID::AC3Audio:
1441  m_primaryAudioCodec = AV_CODEC_ID_AC3;
1442  break;
1443  case StreamID::EAC3Audio:
1444  m_primaryAudioCodec = AV_CODEC_ID_EAC3;
1445  break;
1446  case StreamID::DTSAudio:
1447  m_primaryAudioCodec = AV_CODEC_ID_DTS;
1448  break;
1449  default:
1450  break;
1451  }
1452 
1453  if (m_primaryAudioCodec != AV_CODEC_ID_NONE)
1455  }
1456 
1457 // LOG(VB_GENERAL, LOG_DEBUG, QString("Recording(%1): Stream #%2: %3 ")
1458 // .arg(m_curRecording ? QString::number(m_curRecording->GetRecordingID()) : "")
1459 // .arg(i)
1460 // .arg(StreamID::GetDescription(pmt->StreamType(i))));
1461  m_streamId[pmt->StreamPID(i)] = pmt->StreamType(i);
1462  }
1463 
1464  // If the PCRPID is valid and the PCR is not contained
1465  // in another stream, make sure the PCR stream is not
1466  // discarded (use PrivSec type as dummy 'valid' value)
1467  if (pmt->PCRPID() != 0x1fff && pmt->FindPID(pmt->PCRPID()) == -1)
1469 
1470  if (!m_ringBuffer)
1471  return;
1472 
1473  uint next_cc = (pmt->tsheader()->ContinuityCounter()+1)&0xf;
1474  pmt->tsheader()->SetContinuityCounter(next_cc);
1475  pmt->GetAsTSPackets(m_scratch, next_cc);
1476 
1477  for (const auto & tspacket : m_scratch)
1478  DTVRecorder::BufferedWrite(tspacket, insert);
1479 }
1480 
1482 {
1483  const uint pid = tspacket.PID();
1484 
1485  if (pid != 0x1fff)
1486  m_packetCount.fetchAndAddAcquire(1);
1487 
1488  // Check continuity counter
1489  uint old_cnt = m_continuityCounter[pid];
1490  if ((pid != 0x1fff) && !CheckCC(pid, tspacket.ContinuityCounter()))
1491  {
1492  int v = m_continuityErrorCount.fetchAndAddRelaxed(1) + 1;
1493  double erate = v * 100.0 / m_packetCount.fetchAndAddRelaxed(0);
1494  LOG(VB_RECORD, LOG_WARNING, LOC +
1495  QString("PID 0x%1 discontinuity detected ((%2+1)%16!=%3) %4%")
1496  .arg(pid,0,16).arg(old_cnt,2)
1497  .arg(tspacket.ContinuityCounter(),2)
1498  .arg(erate));
1499  }
1500 
1501  // Only create fake keyframe[s] if there are no audio/video streams
1502  if (m_inputPmt && m_hasNoAV)
1503  {
1504  FindOtherKeyframes(&tspacket);
1505  m_bufferPackets = false;
1506  }
1507  else if (m_recordMptsOnly)
1508  {
1509  /* When recording the full, unfiltered, MPTS, trigger a write
1510  * every 0.5 seconds. Since the packets are unfiltered and
1511  * unprocessed we cannot wait for a keyframe to trigger the
1512  * writes. */
1513 
1514  if (m_framesSeenCount++ == 0)
1516 
1517  if (m_recordMptsTimer.elapsed() > 0.5s)
1518  {
1522  m_recordMptsTimer.addMSecs(-500ms);
1523  }
1524  }
1525  else if (m_streamId[pid] == 0)
1526  {
1527  // Ignore this packet if the PID should be stripped
1528  return true;
1529  }
1530  else
1531  {
1532  // There are audio/video streams. Only write the packet
1533  // if audio/video key-frames have been found
1535  return true;
1536  }
1537 
1538  BufferedWrite(tspacket);
1539 
1540  return true;
1541 }
1542 
1544 {
1545  if (!m_ringBuffer)
1546  return true;
1547 
1548  uint streamType = m_streamId[tspacket.PID()];
1549 
1550  if (tspacket.HasPayload() && tspacket.PayloadStart())
1551  {
1552  if (m_bufferPackets && m_firstKeyframe >= 0 && !m_payloadBuffer.empty())
1553  {
1554  // Flush the buffer
1555  if (m_ringBuffer)
1557  m_payloadBuffer.clear();
1558  }
1559 
1560  // buffer packets until we know if this is a keyframe
1561  m_bufferPackets = true;
1562  }
1563 
1564  // Check for keyframes and count frames
1565  if (streamType == StreamID::H264Video ||
1566  streamType == StreamID::H265Video)
1567  FindH2645Keyframes(&tspacket);
1568  else if (streamType != 0)
1569  FindMPEG2Keyframes(&tspacket);
1570  else
1571  LOG(VB_RECORD, LOG_ERR, LOC +
1572  "ProcessVideoTSPacket: unknown stream type!");
1573 
1574  return ProcessAVTSPacket(tspacket);
1575 }
1576 
1578 {
1579  if (!m_ringBuffer)
1580  return true;
1581 
1582  if (tspacket.HasPayload() && tspacket.PayloadStart())
1583  {
1584  if (m_bufferPackets && m_firstKeyframe >= 0 && !m_payloadBuffer.empty())
1585  {
1586  // Flush the buffer
1587  if (m_ringBuffer)
1589  m_payloadBuffer.clear();
1590  }
1591 
1592  // buffer packets until we know if this is a keyframe
1593  m_bufferPackets = true;
1594  }
1595 
1596  FindAudioKeyframes(&tspacket);
1597  return ProcessAVTSPacket(tspacket);
1598 }
1599 
1602 {
1603  // Sync recording start to first keyframe
1605  {
1606  if (m_bufferPackets)
1607  BufferedWrite(tspacket);
1608  return true;
1609  }
1610 
1611  const uint pid = tspacket.PID();
1612 
1613  if (pid != 0x1fff)
1614  m_packetCount.fetchAndAddAcquire(1);
1615 
1616  // Check continuity counter
1617  uint old_cnt = m_continuityCounter[pid];
1618  if ((pid != 0x1fff) && !CheckCC(pid, tspacket.ContinuityCounter()))
1619  {
1620  int v = m_continuityErrorCount.fetchAndAddRelaxed(1) + 1;
1621  double erate = v * 100.0 / m_packetCount.fetchAndAddRelaxed(0);
1622  LOG(VB_RECORD, LOG_WARNING, LOC +
1623  QString("A/V PID 0x%1 discontinuity detected ((%2+1)%16!=%3) %4%")
1624  .arg(pid,0,16).arg(old_cnt).arg(tspacket.ContinuityCounter())
1625  .arg(erate,5,'f',2));
1626  }
1627 
1628  if (!(m_pidStatus[pid] & kPayloadStartSeen))
1629  {
1631  LOG(VB_RECORD, LOG_INFO, LOC +
1632  QString("PID 0x%1 Found Payload Start").arg(pid,0,16));
1633  }
1634 
1635  BufferedWrite(tspacket);
1636 
1637  return true;
1638 }
1639 
1641 {
1643  recq->AddTSStatistics(
1644  m_continuityErrorCount.fetchAndAddRelaxed(0),
1645  m_packetCount.fetchAndAddRelaxed(0));
1646  return recq;
1647 }
1648 
1649 /* 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:1346
DTVRecorder::m_pidStatus
std::array< uint8_t, 0x1fff+1 > m_pidStatus
Definition: dtvrecorder.h:181
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:173
RecorderBase::VideoCodecChange
void VideoCodecChange(AVCodecID vCodec)
Note a change in video codec.
Definition: recorderbase.cpp:818
dtvrecorder.h
FrameRate::toDouble
double toDouble(void) const
Definition: recorderbase.h:40
DTVRecorder::CheckCC
bool CheckCC(uint pid, uint new_cnt)
Definition: dtvrecorder.h:218
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:195
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:214
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:34
DTVRecorder::m_inputPat
ProgramAssociationTable * m_inputPat
PAT on input side.
Definition: dtvrecorder.h:171
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:1775
DTVRecorder::m_scanType
SCAN_t m_scanType
Definition: dtvrecorder.h:202
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:1543
DTVRecorder::m_packetCount
QAtomicInt m_packetCount
Definition: dtvrecorder.h:192
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
arg
arg(title).arg(filename).arg(doDelete))
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:1613
atscstreamdata.h
DTVRecorder::m_tsCount
std::array< uint64_t, 256 > m_tsCount
Definition: dtvrecorder.h:188
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:194
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:190
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:182
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:215
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:201
RecorderBase::m_durationMapDelta
frm_pos_map_t m_durationMapDelta
Definition: recorderbase.h:361
DTVRecorder::m_hasNoAV
bool m_hasNoAV
Definition: dtvrecorder.h:174
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:196
DTVRecorder::HandleSingleProgramPAT
void HandleSingleProgramPAT(ProgramAssociationTable *pat, bool insert) override
Definition: dtvrecorder.cpp:1326
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:445
DTVRecorder::ProcessAVTSPacket
bool ProcessAVTSPacket(const TSPacket &tspacket)
Common code for processing either audio or video packets.
Definition: dtvrecorder.cpp:1601
hardwareprofile.scan.profile
profile
Definition: scan.py:99
DTVRecorder::m_recordMptsOnly
bool m_recordMptsOnly
Definition: dtvrecorder.h:178
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:730
MARK_DURATION_MS
@ MARK_DURATION_MS
Definition: programtypes.h:74
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:200
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:423
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:503
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:781
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:827
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:180
RecorderBase::SetTotalFrames
void SetTotalFrames(uint64_t total_frames)
Note the total frames in the recordedmark table.
Definition: recorderbase.cpp:842
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:1481
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:179
StreamID::H264Video
@ H264Video
ISO 14492-10 & ITU H.264 (aka MPEG-4-AVC)
Definition: mpegtables.h:118
uint
unsigned int uint
Definition: compat.h:140
DTVRecorder::m_tdBase
double m_tdBase
Definition: dtvrecorder.h:199
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:836
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:797
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:932
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:434
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:1686
RecorderBase::m_ringBuffer
MythMediaBuffer * m_ringBuffer
Definition: recorderbase.h:315
DTVRecorder::ProcessAudioTSPacket
bool ProcessAudioTSPacket(const TSPacket &tspacket) override
Definition: dtvrecorder.cpp:1577
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
DTVRecorder::m_pidLock
QMutex m_pidLock
Definition: dtvrecorder.h:169
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:64
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:208
ProgramInfo::ClearPositionMap
void ClearPositionMap(MarkTypes type) const
Definition: programinfo.cpp:3636
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:177
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:1297
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:142
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:193
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:187
DTVRecorder::m_scratch
std::vector< TSPacket > m_scratch
Definition: dtvrecorder.h:183
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:331
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:186
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::FindH2645Keyframes
bool FindH2645Keyframes(const TSPacket *tspacket)
Definition: dtvrecorder.cpp:826
DTVRecorder::m_musicChoice
bool m_musicChoice
Definition: dtvrecorder.h:206
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:1640
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:812
DTVRecorder::m_tsLast
std::array< int64_t, 256 > m_tsLast
Definition: dtvrecorder.h:189
DTVRecorder::m_tsFirstDt
std::array< QDateTime, 256 > m_tsFirstDt
Definition: dtvrecorder.h:191
formatMPEG2_TS
@ formatMPEG2_TS
Definition: recordingfile.h:16
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:918
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