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