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 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+1] != 0x00 ||
924  tspacket->data()[i+2] != 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  i += 3;
935 
936  m_pesTimer.stop();
937 
938  // bounds check
939  if (i + 5 >= TSPacket::kSize)
940  {
941  LOG(VB_GENERAL, LOG_ERR, LOC +
942  "PES packet headers overflow to next TS packet, "
943  "aborting keyframe search");
944  break;
945  }
946 
947  // now we need to compute where the PES payload begins
948  // skip past the stream_id (+1)
949  // the next two bytes are the PES packet length (+2)
950  // after that, one byte of PES packet control bits (+1)
951  // after that, one byte of PES header flags bits (+1)
952  // and finally, one byte for the PES header length
953  const unsigned char pes_header_length = tspacket->data()[i + 5];
954 
955  // bounds check
956  if ((i + 6 + pes_header_length) >= TSPacket::kSize)
957  {
958  LOG(VB_GENERAL, LOG_ERR, LOC +
959  "PES packet headers overflow to next TS packet, "
960  "aborting keyframe search");
961  break;
962  }
963 
964  // we now know where the PES payload is
965  // normally, we should have used 6, but use 5 because the for
966  // loop will bump i
967  i += 5 + pes_header_length;
968  m_pesSynced = true;
969 
970 #if 0
971  LOG(VB_RECORD, LOG_DEBUG, LOC + "PES synced");
972 #endif
973  continue;
974  }
975 
976  if (!m_pesSynced)
977  break;
978 
979  // scan the NAL units
980  uint32_t bytes_used = m_h2645Parser->addBytes
981  (tspacket->data() + i, TSPacket::kSize - i,
983  i += (bytes_used - 1);
984 
986  {
987  if (m_h2645Parser->onFrameStart() &&
989  {
990  hasKeyFrame = m_h2645Parser->onKeyFrameStart();
991  hasFrame = true;
992  m_seenSps |= hasKeyFrame;
993 
994  width = m_h2645Parser->pictureWidth();
995  height = m_h2645Parser->pictureHeight();
996  aspectRatio = m_h2645Parser->aspectRatio();
997  scantype = m_h2645Parser->GetScanType();
998  m_h2645Parser->getFrameRate(frameRate);
999  }
1000  }
1001  } // for (; i < TSPacket::kSize; ++i)
1002 
1003  // If it has been more than 511 frames since the last keyframe,
1004  // pretend we have one.
1005  if (hasFrame && !hasKeyFrame &&
1007  {
1008  hasKeyFrame = true;
1009  LOG(VB_RECORD, LOG_WARNING, LOC +
1010  QString("FindH2645Keyframes: %1 frames without a keyframe.")
1012  }
1013 
1014  // m_bufferPackets will only be true if a payload start has been seen
1015  if (hasKeyFrame && (m_bufferPackets || m_firstKeyframe >= 0))
1016  {
1017  LOG(VB_RECORD, LOG_DEBUG, LOC + QString
1018  ("Keyframe @ %1 + %2 = %3 AU %4")
1020  .arg(m_payloadBuffer.size())
1023 
1026  }
1027 
1028  if (hasFrame)
1029  {
1030  LOG(VB_RECORD, LOG_DEBUG, LOC + QString
1031  ("Frame @ %1 + %2 = %3 AU %4")
1033  .arg(m_payloadBuffer.size())
1036 
1037  m_bufferPackets = false; // We now know if this is a keyframe
1041  else
1042  {
1043  /* Found a frame that is not a keyframe, and we want to
1044  * start on a keyframe */
1045  m_payloadBuffer.clear();
1046  }
1047  }
1048 
1049  if ((aspectRatio > 0) && (aspectRatio != m_videoAspect))
1050  {
1051  m_videoAspect = aspectRatio;
1053  }
1054 
1055  if (height && width && (height != m_videoHeight || m_videoWidth != width))
1056  {
1057  m_videoHeight = height;
1058  m_videoWidth = width;
1059  ResolutionChange(width, height, m_framesWrittenCount);
1060  }
1061 
1062  if (frameRate.isNonzero() && frameRate != m_frameRate)
1063  {
1064  LOG(VB_RECORD, LOG_INFO, LOC +
1065  QString("FindH2645Keyframes: timescale: %1, tick: %2, framerate: %3")
1066  .arg( m_h2645Parser->GetTimeScale() )
1067  .arg( m_h2645Parser->GetUnitsInTick() )
1068  .arg( frameRate.toDouble() * 1000 ) );
1069  m_frameRate = frameRate;
1070  FrameRateChange(frameRate.toDouble() * 1000, m_framesWrittenCount);
1071  }
1072 
1073  if (scantype != SCAN_t::UNKNOWN_SCAN && scantype != m_scanType)
1074  {
1075  LOG(VB_RECORD, LOG_INFO, LOC +
1076  QString("FindH2645Keyframes: scan type: %1")
1077  .arg(scantype == SCAN_t::INTERLACED ?
1078  "Interlaced" : "Progressive"));
1079  m_scanType = scantype;
1081  }
1082 
1083  return m_seenSps;
1084 }
1085 
1091 {
1092  // Perform ringbuffer switch if needed.
1094 
1095  uint64_t startpos = 0;
1096  uint64_t frameNum = m_framesWrittenCount;
1097 
1098  if (m_firstKeyframe < 0)
1099  {
1100  m_firstKeyframe = frameNum;
1101  startpos = 0;
1102  SendMythSystemRecEvent("REC_STARTED_WRITING", m_curRecording);
1103  }
1104  else
1105  {
1106  startpos = m_h2645Parser->keyframeAUstreamOffset();
1107  }
1108 
1109  // Add key frame to position map
1110  m_positionMapLock.lock();
1111  if (!m_positionMap.contains(frameNum))
1112  {
1113  m_positionMapDelta[frameNum] = startpos;
1114  m_positionMap[frameNum] = startpos;
1115  m_durationMap[frameNum] = llround(m_totalDuration);
1116  m_durationMapDelta[frameNum] = llround(m_totalDuration);
1117  }
1118  m_positionMapLock.unlock();
1119 }
1120 
1121 void DTVRecorder::FindPSKeyFrames(const uint8_t *buffer, uint len)
1122 {
1123  const uint maxKFD = kMaxKeyFrameDistance;
1124 
1125  const uint8_t *bufstart = buffer;
1126  const uint8_t *bufptr = buffer;
1127  const uint8_t *bufend = buffer + len;
1128 
1129  uint aspectRatio = 0;
1130  uint height = 0;
1131  uint width = 0;
1132  FrameRate frameRate(0);
1133 
1135  while (bufptr + skip < bufend)
1136  {
1137  bool hasFrame = false;
1138  bool hasKeyFrame = false;
1139 
1140  const uint8_t *tmp = bufptr;
1141  bufptr = ByteReader::find_start_code_truncated(bufptr + skip, bufend, &m_startCode);
1144  m_videoBytesRemaining -= std::min(
1145  (uint)(bufptr - tmp), m_videoBytesRemaining);
1146 
1148  continue;
1149 
1150  // NOTE: Length may be zero for packets that only contain bytes from
1151  // video elementary streams in TS packets. 13818-1:2000 2.4.3.7
1152  int pes_packet_length = -1;
1153  if ((bufend - bufptr) >= 2)
1154  pes_packet_length = ((bufptr[0]<<8) | bufptr[1]) + 2 + 6;
1155 
1156  const int stream_id = m_startCode & 0x000000ff;
1158  {
1159  if (PESStreamID::PictureStartCode == stream_id)
1160  { // pes_packet_length is meaningless
1161  pes_packet_length = -1;
1162  if (bufend-bufptr >= 4)
1163  {
1164  uint frmtypei = (bufptr[1]>>3) & 0x7;
1165  if ((1 <= frmtypei) && (frmtypei <= 5))
1166  hasFrame = true;
1167  }
1168  else
1169  {
1170  hasFrame = true;
1171  }
1172  }
1173  else if (PESStreamID::GOPStartCode == stream_id)
1174  { // pes_packet_length is meaningless
1175  pes_packet_length = -1;
1177  hasKeyFrame = true;
1178  }
1179  else if (PESStreamID::SequenceStartCode == stream_id)
1180  { // pes_packet_length is meaningless
1181  pes_packet_length = -1;
1183  hasKeyFrame |= (m_lastGopSeen + maxKFD)<m_framesSeenCount;
1184 
1185  // Look for aspectRatio changes and store them in the database
1186  aspectRatio = (bufptr[3] >> 4);
1187 
1188  // Get resolution
1189  height = ((bufptr[1] & 0xf) << 8) | bufptr[2];
1190  width = (bufptr[0] <<4) | (bufptr[1]>>4);
1191 
1192  frameRate = frameRateMap[(bufptr[3] & 0x0000000f)];
1193  }
1194  }
1195  else if (!m_audioBytesRemaining)
1196  {
1197  if ((stream_id >= PESStreamID::MPEGVideoStreamBegin) &&
1198  (stream_id <= PESStreamID::MPEGVideoStreamEnd))
1199  { // ok-dvdinfo
1200  m_videoBytesRemaining = std::max(0, pes_packet_length);
1201  }
1202  else if ((stream_id >= PESStreamID::MPEGAudioStreamBegin) &&
1203  (stream_id <= PESStreamID::MPEGAudioStreamEnd))
1204  { // ok-dvdinfo
1205  m_audioBytesRemaining = std::max(0, pes_packet_length);
1206  }
1207  }
1208 
1209  if (PESStreamID::PaddingStream == stream_id)
1210  { // ok-dvdinfo
1211  m_otherBytesRemaining = std::max(0, pes_packet_length);
1212  }
1213 
1214  m_startCode = 0xffffffff; // reset start code
1215 
1216  if (hasFrame && !hasKeyFrame)
1217  {
1218  // If we have seen kMaxKeyFrameDistance frames since the
1219  // last GOP or SEQ stream_id, then pretend this picture
1220  // is a keyframe. We may get artifacts but at least
1221  // we will be able to skip frames.
1222  hasKeyFrame = ((m_framesSeenCount & 0xf) == 0U);
1223  hasKeyFrame &= (m_lastGopSeen + maxKFD) < m_framesSeenCount;
1224  hasKeyFrame &= (m_lastSeqSeen + maxKFD) < m_framesSeenCount;
1225  }
1226 
1227  if (hasFrame)
1228  {
1232  }
1233 
1234  if (hasKeyFrame)
1235  {
1237  HandleKeyframe((int64_t)m_payloadBuffer.size() - (bufptr - bufstart));
1238  }
1239 
1240  if ((aspectRatio > 0) && (aspectRatio != m_videoAspect))
1241  {
1242  m_videoAspect = aspectRatio;
1244  }
1245 
1246  if (height && width &&
1247  (height != m_videoHeight || m_videoWidth != width))
1248  {
1249  m_videoHeight = height;
1250  m_videoWidth = width;
1251  ResolutionChange(width, height, m_framesWrittenCount);
1252  }
1253 
1254  if (frameRate.isNonzero() && frameRate != m_frameRate)
1255  {
1256  m_frameRate = frameRate;
1257  LOG(VB_RECORD, LOG_INFO, LOC +
1258  QString("FindPSKeyFrames: frame rate = %1")
1259  .arg(frameRate.toDouble() * 1000));
1260  FrameRateChange(frameRate.toDouble() * 1000, m_framesWrittenCount);
1261  }
1262 
1263  if (hasKeyFrame || hasFrame)
1264  {
1265  // We are free to write the packet, but if we have
1266  // buffered packet[s] we have to write them first...
1267  if (!m_payloadBuffer.empty())
1268  {
1269  if (m_ringBuffer)
1270  {
1272  (m_payloadBuffer).data(), m_payloadBuffer.size());
1273  }
1274  m_payloadBuffer.clear();
1275  }
1276 
1277  if (m_ringBuffer)
1278  m_ringBuffer->Write(bufstart, (bufptr - bufstart));
1279 
1280  bufstart = bufptr;
1281  }
1282 
1283  skip = std::max(m_audioBytesRemaining, m_otherBytesRemaining);
1284  }
1285 
1286  int bytes_skipped = bufend - bufptr;
1287  if (bytes_skipped > 0)
1288  {
1289  m_audioBytesRemaining -= std::min(
1290  (uint)bytes_skipped, m_audioBytesRemaining);
1291  m_videoBytesRemaining -= std::min(
1292  (uint)bytes_skipped, m_videoBytesRemaining);
1293  m_otherBytesRemaining -= std::min(
1294  (uint)bytes_skipped, m_otherBytesRemaining);
1295  }
1296 
1297  uint64_t idx = m_payloadBuffer.size();
1298  uint64_t rem = (bufend - bufstart);
1299  m_payloadBuffer.resize(idx + rem);
1300  memcpy(&m_payloadBuffer[idx], bufstart, rem);
1301 #if 0
1302  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1303  QString("idx: %1, rem: %2").arg(idx).arg(rem));
1304 #endif
1305 }
1306 
1308 {
1309  if (!_pat)
1310  {
1311  LOG(VB_RECORD, LOG_ERR, LOC + "SetPAT(NULL)");
1312  return;
1313  }
1314 
1315  QMutexLocker change_lock(&m_pidLock);
1316 
1317  int progNum = m_streamData->DesiredProgram();
1318  uint pmtpid = _pat->FindPID(progNum);
1319 
1320  // If we have not found the desired program in the PAT and this happens to be
1321  // an SPTS then update the desired program to the one that is present in the PAT.
1322  if (!pmtpid)
1323  {
1324  if (_pat->ProgramCount() == 1)
1325  {
1326  int oldProgNum = progNum;
1327  progNum = _pat->ProgramNumber(0);
1328  LOG(VB_GENERAL, LOG_INFO, LOC +
1329  QString("Update desired program found in SPTS PAT from %1 to %2")
1330  .arg(oldProgNum).arg(progNum));
1331  GetStreamData()->SetDesiredProgram(progNum);
1332  pmtpid = _pat->FindPID(progNum);
1333  }
1334  }
1335  if (!pmtpid)
1336  {
1337  LOG(VB_RECORD, LOG_ERR, LOC +
1338  QString("SetPAT(): Ignoring PAT not containing our desired "
1339  "program (%1)...").arg(progNum));
1340  return;
1341  }
1342 
1343  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPAT(%1 on pid 0x%2)")
1344  .arg(progNum).arg(pmtpid,0,16));
1345 
1347  m_inputPat = new ProgramAssociationTable(*_pat);
1348  delete oldpat;
1349 
1350  // Listen for the other PMTs for faster channel switching
1351  for (uint i = 0; m_inputPat && (i < m_inputPat->ProgramCount()); ++i)
1352  {
1353  uint pmt_pid = m_inputPat->ProgramPID(i);
1354  if (!m_streamData->IsListeningPID(pmt_pid))
1356  }
1357 }
1358 
1359 void DTVRecorder::HandlePMT(uint progNum, const ProgramMapTable *_pmt)
1360 {
1361  QMutexLocker change_lock(&m_pidLock);
1362 
1363  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPMT(%1, %2)").arg(progNum)
1364  .arg(_pmt == nullptr ? "NULL" : "valid"));
1365 
1366 
1367  if ((int)progNum == m_streamData->DesiredProgram())
1368  {
1369  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPMT(%1)").arg(progNum));
1370  ProgramMapTable *oldpmt = m_inputPmt;
1371  m_inputPmt = new ProgramMapTable(*_pmt);
1372 
1373  QString sistandard = GetSIStandard();
1374 
1375  bool has_no_av = true;
1376  for (uint i = 0; i < m_inputPmt->StreamCount() && has_no_av; ++i)
1377  {
1378  has_no_av &= !m_inputPmt->IsVideo(i, sistandard);
1379  has_no_av &= !m_inputPmt->IsAudio(i, sistandard);
1380  }
1381  m_hasNoAV = has_no_av;
1382 
1384  delete oldpmt;
1385  }
1386 }
1387 
1389  bool insert)
1390 {
1391  if (!pat)
1392  {
1393  LOG(VB_RECORD, LOG_ERR, LOC + "HandleSingleProgramPAT(NULL)");
1394  return;
1395  }
1396 
1397  if (!m_ringBuffer)
1398  return;
1399 
1400  uint next_cc = (pat->tsheader()->ContinuityCounter()+1)&0xf;
1401  pat->tsheader()->SetContinuityCounter(next_cc);
1402  pat->GetAsTSPackets(m_scratch, next_cc);
1403 
1404  for (const auto & tspacket : m_scratch)
1405  DTVRecorder::BufferedWrite(tspacket, insert);
1406 }
1407 
1409 {
1410  if (!pmt)
1411  {
1412  LOG(VB_RECORD, LOG_ERR, LOC + "HandleSingleProgramPMT(NULL)");
1413  return;
1414  }
1415 
1416  // We only want to do these checks once per recording
1417  bool seenVideo = (m_primaryVideoCodec != AV_CODEC_ID_NONE);
1418  bool seenAudio = (m_primaryAudioCodec != AV_CODEC_ID_NONE);
1419  uint bestAudioCodec = 0;
1420  // collect stream types for H.264 (MPEG-4 AVC) keyframe detection
1421  for (uint i = 0; i < pmt->StreamCount(); ++i)
1422  {
1423  // We only care about the first identifiable video stream
1424  if (!seenVideo && (m_primaryVideoCodec == AV_CODEC_ID_NONE) &&
1425  StreamID::IsVideo(pmt->StreamType(i)))
1426  {
1427  seenVideo = true; // Ignore other video streams
1428  switch (pmt->StreamType(i))
1429  {
1430  case StreamID::MPEG1Video:
1431  m_primaryVideoCodec = AV_CODEC_ID_MPEG1VIDEO;
1432  break;
1433  case StreamID::MPEG2Video:
1434  m_primaryVideoCodec = AV_CODEC_ID_MPEG2VIDEO;
1435  break;
1436  case StreamID::MPEG4Video:
1437  m_primaryVideoCodec = AV_CODEC_ID_MPEG4;
1438  break;
1439  case StreamID::H264Video:
1440  m_primaryVideoCodec = AV_CODEC_ID_H264;
1441  if (dynamic_cast<AVCParser *>(m_h2645Parser) == nullptr)
1442  {
1443  delete m_h2645Parser;
1444  m_h2645Parser = nullptr;
1445  }
1446  if (m_h2645Parser == nullptr)
1447  {
1448  auto * avcParser = new AVCParser;
1449  if (avcParser != nullptr)
1450  {
1451  m_h2645Parser = reinterpret_cast<H2645Parser *>
1452  (avcParser);
1453  avcParser->use_I_forKeyframes(m_useIForKeyframe);
1454  }
1455  }
1456  break;
1457  case StreamID::H265Video:
1458  LOG(VB_GENERAL, LOG_INFO, LOC + "HEVC detected");
1459  m_primaryVideoCodec = AV_CODEC_ID_H265;
1460  if (dynamic_cast<HEVCParser *>(m_h2645Parser) == nullptr)
1461  {
1462  delete m_h2645Parser;
1463  m_h2645Parser = nullptr;
1464  }
1465  if (m_h2645Parser == nullptr)
1466  {
1467  m_h2645Parser = reinterpret_cast<H2645Parser *>
1468  (new HEVCParser);
1469  }
1470  break;
1472  m_primaryVideoCodec = AV_CODEC_ID_MPEG2VIDEO; // TODO Will it always be MPEG2?
1473  break;
1474  case StreamID::VC1Video:
1475  m_primaryVideoCodec = AV_CODEC_ID_VC1;
1476  break;
1477  default:
1478  break;
1479  }
1480 
1481  if (m_primaryVideoCodec != AV_CODEC_ID_NONE)
1483  }
1484 
1485  // We want the 'best' identifiable audio stream, where 'best' is
1486  // subjective and no-one will likely agree.
1487  // For now it's the 'best' codec, assuming mpeg stream types range
1488  // from worst to best, which it does
1489  if (!seenAudio && StreamID::IsAudio(pmt->StreamType(i)) &&
1490  pmt->StreamType(i) > bestAudioCodec)
1491  {
1492  bestAudioCodec = pmt->StreamType(i);
1493  switch (pmt->StreamType(i))
1494  {
1495  case StreamID::MPEG1Audio: // MPEG-1 Layer 2 (MP2)
1496  case StreamID::MPEG2Audio: // MPEG-2 Part 3 (MP2 Multichannel)
1497  m_primaryAudioCodec = AV_CODEC_ID_MP2;
1498  break;
1500  m_primaryAudioCodec = AV_CODEC_ID_AAC;
1501  break;
1503  m_primaryAudioCodec = AV_CODEC_ID_AAC_LATM;
1504  break;
1505  case StreamID::AC3Audio:
1506  m_primaryAudioCodec = AV_CODEC_ID_AC3;
1507  break;
1508  case StreamID::EAC3Audio:
1509  m_primaryAudioCodec = AV_CODEC_ID_EAC3;
1510  break;
1511  case StreamID::DTSAudio:
1512  m_primaryAudioCodec = AV_CODEC_ID_DTS;
1513  break;
1514  default:
1515  break;
1516  }
1517 
1518  if (m_primaryAudioCodec != AV_CODEC_ID_NONE)
1520  }
1521 
1522 // LOG(VB_GENERAL, LOG_DEBUG, QString("Recording(%1): Stream #%2: %3 ")
1523 // .arg(m_curRecording ? QString::number(m_curRecording->GetRecordingID()) : "")
1524 // .arg(i)
1525 // .arg(StreamID::GetDescription(pmt->StreamType(i))));
1526  m_streamId[pmt->StreamPID(i)] = pmt->StreamType(i);
1527  }
1528 
1529  // If the PCRPID is valid and the PCR is not contained
1530  // in another stream, make sure the PCR stream is not
1531  // discarded (use PrivSec type as dummy 'valid' value)
1532  if (pmt->PCRPID() != 0x1fff && pmt->FindPID(pmt->PCRPID()) == -1)
1534 
1535  if (!m_ringBuffer)
1536  return;
1537 
1538  uint next_cc = (pmt->tsheader()->ContinuityCounter()+1)&0xf;
1539  pmt->tsheader()->SetContinuityCounter(next_cc);
1540  pmt->GetAsTSPackets(m_scratch, next_cc);
1541 
1542  for (const auto & tspacket : m_scratch)
1543  DTVRecorder::BufferedWrite(tspacket, insert);
1544 }
1545 
1547 {
1548  const uint pid = tspacket.PID();
1549 
1550  if (pid != 0x1fff)
1551  m_packetCount.fetchAndAddAcquire(1);
1552 
1553  // Check continuity counter
1554  uint old_cnt = m_continuityCounter[pid];
1555  if ((pid != 0x1fff) && !CheckCC(pid, tspacket.ContinuityCounter()))
1556  {
1557  int v = m_continuityErrorCount.fetchAndAddRelaxed(1) + 1;
1558  double erate = v * 100.0 / m_packetCount.fetchAndAddRelaxed(0);
1559  LOG(VB_RECORD, LOG_WARNING, LOC +
1560  QString("PID 0x%1 discontinuity detected ((%2+1)%16!=%3) %4%")
1561  .arg(pid,0,16).arg(old_cnt,2)
1562  .arg(tspacket.ContinuityCounter(),2)
1563  .arg(erate));
1564  }
1565 
1566  // Only create fake keyframe[s] if there are no audio/video streams
1567  if (m_inputPmt && m_hasNoAV)
1568  {
1569  FindOtherKeyframes(&tspacket);
1570  m_bufferPackets = false;
1571  }
1572  else if (m_recordMptsOnly)
1573  {
1574  /* When recording the full, unfiltered, MPTS, trigger a write
1575  * every 0.5 seconds. Since the packets are unfiltered and
1576  * unprocessed we cannot wait for a keyframe to trigger the
1577  * writes. */
1578 
1579  if (m_framesSeenCount++ == 0)
1581 
1582  if (m_recordMptsTimer.elapsed() > 0.5s)
1583  {
1587  m_recordMptsTimer.addMSecs(-500ms);
1588  }
1589  }
1590  else if (m_streamId[pid] == 0)
1591  {
1592  // Ignore this packet if the PID should be stripped
1593  return true;
1594  }
1595  else
1596  {
1597  // There are audio/video streams. Only write the packet
1598  // if audio/video key-frames have been found
1600  return true;
1601  }
1602 
1603  BufferedWrite(tspacket);
1604 
1605  return true;
1606 }
1607 
1609 {
1610  if (!m_ringBuffer)
1611  return true;
1612 
1613  uint streamType = m_streamId[tspacket.PID()];
1614 
1615  if (tspacket.HasPayload() && tspacket.PayloadStart())
1616  {
1617  if (m_bufferPackets && m_firstKeyframe >= 0 && !m_payloadBuffer.empty())
1618  {
1619  // Flush the buffer
1620  if (m_ringBuffer)
1622  m_payloadBuffer.clear();
1623  }
1624 
1625  // buffer packets until we know if this is a keyframe
1626  m_bufferPackets = true;
1627  }
1628 
1629  // Check for keyframes and count frames
1630  if (streamType == StreamID::H264Video ||
1631  streamType == StreamID::H265Video)
1632  FindH2645Keyframes(&tspacket);
1633  else if (streamType != 0)
1634  FindMPEG2Keyframes(&tspacket);
1635  else
1636  LOG(VB_RECORD, LOG_ERR, LOC +
1637  "ProcessVideoTSPacket: unknown stream type!");
1638 
1639  return ProcessAVTSPacket(tspacket);
1640 }
1641 
1643 {
1644  if (!m_ringBuffer)
1645  return true;
1646 
1647  if (tspacket.HasPayload() && tspacket.PayloadStart())
1648  {
1649  if (m_bufferPackets && m_firstKeyframe >= 0 && !m_payloadBuffer.empty())
1650  {
1651  // Flush the buffer
1652  if (m_ringBuffer)
1654  m_payloadBuffer.clear();
1655  }
1656 
1657  // buffer packets until we know if this is a keyframe
1658  m_bufferPackets = true;
1659  }
1660 
1661  FindAudioKeyframes(&tspacket);
1662  return ProcessAVTSPacket(tspacket);
1663 }
1664 
1667 {
1668  // Sync recording start to first keyframe
1670  {
1671  if (m_bufferPackets)
1672  BufferedWrite(tspacket);
1673  return true;
1674  }
1675 
1676  const uint pid = tspacket.PID();
1677 
1678  if (pid != 0x1fff)
1679  m_packetCount.fetchAndAddAcquire(1);
1680 
1681  // Check continuity counter
1682  uint old_cnt = m_continuityCounter[pid];
1683  if ((pid != 0x1fff) && !CheckCC(pid, tspacket.ContinuityCounter()))
1684  {
1685  int v = m_continuityErrorCount.fetchAndAddRelaxed(1) + 1;
1686  double erate = v * 100.0 / m_packetCount.fetchAndAddRelaxed(0);
1687  LOG(VB_RECORD, LOG_WARNING, LOC +
1688  QString("A/V PID 0x%1 discontinuity detected ((%2+1)%16!=%3) %4%")
1689  .arg(pid,0,16).arg(old_cnt).arg(tspacket.ContinuityCounter())
1690  .arg(erate,5,'f',2));
1691  }
1692 
1693  if (!(m_pidStatus[pid] & kPayloadStartSeen))
1694  {
1696  LOG(VB_RECORD, LOG_INFO, LOC +
1697  QString("PID 0x%1 Found Payload Start").arg(pid,0,16));
1698  }
1699 
1700  BufferedWrite(tspacket);
1701 
1702  return true;
1703 }
1704 
1706 {
1708  recq->AddTSStatistics(
1709  m_continuityErrorCount.fetchAndAddRelaxed(0),
1710  m_packetCount.fetchAndAddRelaxed(0));
1711  return recq;
1712 }
1713 
1714 /* vim: set expandtab tabstop=4 shiftwidth=4: */
bytereader.h
DTVRecorder::FindPSKeyFrames
void FindPSKeyFrames(const uint8_t *buffer, uint len) override
Definition: dtvrecorder.cpp:1121
DTVRecorder::m_pesSynced
bool m_pesSynced
Definition: dtvrecorder.h:150
DTVRecorder::HandleSingleProgramPMT
void HandleSingleProgramPMT(ProgramMapTable *pmt, bool insert) override
Definition: dtvrecorder.cpp:1408
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:375
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:273
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:374
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
FrameRate::toDouble
double toDouble(void) const
Definition: recorderbase.h:42
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:219
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:1715
HEVCParser
Definition: HEVCParser.h:31
RecorderBase::m_statisticsLock
QMutex m_statisticsLock
Definition: recorderbase.h:369
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:215
FrameRate::getNum
uint getNum(void) const
Definition: recorderbase.h:44
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:1792
DTVRecorder::m_scanType
SCAN_t m_scanType
Definition: dtvrecorder.h:203
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:1608
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:805
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:1307
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:323
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:1630
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:326
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:23
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:373
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:183
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:43
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:216
MPEGStreamData::IsListeningPID
virtual bool IsListeningPID(uint pid) const
Definition: mpegstreamdata.cpp:1107
RecorderBase::m_curRecording
RecordingInfo * m_curRecording
Definition: recorderbase.h:329
DTVRecorder::m_tdTickFramerate
FrameRate m_tdTickFramerate
Definition: dtvrecorder.h:202
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:356
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
Definition: dtvrecorder.h:197
DTVRecorder::HandleSingleProgramPAT
void HandleSingleProgramPAT(ProgramAssociationTable *pat, bool insert) override
Definition: dtvrecorder.cpp:1388
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:1666
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:831
DTVRecorder::m_tdTickCount
uint64_t m_tdTickCount
Definition: dtvrecorder.h:201
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:376
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
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:325
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
DTVRecorder::FindAudioKeyframes
bool FindAudioKeyframes(const TSPacket *tspacket)
Definition: dtvrecorder.cpp:765
FrameRate
Definition: recorderbase.h:38
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:1090
frameRateMap
static const std::array< const FrameRate, 16 > frameRateMap
Definition: dtvrecorder.cpp:370
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:321
DTVRecorder::GetStreamData
MPEGStreamData * GetStreamData(void) const
Definition: dtvrecorder.h:58
RecorderBase::m_durationMap
frm_pos_map_t m_durationMap
Definition: recorderbase.h:355
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:1546
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
uint
unsigned int uint
Definition: compat.h:81
PESStreamID::MPEGVideoStreamBegin
@ MPEGVideoStreamBegin
First MPEG-1/2 video stream (w/ext hdr)
Definition: mpegtables.h:80
DTVRecorder::m_tdBase
double m_tdBase
Definition: dtvrecorder.h:200
RecorderBase::m_positionMap
frm_pos_map_t m_positionMap
Definition: recorderbase.h:353
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:313
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:64
AVCParser.h
RecorderBase::m_positionMapDelta
frm_pos_map_t m_positionMapDelta
Definition: recorderbase.h:354
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:1623
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:1703
RecorderBase::m_ringBuffer
MythMediaBuffer * m_ringBuffer
Definition: recorderbase.h:310
MARK_DURATION_MS
@ MARK_DURATION_MS
Definition: programtypes.h:74
DTVRecorder::ProcessAudioTSPacket
bool ProcessAudioTSPacket(const TSPacket &tspacket) override
Definition: dtvrecorder.cpp:1642
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
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:371
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
MPEGStreamData::DesiredProgram
int DesiredProgram(void) const
Definition: mpegstreamdata.h:260
FrameRate::getDen
uint getDen(void) const
Definition: recorderbase.h:45
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:209
ProgramInfo::ClearPositionMap
void ClearPositionMap(MarkTypes type) const
Definition: programinfo.cpp:3824
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:370
DTVRecorder::m_recordMpts
bool m_recordMpts
Definition: dtvrecorder.h:178
RecorderBase::m_positionMapLock
QMutex m_positionMapLock
Definition: recorderbase.h:352
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:68
DTVRecorder::HandlePMT
void HandlePMT(uint progNum, const ProgramMapTable *_pmt) override
Definition: dtvrecorder.cpp:1359
RecorderBase::AspectRatio
AspectRatio
Definition: recorderbase.h:238
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:315
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:388
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:314
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
FrameRate::toString
QString toString(void) const
Definition: recorderbase.h:46
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: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
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
RecorderBase::m_frameRate
FrameRate m_frameRate
Definition: recorderbase.h:327
PESStreamID::PictureStartCode
@ PictureStartCode
Definition: mpegtables.h:49
RecorderBase::kTimeOfLatestDataIntervalTarget
static constexpr std::chrono::milliseconds kTimeOfLatestDataIntervalTarget
timeOfLatest update interval target in milliseconds.
Definition: recorderbase.h:378
DTVRecorder::m_pidLock
QRecursiveMutex m_pidLock
Definition: dtvrecorder.h:170
DTVRecorder::FindH2645Keyframes
bool FindH2645Keyframes(const TSPacket *tspacket)
Definition: dtvrecorder.cpp:871
DTVRecorder::m_musicChoice
bool m_musicChoice
Definition: dtvrecorder.h:207
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:1705
TSHeader::data
const unsigned char * data(void) const
Definition: tspacket.h:172
RecorderBase::m_timeOfLatestDataCount
QAtomicInt m_timeOfLatestDataCount
Definition: recorderbase.h:372
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
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