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