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