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 
332 enum : std::uint8_t { kExtractPTS, kExtractDTS };
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 MythAVRational,16> frameRateMap = {
371  MythAVRational(0), MythAVRational(24000, 1001), MythAVRational(24),
372  MythAVRational(25), MythAVRational(30000, 1001), MythAVRational(30),
373  MythAVRational(50), MythAVRational(60000, 1001), MythAVRational(60),
376  MythAVRational(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  MythAVRational 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  // not 1000 since m_tdTickCount needs to be divided by 2 to get an equivalent frame count
748  m_totalDuration = m_tdBase + (int64_t) 500 * m_tdTickCount *
750  }
751 
752  if (m_framesWrittenCount < 2000 || m_framesWrittenCount % 1000 == 0)
753  {
754  LOG(VB_RECORD, LOG_DEBUG,
755  QString("count=%1 m_frameRate=%2 tick_frameRate=%3 "
756  "tick_cnt=%4 tick_base=%5 _total_dur=%6")
758  .arg(m_frameRate.toString(),
760  .arg(m_tdTickCount)
761  .arg(m_tdBase)
762  .arg(m_totalDuration));
763  }
764 }
765 
766 bool DTVRecorder::FindAudioKeyframes(const TSPacket* /*tspacket*/)
767 {
768  bool hasKeyFrame = false;
769  if (!m_ringBuffer || (GetStreamData()->VideoPIDSingleProgram() <= 0x1fff))
770  return hasKeyFrame;
771 
772  static constexpr uint64_t kMsecPerDay { 24ULL * 60 * 60 * 1000 };
773  const double frame_interval = (1000.0 / m_videoFrameRate);
774  uint64_t elapsed = 0;
775  if (m_audioTimer.isValid())
776  elapsed = m_audioTimer.elapsed();
777  auto expected_frame = (uint64_t) ((double)elapsed / frame_interval);
778 
779  while (m_framesSeenCount > expected_frame + 10000)
780  expected_frame += (uint64_t) ((double)kMsecPerDay / frame_interval);
781 
782  if (!m_framesSeenCount || (m_framesSeenCount < expected_frame))
783  {
784  if (!m_framesSeenCount)
785  m_audioTimer.start();
786 
787  m_bufferPackets = false;
789 
790  if (1 == (m_framesSeenCount & 0x7))
791  {
794  hasKeyFrame = true;
795  }
796 
799  }
800 
801  return hasKeyFrame;
802 }
803 
806 bool DTVRecorder::FindOtherKeyframes(const TSPacket */*tspacket*/)
807 {
808  if (!m_ringBuffer || (GetStreamData()->VideoPIDSingleProgram() <= 0x1fff))
809  return true;
810 
812  return true;
813 
814  LOG(VB_RECORD, LOG_INFO, LOC + "DSMCC - FindOtherKeyframes() - "
815  "generating initial key-frame");
816 
820 
822 
824 
825  return true;
826 }
827 
832 void DTVRecorder::HandleKeyframe(int64_t extra)
833 {
834  if (!m_ringBuffer)
835  return;
836 
837  // Perform ringbuffer switch if needed.
839 
840  uint64_t frameNum = m_framesWrittenCount;
841  if (m_firstKeyframe < 0)
842  {
843  m_firstKeyframe = frameNum;
844  SendMythSystemRecEvent("REC_STARTED_WRITING", m_curRecording);
845  }
846 
847  // Add key frame to position map
848  m_positionMapLock.lock();
849  if (!m_positionMap.contains(frameNum))
850  {
851  int64_t startpos = m_ringBuffer->GetWritePosition() + extra;
852 
853  // Don't put negative offsets into the database, they get munged into
854  // MAX_INT64 - offset, which is an exceedingly large number, and
855  // certainly not valid.
856  if (startpos >= 0)
857  {
858  m_positionMapDelta[frameNum] = startpos;
859  m_positionMap[frameNum] = startpos;
860  m_durationMap[frameNum] = llround(m_totalDuration);
861  m_durationMapDelta[frameNum] = llround(m_totalDuration);
862  }
863  }
864  m_positionMapLock.unlock();
865 }
866 
873 {
874  if (!tspacket->HasPayload()) // no payload to scan
875  return m_firstKeyframe >= 0;
876 
877  if (!m_ringBuffer)
878  {
879  LOG(VB_GENERAL, LOG_ERR, LOC + "FindH2645Keyframes: No ringbuffer");
880  return m_firstKeyframe >= 0;
881  }
882 
883  if (!m_h2645Parser)
884  {
885  LOG(VB_GENERAL, LOG_ERR, LOC + "FindH2645Keyframes: m_h2645Parser not present");
886  return m_firstKeyframe >= 0;
887  }
888 
889  const bool payloadStart = tspacket->PayloadStart();
890  if (payloadStart)
891  {
892  // reset PES sync state
893  m_pesSynced = false;
894  m_startCode = 0xffffffff;
895  }
896 
897  uint aspectRatio = 0;
898  uint height = 0;
899  uint width = 0;
900  MythAVRational frameRate {0};
901  SCAN_t scantype(SCAN_t::UNKNOWN_SCAN);
902 
903  bool hasFrame = false;
904  bool hasKeyFrame = false;
905 
906  // scan for PES packets and H.264 NAL units
907  uint i = tspacket->AFCOffset();
908  for (; i < TSPacket::kSize; ++i)
909  {
910  // special handling required when a new PES packet begins
911  if (payloadStart && !m_pesSynced)
912  {
913  // bounds check
914  if (i + 2 >= TSPacket::kSize)
915  {
916  LOG(VB_GENERAL, LOG_ERR, LOC +
917  "PES packet start code may overflow to next TS packet, "
918  "aborting keyframe search");
919  break;
920  }
921 
922  // must find the PES start code
923  if (tspacket->data()[i ] != 0x00 ||
924  tspacket->data()[i+1] != 0x00 ||
925  tspacket->data()[i+2] != 0x01)
926  {
927  if (!m_pesTimer.isRunning() || m_pesTimer.elapsed() > 20000ms)
928  {
930  LOG(VB_GENERAL, LOG_ERR, LOC +
931  "PES start code not found in TS packet with PUSI set");
932  }
933  break;
934  }
935  i += 3;
936 
937  m_pesTimer.stop();
938 
939  // bounds check
940  if (i + 5 >= TSPacket::kSize)
941  {
942  LOG(VB_GENERAL, LOG_ERR, LOC +
943  "PES packet headers overflow to next TS packet, "
944  "aborting keyframe search");
945  break;
946  }
947 
948  // now we need to compute where the PES payload begins
949  // skip past the stream_id (+1)
950  // the next two bytes are the PES packet length (+2)
951  // after that, one byte of PES packet control bits (+1)
952  // after that, one byte of PES header flags bits (+1)
953  // and finally, one byte for the PES header length
954  const unsigned char pes_header_length = tspacket->data()[i + 5];
955 
956  // bounds check
957  if ((i + 6 + pes_header_length) >= TSPacket::kSize)
958  {
959  LOG(VB_GENERAL, LOG_ERR, LOC +
960  "PES packet headers overflow to next TS packet, "
961  "aborting keyframe search");
962  break;
963  }
964 
965  // we now know where the PES payload is
966  // normally, we should have used 6, but use 5 because the for
967  // loop will bump i
968  i += 5 + pes_header_length;
969  m_pesSynced = true;
970 
971 #if 0
972  LOG(VB_RECORD, LOG_DEBUG, LOC + "PES synced");
973 #endif
974  continue;
975  }
976 
977  if (!m_pesSynced)
978  break;
979 
980  // scan the NAL units
981  uint32_t bytes_used = m_h2645Parser->addBytes
982  (tspacket->data() + i, TSPacket::kSize - i,
984  i += (bytes_used - 1);
985 
987  {
988  if (m_h2645Parser->onFrameStart() &&
990  {
991  hasKeyFrame = m_h2645Parser->onKeyFrameStart();
992  hasFrame = true;
993  m_seenSps |= hasKeyFrame;
994 
995  width = m_h2645Parser->pictureWidth();
996  height = m_h2645Parser->pictureHeight();
997  aspectRatio = m_h2645Parser->aspectRatio();
998  scantype = m_h2645Parser->GetScanType();
999  frameRate = m_h2645Parser->getFrameRate();
1000  }
1001  }
1002  } // for (; i < TSPacket::kSize; ++i)
1003 
1004  // If it has been more than 511 frames since the last keyframe,
1005  // pretend we have one.
1006  if (hasFrame && !hasKeyFrame &&
1008  {
1009  hasKeyFrame = true;
1010  LOG(VB_RECORD, LOG_WARNING, LOC +
1011  QString("FindH2645Keyframes: %1 frames without a keyframe.")
1013  }
1014 
1015  // m_bufferPackets will only be true if a payload start has been seen
1016  if (hasKeyFrame && (m_bufferPackets || m_firstKeyframe >= 0))
1017  {
1018  LOG(VB_RECORD, LOG_DEBUG, LOC + QString
1019  ("Keyframe @ %1 + %2 = %3 AU %4")
1021  .arg(m_payloadBuffer.size())
1024 
1027  }
1028 
1029  if (hasFrame)
1030  {
1031  LOG(VB_RECORD, LOG_DEBUG, LOC + QString
1032  ("Frame @ %1 + %2 = %3 AU %4")
1034  .arg(m_payloadBuffer.size())
1037 
1038  m_bufferPackets = false; // We now know if this is a keyframe
1042  else
1043  {
1044  /* Found a frame that is not a keyframe, and we want to
1045  * start on a keyframe */
1046  m_payloadBuffer.clear();
1047  }
1048  }
1049 
1050  if ((aspectRatio > 0) && (aspectRatio != m_videoAspect))
1051  {
1052  m_videoAspect = aspectRatio;
1054  }
1055 
1056  if (height && width && (height != m_videoHeight || m_videoWidth != width))
1057  {
1058  m_videoHeight = height;
1059  m_videoWidth = width;
1060  ResolutionChange(width, height, m_framesWrittenCount);
1061  }
1062 
1063  if (frameRate.isNonzero() && frameRate != m_frameRate)
1064  {
1065  LOG(VB_RECORD, LOG_INFO, LOC +
1066  QString("FindH2645Keyframes: timescale: %1, tick: %2, framerate: %3")
1067  .arg( m_h2645Parser->GetTimeScale() )
1068  .arg( m_h2645Parser->GetUnitsInTick() )
1069  .arg( frameRate.toDouble() * 1000 ) );
1070  m_frameRate = frameRate;
1071  FrameRateChange(frameRate.toDouble() * 1000, m_framesWrittenCount);
1072  }
1073 
1074  if (scantype != SCAN_t::UNKNOWN_SCAN && scantype != m_scanType)
1075  {
1076  LOG(VB_RECORD, LOG_INFO, LOC +
1077  QString("FindH2645Keyframes: scan type: %1")
1078  .arg(scantype == SCAN_t::INTERLACED ?
1079  "Interlaced" : "Progressive"));
1080  m_scanType = scantype;
1082  }
1083 
1084  return m_seenSps;
1085 }
1086 
1092 {
1093  // Perform ringbuffer switch if needed.
1095 
1096  uint64_t startpos = 0;
1097  uint64_t frameNum = m_framesWrittenCount;
1098 
1099  if (m_firstKeyframe < 0)
1100  {
1101  m_firstKeyframe = frameNum;
1102  startpos = 0;
1103  SendMythSystemRecEvent("REC_STARTED_WRITING", m_curRecording);
1104  }
1105  else
1106  {
1107  startpos = m_h2645Parser->keyframeAUstreamOffset();
1108  }
1109 
1110  // Add key frame to position map
1111  m_positionMapLock.lock();
1112  if (!m_positionMap.contains(frameNum))
1113  {
1114  m_positionMapDelta[frameNum] = startpos;
1115  m_positionMap[frameNum] = startpos;
1116  m_durationMap[frameNum] = llround(m_totalDuration);
1117  m_durationMapDelta[frameNum] = llround(m_totalDuration);
1118  }
1119  m_positionMapLock.unlock();
1120 }
1121 
1122 void DTVRecorder::FindPSKeyFrames(const uint8_t *buffer, uint len)
1123 {
1124  const uint maxKFD = kMaxKeyFrameDistance;
1125 
1126  const uint8_t *bufstart = buffer;
1127  const uint8_t *bufptr = buffer;
1128  const uint8_t *bufend = buffer + len;
1129 
1130  uint aspectRatio = 0;
1131  uint height = 0;
1132  uint width = 0;
1133  MythAVRational frameRate {0};
1134 
1136  while (bufptr + skip < bufend)
1137  {
1138  bool hasFrame = false;
1139  bool hasKeyFrame = false;
1140 
1141  const uint8_t *tmp = bufptr;
1142  bufptr = ByteReader::find_start_code_truncated(bufptr + skip, bufend, &m_startCode);
1145  m_videoBytesRemaining -= std::min(
1146  (uint)(bufptr - tmp), m_videoBytesRemaining);
1147 
1149  continue;
1150 
1151  // NOTE: Length may be zero for packets that only contain bytes from
1152  // video elementary streams in TS packets. 13818-1:2000 2.4.3.7
1153  int pes_packet_length = -1;
1154  if ((bufend - bufptr) >= 2)
1155  pes_packet_length = ((bufptr[0]<<8) | bufptr[1]) + 2 + 6;
1156 
1157  const int stream_id = m_startCode & 0x000000ff;
1159  {
1160  if (PESStreamID::PictureStartCode == stream_id)
1161  { // pes_packet_length is meaningless
1162  pes_packet_length = -1;
1163  if (bufend-bufptr >= 4)
1164  {
1165  uint frmtypei = (bufptr[1]>>3) & 0x7;
1166  if ((1 <= frmtypei) && (frmtypei <= 5))
1167  hasFrame = true;
1168  }
1169  else
1170  {
1171  hasFrame = true;
1172  }
1173  }
1174  else if (PESStreamID::GOPStartCode == stream_id)
1175  { // pes_packet_length is meaningless
1176  pes_packet_length = -1;
1178  hasKeyFrame = true;
1179  }
1180  else if (PESStreamID::SequenceStartCode == stream_id)
1181  { // pes_packet_length is meaningless
1182  pes_packet_length = -1;
1184  hasKeyFrame |= (m_lastGopSeen + maxKFD)<m_framesSeenCount;
1185 
1186  // Look for aspectRatio changes and store them in the database
1187  aspectRatio = (bufptr[3] >> 4);
1188 
1189  // Get resolution
1190  height = ((bufptr[1] & 0xf) << 8) | bufptr[2];
1191  width = (bufptr[0] <<4) | (bufptr[1]>>4);
1192 
1193  frameRate = frameRateMap[(bufptr[3] & 0x0000000f)];
1194  }
1195  }
1196  else if (!m_audioBytesRemaining)
1197  {
1198  if ((stream_id >= PESStreamID::MPEGVideoStreamBegin) &&
1199  (stream_id <= PESStreamID::MPEGVideoStreamEnd))
1200  { // ok-dvdinfo
1201  m_videoBytesRemaining = std::max(0, pes_packet_length);
1202  }
1203  else if ((stream_id >= PESStreamID::MPEGAudioStreamBegin) &&
1204  (stream_id <= PESStreamID::MPEGAudioStreamEnd))
1205  { // ok-dvdinfo
1206  m_audioBytesRemaining = std::max(0, pes_packet_length);
1207  }
1208  }
1209 
1210  if (PESStreamID::PaddingStream == stream_id)
1211  { // ok-dvdinfo
1212  m_otherBytesRemaining = std::max(0, pes_packet_length);
1213  }
1214 
1215  m_startCode = 0xffffffff; // reset start code
1216 
1217  if (hasFrame && !hasKeyFrame)
1218  {
1219  // If we have seen kMaxKeyFrameDistance frames since the
1220  // last GOP or SEQ stream_id, then pretend this picture
1221  // is a keyframe. We may get artifacts but at least
1222  // we will be able to skip frames.
1223  hasKeyFrame = ((m_framesSeenCount & 0xf) == 0U);
1224  hasKeyFrame &= (m_lastGopSeen + maxKFD) < m_framesSeenCount;
1225  hasKeyFrame &= (m_lastSeqSeen + maxKFD) < m_framesSeenCount;
1226  }
1227 
1228  if (hasFrame)
1229  {
1233  }
1234 
1235  if (hasKeyFrame)
1236  {
1238  HandleKeyframe((int64_t)m_payloadBuffer.size() - (bufptr - bufstart));
1239  }
1240 
1241  if ((aspectRatio > 0) && (aspectRatio != m_videoAspect))
1242  {
1243  m_videoAspect = aspectRatio;
1245  }
1246 
1247  if (height && width &&
1248  (height != m_videoHeight || m_videoWidth != width))
1249  {
1250  m_videoHeight = height;
1251  m_videoWidth = width;
1252  ResolutionChange(width, height, m_framesWrittenCount);
1253  }
1254 
1255  if (frameRate.isNonzero() && frameRate != m_frameRate)
1256  {
1257  m_frameRate = frameRate;
1258  LOG(VB_RECORD, LOG_INFO, LOC +
1259  QString("FindPSKeyFrames: frame rate = %1")
1260  .arg(frameRate.toDouble() * 1000));
1261  FrameRateChange(frameRate.toDouble() * 1000, m_framesWrittenCount);
1262  }
1263 
1264  if (hasKeyFrame || hasFrame)
1265  {
1266  // We are free to write the packet, but if we have
1267  // buffered packet[s] we have to write them first...
1268  if (!m_payloadBuffer.empty())
1269  {
1270  if (m_ringBuffer)
1271  {
1273  (m_payloadBuffer).data(), m_payloadBuffer.size());
1274  }
1275  m_payloadBuffer.clear();
1276  }
1277 
1278  if (m_ringBuffer)
1279  m_ringBuffer->Write(bufstart, (bufptr - bufstart));
1280 
1281  bufstart = bufptr;
1282  }
1283 
1284  skip = std::max(m_audioBytesRemaining, m_otherBytesRemaining);
1285  }
1286 
1287  int bytes_skipped = bufend - bufptr;
1288  if (bytes_skipped > 0)
1289  {
1290  m_audioBytesRemaining -= std::min(
1291  (uint)bytes_skipped, m_audioBytesRemaining);
1292  m_videoBytesRemaining -= std::min(
1293  (uint)bytes_skipped, m_videoBytesRemaining);
1294  m_otherBytesRemaining -= std::min(
1295  (uint)bytes_skipped, m_otherBytesRemaining);
1296  }
1297 
1298  uint64_t idx = m_payloadBuffer.size();
1299  uint64_t rem = (bufend - bufstart);
1300  m_payloadBuffer.resize(idx + rem);
1301  memcpy(&m_payloadBuffer[idx], bufstart, rem);
1302 #if 0
1303  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1304  QString("idx: %1, rem: %2").arg(idx).arg(rem));
1305 #endif
1306 }
1307 
1309 {
1310  if (!_pat)
1311  {
1312  LOG(VB_RECORD, LOG_ERR, LOC + "SetPAT(NULL)");
1313  return;
1314  }
1315 
1316  QMutexLocker change_lock(&m_pidLock);
1317 
1318  int progNum = m_streamData->DesiredProgram();
1319  uint pmtpid = _pat->FindPID(progNum);
1320 
1321  // If we have not found the desired program in the PAT and this happens to be
1322  // an SPTS then update the desired program to the one that is present in the PAT.
1323  if (!pmtpid)
1324  {
1325  if (_pat->ProgramCount() == 1)
1326  {
1327  int oldProgNum = progNum;
1328  progNum = _pat->ProgramNumber(0);
1329  LOG(VB_GENERAL, LOG_INFO, LOC +
1330  QString("Update desired program found in SPTS PAT from %1 to %2")
1331  .arg(oldProgNum).arg(progNum));
1332  GetStreamData()->SetDesiredProgram(progNum);
1333  pmtpid = _pat->FindPID(progNum);
1334  }
1335  }
1336  if (!pmtpid)
1337  {
1338  LOG(VB_RECORD, LOG_ERR, LOC +
1339  QString("SetPAT(): Ignoring PAT not containing our desired "
1340  "program (%1)...").arg(progNum));
1341  return;
1342  }
1343 
1344  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPAT(%1 on pid 0x%2)")
1345  .arg(progNum).arg(pmtpid,0,16));
1346 
1348  m_inputPat = new ProgramAssociationTable(*_pat);
1349  delete oldpat;
1350 
1351  // Listen for the other PMTs for faster channel switching
1352  for (uint i = 0; m_inputPat && (i < m_inputPat->ProgramCount()); ++i)
1353  {
1354  uint pmt_pid = m_inputPat->ProgramPID(i);
1355  if (!m_streamData->IsListeningPID(pmt_pid))
1357  }
1358 }
1359 
1360 void DTVRecorder::HandlePMT(uint progNum, const ProgramMapTable *_pmt)
1361 {
1362  QMutexLocker change_lock(&m_pidLock);
1363 
1364  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPMT(%1, %2)").arg(progNum)
1365  .arg(_pmt == nullptr ? "NULL" : "valid"));
1366 
1367 
1368  if ((int)progNum == m_streamData->DesiredProgram())
1369  {
1370  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPMT(%1)").arg(progNum));
1371  ProgramMapTable *oldpmt = m_inputPmt;
1372  m_inputPmt = new ProgramMapTable(*_pmt);
1373 
1374  QString sistandard = GetSIStandard();
1375 
1376  bool has_no_av = true;
1377  for (uint i = 0; i < m_inputPmt->StreamCount() && has_no_av; ++i)
1378  {
1379  has_no_av &= !m_inputPmt->IsVideo(i, sistandard);
1380  has_no_av &= !m_inputPmt->IsAudio(i, sistandard);
1381  }
1382  m_hasNoAV = has_no_av;
1383 
1385  delete oldpmt;
1386  }
1387 }
1388 
1390  bool insert)
1391 {
1392  if (!pat)
1393  {
1394  LOG(VB_RECORD, LOG_ERR, LOC + "HandleSingleProgramPAT(NULL)");
1395  return;
1396  }
1397 
1398  if (!m_ringBuffer)
1399  return;
1400 
1401  uint next_cc = (pat->tsheader()->ContinuityCounter()+1)&0xf;
1402  pat->tsheader()->SetContinuityCounter(next_cc);
1403  pat->GetAsTSPackets(m_scratch, next_cc);
1404 
1405  for (const auto & tspacket : m_scratch)
1406  DTVRecorder::BufferedWrite(tspacket, insert);
1407 }
1408 
1410 {
1411  if (!pmt)
1412  {
1413  LOG(VB_RECORD, LOG_ERR, LOC + "HandleSingleProgramPMT(NULL)");
1414  return;
1415  }
1416 
1417  // We only want to do these checks once per recording
1418  bool seenVideo = (m_primaryVideoCodec != AV_CODEC_ID_NONE);
1419  bool seenAudio = (m_primaryAudioCodec != AV_CODEC_ID_NONE);
1420  uint bestAudioCodec = 0;
1421  // collect stream types for H.264 (MPEG-4 AVC) keyframe detection
1422  for (uint i = 0; i < pmt->StreamCount(); ++i)
1423  {
1424  // We only care about the first identifiable video stream
1425  if (!seenVideo && (m_primaryVideoCodec == AV_CODEC_ID_NONE) &&
1426  StreamID::IsVideo(pmt->StreamType(i)))
1427  {
1428  seenVideo = true; // Ignore other video streams
1429  switch (pmt->StreamType(i))
1430  {
1431  case StreamID::MPEG1Video:
1432  m_primaryVideoCodec = AV_CODEC_ID_MPEG1VIDEO;
1433  break;
1434  case StreamID::MPEG2Video:
1435  m_primaryVideoCodec = AV_CODEC_ID_MPEG2VIDEO;
1436  break;
1437  case StreamID::MPEG4Video:
1438  m_primaryVideoCodec = AV_CODEC_ID_MPEG4;
1439  break;
1440  case StreamID::H264Video:
1441  m_primaryVideoCodec = AV_CODEC_ID_H264;
1442  if (dynamic_cast<AVCParser *>(m_h2645Parser) == nullptr)
1443  {
1444  delete m_h2645Parser;
1445  m_h2645Parser = nullptr;
1446  }
1447  if (m_h2645Parser == nullptr)
1448  {
1449  auto * avcParser = new AVCParser;
1450  if (avcParser != nullptr)
1451  {
1452  m_h2645Parser = reinterpret_cast<H2645Parser *>
1453  (avcParser);
1454  avcParser->use_I_forKeyframes(m_useIForKeyframe);
1455  }
1456  }
1457  break;
1458  case StreamID::H265Video:
1459  LOG(VB_GENERAL, LOG_INFO, LOC + "HEVC detected");
1460  m_primaryVideoCodec = AV_CODEC_ID_H265;
1461  if (dynamic_cast<HEVCParser *>(m_h2645Parser) == nullptr)
1462  {
1463  delete m_h2645Parser;
1464  m_h2645Parser = nullptr;
1465  }
1466  if (m_h2645Parser == nullptr)
1467  {
1468  m_h2645Parser = reinterpret_cast<H2645Parser *>
1469  (new HEVCParser);
1470  }
1471  break;
1473  m_primaryVideoCodec = AV_CODEC_ID_MPEG2VIDEO; // TODO Will it always be MPEG2?
1474  break;
1475  case StreamID::VC1Video:
1476  m_primaryVideoCodec = AV_CODEC_ID_VC1;
1477  break;
1478  default:
1479  break;
1480  }
1481 
1482  if (m_primaryVideoCodec != AV_CODEC_ID_NONE)
1484  }
1485 
1486  // We want the 'best' identifiable audio stream, where 'best' is
1487  // subjective and no-one will likely agree.
1488  // For now it's the 'best' codec, assuming mpeg stream types range
1489  // from worst to best, which it does
1490  if (!seenAudio && StreamID::IsAudio(pmt->StreamType(i)) &&
1491  pmt->StreamType(i) > bestAudioCodec)
1492  {
1493  bestAudioCodec = pmt->StreamType(i);
1494  switch (pmt->StreamType(i))
1495  {
1496  case StreamID::MPEG1Audio: // MPEG-1 Layer 2 (MP2)
1497  case StreamID::MPEG2Audio: // MPEG-2 Part 3 (MP2 Multichannel)
1498  m_primaryAudioCodec = AV_CODEC_ID_MP2;
1499  break;
1501  m_primaryAudioCodec = AV_CODEC_ID_AAC;
1502  break;
1504  m_primaryAudioCodec = AV_CODEC_ID_AAC_LATM;
1505  break;
1506  case StreamID::AC3Audio:
1507  m_primaryAudioCodec = AV_CODEC_ID_AC3;
1508  break;
1509  case StreamID::EAC3Audio:
1510  m_primaryAudioCodec = AV_CODEC_ID_EAC3;
1511  break;
1512  case StreamID::DTSAudio:
1513  m_primaryAudioCodec = AV_CODEC_ID_DTS;
1514  break;
1515  default:
1516  break;
1517  }
1518 
1519  if (m_primaryAudioCodec != AV_CODEC_ID_NONE)
1521  }
1522 
1523 // LOG(VB_GENERAL, LOG_DEBUG, QString("Recording(%1): Stream #%2: %3 ")
1524 // .arg(m_curRecording ? QString::number(m_curRecording->GetRecordingID()) : "")
1525 // .arg(i)
1526 // .arg(StreamID::GetDescription(pmt->StreamType(i))));
1527  m_streamId[pmt->StreamPID(i)] = pmt->StreamType(i);
1528  }
1529 
1530  // If the PCRPID is valid and the PCR is not contained
1531  // in another stream, make sure the PCR stream is not
1532  // discarded (use PrivSec type as dummy 'valid' value)
1533  if (pmt->PCRPID() != 0x1fff && pmt->FindPID(pmt->PCRPID()) == -1)
1535 
1536  if (!m_ringBuffer)
1537  return;
1538 
1539  uint next_cc = (pmt->tsheader()->ContinuityCounter()+1)&0xf;
1540  pmt->tsheader()->SetContinuityCounter(next_cc);
1541  pmt->GetAsTSPackets(m_scratch, next_cc);
1542 
1543  for (const auto & tspacket : m_scratch)
1544  DTVRecorder::BufferedWrite(tspacket, insert);
1545 }
1546 
1548 {
1549  const uint pid = tspacket.PID();
1550 
1551  if (pid != 0x1fff)
1552  m_packetCount.fetchAndAddAcquire(1);
1553 
1554  // Check continuity counter
1555  uint old_cnt = m_continuityCounter[pid];
1556  if ((pid != 0x1fff) && !CheckCC(pid, tspacket.ContinuityCounter()))
1557  {
1558  int v = m_continuityErrorCount.fetchAndAddRelaxed(1) + 1;
1559  double erate = v * 100.0 / m_packetCount.fetchAndAddRelaxed(0);
1560  LOG(VB_RECORD, LOG_WARNING, LOC +
1561  QString("PID 0x%1 discontinuity detected ((%2+1)%16!=%3) %4%")
1562  .arg(pid,0,16).arg(old_cnt,2)
1563  .arg(tspacket.ContinuityCounter(),2)
1564  .arg(erate));
1565  }
1566 
1567  // Only create fake keyframe[s] if there are no audio/video streams
1568  if (m_inputPmt && m_hasNoAV)
1569  {
1570  FindOtherKeyframes(&tspacket);
1571  m_bufferPackets = false;
1572  }
1573  else if (m_recordMptsOnly)
1574  {
1575  /* When recording the full, unfiltered, MPTS, trigger a write
1576  * every 0.5 seconds. Since the packets are unfiltered and
1577  * unprocessed we cannot wait for a keyframe to trigger the
1578  * writes. */
1579 
1580  if (m_framesSeenCount++ == 0)
1582 
1583  if (m_recordMptsTimer.elapsed() > 0.5s)
1584  {
1588  m_recordMptsTimer.addMSecs(-500ms);
1589  }
1590  }
1591  else if (m_streamId[pid] == 0)
1592  {
1593  // Ignore this packet if the PID should be stripped
1594  return true;
1595  }
1596  else
1597  {
1598  // There are audio/video streams. Only write the packet
1599  // if audio/video key-frames have been found
1601  return true;
1602  }
1603 
1604  BufferedWrite(tspacket);
1605 
1606  return true;
1607 }
1608 
1610 {
1611  if (!m_ringBuffer)
1612  return true;
1613 
1614  uint streamType = m_streamId[tspacket.PID()];
1615 
1616  if (tspacket.HasPayload() && tspacket.PayloadStart())
1617  {
1618  if (m_bufferPackets && m_firstKeyframe >= 0 && !m_payloadBuffer.empty())
1619  {
1620  // Flush the buffer
1621  if (m_ringBuffer)
1623  m_payloadBuffer.clear();
1624  }
1625 
1626  // buffer packets until we know if this is a keyframe
1627  m_bufferPackets = true;
1628  }
1629 
1630  // Check for keyframes and count frames
1631  if (streamType == StreamID::H264Video ||
1632  streamType == StreamID::H265Video)
1633  FindH2645Keyframes(&tspacket);
1634  else if (streamType != 0)
1635  FindMPEG2Keyframes(&tspacket);
1636  else
1637  LOG(VB_RECORD, LOG_ERR, LOC +
1638  "ProcessVideoTSPacket: unknown stream type!");
1639 
1640  return ProcessAVTSPacket(tspacket);
1641 }
1642 
1644 {
1645  if (!m_ringBuffer)
1646  return true;
1647 
1648  if (tspacket.HasPayload() && tspacket.PayloadStart())
1649  {
1650  if (m_bufferPackets && m_firstKeyframe >= 0 && !m_payloadBuffer.empty())
1651  {
1652  // Flush the buffer
1653  if (m_ringBuffer)
1655  m_payloadBuffer.clear();
1656  }
1657 
1658  // buffer packets until we know if this is a keyframe
1659  m_bufferPackets = true;
1660  }
1661 
1662  FindAudioKeyframes(&tspacket);
1663  return ProcessAVTSPacket(tspacket);
1664 }
1665 
1668 {
1669  // Sync recording start to first keyframe
1671  {
1672  if (m_bufferPackets)
1673  BufferedWrite(tspacket);
1674  return true;
1675  }
1676 
1677  const uint pid = tspacket.PID();
1678 
1679  if (pid != 0x1fff)
1680  m_packetCount.fetchAndAddAcquire(1);
1681 
1682  // Check continuity counter
1683  uint old_cnt = m_continuityCounter[pid];
1684  if ((pid != 0x1fff) && !CheckCC(pid, tspacket.ContinuityCounter()))
1685  {
1686  int v = m_continuityErrorCount.fetchAndAddRelaxed(1) + 1;
1687  double erate = v * 100.0 / m_packetCount.fetchAndAddRelaxed(0);
1688  LOG(VB_RECORD, LOG_WARNING, LOC +
1689  QString("A/V PID 0x%1 discontinuity detected ((%2+1)%16!=%3) %4%")
1690  .arg(pid,0,16).arg(old_cnt).arg(tspacket.ContinuityCounter())
1691  .arg(erate,5,'f',2));
1692  }
1693 
1694  if (!(m_pidStatus[pid] & kPayloadStartSeen))
1695  {
1697  LOG(VB_RECORD, LOG_INFO, LOC +
1698  QString("PID 0x%1 Found Payload Start").arg(pid,0,16));
1699  }
1700 
1701  BufferedWrite(tspacket);
1702 
1703  return true;
1704 }
1705 
1707 {
1709  recq->AddTSStatistics(
1710  m_continuityErrorCount.fetchAndAddRelaxed(0),
1711  m_packetCount.fetchAndAddRelaxed(0));
1712  return recq;
1713 }
1714 
1715 /* vim: set expandtab tabstop=4 shiftwidth=4: */
bytereader.h
DTVRecorder::FindPSKeyFrames
void FindPSKeyFrames(const uint8_t *buffer, uint len) override
Definition: dtvrecorder.cpp:1122
DTVRecorder::m_pesSynced
bool m_pesSynced
Definition: dtvrecorder.h:150
DTVRecorder::HandleSingleProgramPMT
void HandleSingleProgramPMT(ProgramMapTable *pmt, bool insert) override
Definition: dtvrecorder.cpp:1409
DTVRecorder::m_pidStatus
std::array< uint8_t, 0x1fff+1 > m_pidStatus
Definition: dtvrecorder.h:182
AVCParser
Definition: AVCParser.h:30
RecorderBase::m_timeOfLatestDataTimer
MythTimer m_timeOfLatestDataTimer
Definition: recorderbase.h:358
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:256
PESStreamID::MPEG2ExtensionStartCode
@ MPEG2ExtensionStartCode
Followed by an extension byte, not documented here.
Definition: mpegtables.h:59
PESStreamID::MPEGVideoStreamEnd
@ MPEGVideoStreamEnd
Last MPEG-1/2 video stream (w/ext hdr)
Definition: mpegtables.h:82
StreamID::OpenCableVideo
@ OpenCableVideo
Always MPEG-2??
Definition: mpegtables.h:120
SCAN_t
SCAN_t
Definition: scantype.h:6
RecorderBase::m_timeOfLatestData
QDateTime m_timeOfLatestData
Definition: recorderbase.h:357
DTVRecorder::m_inputPmt
ProgramMapTable * m_inputPmt
PMT on input side.
Definition: dtvrecorder.h:174
RecorderBase::VideoCodecChange
void VideoCodecChange(AVCodecID vCodec)
Note a change in video codec.
Definition: recorderbase.cpp:838
dtvrecorder.h
PESStreamID::MPEGAudioStreamBegin
@ MPEGAudioStreamBegin
First MPEG-1/2 audio stream (w/ext hdr)
Definition: mpegtables.h:76
kExtractPTS
@ kExtractPTS
Definition: dtvrecorder.cpp:332
DTVRecorder::CheckCC
bool CheckCC(uint pid, uint new_cnt)
Definition: dtvrecorder.h:225
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:166
DTVRecorder::m_framesWrittenCount
unsigned long long m_framesWrittenCount
Definition: dtvrecorder.h:196
DTVRecorder::m_payloadBuffer
std::vector< unsigned char > m_payloadBuffer
Definition: dtvrecorder.h:167
MPEGStreamData::AddMPEGSPListener
void AddMPEGSPListener(MPEGSingleProgramStreamListener *val)
Definition: mpegstreamdata.cpp:1721
HEVCParser
Definition: HEVCParser.h:31
RecorderBase::m_statisticsLock
QMutex m_statisticsLock
Definition: recorderbase.h:352
DTVRecorder::kMaxKeyFrameDistance
static const uint kMaxKeyFrameDistance
If the number of regular frames detected since the last detected keyframe exceeds this value,...
Definition: dtvrecorder.h:221
DTVRecorder::ResetForNewFile
void ResetForNewFile(void) override
Definition: dtvrecorder.cpp:140
DTVRecorder::m_audioTimer
QElapsedTimer m_audioTimer
Definition: dtvrecorder.h:135
RecordingInfo
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:35
DTVRecorder::m_inputPat
ProgramAssociationTable * m_inputPat
PAT on input side.
Definition: dtvrecorder.h:172
DTVRecorder::m_startCode
uint32_t m_startCode
Definition: dtvrecorder.h:136
SCAN_t::INTERLACED
@ INTERLACED
MythMediaBuffer::GetWritePosition
long long GetWritePosition(void) const
Returns how far into a ThreadedFileWriter file we have written.
Definition: mythmediabuffer.cpp:1796
DTVRecorder::m_scanType
SCAN_t m_scanType
Definition: dtvrecorder.h:209
SCAN_t::UNKNOWN_SCAN
@ UNKNOWN_SCAN
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:1609
StreamID::MPEG2AudioAmd1
@ MPEG2AudioAmd1
ISO 13818-3/AMD-1 Audio using LATM syntax.
Definition: mpegtables.h:127
DTVRecorder::m_packetCount
QAtomicInt m_packetCount
Definition: dtvrecorder.h:193
PESPacket::tsheader
const TSHeader * tsheader() const
Definition: pespacket.h:92
TSHeader::PID
unsigned int PID(void) const
Definition: tspacket.h:91
DTVRecorder::FindOtherKeyframes
bool FindOtherKeyframes(const TSPacket *tspacket)
Non-Audio/Video data.
Definition: dtvrecorder.cpp:806
DTVRecorder::m_videoBytesRemaining
unsigned int m_videoBytesRemaining
Definition: dtvrecorder.h:142
ProgramMapTable::FindPID
int FindPID(uint pid) const
Locates stream index of pid.
Definition: mpegtables.h: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:1308
DTVRecorder::m_lastGopSeen
unsigned long long m_lastGopSeen
Definition: dtvrecorder.h:138
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:306
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:1634
atscstreamdata.h
DTVRecorder::m_tsCount
std::array< uint64_t, 256 > m_tsCount
Definition: dtvrecorder.h:189
RecorderBase::m_videoWidth
uint m_videoWidth
Definition: recorderbase.h:309
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
DTVRecorder::m_framesSeenCount
unsigned long long m_framesSeenCount
Definition: dtvrecorder.h:195
RecordingQuality::IsDamaged
bool IsDamaged(void) const
Definition: recordingquality.cpp:108
mythsystemevent.h
ProgramAssociationTable::ProgramCount
uint ProgramCount(void) const
Definition: mpegtables.h:638
kSingleRecord
@ kSingleRecord
Definition: recordingtypes.h:22
DTVRecorder::GetSIStandard
virtual QString GetSIStandard(void) const
Definition: dtvrecorder.h:124
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:125
DTVRecorder::m_tsFirst
std::array< int64_t, 256 > m_tsFirst
Definition: dtvrecorder.h:191
DTVRecorder::m_recordingType
QString m_recordingType
Definition: dtvrecorder.h:131
RecorderBase::m_timeOfLatestDataPacketInterval
QAtomicInt m_timeOfLatestDataPacketInterval
Definition: recorderbase.h:356
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
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
RecorderBase::m_frameRate
MythAVRational m_frameRate
Definition: recorderbase.h:310
DTVRecorder::m_continuityCounter
std::array< uint8_t, 0x1fff+1 > m_continuityCounter
Definition: dtvrecorder.h:183
TSHeader::ContinuityCounter
unsigned int ContinuityCounter(void) const
Definition: tspacket.h:107
TSHeader::SetContinuityCounter
void SetContinuityCounter(unsigned int cc)
Definition: tspacket.h:168
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:222
MPEGStreamData::IsListeningPID
virtual bool IsListeningPID(uint pid) const
Definition: mpegstreamdata.cpp:1113
RecorderBase::m_curRecording
RecordingInfo * m_curRecording
Definition: recorderbase.h:312
StreamID::MPEG2AACAudio
@ MPEG2AACAudio
ISO 13818-7 Audio w/ADTS syntax.
Definition: mpegtables.h:126
RecorderBase::m_durationMapDelta
frm_pos_map_t m_durationMapDelta
Definition: recorderbase.h:339
StreamID::PrivSec
@ PrivSec
ISO 13818-1 private tables & ITU H.222.0.
Definition: mpegtables.h:146
DTVRecorder::m_hasNoAV
bool m_hasNoAV
Definition: dtvrecorder.h:175
RecorderBase::SetStrOption
void SetStrOption(RecordingProfile *profile, const QString &name)
Convenience function used to set QString options from a profile.
Definition: recorderbase.cpp:215
DTVRecorder::m_totalDuration
double m_totalDuration
Total milliseconds that have passed since the start of the recording.
Definition: dtvrecorder.h:198
DTVRecorder::HandleSingleProgramPAT
void HandleSingleProgramPAT(ProgramAssociationTable *pat, bool insert) override
Definition: dtvrecorder.cpp:1389
programinfo.h
ATSCStreamData::SetDesiredChannel
void SetDesiredChannel(int major, int minor)
Definition: atscstreamdata.cpp:57
PESStreamID::SequenceStartCode
@ SequenceStartCode
Sequence (SEQ) start code contains frame size, aspect ratio and fps.
Definition: mpegtables.h:56
mythlogging.h
ProgramInfo::GetRecordingStatus
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:451
DTVRecorder::ProcessAVTSPacket
bool ProcessAVTSPacket(const TSPacket &tspacket)
Common code for processing either audio or video packets.
Definition: dtvrecorder.cpp:1667
ProgramAssociationTable::ProgramNumber
uint ProgramNumber(uint i) const
Definition: mpegtables.h:645
hardwareprofile.scan.profile
profile
Definition: scan.py:97
DTVRecorder::m_recordMptsOnly
bool m_recordMptsOnly
Definition: dtvrecorder.h:179
StreamID::VC1Video
@ VC1Video
SMPTE 421M video codec (aka VC1) in Blu-Ray.
Definition: mpegtables.h:121
DTVRecorder::m_lastKeyframeSeen
unsigned long long m_lastKeyframeSeen
Definition: dtvrecorder.h:140
RecorderBase::AspectChange
void AspectChange(uint aspect, long long frame)
Note a change in aspect ratio in the recordedmark table.
Definition: recorderbase.cpp:750
DTVRecorder::m_progressiveSequence
int m_progressiveSequence
Definition: dtvrecorder.h:146
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:832
DTVRecorder::m_tdTickCount
uint64_t m_tdTickCount
Count of the number of equivalent interlaced fields that have passed since m_tdBase.
Definition: dtvrecorder.h:207
formatMPEG2_TS
@ formatMPEG2_TS
Definition: recordingfile.h:16
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:359
RecorderBase::ClearStatistics
virtual void ClearStatistics(void)
Definition: recorderbase.cpp:431
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
H2645Parser::getFrameRate
virtual MythAVRational getFrameRate() const =0
DTVRecorder::m_firstKeyframe
int m_firstKeyframe
Definition: dtvrecorder.h:137
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:308
RecorderBase::GetRecordingQuality
virtual RecordingQuality * GetRecordingQuality(const RecordingInfo *ri) const
Returns a report about the current recordings quality.
Definition: recorderbase.cpp:513
DTVRecorder::DTVRecorder
DTVRecorder(TVRec *rec)
Definition: dtvrecorder.cpp:51
RecStatus::Failing
@ Failing
Definition: recordingstatus.h:18
MPEGStreamData
Encapsulates data about MPEG stream and emits events for each table.
Definition: mpegstreamdata.h:85
MythTimer::restart
std::chrono::milliseconds restart(void)
Returns milliseconds elapsed since last start() or restart() and resets the count.
Definition: mythtimer.cpp:62
H2645Parser::onFrameStart
bool onFrameStart(void) const
Definition: H2645Parser.h: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:801
MythAVRational::toString
QString toString() const
Definition: mythavrational.h:37
DTVRecorder::FindAudioKeyframes
bool FindAudioKeyframes(const TSPacket *tspacket)
Definition: dtvrecorder.cpp:766
DVBStreamData
Definition: dvbstreamdata.h:33
RecorderBase::AudioCodecChange
void AudioCodecChange(AVCodecID aCodec)
Note a change in audio codec.
Definition: recorderbase.cpp:847
DTVRecorder::m_h2645Parser
H2645Parser * m_h2645Parser
Definition: dtvrecorder.h:152
DTVRecorder::HandleH2645Keyframe
void HandleH2645Keyframe(void)
This save the current frame to the position maps and handles ringbuffer switching.
Definition: dtvrecorder.cpp:1091
DTVRecorder::m_seenSps
bool m_seenSps
Definition: dtvrecorder.h:151
StreamID::DTSAudio
@ DTSAudio
Definition: mpegtables.h:130
RecorderBase::m_videoFrameRate
double m_videoFrameRate
Definition: recorderbase.h:304
DTVRecorder::GetStreamData
MPEGStreamData * GetStreamData(void) const
Definition: dtvrecorder.h:58
RecorderBase::m_durationMap
frm_pos_map_t m_durationMap
Definition: recorderbase.h:338
mpegtables.h
StreamID::H264Video
@ H264Video
ISO 14492-10 & ITU H.264 (aka MPEG-4-AVC)
Definition: mpegtables.h:118
DTVRecorder::m_streamId
std::array< uint8_t, 0x1fff+1 > m_streamId
Definition: dtvrecorder.h:181
RecorderBase::SetTotalFrames
void SetTotalFrames(uint64_t total_frames)
Note the total frames in the recordedmark table.
Definition: recorderbase.cpp:862
StreamID::H265Video
@ H265Video
ISO 23008-2 & ITU H.265 (aka HEVC, Ultra HD)
Definition: mpegtables.h:119
RecordingQuality::AddTSStatistics
void AddTSStatistics(int continuity_error_count, int packet_count)
Definition: recordingquality.cpp:88
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:1547
PESStreamID::GOPStartCode
@ GOPStartCode
Group of Pictures (GOP) start code.
Definition: mpegtables.h:65
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:180
MythAVRational::isNonzero
bool isNonzero() const
Definition: mythavrational.h:42
PESStreamID::MPEGVideoStreamBegin
@ MPEGVideoStreamBegin
First MPEG-1/2 video stream (w/ext hdr)
Definition: mpegtables.h:80
DTVRecorder::m_tdBase
double m_tdBase
Milliseconds from the start to m_tdTickCount = 0.
Definition: dtvrecorder.h:200
RecorderBase::m_positionMap
frm_pos_map_t m_positionMap
Definition: recorderbase.h:336
DTVRecorder::m_tdTickFramerate
MythAVRational m_tdTickFramerate
Definition: dtvrecorder.h:208
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:296
RecorderBase::SetDuration
void SetDuration(std::chrono::milliseconds duration)
Note the total duration in the recordedmark table.
Definition: recorderbase.cpp:856
ProgramAssociationTable
The Program Association Table lists all the programs in a stream, and is always found on PID 0.
Definition: mpegtables.h:617
StreamID::MPEG4Video
@ MPEG4Video
ISO 14492-2 (aka MPEG-4)
Definition: mpegtables.h:117
RecorderBase::FrameRateChange
void FrameRateChange(uint framerate, uint64_t frame)
Note a change in video frame rate in the recordedmark table.
Definition: recorderbase.cpp:817
DTVRecorder::m_lastSeqSeen
unsigned long long m_lastSeqSeen
Definition: dtvrecorder.h:139
DTVRecorder::~DTVRecorder
~DTVRecorder() override
Definition: dtvrecorder.cpp:66
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:916
ProgramMapTable::IsAudio
bool IsAudio(uint i, const QString &sistandard) const
Returns true iff the stream at index i is an audio stream.
Definition: mpegtables.cpp: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:442
MARK_GOP_BYFRAME
@ MARK_GOP_BYFRAME
Definition: programtypes.h:63
AVCParser.h
RecorderBase::m_positionMapDelta
frm_pos_map_t m_positionMapDelta
Definition: recorderbase.h:337
StreamID::MPEG1Audio
@ MPEG1Audio
ISO 11172-3.
Definition: mpegtables.h:124
DTVRecorder::m_repeatPict
int m_repeatPict
Definition: dtvrecorder.h:147
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:141
RecorderBase::StopRecording
virtual void StopRecording(void)
StopRecording() signals to the recorder that it should stop recording and exit cleanly.
Definition: recorderbase.cpp:230
DTVRecorder::m_hasWrittenOtherKeyframe
bool m_hasWrittenOtherKeyframe
Definition: dtvrecorder.h:157
MPEGStreamData::AddMPEGListener
void AddMPEGListener(MPEGStreamListener *val)
Definition: mpegstreamdata.cpp:1629
dvbstreamdata.h
DTVRecorder::m_otherBytesRemaining
unsigned int m_otherBytesRemaining
Definition: dtvrecorder.h:143
MythMediaBuffer::WriterFlush
void WriterFlush(void)
Calls ThreadedFileWriter::Flush(void)
Definition: mythmediabuffer.cpp:1707
RecorderBase::m_ringBuffer
MythMediaBuffer * m_ringBuffer
Definition: recorderbase.h:293
MARK_DURATION_MS
@ MARK_DURATION_MS
Definition: programtypes.h:73
DTVRecorder::ProcessAudioTSPacket
bool ProcessAudioTSPacket(const TSPacket &tspacket) override
Definition: dtvrecorder.cpp:1643
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
DTVRecorder::UpdateFramesWritten
void UpdateFramesWritten(void)
Definition: dtvrecorder.cpp:733
kPIDPriorityLow
@ kPIDPriorityLow
Definition: mpegstreamdata.h:79
DTVRecorder::m_waitForKeyframeOption
bool m_waitForKeyframeOption
Wait for the a GOP/SEQ-start before sending data.
Definition: dtvrecorder.h:155
MythAVRational::toDouble
double toDouble() const
Definition: mythavrational.h:36
RecordingGap
Definition: recordingquality.h:14
RecorderBase::SetRecordingStatus
virtual void SetRecordingStatus(RecStatus::Type status, const QString &file, int line)
Definition: recorderbase.cpp:401
DTVRecorder::m_streamData
MPEGStreamData * m_streamData
Definition: dtvrecorder.h:163
RecorderBase::m_timeOfFirstData
QDateTime m_timeOfFirstData
Definition: recorderbase.h:354
StreamID::MPEG2Audio
@ MPEG2Audio
ISO 13818-3.
Definition: mpegtables.h:125
extract_timestamp
static int64_t extract_timestamp(const uint8_t *bufptr, int bytes_left, int pts_or_dts)
Definition: dtvrecorder.cpp:333
MythAVRational::invert
MythAVRational invert() const
Definition: mythavrational.h:57
MPEGStreamData::DesiredProgram
int DesiredProgram(void) const
Definition: mpegstreamdata.h:260
DTVRecorder::m_error
QString m_error
non-empty iff irrecoverable recording error detected
Definition: dtvrecorder.h:161
DTVRecorder::m_useIForKeyframe
bool m_useIForKeyframe
Definition: dtvrecorder.h:215
ProgramInfo::ClearPositionMap
void ClearPositionMap(MarkTypes type) const
Definition: programinfo.cpp:3830
StreamID::AC3Audio
@ AC3Audio
A/53 Part 3:2009 6.7.1.
Definition: mpegtables.h:128
H2645Parser::getFieldType
virtual field_type getFieldType(void) const =0
RecorderBase::m_timeOfFirstDataIsSet
QAtomicInt m_timeOfFirstDataIsSet
Definition: recorderbase.h:353
DTVRecorder::m_recordMpts
bool m_recordMpts
Definition: dtvrecorder.h:178
RecorderBase::m_positionMapLock
QMutex m_positionMapLock
Definition: recorderbase.h:335
frameRateMap
static const std::array< const MythAVRational, 16 > frameRateMap
Definition: dtvrecorder.cpp:370
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:51
DTVRecorder::HandlePMT
void HandlePMT(uint progNum, const ProgramMapTable *_pmt) override
Definition: dtvrecorder.cpp:1360
RecorderBase::AspectRatio
AspectRatio
Definition: recorderbase.h:221
H2645Parser::FIELD_BOTTOM
@ FIELD_BOTTOM
Definition: H2645Parser.h:46
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
DTVRecorder::m_continuityErrorCount
QAtomicInt m_continuityErrorCount
Definition: dtvrecorder.h:194
H2645Parser::onKeyFrameStart
bool onKeyFrameStart(void) const
Definition: H2645Parser.h:63
RecorderBase::m_primaryAudioCodec
AVCodecID m_primaryAudioCodec
Definition: recorderbase.h:298
kExtractDTS
@ kExtractDTS
Definition: dtvrecorder.cpp:332
tv_rec.h
H2645Parser::aspectRatio
uint aspectRatio(void) const
Computes aspect ratio from picture size and sample aspect ratio.
Definition: H2645Parser.cpp:387
ts_to_qdatetime
static QDateTime ts_to_qdatetime(uint64_t pts, uint64_t pts_first, const QDateTime &pts_first_dt)
Definition: dtvrecorder.cpp:361
RecorderBase::SetOption
virtual void SetOption(const QString &name, const QString &value)
Set an specific option.
Definition: recorderbase.cpp:155
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:297
DTVRecorder::m_usePts
bool m_usePts
Definition: dtvrecorder.h:188
DTVRecorder::m_scratch
std::vector< TSPacket > m_scratch
Definition: dtvrecorder.h:184
DTVRecorder::m_pesTimer
MythTimer m_pesTimer
Definition: dtvrecorder.h:134
MythAVRational
C++ wrapper for FFmpeg libavutil AVRational.
Definition: mythavrational.h:14
StreamID::MPEG2Video
@ MPEG2Video
ISO 13818-2 & ITU H.262 (aka MPEG-2)
Definition: mpegtables.h:116
PESPacket::GetAsTSPackets
void GetAsTSPackets(std::vector< TSPacket > &output, uint cc) const
Returns payload only PESPacket as series of TSPackets.
Definition: pespacket.cpp:133
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
PESStreamID::PaddingStream
@ PaddingStream
Definition: mpegtables.h:72
H2645Parser
Definition: H2645Parser.h:39
H2645Parser::GetTimeScale
uint32_t GetTimeScale(void) const
Definition: H2645Parser.h:80
DTVRecorder::m_minimumRecordingQuality
int m_minimumRecordingQuality
Definition: dtvrecorder.h:187
RecordingProfile
Definition: recordingprofile.h:41
PESStreamID::PictureStartCode
@ PictureStartCode
Definition: mpegtables.h:49
RecorderBase::kTimeOfLatestDataIntervalTarget
static constexpr std::chrono::milliseconds kTimeOfLatestDataIntervalTarget
timeOfLatest update interval target in milliseconds.
Definition: recorderbase.h:361
DTVRecorder::m_pidLock
QRecursiveMutex m_pidLock
Definition: dtvrecorder.h:170
DTVRecorder::FindH2645Keyframes
bool FindH2645Keyframes(const TSPacket *tspacket)
Definition: dtvrecorder.cpp:872
DTVRecorder::m_musicChoice
bool m_musicChoice
Definition: dtvrecorder.h:213
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::MPEG1Video
@ MPEG1Video
ISO 11172-2 (aka MPEG-1)
Definition: mpegtables.h:115
RecorderBase::CheckForRingBufferSwitch
virtual bool CheckForRingBufferSwitch(void)
If requested, switch to new RingBuffer/ProgramInfo objects.
Definition: recorderbase.cpp:357
ProgramMapTable::StreamPID
uint StreamPID(uint i) const
Definition: mpegtables.h:743
PESStreamID::MPEGAudioStreamEnd
@ MPEGAudioStreamEnd
Last MPEG-1/2 audio stream (w/ext hdr)
Definition: mpegtables.h:78
DTVRecorder::GetRecordingQuality
RecordingQuality * GetRecordingQuality(const RecordingInfo *r) const override
Returns a report about the current recordings quality.
Definition: dtvrecorder.cpp:1706
TSHeader::data
const unsigned char * data(void) const
Definition: tspacket.h:172
RecorderBase::m_timeOfLatestDataCount
QAtomicInt m_timeOfLatestDataCount
Definition: recorderbase.h:355
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:74
StreamID::EAC3Audio
@ EAC3Audio
A/53 Part 3:2009 6.7.3.
Definition: mpegtables.h:129
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:832
DTVRecorder::m_tsLast
std::array< int64_t, 256 > m_tsLast
Definition: dtvrecorder.h:190
DTVRecorder::m_tsFirstDt
std::array< QDateTime, 256 > m_tsFirstDt
Definition: dtvrecorder.h:192
uint
unsigned int uint
Definition: freesurround.h:24
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:902
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
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:205