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