MythTV  master
dtvrecorder.cpp
Go to the documentation of this file.
1 
21 #include "atscstreamdata.h"
22 #include "mpegstreamdata.h"
23 #include "dvbstreamdata.h"
24 #include "dtvrecorder.h"
25 #include "programinfo.h"
26 #include "mythlogging.h"
27 #include "mpegtables.h"
28 #include "ringbuffer.h"
29 #include "tv_rec.h"
30 #include "mythsystemevent.h"
31 
32 #define LOC ((m_tvrec) ? \
33  QString("DTVRec[%1]: ").arg(m_tvrec->GetInputId()) : \
34  QString("DTVRec(0x%1): ").arg(intptr_t(this),0,16))
35 
37 
47  RecorderBase(rec)
48 {
50  m_payloadBuffer.reserve(TSPacket::kSize * (50 + 1));
51 
53 
55  gCoreContext->GetNumSetting("MinimumRecordingQuality", 95);
56 
58 }
59 
61 {
62  StopRecording();
63 
65 
66  if (m_inputPat)
67  {
68  delete m_inputPat;
69  m_inputPat = nullptr;
70  }
71 
72  if (m_inputPmt)
73  {
74  delete m_inputPmt;
75  m_inputPmt = nullptr;
76  }
77 }
78 
79 void DTVRecorder::SetOption(const QString &name, const QString &value)
80 {
81  if (name == "recordingtype")
82  m_recordingType = value;
83  else
84  RecorderBase::SetOption(name, value);
85 }
86 
90 void DTVRecorder::SetOption(const QString &name, int value)
91 {
92  if (name == "wait_for_seqstart")
93  m_waitForKeyframeOption = (value == 1);
94  else if (name == "recordmpts")
95  m_recordMpts = (value != 0);
96  else
97  RecorderBase::SetOption(name, value);
98 }
99 
101  const QString &videodev,
102  const QString& /*audiodev*/, const QString& /*vbidev*/)
103 {
104  SetOption("videodevice", videodev);
105  DTVRecorder::SetOption("tvformat", gCoreContext->GetSetting("TVFormat"));
106  SetStrOption(profile, "recordingtype");
107  SetIntOption(profile, "recordmpts");
108 }
109 
115 {
116  if (m_ringBuffer)
118 
119  if (m_curRecording)
120  {
121  SetDuration((int64_t)(m_totalDuration * 1000));
123  }
124 
126 }
127 
129 {
130  LOG(VB_RECORD, LOG_INFO, LOC + "ResetForNewFile(void)");
131  QMutexLocker locker(&m_positionMapLock);
132 
133  // m_seen_psp and m_h264_parser should
134  // not be reset here. This will only be called just as
135  // we're seeing the first packet of a new keyframe for
136  // writing to the new file and anything that makes the
137  // recorder think we're waiting on another keyframe will
138  // send significant amounts of good data to /dev/null.
139  // -- Daniel Kristjansson 2011-02-26
140 
141  m_startCode = 0xffffffff;
142  m_firstKeyframe = -1;
144  m_lastKeyframeSeen = 0;
145  m_lastGopSeen = 0;
146  m_lastSeqSeen = 0;
150  //_recording
151  m_error = QString();
152 
154  m_repeatPict = 0;
155 
156  //m_pes_synced
157  //m_seen_sps
158  m_positionMap.clear();
159  m_positionMapDelta.clear();
160  m_durationMap.clear();
161  m_durationMapDelta.clear();
162 
163  locker.unlock();
165 }
166 
168 {
170 
171  memset(m_tsCount, 0, sizeof(m_tsCount));
172  for (int64_t & ts : m_tsLast)
173  ts = -1LL;
174  for (int64_t & ts : m_tsFirst)
175  ts = -1LL;
176  //m_tsFirst_dt -- doesn't need to be cleared only used if m_tsFirst>=0
177  m_packetCount.fetchAndStoreRelaxed(0);
178  m_continuityErrorCount.fetchAndStoreRelaxed(0);
179  m_framesSeenCount = 0;
181  m_totalDuration = 0;
182  m_tdBase = 0;
183  m_tdTickCount = 0;
185 }
186 
187 // documented in recorderbase.h
189 {
190  LOG(VB_RECORD, LOG_INFO, LOC + "Reset(void)");
191  ResetForNewFile();
192 
193  m_startCode = 0xffffffff;
194 
195  if (m_curRecording)
196  {
199  }
200 }
201 
203 {
204  if (data == m_streamData)
205  return;
206 
207  MPEGStreamData *old_data = m_streamData;
208  m_streamData = data;
209  delete old_data;
210 
211  if (m_streamData)
212  InitStreamData();
213 }
214 
216 {
219 
220  auto *dvb = dynamic_cast<DVBStreamData*>(m_streamData);
221  if (dvb)
222  dvb->AddDVBMainListener(this);
223 
224  auto *atsc = dynamic_cast<ATSCStreamData*>(m_streamData);
225 
226  if (atsc && atsc->DesiredMinorChannel())
227  {
228  atsc->SetDesiredChannel(atsc->DesiredMajorChannel(),
229  atsc->DesiredMinorChannel());
230  }
231  else if (m_streamData->DesiredProgram() >= 0)
232  {
234  }
235 }
236 
237 void DTVRecorder::BufferedWrite(const TSPacket &tspacket, bool insert)
238 {
239  if (!insert) // PAT/PMT may need inserted in front of any buffered data
240  {
241  // delay until first GOP to avoid decoder crash on res change
243  m_firstKeyframe < 0)
244  return;
245 
246  if (m_curRecording && m_timeOfFirstDataIsSet.testAndSetRelaxed(0,1))
247  {
248  QMutexLocker locker(&m_statisticsLock);
252  }
253 
254  int val = m_timeOfLatestDataCount.fetchAndAddRelaxed(1);
255  int thresh = m_timeOfLatestDataPacketInterval.fetchAndAddRelaxed(0);
256  if (val > thresh)
257  {
258  QMutexLocker locker(&m_statisticsLock);
260  int interval = thresh;
261  if (elapsed > kTimeOfLatestDataIntervalTarget + 250)
262  {
264  .fetchAndStoreRelaxed(thresh * 4 / 5);
265  }
266  else if (elapsed + 250 < kTimeOfLatestDataIntervalTarget)
267  {
269  .fetchAndStoreRelaxed(thresh * 9 / 8);
270  }
271 
272  m_timeOfLatestDataCount.fetchAndStoreRelaxed(1);
274 
275  LOG(VB_RECORD, LOG_DEBUG, LOC +
276  QString("Updating timeOfLatestData elapsed(%1) interval(%2)")
277  .arg(elapsed).arg(interval));
278  }
279 
280  // Do we have to buffer the packet for exact keyframe detection?
281  if (m_bufferPackets)
282  {
283  int idx = m_payloadBuffer.size();
284  m_payloadBuffer.resize(idx + TSPacket::kSize);
285  memcpy(&m_payloadBuffer[idx], tspacket.data(), TSPacket::kSize);
286  return;
287  }
288 
289  // We are free to write the packet, but if we have buffered packet[s]
290  // we have to write them first...
291  if (!m_payloadBuffer.empty())
292  {
293  if (m_ringBuffer)
295  m_payloadBuffer.clear();
296  }
297  }
298 
299  if (m_ringBuffer && m_ringBuffer->Write(tspacket.data(), TSPacket::kSize) < 0 &&
301  {
302  LOG(VB_GENERAL, LOG_INFO, LOC +
303  QString("BufferedWrite: Writes are failing, "
304  "setting status to %1")
306  SetRecordingStatus(RecStatus::Failing, __FILE__, __LINE__);
307  }
308 }
309 
311 static int64_t extract_timestamp(
312  const uint8_t *bufptr, int bytes_left, int pts_or_dts)
313 {
314  if (bytes_left < 4)
315  return -1LL;
316 
317  bool has_pts = (bufptr[3] & 0x80) != 0;
318  int offset = 5;
319  if (((kExtractPTS == pts_or_dts) && !has_pts) || (offset + 5 > bytes_left))
320  return -1LL;
321 
322  bool has_dts = (bufptr[3] & 0x40) != 0;
323  if (kExtractDTS == pts_or_dts)
324  {
325  if (!has_dts)
326  return -1LL;
327  offset += has_pts ? 5 : 0;
328  if (offset + 5 > bytes_left)
329  return -1LL;
330  }
331 
332  return ((uint64_t(bufptr[offset+0] & 0x0e) << 29) |
333  (uint64_t(bufptr[offset+1] ) << 22) |
334  (uint64_t(bufptr[offset+2] & 0xfe) << 14) |
335  (uint64_t(bufptr[offset+3] ) << 7) |
336  (uint64_t(bufptr[offset+4] & 0xfe) >> 1));
337 }
338 
339 static QDateTime ts_to_qdatetime(
340  uint64_t pts, uint64_t pts_first, QDateTime &pts_first_dt)
341 {
342  if (pts < pts_first)
343  pts += 0x1FFFFFFFFLL;
344  const QDateTime& dt = pts_first_dt;
345  return dt.addMSecs((pts - pts_first)/90);
346 }
347 
348 static const FrameRate frameRateMap[16] = {
349  FrameRate(0), FrameRate(24000, 1001), FrameRate(24),
350  FrameRate(25), FrameRate(30000, 1001), FrameRate(30),
351  FrameRate(50), FrameRate(60000, 1001), FrameRate(60),
352  FrameRate(0), FrameRate(0), FrameRate(0),
353  FrameRate(0), FrameRate(0), FrameRate(0),
354  FrameRate(0)
355 };
356 
384 {
385  if (!tspacket->HasPayload()) // no payload to scan
386  return m_firstKeyframe >= 0;
387 
388  if (!m_ringBuffer)
389  return m_firstKeyframe >= 0;
390 
391  // if packet contains start of PES packet, start
392  // looking for first byte of MPEG start code (3 bytes 0 0 1)
393  // otherwise, pick up search where we left off.
394  const bool payloadStart = tspacket->PayloadStart();
395  m_startCode = (payloadStart) ? 0xffffffff : m_startCode;
396 
397  // Just make these local for efficiency reasons (gcc not so smart..)
398  const uint maxKFD = kMaxKeyFrameDistance;
399  bool hasFrame = false;
400  bool hasKeyFrame = false;
401 
402  uint aspectRatio = 0;
403  uint height = 0;
404  uint width = 0;
405  FrameRate frameRate(0);
406 
407  // Scan for PES header codes; specifically picture_start
408  // sequence_start (SEQ) and group_start (GOP).
409  // 00 00 01 00: picture_start_code
410  // 00 00 01 B8: group_start_code
411  // 00 00 01 B3: seq_start_code
412  // 00 00 01 B5: ext_start_code
413  // (there are others that we don't care about)
414  const uint8_t *bufptr = tspacket->data() + tspacket->AFCOffset();
415  const uint8_t *bufend = tspacket->data() + TSPacket::kSize;
416  m_repeatPict = 0;
417 
418  while (bufptr < bufend)
419  {
420  bufptr = avpriv_find_start_code(bufptr, bufend, &m_startCode);
421  int bytes_left = bufend - bufptr;
422  if ((m_startCode & 0xffffff00) == 0x00000100)
423  {
424  // At this point we have seen the start code 0 0 1
425  // the next byte will be the PES packet stream id.
426  const int stream_id = m_startCode & 0x000000ff;
427  if (PESStreamID::PictureStartCode == stream_id)
428  hasFrame = true;
429  else if (PESStreamID::GOPStartCode == stream_id)
430  {
432  hasKeyFrame = true;
433  }
434  else if (PESStreamID::SequenceStartCode == stream_id)
435  {
437  hasKeyFrame |= (m_lastGopSeen + maxKFD)<m_framesSeenCount;
438 
439  // Look for aspectRatio changes and store them in the database
440  aspectRatio = (bufptr[3] >> 4);
441 
442  // Get resolution
443  height = ((bufptr[1] & 0xf) << 8) | bufptr[2];
444  width = (bufptr[0] <<4) | (bufptr[1]>>4);
445 
446  frameRate = frameRateMap[(bufptr[3] & 0x0000000f)];
447  }
448  else if (PESStreamID::MPEG2ExtensionStartCode == stream_id)
449  {
450  if (bytes_left >= 1)
451  {
452  int ext_type = (bufptr[0] >> 4);
453  switch(ext_type)
454  {
455  case 0x1: /* sequence extension */
456  if (bytes_left >= 6)
457  {
458  m_progressiveSequence = bufptr[1] & (1 << 3);
459  }
460  break;
461  case 0x8: /* picture coding extension */
462  if (bytes_left >= 5)
463  {
464  //int picture_structure = bufptr[2]&3;
465  int top_field_first = bufptr[3] & (1 << 7);
466  int repeat_first_field = bufptr[3] & (1 << 1);
467  int progressive_frame = bufptr[4] & (1 << 7);
468 
469  /* check if we must repeat the frame */
470  m_repeatPict = 1;
471  if (repeat_first_field)
472  {
474  {
475  if (top_field_first)
476  m_repeatPict = 5;
477  else
478  m_repeatPict = 3;
479  }
480  else if (progressive_frame)
481  {
482  m_repeatPict = 2;
483  }
484  }
485  // The m_repeatPict code above matches
486  // mpegvideo_extract_headers(), but the
487  // code in mpeg_field_start() computes a
488  // value one less, which seems correct.
489  --m_repeatPict;
490  }
491  break;
492  }
493  }
494  }
495  if ((stream_id >= PESStreamID::MPEGVideoStreamBegin) &&
496  (stream_id <= PESStreamID::MPEGVideoStreamEnd))
497  {
498  int64_t pts = extract_timestamp(
499  bufptr, bytes_left, kExtractPTS);
500  int64_t dts = extract_timestamp(
501  bufptr, bytes_left, kExtractPTS);
502  HandleTimestamps(stream_id, pts, dts);
503  // Detect music choice program (very slow frame rate and audio)
504  if (m_firstKeyframe < 0
505  && m_tsLast[stream_id] - m_tsFirst[stream_id] > 3*90000)
506  {
507  hasKeyFrame = true;
508  m_musicChoice = true;
509  LOG(VB_GENERAL, LOG_INFO, LOC + "Music Choice program detected");
510  }
511  }
512  }
513  }
514 
515  if (hasFrame && !hasKeyFrame)
516  {
517  // If we have seen kMaxKeyFrameDistance frames since the
518  // last GOP or SEQ stream_id, then pretend this picture
519  // is a keyframe. We may get artifacts but at least
520  // we will be able to skip frames.
521  hasKeyFrame = ((m_framesSeenCount & 0xf) == 0U);
522  hasKeyFrame &= (m_lastGopSeen + maxKFD) < m_framesSeenCount;
523  hasKeyFrame &= (m_lastSeqSeen + maxKFD) < m_framesSeenCount;
524  }
525 
526  // m_bufferPackets will only be true if a payload start has been seen
527  if (hasKeyFrame && (m_bufferPackets || m_firstKeyframe >= 0))
528  {
529  LOG(VB_RECORD, LOG_DEBUG, LOC + QString
530  ("Keyframe @ %1 + %2 = %3")
532  .arg(m_payloadBuffer.size())
533  .arg(m_ringBuffer->GetWritePosition() + m_payloadBuffer.size()));
534 
536  HandleKeyframe(0);
537  }
538 
539  if (hasFrame)
540  {
541  LOG(VB_RECORD, LOG_DEBUG, LOC + QString
542  ("Frame @ %1 + %2 = %3")
544  .arg(m_payloadBuffer.size())
545  .arg(m_ringBuffer->GetWritePosition() + m_payloadBuffer.size()));
546 
547  m_bufferPackets = false; // We now know if it is a keyframe, or not
551  else
552  {
553  /* Found a frame that is not a keyframe, and we want to
554  * start on a keyframe */
555  m_payloadBuffer.clear();
556  }
557  }
558 
559  if ((aspectRatio > 0) && (aspectRatio != m_videoAspect))
560  {
561  m_videoAspect = aspectRatio;
563  }
564 
565  if (height && width && (height != m_videoHeight || m_videoWidth != width))
566  {
567  m_videoHeight = height;
568  m_videoWidth = width;
569  ResolutionChange(width, height, m_framesWrittenCount);
570  }
571 
572  if (frameRate.isNonzero() && frameRate != m_frameRate)
573  {
574  m_frameRate = frameRate;
575  LOG(VB_RECORD, LOG_INFO, LOC +
576  QString("FindMPEG2Keyframes: frame rate = %1")
577  .arg(frameRate.toDouble() * 1000));
578  FrameRateChange(frameRate.toDouble() * 1000, m_framesWrittenCount);
579  }
580 
581  return m_firstKeyframe >= 0;
582 }
583 
584 void DTVRecorder::HandleTimestamps(int stream_id, int64_t pts, int64_t dts)
585 {
586  if (pts < 0)
587  {
588  m_tsLast[stream_id] = -1;
589  return;
590  }
591 
592  if ((dts < 0) && !m_use_pts)
593  {
594  m_tsLast[stream_id] = -1;
595  m_use_pts = true;
596  LOG(VB_RECORD, LOG_DEBUG,
597  "Switching from dts tracking to pts tracking." +
598  QString("TS count is %1").arg(m_tsCount[stream_id]));
599  }
600 
601  int64_t ts = dts;
602  int64_t gap_threshold = 90000; // 1 second
603  if (m_use_pts)
604  {
605  ts = dts;
606  gap_threshold = 2*90000; // two seconds, compensate for GOP ordering
607  }
608 
609  if (m_musicChoice)
610  gap_threshold = 8*90000; // music choice uses frames every 6 seconds
611 
612  if (m_tsLast[stream_id] >= 0)
613  {
614  int64_t diff = ts - m_tsLast[stream_id];
615 
616  // time jumped back more then 10 seconds, handle it as 33bit overflow
617  if (diff < (10 * -90000))
618  // MAX_PTS is 33bits all 1
619  diff += 0x1ffffffffLL;
620 
621  // FIXME why do we handle negative gaps (aka overlap) like a gap?
622  if (diff < 0)
623  diff = -diff;
624 
625  if (diff > gap_threshold && m_firstKeyframe >= 0)
626  {
627  QMutexLocker locker(&m_statisticsLock);
628 
629  m_recordingGaps.push_back(
630  RecordingGap(
632  m_tsLast[stream_id], m_tsFirst[stream_id],
633  m_tsFirstDt[stream_id]),
635  ts, m_tsFirst[stream_id], m_tsFirstDt[stream_id])));
636  LOG(VB_RECORD, LOG_DEBUG, LOC + QString("Inserted gap %1 dur %2")
637  .arg(m_recordingGaps.back().toString()).arg(diff/90000.0));
638 
640  {
642  if (recq.IsDamaged())
643  {
644  LOG(VB_GENERAL, LOG_INFO, LOC +
645  QString("HandleTimestamps: too much damage, "
646  "setting status to %1")
648  SetRecordingStatus(RecStatus::Failing, __FILE__, __LINE__);
649  }
650  }
651  }
652  }
653 
654  m_tsLast[stream_id] = ts;
655 
656  if (m_tsCount[stream_id] < 30)
657  {
658  if (!m_tsCount[stream_id] || (ts < m_tsFirst[stream_id]))
659  {
660  m_tsFirst[stream_id] = ts;
661  m_tsFirstDt[stream_id] = MythDate::current();
662  }
663  }
664 
665  m_tsCount[stream_id]++;
666 }
667 
669 {
674  {
676  m_tdTickCount = 0;
678  }
679  m_tdTickCount += (2 + m_repeatPict);
681  {
682  m_totalDuration = m_tdBase + (int64_t) 500 * m_tdTickCount *
684  }
685 
686  if (m_framesWrittenCount < 2000 || m_framesWrittenCount % 1000 == 0)
687  {
688  LOG(VB_RECORD, LOG_DEBUG,
689  QString("count=%1 m_frameRate=%2 tick_frameRate=%3 "
690  "tick_cnt=%4 tick_base=%5 _total_dur=%6")
692  .arg(m_frameRate.toString())
694  .arg(m_tdTickCount)
695  .arg(m_tdBase)
696  .arg(m_totalDuration));
697  }
698 }
699 
700 bool DTVRecorder::FindAudioKeyframes(const TSPacket* /*tspacket*/)
701 {
702  bool hasKeyFrame = false;
703  if (!m_ringBuffer || (GetStreamData()->VideoPIDSingleProgram() <= 0x1fff))
704  return hasKeyFrame;
705 
706  static constexpr uint64_t kMsecPerDay = 24 * 60 * 60 * 1000ULL;
707  const double frame_interval = (1000.0 / m_videoFrameRate);
708  uint64_t elapsed = 0;
709  if (m_audioTimer.isValid())
710  elapsed = m_audioTimer.elapsed();
711  auto expected_frame = (uint64_t) ((double)elapsed / frame_interval);
712 
713  while (m_framesSeenCount > expected_frame + 10000)
714  expected_frame += (uint64_t) ((double)kMsecPerDay / frame_interval);
715 
716  if (!m_framesSeenCount || (m_framesSeenCount < expected_frame))
717  {
718  if (!m_framesSeenCount)
719  m_audioTimer.start();
720 
721  m_bufferPackets = false;
723 
724  if (1 == (m_framesSeenCount & 0x7))
725  {
728  hasKeyFrame = true;
729  }
730 
733  }
734 
735  return hasKeyFrame;
736 }
737 
740 bool DTVRecorder::FindOtherKeyframes(const TSPacket */*tspacket*/)
741 {
742  if (!m_ringBuffer || (GetStreamData()->VideoPIDSingleProgram() <= 0x1fff))
743  return true;
744 
746  return true;
747 
748  LOG(VB_RECORD, LOG_INFO, LOC + "DSMCC - FindOtherKeyframes() - "
749  "generating initial key-frame");
750 
754 
756 
758 
759  return true;
760 }
761 
766 void DTVRecorder::HandleKeyframe(int64_t extra)
767 {
768  if (!m_ringBuffer)
769  return;
770 
771  // Perform ringbuffer switch if needed.
773 
774  uint64_t frameNum = m_framesWrittenCount;
775  if (m_firstKeyframe < 0)
776  {
777  m_firstKeyframe = frameNum;
778  SendMythSystemRecEvent("REC_STARTED_WRITING", m_curRecording);
779  }
780 
781  // Add key frame to position map
782  m_positionMapLock.lock();
783  if (!m_positionMap.contains(frameNum))
784  {
785  int64_t startpos = m_ringBuffer->GetWritePosition() + extra;
786 
787  // Don't put negative offsets into the database, they get munged into
788  // MAX_INT64 - offset, which is an exceedingly large number, and
789  // certainly not valid.
790  if (startpos >= 0)
791  {
792  m_positionMapDelta[frameNum] = startpos;
793  m_positionMap[frameNum] = startpos;
794  m_durationMap[frameNum] = llround(m_totalDuration);
795  m_durationMapDelta[frameNum] = llround(m_totalDuration);
796  }
797  }
798  m_positionMapLock.unlock();
799 }
800 
807 {
808  if (!tspacket->HasPayload()) // no payload to scan
809  return m_firstKeyframe >= 0;
810 
811  if (!m_ringBuffer)
812  {
813  LOG(VB_GENERAL, LOG_ERR, LOC + "FindH264Keyframes: No ringbuffer");
814  return m_firstKeyframe >= 0;
815  }
816 
817  const bool payloadStart = tspacket->PayloadStart();
818  if (payloadStart)
819  {
820  // reset PES sync state
821  m_pesSynced = false;
822  m_startCode = 0xffffffff;
823  }
824 
825  uint aspectRatio = 0;
826  uint height = 0;
827  uint width = 0;
828  FrameRate frameRate(0);
829 
830  bool hasFrame = false;
831  bool hasKeyFrame = false;
832 
833  // scan for PES packets and H.264 NAL units
834  uint i = tspacket->AFCOffset();
835  for (; i < TSPacket::kSize; ++i)
836  {
837  // special handling required when a new PES packet begins
838  if (payloadStart && !m_pesSynced)
839  {
840  // bounds check
841  if (i + 2 >= TSPacket::kSize)
842  {
843  LOG(VB_GENERAL, LOG_ERR, LOC +
844  "PES packet start code may overflow to next TS packet, "
845  "aborting keyframe search");
846  break;
847  }
848 
849  // must find the PES start code
850  if (tspacket->data()[i++] != 0x00 ||
851  tspacket->data()[i++] != 0x00 ||
852  tspacket->data()[i++] != 0x01)
853  {
854  LOG(VB_GENERAL, LOG_ERR, LOC +
855  "PES start code not found in TS packet with PUSI set");
856  break;
857  }
858 
859  // bounds check
860  if (i + 5 >= TSPacket::kSize)
861  {
862  LOG(VB_GENERAL, LOG_ERR, LOC +
863  "PES packet headers overflow to next TS packet, "
864  "aborting keyframe search");
865  break;
866  }
867 
868  // now we need to compute where the PES payload begins
869  // skip past the stream_id (+1)
870  // the next two bytes are the PES packet length (+2)
871  // after that, one byte of PES packet control bits (+1)
872  // after that, one byte of PES header flags bits (+1)
873  // and finally, one byte for the PES header length
874  const unsigned char pes_header_length = tspacket->data()[i + 5];
875 
876  // bounds check
877  if ((i + 6 + pes_header_length) >= TSPacket::kSize)
878  {
879  LOG(VB_GENERAL, LOG_ERR, LOC +
880  "PES packet headers overflow to next TS packet, "
881  "aborting keyframe search");
882  break;
883  }
884 
885  // we now know where the PES payload is
886  // normally, we should have used 6, but use 5 because the for
887  // loop will bump i
888  i += 5 + pes_header_length;
889  m_pesSynced = true;
890 
891 #if 0
892  LOG(VB_RECORD, LOG_DEBUG, LOC + "PES synced");
893 #endif
894  continue;
895  }
896 
897  // ain't going nowhere if we're not PES synced
898  if (!m_pesSynced)
899  break;
900 
901  // scan for a NAL unit start code
902 
903  uint32_t bytes_used = m_h264Parser.addBytes
904  (tspacket->data() + i, TSPacket::kSize - i,
906  i += (bytes_used - 1);
907 
909  {
910  if (m_h264Parser.onFrameStart() &&
912  {
913  hasKeyFrame = m_h264Parser.onKeyFrameStart();
914  hasFrame = true;
915  m_seenSps |= hasKeyFrame;
916 
917  width = m_h264Parser.pictureWidth();
918  height = m_h264Parser.pictureHeight();
919  aspectRatio = m_h264Parser.aspectRatio();
920  m_h264Parser.getFrameRate(frameRate);
921  }
922  }
923  } // for (; i < TSPacket::kSize; ++i)
924 
925  // If it has been more than 511 frames since the last keyframe,
926  // pretend we have one.
927  if (hasFrame && !hasKeyFrame &&
929  {
930  hasKeyFrame = true;
931  LOG(VB_RECORD, LOG_WARNING, LOC +
932  QString("FindH264Keyframes: %1 frames without a keyframe.")
934  }
935 
936  // m_bufferPackets will only be true if a payload start has been seen
937  if (hasKeyFrame && (m_bufferPackets || m_firstKeyframe >= 0))
938  {
939  LOG(VB_RECORD, LOG_DEBUG, LOC + QString
940  ("Keyframe @ %1 + %2 = %3 AU %4")
942  .arg(m_payloadBuffer.size())
945 
948  }
949 
950  if (hasFrame)
951  {
952  LOG(VB_RECORD, LOG_DEBUG, LOC + QString
953  ("Frame @ %1 + %2 = %3 AU %4")
955  .arg(m_payloadBuffer.size())
958 
959  m_bufferPackets = false; // We now know if this is a keyframe
963  else
964  {
965  /* Found a frame that is not a keyframe, and we want to
966  * start on a keyframe */
967  m_payloadBuffer.clear();
968  }
969  }
970 
971  if ((aspectRatio > 0) && (aspectRatio != m_videoAspect))
972  {
973  m_videoAspect = aspectRatio;
975  }
976 
977  if (height && width && (height != m_videoHeight || m_videoWidth != width))
978  {
979  m_videoHeight = height;
980  m_videoWidth = width;
981  ResolutionChange(width, height, m_framesWrittenCount);
982  }
983 
984  if (frameRate.isNonzero() && frameRate != m_frameRate)
985  {
986  LOG(VB_RECORD, LOG_INFO, LOC +
987  QString("FindH264Keyframes: timescale: %1, tick: %2, framerate: %3")
988  .arg( m_h264Parser.GetTimeScale() )
989  .arg( m_h264Parser.GetUnitsInTick() )
990  .arg( frameRate.toDouble() * 1000 ) );
991  m_frameRate = frameRate;
992  FrameRateChange(frameRate.toDouble() * 1000, m_framesWrittenCount);
993  }
994 
995  return m_seenSps;
996 }
997 
1003 {
1004  // Perform ringbuffer switch if needed.
1006 
1007  uint64_t startpos = 0;
1008  uint64_t frameNum = m_framesWrittenCount;
1009 
1010  if (m_firstKeyframe < 0)
1011  {
1012  m_firstKeyframe = frameNum;
1013  startpos = 0;
1014  SendMythSystemRecEvent("REC_STARTED_WRITING", m_curRecording);
1015  }
1016  else
1017  startpos = m_h264Parser.keyframeAUstreamOffset();
1018 
1019  // Add key frame to position map
1020  m_positionMapLock.lock();
1021  if (!m_positionMap.contains(frameNum))
1022  {
1023  m_positionMapDelta[frameNum] = startpos;
1024  m_positionMap[frameNum] = startpos;
1025  m_durationMap[frameNum] = llround(m_totalDuration);
1026  m_durationMapDelta[frameNum] = llround(m_totalDuration);
1027  }
1028  m_positionMapLock.unlock();
1029 }
1030 
1031 void DTVRecorder::FindPSKeyFrames(const uint8_t *buffer, uint len)
1032 {
1033  const uint maxKFD = kMaxKeyFrameDistance;
1034 
1035  const uint8_t *bufstart = buffer;
1036  const uint8_t *bufptr = buffer;
1037  const uint8_t *bufend = buffer + len;
1038 
1039  uint aspectRatio = 0;
1040  uint height = 0;
1041  uint width = 0;
1042  FrameRate frameRate(0);
1043 
1045  while (bufptr + skip < bufend)
1046  {
1047  bool hasFrame = false;
1048  bool hasKeyFrame = false;
1049 
1050  const uint8_t *tmp = bufptr;
1051  bufptr =
1052  avpriv_find_start_code(bufptr + skip, bufend, &m_startCode);
1055  m_videoBytesRemaining -= std::min(
1056  (uint)(bufptr - tmp), m_videoBytesRemaining);
1057 
1058  if ((m_startCode & 0xffffff00) != 0x00000100)
1059  continue;
1060 
1061  // NOTE: Length may be zero for packets that only contain bytes from
1062  // video elementary streams in TS packets. 13818-1:2000 2.4.3.7
1063  int pes_packet_length = -1;
1064  if ((bufend - bufptr) >= 2)
1065  pes_packet_length = ((bufptr[0]<<8) | bufptr[1]) + 2 + 6;
1066 
1067  const int stream_id = m_startCode & 0x000000ff;
1069  {
1070  if (PESStreamID::PictureStartCode == stream_id)
1071  { // pes_packet_length is meaningless
1072  pes_packet_length = -1;
1073  if (bufend-bufptr >= 4)
1074  {
1075  uint frmtypei = (bufptr[1]>>3) & 0x7;
1076  if ((1 <= frmtypei) && (frmtypei <= 5))
1077  hasFrame = true;
1078  }
1079  else
1080  {
1081  hasFrame = true;
1082  }
1083  }
1084  else if (PESStreamID::GOPStartCode == stream_id)
1085  { // pes_packet_length is meaningless
1086  pes_packet_length = -1;
1088  hasKeyFrame = true;
1089  }
1090  else if (PESStreamID::SequenceStartCode == stream_id)
1091  { // pes_packet_length is meaningless
1092  pes_packet_length = -1;
1094  hasKeyFrame |= (m_lastGopSeen + maxKFD)<m_framesSeenCount;
1095 
1096  // Look for aspectRatio changes and store them in the database
1097  aspectRatio = (bufptr[3] >> 4);
1098 
1099  // Get resolution
1100  height = ((bufptr[1] & 0xf) << 8) | bufptr[2];
1101  width = (bufptr[0] <<4) | (bufptr[1]>>4);
1102 
1103  frameRate = frameRateMap[(bufptr[3] & 0x0000000f)];
1104  }
1105  }
1107  {
1108  if ((stream_id >= PESStreamID::MPEGVideoStreamBegin) &&
1109  (stream_id <= PESStreamID::MPEGVideoStreamEnd))
1110  { // ok-dvdinfo
1111  m_videoBytesRemaining = std::max(0, pes_packet_length);
1112  }
1113  else if ((stream_id >= PESStreamID::MPEGAudioStreamBegin) &&
1114  (stream_id <= PESStreamID::MPEGAudioStreamEnd))
1115  { // ok-dvdinfo
1116  m_audioBytesRemaining = std::max(0, pes_packet_length);
1117  }
1118  }
1119 
1120  if (PESStreamID::PaddingStream == stream_id)
1121  { // ok-dvdinfo
1122  m_otherBytesRemaining = std::max(0, pes_packet_length);
1123  }
1124 
1125  m_startCode = 0xffffffff; // reset start code
1126 
1127  if (hasFrame && !hasKeyFrame)
1128  {
1129  // If we have seen kMaxKeyFrameDistance frames since the
1130  // last GOP or SEQ stream_id, then pretend this picture
1131  // is a keyframe. We may get artifacts but at least
1132  // we will be able to skip frames.
1133  hasKeyFrame = ((m_framesSeenCount & 0xf) == 0U);
1134  hasKeyFrame &= (m_lastGopSeen + maxKFD) < m_framesSeenCount;
1135  hasKeyFrame &= (m_lastSeqSeen + maxKFD) < m_framesSeenCount;
1136  }
1137 
1138  if (hasFrame)
1139  {
1143  }
1144 
1145  if (hasKeyFrame)
1146  {
1148  HandleKeyframe((int64_t)m_payloadBuffer.size() - (bufptr - bufstart));
1149  }
1150 
1151  if ((aspectRatio > 0) && (aspectRatio != m_videoAspect))
1152  {
1153  m_videoAspect = aspectRatio;
1155  }
1156 
1157  if (height && width &&
1158  (height != m_videoHeight || m_videoWidth != width))
1159  {
1160  m_videoHeight = height;
1161  m_videoWidth = width;
1162  ResolutionChange(width, height, m_framesWrittenCount);
1163  }
1164 
1165  if (frameRate.isNonzero() && frameRate != m_frameRate)
1166  {
1167  m_frameRate = frameRate;
1168  LOG(VB_RECORD, LOG_INFO, LOC +
1169  QString("FindPSKeyFrames: frame rate = %1")
1170  .arg(frameRate.toDouble() * 1000));
1171  FrameRateChange(frameRate.toDouble() * 1000, m_framesWrittenCount);
1172  }
1173 
1174  if (hasKeyFrame || hasFrame)
1175  {
1176  // We are free to write the packet, but if we have
1177  // buffered packet[s] we have to write them first...
1178  if (!m_payloadBuffer.empty())
1179  {
1180  if (m_ringBuffer)
1181  {
1183  &m_payloadBuffer[0], m_payloadBuffer.size());
1184  }
1185  m_payloadBuffer.clear();
1186  }
1187 
1188  if (m_ringBuffer)
1189  m_ringBuffer->Write(bufstart, (bufptr - bufstart));
1190 
1191  bufstart = bufptr;
1192  }
1193 
1194  skip = std::max(m_audioBytesRemaining, m_otherBytesRemaining);
1195  }
1196 
1197  int bytes_skipped = bufend - bufptr;
1198  if (bytes_skipped > 0)
1199  {
1200  m_audioBytesRemaining -= std::min(
1201  (uint)bytes_skipped, m_audioBytesRemaining);
1202  m_videoBytesRemaining -= std::min(
1203  (uint)bytes_skipped, m_videoBytesRemaining);
1204  m_otherBytesRemaining -= std::min(
1205  (uint)bytes_skipped, m_otherBytesRemaining);
1206  }
1207 
1208  uint64_t idx = m_payloadBuffer.size();
1209  uint64_t rem = (bufend - bufstart);
1210  m_payloadBuffer.resize(idx + rem);
1211  memcpy(&m_payloadBuffer[idx], bufstart, rem);
1212 #if 0
1213  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1214  QString("idx: %1, rem: %2").arg(idx).arg(rem));
1215 #endif
1216 }
1217 
1219 {
1220  if (!_pat)
1221  {
1222  LOG(VB_RECORD, LOG_ERR, LOC + "SetPAT(NULL)");
1223  return;
1224  }
1225 
1226  QMutexLocker change_lock(&m_pidLock);
1227 
1228  int progNum = m_streamData->DesiredProgram();
1229  uint pmtpid = _pat->FindPID(progNum);
1230 
1231  if (!pmtpid)
1232  {
1233  LOG(VB_RECORD, LOG_ERR, LOC +
1234  QString("SetPAT(): Ignoring PAT not containing our desired "
1235  "program (%1)...").arg(progNum));
1236  return;
1237  }
1238 
1239  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPAT(%1 on pid 0x%2)")
1240  .arg(progNum).arg(pmtpid,0,16));
1241 
1243  m_inputPat = new ProgramAssociationTable(*_pat);
1244  delete oldpat;
1245 
1246  // Listen for the other PMTs for faster channel switching
1247  for (uint i = 0; m_inputPat && (i < m_inputPat->ProgramCount()); ++i)
1248  {
1249  uint pmt_pid = m_inputPat->ProgramPID(i);
1250  if (!m_streamData->IsListeningPID(pmt_pid))
1252  }
1253 }
1254 
1255 void DTVRecorder::HandlePMT(uint progNum, const ProgramMapTable *_pmt)
1256 {
1257  QMutexLocker change_lock(&m_pidLock);
1258 
1259  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPMT(%1, %2)").arg(progNum)
1260  .arg(_pmt == nullptr ? "NULL" : "valid"));
1261 
1262 
1263  if ((int)progNum == m_streamData->DesiredProgram())
1264  {
1265  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPMT(%1)").arg(progNum));
1266  ProgramMapTable *oldpmt = m_inputPmt;
1267  m_inputPmt = new ProgramMapTable(*_pmt);
1268 
1269  QString sistandard = GetSIStandard();
1270 
1271  bool has_no_av = true;
1272  for (uint i = 0; i < m_inputPmt->StreamCount() && has_no_av; ++i)
1273  {
1274  has_no_av &= !m_inputPmt->IsVideo(i, sistandard);
1275  has_no_av &= !m_inputPmt->IsAudio(i, sistandard);
1276  }
1277  m_hasNoAV = has_no_av;
1278 
1280  delete oldpmt;
1281  }
1282 }
1283 
1285  bool insert)
1286 {
1287  if (!pat)
1288  {
1289  LOG(VB_RECORD, LOG_ERR, LOC + "HandleSingleProgramPAT(NULL)");
1290  return;
1291  }
1292 
1293  if (!m_ringBuffer)
1294  return;
1295 
1296  uint next_cc = (pat->tsheader()->ContinuityCounter()+1)&0xf;
1297  pat->tsheader()->SetContinuityCounter(next_cc);
1298  pat->GetAsTSPackets(m_scratch, next_cc);
1299 
1300  for (const auto & tspacket : m_scratch)
1301  DTVRecorder::BufferedWrite(tspacket, insert);
1302 }
1303 
1305 {
1306  if (!pmt)
1307  {
1308  LOG(VB_RECORD, LOG_ERR, LOC + "HandleSingleProgramPMT(NULL)");
1309  return;
1310  }
1311 
1312  // We only want to do these checks once per recording
1313  bool seenVideo = (m_primaryVideoCodec != AV_CODEC_ID_NONE);
1314  bool seenAudio = (m_primaryAudioCodec != AV_CODEC_ID_NONE);
1315  uint bestAudioCodec = 0;
1316  // collect stream types for H.264 (MPEG-4 AVC) keyframe detection
1317  for (uint i = 0; i < pmt->StreamCount(); ++i)
1318  {
1319  // We only care about the first identifiable video stream
1320  if (!seenVideo && (m_primaryVideoCodec == AV_CODEC_ID_NONE) &&
1321  StreamID::IsVideo(pmt->StreamType(i)))
1322  {
1323  seenVideo = true; // Ignore other video streams
1324  switch (pmt->StreamType(i))
1325  {
1326  case StreamID::MPEG1Video:
1327  m_primaryVideoCodec = AV_CODEC_ID_MPEG1VIDEO;
1328  break;
1329  case StreamID::MPEG2Video:
1330  m_primaryVideoCodec = AV_CODEC_ID_MPEG2VIDEO;
1331  break;
1332  case StreamID::MPEG4Video:
1333  m_primaryVideoCodec = AV_CODEC_ID_MPEG4;
1334  break;
1335  case StreamID::H264Video:
1336  m_primaryVideoCodec = AV_CODEC_ID_H264;
1337  break;
1338  case StreamID::H265Video:
1339  m_primaryVideoCodec = AV_CODEC_ID_H265;
1340  break;
1342  m_primaryVideoCodec = AV_CODEC_ID_MPEG2VIDEO; // TODO Will it always be MPEG2?
1343  break;
1344  case StreamID::VC1Video:
1345  m_primaryVideoCodec = AV_CODEC_ID_VC1;
1346  break;
1347  default:
1348  break;
1349  }
1350 
1351  if (m_primaryVideoCodec != AV_CODEC_ID_NONE)
1353  }
1354 
1355  // We want the 'best' identifiable audio stream, where 'best' is
1356  // subjective and no-one will likely agree.
1357  // For now it's the 'best' codec, assuming mpeg stream types range
1358  // from worst to best, which it does
1359  if (!seenAudio && StreamID::IsAudio(pmt->StreamType(i)) &&
1360  pmt->StreamType(i) > bestAudioCodec)
1361  {
1362  bestAudioCodec = pmt->StreamType(i);
1363  switch (pmt->StreamType(i))
1364  {
1365  case StreamID::MPEG1Audio: // MPEG-1 Layer 2 (MP2)
1366  case StreamID::MPEG2Audio: // MPEG-2 Part 3 (MP2 Multichannel)
1367  m_primaryAudioCodec = AV_CODEC_ID_MP2;
1368  break;
1370  m_primaryAudioCodec = AV_CODEC_ID_AAC;
1371  break;
1373  m_primaryAudioCodec = AV_CODEC_ID_AAC_LATM;
1374  break;
1375  case StreamID::AC3Audio:
1376  m_primaryAudioCodec = AV_CODEC_ID_AC3;
1377  break;
1378  case StreamID::EAC3Audio:
1379  m_primaryAudioCodec = AV_CODEC_ID_EAC3;
1380  break;
1381  case StreamID::DTSAudio:
1382  m_primaryAudioCodec = AV_CODEC_ID_DTS;
1383  break;
1384  default:
1385  break;
1386  }
1387 
1388  if (m_primaryAudioCodec != AV_CODEC_ID_NONE)
1390  }
1391 
1392 // LOG(VB_GENERAL, LOG_DEBUG, QString("Recording(%1): Stream #%2: %3 ")
1393 // .arg(m_curRecording ? QString::number(m_curRecording->GetRecordingID()) : "")
1394 // .arg(i)
1395 // .arg(StreamID::GetDescription(pmt->StreamType(i))));
1396  m_streamId[pmt->StreamPID(i)] = pmt->StreamType(i);
1397  }
1398 
1399  // If the PCRPID is valid and the PCR is not contained
1400  // in another stream, make sure the PCR stream is not
1401  // discarded (use PrivSec type as dummy 'valid' value)
1402  if (pmt->PCRPID() != 0x1fff && pmt->FindPID(pmt->PCRPID()) == -1)
1404 
1405  if (!m_ringBuffer)
1406  return;
1407 
1408  uint next_cc = (pmt->tsheader()->ContinuityCounter()+1)&0xf;
1409  pmt->tsheader()->SetContinuityCounter(next_cc);
1410  pmt->GetAsTSPackets(m_scratch, next_cc);
1411 
1412  for (const auto & tspacket : m_scratch)
1413  DTVRecorder::BufferedWrite(tspacket, insert);
1414 }
1415 
1417 {
1418  const uint pid = tspacket.PID();
1419 
1420  if (pid != 0x1fff)
1421  m_packetCount.fetchAndAddAcquire(1);
1422 
1423  // Check continuity counter
1424  uint old_cnt = m_continuityCounter[pid];
1425  if ((pid != 0x1fff) && !CheckCC(pid, tspacket.ContinuityCounter()))
1426  {
1427  int v = m_continuityErrorCount.fetchAndAddRelaxed(1) + 1;
1428  double erate = v * 100.0 / m_packetCount.fetchAndAddRelaxed(0);
1429  LOG(VB_RECORD, LOG_WARNING, LOC +
1430  QString("PID 0x%1 discontinuity detected ((%2+1)%16!=%3) %4%")
1431  .arg(pid,0,16).arg(old_cnt,2)
1432  .arg(tspacket.ContinuityCounter(),2)
1433  .arg(erate));
1434  }
1435 
1436  // Only create fake keyframe[s] if there are no audio/video streams
1437  if (m_inputPmt && m_hasNoAV)
1438  {
1439  FindOtherKeyframes(&tspacket);
1440  m_bufferPackets = false;
1441  }
1442  else if (m_recordMptsOnly)
1443  {
1444  /* When recording the full, unfiltered, MPTS, trigger a write
1445  * every 0.5 seconds. Since the packets are unfiltered and
1446  * unprocessed we cannot wait for a keyframe to trigger the
1447  * writes. */
1448 
1449  static MythTimer s_timer;
1450 
1451  if (m_framesSeenCount++ == 0)
1452  s_timer.start();
1453 
1454  if (s_timer.elapsed() > 500) // 0.5 seconds
1455  {
1459  s_timer.addMSecs(-500);
1460  }
1461  }
1462  else if (m_streamId[pid] == 0)
1463  {
1464  // Ignore this packet if the PID should be stripped
1465  return true;
1466  }
1467  else
1468  {
1469  // There are audio/video streams. Only write the packet
1470  // if audio/video key-frames have been found
1472  return true;
1473  }
1474 
1475  BufferedWrite(tspacket);
1476 
1477  return true;
1478 }
1479 
1481 {
1482  if (!m_ringBuffer)
1483  return true;
1484 
1485  uint streamType = m_streamId[tspacket.PID()];
1486 
1487  if (tspacket.HasPayload() && tspacket.PayloadStart())
1488  {
1489  if (m_bufferPackets && m_firstKeyframe >= 0 && !m_payloadBuffer.empty())
1490  {
1491  // Flush the buffer
1492  if (m_ringBuffer)
1494  m_payloadBuffer.clear();
1495  }
1496 
1497  // buffer packets until we know if this is a keyframe
1498  m_bufferPackets = true;
1499  }
1500 
1501  // Check for keyframes and count frames
1502  if (streamType == StreamID::H264Video)
1503  FindH264Keyframes(&tspacket);
1504  else if (streamType != 0)
1505  FindMPEG2Keyframes(&tspacket);
1506  else
1507  LOG(VB_RECORD, LOG_ERR, LOC +
1508  "ProcessVideoTSPacket: unknown stream type!");
1509 
1510  return ProcessAVTSPacket(tspacket);
1511 }
1512 
1514 {
1515  if (!m_ringBuffer)
1516  return true;
1517 
1518  if (tspacket.HasPayload() && tspacket.PayloadStart())
1519  {
1520  if (m_bufferPackets && m_firstKeyframe >= 0 && !m_payloadBuffer.empty())
1521  {
1522  // Flush the buffer
1523  if (m_ringBuffer)
1525  m_payloadBuffer.clear();
1526  }
1527 
1528  // buffer packets until we know if this is a keyframe
1529  m_bufferPackets = true;
1530  }
1531 
1532  FindAudioKeyframes(&tspacket);
1533  return ProcessAVTSPacket(tspacket);
1534 }
1535 
1538 {
1539  // Sync recording start to first keyframe
1541  {
1542  if (m_bufferPackets)
1543  BufferedWrite(tspacket);
1544  return true;
1545  }
1546 
1547  const uint pid = tspacket.PID();
1548 
1549  if (pid != 0x1fff)
1550  m_packetCount.fetchAndAddAcquire(1);
1551 
1552  // Check continuity counter
1553  uint old_cnt = m_continuityCounter[pid];
1554  if ((pid != 0x1fff) && !CheckCC(pid, tspacket.ContinuityCounter()))
1555  {
1556  int v = m_continuityErrorCount.fetchAndAddRelaxed(1) + 1;
1557  double erate = v * 100.0 / m_packetCount.fetchAndAddRelaxed(0);
1558  LOG(VB_RECORD, LOG_WARNING, LOC +
1559  QString("A/V PID 0x%1 discontinuity detected ((%2+1)%16!=%3) %4%")
1560  .arg(pid,0,16).arg(old_cnt).arg(tspacket.ContinuityCounter())
1561  .arg(erate,5,'f',2));
1562  }
1563 
1564  if (!(m_pidStatus[pid] & kPayloadStartSeen))
1565  {
1567  LOG(VB_RECORD, LOG_INFO, LOC +
1568  QString("PID 0x%1 Found Payload Start").arg(pid,0,16));
1569  }
1570 
1571  BufferedWrite(tspacket);
1572 
1573  return true;
1574 }
1575 
1577 {
1579  recq->AddTSStatistics(
1580  m_continuityErrorCount.fetchAndAddRelaxed(0),
1581  m_packetCount.fetchAndAddRelaxed(0));
1582  return recq;
1583 }
1584 
1585 /* vim: set expandtab tabstop=4 shiftwidth=4: */
Used to access the data of a Transport Stream packet.
Definition: tspacket.h:166
double m_videoFrameRate
Definition: recorderbase.h:315
Group of Pictures (GOP) start code.
Definition: mpegtables.h:65
virtual void SetOption(const QString &name, const QString &value)
Set an specific option.
const TSHeader * tsheader() const
Definition: pespacket.h:84
ISO 13818-3/AMD-1 Audio using LATM syntax.
Definition: mpegtables.h:127
uint32_t GetUnitsInTick(void) const
Definition: H264Parser.h:193
int restart(void)
Returns milliseconds elapsed since last start() or restart() and resets the count.
Definition: mythtimer.cpp:62
frm_pos_map_t m_durationMap
Definition: recorderbase.h:349
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
unsigned char m_pidStatus[0x1fff+1]
Definition: dtvrecorder.h:181
uint ProgramCount(void) const
Definition: mpegtables.h:610
static const uint kMaxKeyFrameDistance
If the number of regular frames detected since the last detected keyframe exceeds this value,...
Definition: dtvrecorder.h:211
void AudioCodecChange(AVCodecID aCodec)
Note a change in audio codec.
RecordingQuality * GetRecordingQuality(const RecordingInfo *r) const override
Returns a report about the current recordings quality.
bool onKeyFrameStart(void) const
Definition: H264Parser.h:154
QAtomicInt m_timeOfLatestDataPacketInterval
Definition: recorderbase.h:367
frm_pos_map_t m_positionMap
Definition: recorderbase.h:347
virtual void InitStreamData(void)
static const FrameRate frameRateMap[16]
void WriterFlush(void)
Calls ThreadedFileWriter::Flush(void)
QAtomicInt m_packetCount
Definition: dtvrecorder.h:192
bool FindOtherKeyframes(const TSPacket *tspacket)
Non-Audio/Video data.
unsigned long long m_lastGopSeen
Definition: dtvrecorder.h:138
QString toString(void) const
Definition: recorderbase.h:44
unsigned char m_continuityCounter[0x1fff+1]
Definition: dtvrecorder.h:182
unsigned long long m_framesWrittenCount
Definition: dtvrecorder.h:195
int64_t m_tsLast[256]
Definition: dtvrecorder.h:189
void ResetForNewFile(void) override
static bool IsAudio(uint type)
Returns true iff audio is MPEG1/2, AAC, AC3 or DTS audio stream.
Definition: mpegtables.h:179
virtual bool IsListeningPID(uint pid) const
FrameRate m_tdTickFramerate
Definition: dtvrecorder.h:201
ISO 13818-7 Audio w/ADTS syntax.
Definition: mpegtables.h:126
uint32_t m_startCode
Definition: dtvrecorder.h:136
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:34
bool ProcessVideoTSPacket(const TSPacket &tspacket) override
ISO 13818-1 private tables & ITU H.222.0.
Definition: mpegtables.h:146
bool m_hasNoAV
Definition: dtvrecorder.h:175
virtual void SetCAMPMT(const ProgramMapTable *)
Definition: dtvrecorder.h:126
unsigned int ContinuityCounter(void) const
Definition: tspacket.h:87
double m_totalDuration
Definition: dtvrecorder.h:196
void HandleSingleProgramPAT(ProgramAssociationTable *pat, bool insert) override
uint getNum(void) const
Definition: recorderbase.h:42
void Reset(void) override
Reset the recorder to the startup state.
bool CheckCC(uint pid, uint new_cnt)
Definition: dtvrecorder.h:215
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
void BufferedWrite(const TSPacket &tspacket, bool insert=false)
void AspectChange(uint aspect, long long frame)
Note a change in aspect ratio in the recordedmark table.
int DesiredProgram(void) const
virtual RecordingQuality * GetRecordingQuality(const RecordingInfo *ri) const
Returns a report about the current recordings quality.
bool m_use_pts
Definition: dtvrecorder.h:187
QDateTime m_timeOfFirstData
Definition: recorderbase.h:365
uint aspectRatio(void) const
Computes aspect ratio from picture size and sample aspect ratio.
static const unsigned char kPayloadStartSeen
Definition: dtvrecorder.h:212
QElapsedTimer m_audioTimer
Definition: dtvrecorder.h:135
void SetContinuityCounter(unsigned int cc)
Definition: tspacket.h:148
void AddMPEGSPListener(MPEGSingleProgramStreamListener *val)
static guint32 * tmp
Definition: goom_core.c:35
unsigned long long m_framesSeenCount
Definition: dtvrecorder.h:194
void AddMPEGListener(MPEGStreamListener *val)
ProgramAssociationTable * m_inputPat
PAT on input side.
Definition: dtvrecorder.h:172
uint PCRPID(void) const
stream that contains program clock reference.
Definition: mpegtables.h:700
QMutex m_statisticsLock
Definition: recorderbase.h:363
void SetOptionsFromProfile(RecordingProfile *profile, const QString &videodev, const QString &audiodev, const QString &vbidev) override
Sets basic recorder options.
unsigned int m_videoBytesRemaining
Definition: dtvrecorder.h:142
void HandlePAT(const ProgramAssociationTable *_pat) override
QString m_recordingType
Definition: dtvrecorder.h:132
AVCodecID m_primaryAudioCodec
Definition: recorderbase.h:309
bool m_recordMptsOnly
Definition: dtvrecorder.h:179
virtual void SetStreamData(MPEGStreamData *data)
unsigned long long m_lastKeyframeSeen
Definition: dtvrecorder.h:140
bool m_seenSps
Definition: dtvrecorder.h:151
unsigned int AFCOffset(void) const
Definition: tspacket.h:208
Sequence (SEQ) start code contains frame size, aspect ratio and fps.
Definition: mpegtables.h:56
uint64_t m_tdTickCount
Definition: dtvrecorder.h:200
bool PayloadStart(void) const
Definition: tspacket.h:67
void SetTotalFrames(uint64_t total_frames)
Note the total frames in the recordedmark table.
static bool IsVideo(uint type)
Returns true iff video is an MPEG1/2/3, H264 or open cable video stream.
Definition: mpegtables.h:168
uint StreamType(uint i) const
Definition: mpegtables.h:712
double toDouble(void) const
Definition: recorderbase.h:40
RingBuffer * m_ringBuffer
Definition: recorderbase.h:304
void SetDuration(uint64_t duration)
Note the total duration in the recordedmark table.
void HandleH264Keyframe(void)
This save the current frame to the position maps and handles ringbuffer switching.
virtual void FinishRecording(void)
double m_tdBase
Definition: dtvrecorder.h:199
virtual QString GetSIStandard(void) const
Definition: dtvrecorder.h:125
AVContainer m_containerFormat
Definition: recorderbase.h:307
uint StreamPID(uint i) const
Definition: mpegtables.h:715
uint64_t m_tsCount[256]
Definition: dtvrecorder.h:188
void FinishRecording(void) override
Flushes the ringbuffer, and if this is not a live LiveTV recording saves the position map and filesiz...
vector< unsigned char > m_payloadBuffer
Definition: dtvrecorder.h:167
uint StreamCount(void) const
Definition: mpegtables.h:724
bool ProcessAVTSPacket(const TSPacket &tspacket)
Common code for processing either audio or video packets.
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:142
QAtomicInt m_timeOfLatestDataCount
Definition: recorderbase.h:366
void SetPositionMapType(MarkTypes type)
Set seektable type.
Definition: recorderbase.h:271
Last MPEG-1/2 audio stream (w/ext hdr)
Definition: mpegtables.h:78
SMPTE 421M video codec (aka VC1) in Blu-Ray.
Definition: mpegtables.h:121
ISO 11172-3.
Definition: mpegtables.h:124
bool HasPayload(void) const
Definition: tspacket.h:94
int64_t m_tsFirst[256]
Definition: dtvrecorder.h:190
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
int m_progressiveSequence
Definition: dtvrecorder.h:146
QString GetSetting(const QString &key, const QString &defaultval="")
void SetDesiredProgram(int p)
void HandleKeyframe(int64_t extra)
This save the current frame to the position maps and handles ringbuffer switching.
void getFrameRate(FrameRate &result) const
First MPEG-1/2 audio stream (w/ext hdr)
Definition: mpegtables.h:76
unsigned int m_otherBytesRemaining
Definition: dtvrecorder.h:143
ISO 23008-2 & ITU H.265 (aka HEVC, Ultra HD)
Definition: mpegtables.h:119
QDateTime m_timeOfLatestData
Definition: recorderbase.h:368
int m_firstKeyframe
Definition: dtvrecorder.h:137
DTVRecorder(TVRec *rec)
Definition: dtvrecorder.cpp:46
#define LOC
DTVRecorder – base class for Digital Televison recorders Copyright 2003-2004 by Brandon Beattie,...
Definition: dtvrecorder.cpp:32
virtual void SetRecordingStatus(RecStatus::Type status, const QString &file, int line)
bool FindAudioKeyframes(const TSPacket *tspacket)
ISO 13818-3.
Definition: mpegtables.h:125
void SendMythSystemRecEvent(const QString &msg, const RecordingInfo *pginfo)
unsigned int uint
Definition: compat.h:140
bool onFrameStart(void) const
Definition: H264Parser.h:153
void FrameRateChange(uint framerate, long long frame)
Note a change in video frame rate in the recordedmark table.
bool stateChanged(void) const
Definition: H264Parser.h:142
A/53 Part 3:2009 6.7.1.
Definition: mpegtables.h:128
unsigned int m_audioBytesRemaining
Definition: dtvrecorder.h:141
bool m_hasWrittenOtherKeyframe
Definition: dtvrecorder.h:157
MPEGStreamData * GetStreamData(void) const
Definition: dtvrecorder.h:59
MythTimer m_timeOfLatestDataTimer
Definition: recorderbase.h:369
The Program Association Table lists all the programs in a stream, and is always found on PID 0.
Definition: mpegtables.h:589
This is the abstract base class for supporting recorder hardware.
Definition: recorderbase.h:66
bool isNonzero(void) const
Definition: recorderbase.h:41
QDateTime m_tsFirstDt[256]
Definition: dtvrecorder.h:191
void ClearStatistics(void) override
void SetOption(const QString &name, const QString &value) override
Set an specific option.
Definition: dtvrecorder.cpp:79
bool ProcessAudioTSPacket(const TSPacket &tspacket) override
QAtomicInt m_continuityErrorCount
Definition: dtvrecorder.h:193
static QDateTime ts_to_qdatetime(uint64_t pts, uint64_t pts_first, QDateTime &pts_first_dt)
void UpdateFramesWritten(void)
virtual void AddListeningPID(uint pid, PIDPriority priority=kPIDPriorityNormal)
const unsigned char * data(void) const
Definition: tspacket.h:152
ISO 14492-2 (aka MPEG-4)
Definition: mpegtables.h:117
void addMSecs(int ms)
Adds an offset to the last call to start() or restart().
Definition: mythtimer.cpp:145
RecordingGaps m_recordingGaps
Definition: recorderbase.h:370
AVCodecID m_primaryVideoCodec
Definition: recorderbase.h:308
uint32_t addBytes(const uint8_t *bytes, uint32_t byte_count, uint64_t stream_offset)
Definition: H264Parser.cpp:422
FrameRate m_frameRate
Definition: recorderbase.h:321
static int64_t extract_timestamp(const uint8_t *bufptr, int bytes_left, int pts_or_dts)
int GetNumSetting(const QString &key, int defaultval=0)
long long GetWritePosition(void) const
Returns how far into a ThreadedFileWriter file we have written.
ISO 14492-10 & ITU H.264 (aka MPEG-4-AVC)
Definition: mpegtables.h:118
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void ResolutionChange(uint width, uint height, long long frame)
Note a change in video size in the recordedmark table.
int FindPID(uint pid) const
Locates stream index of pid.
Definition: mpegtables.h:771
static const unsigned int kSize
Definition: tspacket.h:220
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:441
void HandlePMT(uint progNum, const ProgramMapTable *_pmt) override
bool ProcessTSPacket(const TSPacket &tspacket) override
uint m_videoAspect
Definition: recorderbase.h:317
static const uint kTimeOfLatestDataIntervalTarget
timeOfLatest update interval target in milliseconds.
Definition: recorderbase.h:372
bool m_musicChoice
Definition: dtvrecorder.h:205
unsigned int PID(void) const
Definition: tspacket.h:71
int elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:90
frame_type FieldType(void) const
Definition: H264Parser.h:146
bool FindMPEG2Keyframes(const TSPacket *tspacket)
Locates the keyframes and saves them to the position map.
unsigned long long m_lastSeqSeen
Definition: dtvrecorder.h:139
virtual bool CheckForRingBufferSwitch(void)
If requested, switch to new RingBuffer/ProgramInfo objects.
unsigned char m_streamId[0x1fff+1]
Definition: dtvrecorder.h:180
~DTVRecorder() override
Definition: dtvrecorder.cpp:60
void GetAsTSPackets(vector< TSPacket > &output, uint cc) const
Returns payload only PESPacket as series of TSPackets.
Definition: pespacket.cpp:104
uint FindPID(uint progNum) const
Definition: mpegtables.h:629
vector< TSPacket > m_scratch
Definition: dtvrecorder.h:183
int m_repeatPict
Definition: dtvrecorder.h:147
frm_pos_map_t m_positionMapDelta
Definition: recorderbase.h:348
void SetIntOption(RecordingProfile *profile, const QString &name)
Convenience function used to set integer options from a profile.
frm_pos_map_t m_durationMapDelta
Definition: recorderbase.h:350
int Write(const void *buf, uint count)
Writes buffer to ThreadedFileWriter::Write(const void*,uint)
RecordingInfo * m_curRecording
Definition: recorderbase.h:323
virtual void StopRecording(void)
StopRecording() signals to the recorder that it should stop recording and exit cleanly.
void HandleTimestamps(int stream_id, int64_t pts, int64_t dts)
bool m_waitForKeyframeOption
Wait for the a GOP/SEQ-start before sending data.
Definition: dtvrecorder.h:155
QMutex m_pidLock
Definition: dtvrecorder.h:170
bool IsVideo(uint i, const QString &sistandard) const
Returns true iff the stream at index i is a video stream.
Definition: mpegtables.cpp:513
MPEGStreamData * m_streamData
Definition: dtvrecorder.h:163
uint pictureHeight(void) const
Definition: H264Parser.h:157
uint pictureWidth(void) const
Definition: H264Parser.h:156
void VideoCodecChange(AVCodecID vCodec)
Note a change in video codec.
QString m_error
non-empty iff irrecoverable recording error detected
Definition: dtvrecorder.h:161
virtual void ClearStatistics(void)
First MPEG-1/2 video stream (w/ext hdr)
Definition: mpegtables.h:80
ISO 13818-2 & ITU H.262 (aka MPEG-2)
Definition: mpegtables.h:116
bool FindH264Keyframes(const TSPacket *tspacket)
This searches the TS packet to identify keyframes.
bool m_recordMpts
Definition: dtvrecorder.h:178
A/53 Part 3:2009 6.7.3.
Definition: mpegtables.h:129
uint32_t GetTimeScale(void) const
Definition: H264Parser.h:191
uint64_t keyframeAUstreamOffset(void) const
Definition: H264Parser.h:169
bool IsDamaged(void) const
void SetStrOption(RecordingProfile *profile, const QString &name)
Convenience function used to set QString options from a profile.
Followed by an extension byte, not documented here.
Definition: mpegtables.h:59
int m_minimumRecordingQuality
Definition: dtvrecorder.h:186
ProgramMapTable * m_inputPmt
PMT on input side.
Definition: dtvrecorder.h:174
static QString toString(Type recstatus, uint id)
Converts "recstatus" into a short (unreadable) string.
Definition: recStatus.cpp:39
A PMT table maps a program described in the ProgramAssociationTable to various PID's which describe t...
Definition: mpegtables.h:666
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
Last MPEG-1/2 video stream (w/ext hdr)
Definition: mpegtables.h:82
void ClearPositionMap(MarkTypes type) const
bool m_bufferPackets
Definition: dtvrecorder.h:166
Encapsulates data about MPEG stream and emits events for each table.
QAtomicInt m_timeOfFirstDataIsSet
Definition: recorderbase.h:364
void FindPSKeyFrames(const uint8_t *buffer, uint len) override
bool m_pesSynced
Definition: dtvrecorder.h:150
QMutex m_positionMapLock
Definition: recorderbase.h:346
H264Parser m_h264Parser
Definition: dtvrecorder.h:152
void HandleSingleProgramPMT(ProgramMapTable *pmt, bool insert) override
ISO 11172-2 (aka MPEG-1)
Definition: mpegtables.h:115
bool IsAudio(uint i, const QString &sistandard) const
Returns true iff the stream at index i is an audio stream.
Definition: mpegtables.cpp:535
uint getDen(void) const
Definition: recorderbase.h:43
uint ProgramPID(uint i) const
Definition: mpegtables.h:620
void AddTSStatistics(int continuity_error_count, int packet_count)
uint m_videoHeight
Definition: recorderbase.h:319
Always MPEG-2??
Definition: mpegtables.h:120