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_payload_buffer.reserve(TSPacket::kSize * (50 + 1));
51 
53 
55  gCoreContext->GetNumSetting("MinimumRecordingQuality", 95);
56 
58 }
59 
61 {
62  StopRecording();
63 
65 
66  if (m_input_pat)
67  {
68  delete m_input_pat;
69  m_input_pat = nullptr;
70  }
71 
72  if (m_input_pmt)
73  {
74  delete m_input_pmt;
75  m_input_pmt = nullptr;
76  }
77 }
78 
79 void DTVRecorder::SetOption(const QString &name, const QString &value)
80 {
81  if (name == "recordingtype")
82  m_recording_type = 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_wait_for_keyframe_option = (value == 1);
94  else if (name == "recordmpts")
95  m_record_mpts = (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_total_duration * 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_start_code = 0xffffffff;
142  m_first_keyframe = -1;
145  m_last_gop_seen = 0;
146  m_last_seq_seen = 0;
150  //_recording
151  m_error = QString();
152 
154  m_repeat_pict = 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_ts_count, 0, sizeof(m_ts_count));
172  for (int i = 0; i < 256; ++i)
173  m_ts_last[i] = -1LL;
174  for (int i = 0; i < 256; ++i)
175  m_ts_first[i] = -1LL;
176  //m_ts_first_dt -- doesn't need to be cleared only used if m_ts_first>=0
177  m_packet_count.fetchAndStoreRelaxed(0);
178  m_continuity_error_count.fetchAndStoreRelaxed(0);
181  m_total_duration = 0;
182  m_td_base = 0;
183  m_td_tick_count = 0;
185 }
186 
187 // documented in recorderbase.h
189 {
190  LOG(VB_RECORD, LOG_INFO, LOC + "Reset(void)");
191  ResetForNewFile();
192 
193  m_start_code = 0xffffffff;
194 
195  if (m_curRecording)
196  {
199  }
200 }
201 
203 {
204  if (data == m_stream_data)
205  return;
206 
207  MPEGStreamData *old_data = m_stream_data;
208  m_stream_data = data;
209  delete old_data;
210 
211  if (m_stream_data)
212  InitStreamData();
213 }
214 
216 {
219 
220  auto *dvb = dynamic_cast<DVBStreamData*>(m_stream_data);
221  if (dvb)
222  dvb->AddDVBMainListener(this);
223 
224  auto *atsc = dynamic_cast<ATSCStreamData*>(m_stream_data);
225 
226  if (atsc && atsc->DesiredMinorChannel())
227  atsc->SetDesiredChannel(atsc->DesiredMajorChannel(),
228  atsc->DesiredMinorChannel());
229  else if (m_stream_data->DesiredProgram() >= 0)
231 }
232 
233 void DTVRecorder::BufferedWrite(const TSPacket &tspacket, bool insert)
234 {
235  if (!insert) // PAT/PMT may need inserted in front of any buffered data
236  {
237  // delay until first GOP to avoid decoder crash on res change
239  m_first_keyframe < 0)
240  return;
241 
242  if (m_curRecording && m_timeOfFirstDataIsSet.testAndSetRelaxed(0,1))
243  {
244  QMutexLocker locker(&m_statisticsLock);
248  }
249 
250  int val = m_timeOfLatestDataCount.fetchAndAddRelaxed(1);
251  int thresh = m_timeOfLatestDataPacketInterval.fetchAndAddRelaxed(0);
252  if (val > thresh)
253  {
254  QMutexLocker locker(&m_statisticsLock);
256  int interval = thresh;
257  if (elapsed > kTimeOfLatestDataIntervalTarget + 250)
259  .fetchAndStoreRelaxed(thresh * 4 / 5);
260  else if (elapsed + 250 < kTimeOfLatestDataIntervalTarget)
262  .fetchAndStoreRelaxed(thresh * 9 / 8);
263 
264  m_timeOfLatestDataCount.fetchAndStoreRelaxed(1);
266 
267  LOG(VB_RECORD, LOG_DEBUG, LOC +
268  QString("Updating timeOfLatestData elapsed(%1) interval(%2)")
269  .arg(elapsed).arg(interval));
270  }
271 
272  // Do we have to buffer the packet for exact keyframe detection?
273  if (m_buffer_packets)
274  {
275  int idx = m_payload_buffer.size();
276  m_payload_buffer.resize(idx + TSPacket::kSize);
277  memcpy(&m_payload_buffer[idx], tspacket.data(), TSPacket::kSize);
278  return;
279  }
280 
281  // We are free to write the packet, but if we have buffered packet[s]
282  // we have to write them first...
283  if (!m_payload_buffer.empty())
284  {
285  if (m_ringBuffer)
287  m_payload_buffer.clear();
288  }
289  }
290 
291  if (m_ringBuffer && m_ringBuffer->Write(tspacket.data(), TSPacket::kSize) < 0 &&
293  {
294  LOG(VB_GENERAL, LOG_INFO, LOC +
295  QString("BufferedWrite: Writes are failing, "
296  "setting status to %1")
298  SetRecordingStatus(RecStatus::Failing, __FILE__, __LINE__);
299  }
300 }
301 
303 static int64_t extract_timestamp(
304  const uint8_t *bufptr, int bytes_left, int pts_or_dts)
305 {
306  if (bytes_left < 4)
307  return -1LL;
308 
309  bool has_pts = (bufptr[3] & 0x80) != 0;
310  int offset = 5;
311  if (((kExtractPTS == pts_or_dts) && !has_pts) || (offset + 5 > bytes_left))
312  return -1LL;
313 
314  bool has_dts = (bufptr[3] & 0x40) != 0;
315  if (kExtractDTS == pts_or_dts)
316  {
317  if (!has_dts)
318  return -1LL;
319  offset += has_pts ? 5 : 0;
320  if (offset + 5 > bytes_left)
321  return -1LL;
322  }
323 
324  return ((uint64_t(bufptr[offset+0] & 0x0e) << 29) |
325  (uint64_t(bufptr[offset+1] ) << 22) |
326  (uint64_t(bufptr[offset+2] & 0xfe) << 14) |
327  (uint64_t(bufptr[offset+3] ) << 7) |
328  (uint64_t(bufptr[offset+4] & 0xfe) >> 1));
329 }
330 
331 static QDateTime ts_to_qdatetime(
332  uint64_t pts, uint64_t pts_first, QDateTime &pts_first_dt)
333 {
334  if (pts < pts_first)
335  pts += 0x1FFFFFFFFLL;
336  const QDateTime& dt = pts_first_dt;
337  return dt.addMSecs((pts - pts_first)/90);
338 }
339 
340 static const FrameRate frameRateMap[16] = {
341  FrameRate(0), FrameRate(24000, 1001), FrameRate(24),
342  FrameRate(25), FrameRate(30000, 1001), FrameRate(30),
343  FrameRate(50), FrameRate(60000, 1001), FrameRate(60),
344  FrameRate(0), FrameRate(0), FrameRate(0),
345  FrameRate(0), FrameRate(0), FrameRate(0),
346  FrameRate(0)
347 };
348 
376 {
377  if (!tspacket->HasPayload()) // no payload to scan
378  return m_first_keyframe >= 0;
379 
380  if (!m_ringBuffer)
381  return m_first_keyframe >= 0;
382 
383  // if packet contains start of PES packet, start
384  // looking for first byte of MPEG start code (3 bytes 0 0 1)
385  // otherwise, pick up search where we left off.
386  const bool payloadStart = tspacket->PayloadStart();
387  m_start_code = (payloadStart) ? 0xffffffff : m_start_code;
388 
389  // Just make these local for efficiency reasons (gcc not so smart..)
390  const uint maxKFD = kMaxKeyFrameDistance;
391  bool hasFrame = false;
392  bool hasKeyFrame = false;
393 
394  uint aspectRatio = 0;
395  uint height = 0;
396  uint width = 0;
397  FrameRate frameRate(0);
398 
399  // Scan for PES header codes; specifically picture_start
400  // sequence_start (SEQ) and group_start (GOP).
401  // 00 00 01 00: picture_start_code
402  // 00 00 01 B8: group_start_code
403  // 00 00 01 B3: seq_start_code
404  // 00 00 01 B5: ext_start_code
405  // (there are others that we don't care about)
406  const uint8_t *bufptr = tspacket->data() + tspacket->AFCOffset();
407  const uint8_t *bufend = tspacket->data() + TSPacket::kSize;
408  m_repeat_pict = 0;
409 
410  while (bufptr < bufend)
411  {
412  bufptr = avpriv_find_start_code(bufptr, bufend, &m_start_code);
413  int bytes_left = bufend - bufptr;
414  if ((m_start_code & 0xffffff00) == 0x00000100)
415  {
416  // At this point we have seen the start code 0 0 1
417  // the next byte will be the PES packet stream id.
418  const int stream_id = m_start_code & 0x000000ff;
419  if (PESStreamID::PictureStartCode == stream_id)
420  hasFrame = true;
421  else if (PESStreamID::GOPStartCode == stream_id)
422  {
424  hasKeyFrame = true;
425  }
426  else if (PESStreamID::SequenceStartCode == stream_id)
427  {
429  hasKeyFrame |= (m_last_gop_seen + maxKFD)<m_frames_seen_count;
430 
431  // Look for aspectRatio changes and store them in the database
432  aspectRatio = (bufptr[3] >> 4);
433 
434  // Get resolution
435  height = ((bufptr[1] & 0xf) << 8) | bufptr[2];
436  width = (bufptr[0] <<4) | (bufptr[1]>>4);
437 
438  frameRate = frameRateMap[(bufptr[3] & 0x0000000f)];
439  }
440  else if (PESStreamID::MPEG2ExtensionStartCode == stream_id)
441  {
442  if (bytes_left >= 1)
443  {
444  int ext_type = (bufptr[0] >> 4);
445  switch(ext_type)
446  {
447  case 0x1: /* sequence extension */
448  if (bytes_left >= 6)
449  {
450  m_progressive_sequence = bufptr[1] & (1 << 3);
451  }
452  break;
453  case 0x8: /* picture coding extension */
454  if (bytes_left >= 5)
455  {
456  //int picture_structure = bufptr[2]&3;
457  int top_field_first = bufptr[3] & (1 << 7);
458  int repeat_first_field = bufptr[3] & (1 << 1);
459  int progressive_frame = bufptr[4] & (1 << 7);
460 
461  /* check if we must repeat the frame */
462  m_repeat_pict = 1;
463  if (repeat_first_field)
464  {
466  {
467  if (top_field_first)
468  m_repeat_pict = 5;
469  else
470  m_repeat_pict = 3;
471  }
472  else if (progressive_frame)
473  {
474  m_repeat_pict = 2;
475  }
476  }
477  // The m_repeat_pict code above matches
478  // mpegvideo_extract_headers(), but the
479  // code in mpeg_field_start() computes a
480  // value one less, which seems correct.
481  --m_repeat_pict;
482  }
483  break;
484  }
485  }
486  }
487  if ((stream_id >= PESStreamID::MPEGVideoStreamBegin) &&
488  (stream_id <= PESStreamID::MPEGVideoStreamEnd))
489  {
490  int64_t pts = extract_timestamp(
491  bufptr, bytes_left, kExtractPTS);
492  int64_t dts = extract_timestamp(
493  bufptr, bytes_left, kExtractPTS);
494  HandleTimestamps(stream_id, pts, dts);
495  // Detect music choice program (very slow frame rate and audio)
496  if (m_first_keyframe < 0
497  && m_ts_last[stream_id] - m_ts_first[stream_id] > 3*90000)
498  {
499  hasKeyFrame = true;
500  m_music_choice = true;
501  LOG(VB_GENERAL, LOG_INFO, LOC + "Music Choice program detected");
502  }
503  }
504  }
505  }
506 
507  if (hasFrame && !hasKeyFrame)
508  {
509  // If we have seen kMaxKeyFrameDistance frames since the
510  // last GOP or SEQ stream_id, then pretend this picture
511  // is a keyframe. We may get artifacts but at least
512  // we will be able to skip frames.
513  hasKeyFrame = ((m_frames_seen_count & 0xf) == 0U);
514  hasKeyFrame &= (m_last_gop_seen + maxKFD) < m_frames_seen_count;
515  hasKeyFrame &= (m_last_seq_seen + maxKFD) < m_frames_seen_count;
516  }
517 
518  // m_buffer_packets will only be true if a payload start has been seen
519  if (hasKeyFrame && (m_buffer_packets || m_first_keyframe >= 0))
520  {
521  LOG(VB_RECORD, LOG_DEBUG, LOC + QString
522  ("Keyframe @ %1 + %2 = %3")
524  .arg(m_payload_buffer.size())
526 
528  HandleKeyframe(0);
529  }
530 
531  if (hasFrame)
532  {
533  LOG(VB_RECORD, LOG_DEBUG, LOC + QString
534  ("Frame @ %1 + %2 = %3")
536  .arg(m_payload_buffer.size())
538 
539  m_buffer_packets = false; // We now know if it is a keyframe, or not
543  else
544  {
545  /* Found a frame that is not a keyframe, and we want to
546  * start on a keyframe */
547  m_payload_buffer.clear();
548  }
549  }
550 
551  if ((aspectRatio > 0) && (aspectRatio != m_videoAspect))
552  {
553  m_videoAspect = aspectRatio;
555  }
556 
557  if (height && width && (height != m_videoHeight || m_videoWidth != width))
558  {
559  m_videoHeight = height;
560  m_videoWidth = width;
562  }
563 
564  if (frameRate.isNonzero() && frameRate != m_frameRate)
565  {
566  m_frameRate = frameRate;
567  LOG(VB_RECORD, LOG_INFO, LOC +
568  QString("FindMPEG2Keyframes: frame rate = %1")
569  .arg(frameRate.toDouble() * 1000));
570  FrameRateChange(frameRate.toDouble() * 1000, m_frames_written_count);
571  }
572 
573  return m_first_keyframe >= 0;
574 }
575 
576 void DTVRecorder::HandleTimestamps(int stream_id, int64_t pts, int64_t dts)
577 {
578  if (pts < 0)
579  {
580  m_ts_last[stream_id] = -1;
581  return;
582  }
583 
584  if ((dts < 0) && !m_use_pts)
585  {
586  m_ts_last[stream_id] = -1;
587  m_use_pts = true;
588  LOG(VB_RECORD, LOG_DEBUG,
589  "Switching from dts tracking to pts tracking." +
590  QString("TS count is %1").arg(m_ts_count[stream_id]));
591  }
592 
593  int64_t ts = dts;
594  int64_t gap_threshold = 90000; // 1 second
595  if (m_use_pts)
596  {
597  ts = dts;
598  gap_threshold = 2*90000; // two seconds, compensate for GOP ordering
599  }
600 
601  if (m_music_choice)
602  gap_threshold = 8*90000; // music choice uses frames every 6 seconds
603 
604  if (m_ts_last[stream_id] >= 0)
605  {
606  int64_t diff = ts - m_ts_last[stream_id];
607 
608  // time jumped back more then 10 seconds, handle it as 33bit overflow
609  if (diff < (10 * -90000))
610  // MAX_PTS is 33bits all 1
611  diff += 0x1ffffffffLL;
612 
613  // FIXME why do we handle negative gaps (aka overlap) like a gap?
614  if (diff < 0)
615  diff = -diff;
616 
617  if (diff > gap_threshold && m_first_keyframe >= 0)
618  {
619  QMutexLocker locker(&m_statisticsLock);
620 
621  m_recordingGaps.push_back(
622  RecordingGap(
624  m_ts_last[stream_id], m_ts_first[stream_id],
625  m_ts_first_dt[stream_id]),
627  ts, m_ts_first[stream_id], m_ts_first_dt[stream_id])));
628  LOG(VB_RECORD, LOG_DEBUG, LOC + QString("Inserted gap %1 dur %2")
629  .arg(m_recordingGaps.back().toString()).arg(diff/90000.0));
630 
632  {
634  if (recq.IsDamaged())
635  {
636  LOG(VB_GENERAL, LOG_INFO, LOC +
637  QString("HandleTimestamps: too much damage, "
638  "setting status to %1")
640  SetRecordingStatus(RecStatus::Failing, __FILE__, __LINE__);
641  }
642  }
643  }
644  }
645 
646  m_ts_last[stream_id] = ts;
647 
648  if (m_ts_count[stream_id] < 30)
649  {
650  if (!m_ts_count[stream_id] || (ts < m_ts_first[stream_id]))
651  {
652  m_ts_first[stream_id] = ts;
653  m_ts_first_dt[stream_id] = MythDate::current();
654  }
655  }
656 
657  m_ts_count[stream_id]++;
658 }
659 
661 {
666  {
668  m_td_tick_count = 0;
670  }
673  {
674  m_total_duration = m_td_base + (int64_t) 500 * m_td_tick_count *
676  }
677 
678  if (m_frames_written_count < 2000 || m_frames_written_count % 1000 == 0)
679  LOG(VB_RECORD, LOG_DEBUG,
680  QString("count=%1 m_frameRate=%2 tick_frameRate=%3 "
681  "tick_cnt=%4 tick_base=%5 _total_dur=%6")
683  .arg(m_frameRate.toString())
685  .arg(m_td_tick_count)
686  .arg(m_td_base)
687  .arg(m_total_duration));
688 }
689 
690 bool DTVRecorder::FindAudioKeyframes(const TSPacket* /*tspacket*/)
691 {
692  bool hasKeyFrame = false;
693  if (!m_ringBuffer || (GetStreamData()->VideoPIDSingleProgram() <= 0x1fff))
694  return hasKeyFrame;
695 
696  static constexpr uint64_t kMsecPerDay = 24 * 60 * 60 * 1000ULL;
697  const double frame_interval = (1000.0 / m_video_frame_rate);
698  uint64_t elapsed = (uint64_t) max(m_audio_timer.elapsed(), 0);
699  auto expected_frame = (uint64_t) ((double)elapsed / frame_interval);
700 
701  while (m_frames_seen_count > expected_frame + 10000)
702  expected_frame += (uint64_t) ((double)kMsecPerDay / frame_interval);
703 
704  if (!m_frames_seen_count || (m_frames_seen_count < expected_frame))
705  {
706  if (!m_frames_seen_count)
707  m_audio_timer.start();
708 
709  m_buffer_packets = false;
711 
712  if (1 == (m_frames_seen_count & 0x7))
713  {
716  hasKeyFrame = true;
717  }
718 
721  }
722 
723  return hasKeyFrame;
724 }
725 
728 bool DTVRecorder::FindOtherKeyframes(const TSPacket */*tspacket*/)
729 {
730  if (!m_ringBuffer || (GetStreamData()->VideoPIDSingleProgram() <= 0x1fff))
731  return true;
732 
734  return true;
735 
736  LOG(VB_RECORD, LOG_INFO, LOC + "DSMCC - FindOtherKeyframes() - "
737  "generating initial key-frame");
738 
742 
744 
746 
747  return true;
748 }
749 
754 void DTVRecorder::HandleKeyframe(int64_t extra)
755 {
756  if (!m_ringBuffer)
757  return;
758 
759  // Perform ringbuffer switch if needed.
761 
762  uint64_t frameNum = m_frames_written_count;
763  if (m_first_keyframe < 0)
764  {
765  m_first_keyframe = frameNum;
766  SendMythSystemRecEvent("REC_STARTED_WRITING", m_curRecording);
767  }
768 
769  // Add key frame to position map
770  m_positionMapLock.lock();
771  if (!m_positionMap.contains(frameNum))
772  {
773  int64_t startpos = m_ringBuffer->GetWritePosition() + extra;
774 
775  // Don't put negative offsets into the database, they get munged into
776  // MAX_INT64 - offset, which is an exceedingly large number, and
777  // certainly not valid.
778  if (startpos >= 0)
779  {
780  m_positionMapDelta[frameNum] = startpos;
781  m_positionMap[frameNum] = startpos;
782  m_durationMap[frameNum] = llround(m_total_duration);
783  m_durationMapDelta[frameNum] = llround(m_total_duration);
784  }
785  }
786  m_positionMapLock.unlock();
787 }
788 
795 {
796  if (!tspacket->HasPayload()) // no payload to scan
797  return m_first_keyframe >= 0;
798 
799  if (!m_ringBuffer)
800  {
801  LOG(VB_GENERAL, LOG_ERR, LOC + "FindH264Keyframes: No ringbuffer");
802  return m_first_keyframe >= 0;
803  }
804 
805  const bool payloadStart = tspacket->PayloadStart();
806  if (payloadStart)
807  {
808  // reset PES sync state
809  m_pes_synced = false;
810  m_start_code = 0xffffffff;
811  }
812 
813  uint aspectRatio = 0;
814  uint height = 0;
815  uint width = 0;
816  FrameRate frameRate(0);
817 
818  bool hasFrame = false;
819  bool hasKeyFrame = false;
820 
821  // scan for PES packets and H.264 NAL units
822  uint i = tspacket->AFCOffset();
823  for (; i < TSPacket::kSize; ++i)
824  {
825  // special handling required when a new PES packet begins
826  if (payloadStart && !m_pes_synced)
827  {
828  // bounds check
829  if (i + 2 >= TSPacket::kSize)
830  {
831  LOG(VB_GENERAL, LOG_ERR, LOC +
832  "PES packet start code may overflow to next TS packet, "
833  "aborting keyframe search");
834  break;
835  }
836 
837  // must find the PES start code
838  if (tspacket->data()[i++] != 0x00 ||
839  tspacket->data()[i++] != 0x00 ||
840  tspacket->data()[i++] != 0x01)
841  {
842  LOG(VB_GENERAL, LOG_ERR, LOC +
843  "PES start code not found in TS packet with PUSI set");
844  break;
845  }
846 
847  // bounds check
848  if (i + 5 >= TSPacket::kSize)
849  {
850  LOG(VB_GENERAL, LOG_ERR, LOC +
851  "PES packet headers overflow to next TS packet, "
852  "aborting keyframe search");
853  break;
854  }
855 
856  // now we need to compute where the PES payload begins
857  // skip past the stream_id (+1)
858  // the next two bytes are the PES packet length (+2)
859  // after that, one byte of PES packet control bits (+1)
860  // after that, one byte of PES header flags bits (+1)
861  // and finally, one byte for the PES header length
862  const unsigned char pes_header_length = tspacket->data()[i + 5];
863 
864  // bounds check
865  if ((i + 6 + pes_header_length) >= TSPacket::kSize)
866  {
867  LOG(VB_GENERAL, LOG_ERR, LOC +
868  "PES packet headers overflow to next TS packet, "
869  "aborting keyframe search");
870  break;
871  }
872 
873  // we now know where the PES payload is
874  // normally, we should have used 6, but use 5 because the for
875  // loop will bump i
876  i += 5 + pes_header_length;
877  m_pes_synced = true;
878 
879 #if 0
880  LOG(VB_RECORD, LOG_DEBUG, LOC + "PES synced");
881 #endif
882  continue;
883  }
884 
885  // ain't going nowhere if we're not PES synced
886  if (!m_pes_synced)
887  break;
888 
889  // scan for a NAL unit start code
890 
891  uint32_t bytes_used = m_h264_parser.addBytes
892  (tspacket->data() + i, TSPacket::kSize - i,
894  i += (bytes_used - 1);
895 
897  {
898  if (m_h264_parser.onFrameStart() &&
900  {
901  hasKeyFrame = m_h264_parser.onKeyFrameStart();
902  hasFrame = true;
903  m_seen_sps |= hasKeyFrame;
904 
905  width = m_h264_parser.pictureWidth();
906  height = m_h264_parser.pictureHeight();
907  aspectRatio = m_h264_parser.aspectRatio();
908  m_h264_parser.getFrameRate(frameRate);
909  }
910  }
911  } // for (; i < TSPacket::kSize; ++i)
912 
913  // If it has been more than 511 frames since the last keyframe,
914  // pretend we have one.
915  if (hasFrame && !hasKeyFrame &&
917  {
918  hasKeyFrame = true;
919  LOG(VB_RECORD, LOG_WARNING, LOC +
920  QString("FindH264Keyframes: %1 frames without a keyframe.")
922  }
923 
924  // m_buffer_packets will only be true if a payload start has been seen
925  if (hasKeyFrame && (m_buffer_packets || m_first_keyframe >= 0))
926  {
927  LOG(VB_RECORD, LOG_DEBUG, LOC + QString
928  ("Keyframe @ %1 + %2 = %3 AU %4")
930  .arg(m_payload_buffer.size())
933 
936  }
937 
938  if (hasFrame)
939  {
940  LOG(VB_RECORD, LOG_DEBUG, LOC + QString
941  ("Frame @ %1 + %2 = %3 AU %4")
943  .arg(m_payload_buffer.size())
946 
947  m_buffer_packets = false; // We now know if this is a keyframe
951  else
952  {
953  /* Found a frame that is not a keyframe, and we want to
954  * start on a keyframe */
955  m_payload_buffer.clear();
956  }
957  }
958 
959  if ((aspectRatio > 0) && (aspectRatio != m_videoAspect))
960  {
961  m_videoAspect = aspectRatio;
963  }
964 
965  if (height && width && (height != m_videoHeight || m_videoWidth != width))
966  {
967  m_videoHeight = height;
968  m_videoWidth = width;
970  }
971 
972  if (frameRate.isNonzero() && frameRate != m_frameRate)
973  {
974  LOG(VB_RECORD, LOG_INFO, LOC +
975  QString("FindH264Keyframes: timescale: %1, tick: %2, framerate: %3")
976  .arg( m_h264_parser.GetTimeScale() )
977  .arg( m_h264_parser.GetUnitsInTick() )
978  .arg( frameRate.toDouble() * 1000 ) );
979  m_frameRate = frameRate;
980  FrameRateChange(frameRate.toDouble() * 1000, m_frames_written_count);
981  }
982 
983  return m_seen_sps;
984 }
985 
991 {
992  // Perform ringbuffer switch if needed.
994 
995  uint64_t startpos;
996  uint64_t frameNum = m_frames_written_count;
997 
998  if (m_first_keyframe < 0)
999  {
1000  m_first_keyframe = frameNum;
1001  startpos = 0;
1002  SendMythSystemRecEvent("REC_STARTED_WRITING", m_curRecording);
1003  }
1004  else
1006 
1007  // Add key frame to position map
1008  m_positionMapLock.lock();
1009  if (!m_positionMap.contains(frameNum))
1010  {
1011  m_positionMapDelta[frameNum] = startpos;
1012  m_positionMap[frameNum] = startpos;
1013  m_durationMap[frameNum] = llround(m_total_duration);
1014  m_durationMapDelta[frameNum] = llround(m_total_duration);
1015  }
1016  m_positionMapLock.unlock();
1017 }
1018 
1019 void DTVRecorder::FindPSKeyFrames(const uint8_t *buffer, uint len)
1020 {
1021  const uint maxKFD = kMaxKeyFrameDistance;
1022 
1023  const uint8_t *bufstart = buffer;
1024  const uint8_t *bufptr = buffer;
1025  const uint8_t *bufend = buffer + len;
1026 
1027  uint aspectRatio = 0;
1028  uint height = 0;
1029  uint width = 0;
1030  FrameRate frameRate(0);
1031 
1033  while (bufptr + skip < bufend)
1034  {
1035  bool hasFrame = false;
1036  bool hasKeyFrame = false;
1037 
1038  const uint8_t *tmp = bufptr;
1039  bufptr =
1040  avpriv_find_start_code(bufptr + skip, bufend, &m_start_code);
1043  m_video_bytes_remaining -= std::min(
1044  (uint)(bufptr - tmp), m_video_bytes_remaining);
1045 
1046  if ((m_start_code & 0xffffff00) != 0x00000100)
1047  continue;
1048 
1049  // NOTE: Length may be zero for packets that only contain bytes from
1050  // video elementary streams in TS packets. 13818-1:2000 2.4.3.7
1051  int pes_packet_length = -1;
1052  if ((bufend - bufptr) >= 2)
1053  pes_packet_length = ((bufptr[0]<<8) | bufptr[1]) + 2 + 6;
1054 
1055  const int stream_id = m_start_code & 0x000000ff;
1057  {
1058  if (PESStreamID::PictureStartCode == stream_id)
1059  { // pes_packet_length is meaningless
1060  pes_packet_length = -1;
1061  if (bufend-bufptr >= 4)
1062  {
1063  uint frmtypei = (bufptr[1]>>3) & 0x7;
1064  if ((1 <= frmtypei) && (frmtypei <= 5))
1065  hasFrame = true;
1066  }
1067  else
1068  {
1069  hasFrame = true;
1070  }
1071  }
1072  else if (PESStreamID::GOPStartCode == stream_id)
1073  { // pes_packet_length is meaningless
1074  pes_packet_length = -1;
1076  hasKeyFrame = true;
1077  }
1078  else if (PESStreamID::SequenceStartCode == stream_id)
1079  { // pes_packet_length is meaningless
1080  pes_packet_length = -1;
1082  hasKeyFrame |= (m_last_gop_seen + maxKFD)<m_frames_seen_count;
1083 
1084  // Look for aspectRatio changes and store them in the database
1085  aspectRatio = (bufptr[3] >> 4);
1086 
1087  // Get resolution
1088  height = ((bufptr[1] & 0xf) << 8) | bufptr[2];
1089  width = (bufptr[0] <<4) | (bufptr[1]>>4);
1090 
1091  frameRate = frameRateMap[(bufptr[3] & 0x0000000f)];
1092  }
1093  }
1095  {
1096  if ((stream_id >= PESStreamID::MPEGVideoStreamBegin) &&
1097  (stream_id <= PESStreamID::MPEGVideoStreamEnd))
1098  { // ok-dvdinfo
1099  m_video_bytes_remaining = std::max(0, pes_packet_length);
1100  }
1101  else if ((stream_id >= PESStreamID::MPEGAudioStreamBegin) &&
1102  (stream_id <= PESStreamID::MPEGAudioStreamEnd))
1103  { // ok-dvdinfo
1104  m_audio_bytes_remaining = std::max(0, pes_packet_length);
1105  }
1106  }
1107 
1108  if (PESStreamID::PaddingStream == stream_id)
1109  { // ok-dvdinfo
1110  m_other_bytes_remaining = std::max(0, pes_packet_length);
1111  }
1112 
1113  m_start_code = 0xffffffff; // reset start code
1114 
1115  if (hasFrame && !hasKeyFrame)
1116  {
1117  // If we have seen kMaxKeyFrameDistance frames since the
1118  // last GOP or SEQ stream_id, then pretend this picture
1119  // is a keyframe. We may get artifacts but at least
1120  // we will be able to skip frames.
1121  hasKeyFrame = ((m_frames_seen_count & 0xf) == 0U);
1122  hasKeyFrame &= (m_last_gop_seen + maxKFD) < m_frames_seen_count;
1123  hasKeyFrame &= (m_last_seq_seen + maxKFD) < m_frames_seen_count;
1124  }
1125 
1126  if (hasFrame)
1127  {
1131  }
1132 
1133  if (hasKeyFrame)
1134  {
1136  HandleKeyframe((int64_t)m_payload_buffer.size() - (bufptr - bufstart));
1137  }
1138 
1139  if ((aspectRatio > 0) && (aspectRatio != m_videoAspect))
1140  {
1141  m_videoAspect = aspectRatio;
1143  }
1144 
1145  if (height && width &&
1146  (height != m_videoHeight || m_videoWidth != width))
1147  {
1148  m_videoHeight = height;
1149  m_videoWidth = width;
1150  ResolutionChange(width, height, m_frames_written_count);
1151  }
1152 
1153  if (frameRate.isNonzero() && frameRate != m_frameRate)
1154  {
1155  m_frameRate = frameRate;
1156  LOG(VB_RECORD, LOG_INFO, LOC +
1157  QString("FindPSKeyFrames: frame rate = %1")
1158  .arg(frameRate.toDouble() * 1000));
1159  FrameRateChange(frameRate.toDouble() * 1000, m_frames_written_count);
1160  }
1161 
1162  if (hasKeyFrame || hasFrame)
1163  {
1164  // We are free to write the packet, but if we have
1165  // buffered packet[s] we have to write them first...
1166  if (!m_payload_buffer.empty())
1167  {
1168  if (m_ringBuffer)
1169  {
1171  &m_payload_buffer[0], m_payload_buffer.size());
1172  }
1173  m_payload_buffer.clear();
1174  }
1175 
1176  if (m_ringBuffer)
1177  m_ringBuffer->Write(bufstart, (bufptr - bufstart));
1178 
1179  bufstart = bufptr;
1180  }
1181 
1183  }
1184 
1185  int bytes_skipped = bufend - bufptr;
1186  if (bytes_skipped > 0)
1187  {
1188  m_audio_bytes_remaining -= std::min(
1189  (uint)bytes_skipped, m_audio_bytes_remaining);
1190  m_video_bytes_remaining -= std::min(
1191  (uint)bytes_skipped, m_video_bytes_remaining);
1192  m_other_bytes_remaining -= std::min(
1193  (uint)bytes_skipped, m_other_bytes_remaining);
1194  }
1195 
1196  uint64_t idx = m_payload_buffer.size();
1197  uint64_t rem = (bufend - bufstart);
1198  m_payload_buffer.resize(idx + rem);
1199  memcpy(&m_payload_buffer[idx], bufstart, rem);
1200 #if 0
1201  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1202  QString("idx: %1, rem: %2").arg(idx).arg(rem));
1203 #endif
1204 }
1205 
1207 {
1208  if (!_pat)
1209  {
1210  LOG(VB_RECORD, LOG_ERR, LOC + "SetPAT(NULL)");
1211  return;
1212  }
1213 
1214  QMutexLocker change_lock(&m_pid_lock);
1215 
1216  int progNum = m_stream_data->DesiredProgram();
1217  uint pmtpid = _pat->FindPID(progNum);
1218 
1219  if (!pmtpid)
1220  {
1221  LOG(VB_RECORD, LOG_ERR, LOC +
1222  QString("SetPAT(): Ignoring PAT not containing our desired "
1223  "program (%1)...").arg(progNum));
1224  return;
1225  }
1226 
1227  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPAT(%1 on 0x%2)")
1228  .arg(progNum).arg(pmtpid,0,16));
1229 
1231  m_input_pat = new ProgramAssociationTable(*_pat);
1232  delete oldpat;
1233 
1234  // Listen for the other PMTs for faster channel switching
1235  for (uint i = 0; m_input_pat && (i < m_input_pat->ProgramCount()); ++i)
1236  {
1237  uint pmt_pid = m_input_pat->ProgramPID(i);
1238  if (!m_stream_data->IsListeningPID(pmt_pid))
1240  }
1241 }
1242 
1243 void DTVRecorder::HandlePMT(uint progNum, const ProgramMapTable *_pmt)
1244 {
1245  QMutexLocker change_lock(&m_pid_lock);
1246 
1247  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPMT(%1, %2)").arg(progNum)
1248  .arg(_pmt == nullptr ? "NULL" : "valid"));
1249 
1250 
1251  if ((int)progNum == m_stream_data->DesiredProgram())
1252  {
1253  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetPMT(%1)").arg(progNum));
1254  ProgramMapTable *oldpmt = m_input_pmt;
1255  m_input_pmt = new ProgramMapTable(*_pmt);
1256 
1257  QString sistandard = GetSIStandard();
1258 
1259  bool has_no_av = true;
1260  for (uint i = 0; i < m_input_pmt->StreamCount() && has_no_av; ++i)
1261  {
1262  has_no_av &= !m_input_pmt->IsVideo(i, sistandard);
1263  has_no_av &= !m_input_pmt->IsAudio(i, sistandard);
1264  }
1265  m_has_no_av = has_no_av;
1266 
1268  delete oldpmt;
1269  }
1270 }
1271 
1273  bool insert)
1274 {
1275  if (!pat)
1276  {
1277  LOG(VB_RECORD, LOG_ERR, LOC + "HandleSingleProgramPAT(NULL)");
1278  return;
1279  }
1280 
1281  if (!m_ringBuffer)
1282  return;
1283 
1284  uint next_cc = (pat->tsheader()->ContinuityCounter()+1)&0xf;
1285  pat->tsheader()->SetContinuityCounter(next_cc);
1286  pat->GetAsTSPackets(m_scratch, next_cc);
1287 
1288  for (size_t i = 0; i < m_scratch.size(); ++i)
1290 }
1291 
1293 {
1294  if (!pmt)
1295  {
1296  LOG(VB_RECORD, LOG_ERR, LOC + "HandleSingleProgramPMT(NULL)");
1297  return;
1298  }
1299 
1300  // We only want to do these checks once per recording
1301  bool seenVideo = (m_primaryVideoCodec != AV_CODEC_ID_NONE);
1302  bool seenAudio = (m_primaryAudioCodec != AV_CODEC_ID_NONE);
1303  uint bestAudioCodec = 0;
1304  // collect stream types for H.264 (MPEG-4 AVC) keyframe detection
1305  for (uint i = 0; i < pmt->StreamCount(); ++i)
1306  {
1307  // We only care about the first identifiable video stream
1308  if (!seenVideo && (m_primaryVideoCodec == AV_CODEC_ID_NONE) &&
1309  StreamID::IsVideo(pmt->StreamType(i)))
1310  {
1311  seenVideo = true; // Ignore other video streams
1312  switch (pmt->StreamType(i))
1313  {
1314  case StreamID::MPEG1Video:
1315  m_primaryVideoCodec = AV_CODEC_ID_MPEG1VIDEO;
1316  break;
1317  case StreamID::MPEG2Video:
1318  m_primaryVideoCodec = AV_CODEC_ID_MPEG2VIDEO;
1319  break;
1320  case StreamID::MPEG4Video:
1321  m_primaryVideoCodec = AV_CODEC_ID_MPEG4;
1322  break;
1323  case StreamID::H264Video:
1324  m_primaryVideoCodec = AV_CODEC_ID_H264;
1325  break;
1326  case StreamID::H265Video:
1327  m_primaryVideoCodec = AV_CODEC_ID_H265;
1328  break;
1330  m_primaryVideoCodec = AV_CODEC_ID_MPEG2VIDEO; // TODO Will it always be MPEG2?
1331  break;
1332  case StreamID::VC1Video:
1333  m_primaryVideoCodec = AV_CODEC_ID_VC1;
1334  break;
1335  default:
1336  break;
1337  }
1338 
1339  if (m_primaryVideoCodec != AV_CODEC_ID_NONE)
1341  }
1342 
1343  // We want the 'best' identifiable audio stream, where 'best' is
1344  // subjective and no-one will likely agree.
1345  // For now it's the 'best' codec, assuming mpeg stream types range
1346  // from worst to best, which it does
1347  if (!seenAudio && StreamID::IsAudio(pmt->StreamType(i)) &&
1348  pmt->StreamType(i) > bestAudioCodec)
1349  {
1350  bestAudioCodec = pmt->StreamType(i);
1351  switch (pmt->StreamType(i))
1352  {
1353  case StreamID::MPEG1Audio: // MPEG-1 Layer 2 (MP2)
1354  case StreamID::MPEG2Audio: // MPEG-2 Part 3 (MP2 Multichannel)
1355  m_primaryAudioCodec = AV_CODEC_ID_MP2;
1356  break;
1358  m_primaryAudioCodec = AV_CODEC_ID_AAC;
1359  break;
1361  m_primaryAudioCodec = AV_CODEC_ID_AAC_LATM;
1362  break;
1363  case StreamID::AC3Audio:
1364  m_primaryAudioCodec = AV_CODEC_ID_AC3;
1365  break;
1366  case StreamID::EAC3Audio:
1367  m_primaryAudioCodec = AV_CODEC_ID_EAC3;
1368  break;
1369  case StreamID::DTSAudio:
1370  m_primaryAudioCodec = AV_CODEC_ID_DTS;
1371  break;
1372  default:
1373  break;
1374  }
1375 
1376  if (m_primaryAudioCodec != AV_CODEC_ID_NONE)
1378  }
1379 
1380 // LOG(VB_GENERAL, LOG_DEBUG, QString("Recording(%1): Stream #%2: %3 ")
1381 // .arg(m_curRecording ? QString::number(m_curRecording->GetRecordingID()) : "")
1382 // .arg(i)
1383 // .arg(StreamID::GetDescription(pmt->StreamType(i))));
1384  m_stream_id[pmt->StreamPID(i)] = pmt->StreamType(i);
1385  }
1386 
1387  // If the PCRPID is valid and the PCR is not contained
1388  // in another stream, make sure the PCR stream is not
1389  // discarded (use PrivSec type as dummy 'valid' value)
1390  if (pmt->PCRPID() != 0x1fff && pmt->FindPID(pmt->PCRPID()) == -1)
1392 
1393  if (!m_ringBuffer)
1394  return;
1395 
1396  uint next_cc = (pmt->tsheader()->ContinuityCounter()+1)&0xf;
1397  pmt->tsheader()->SetContinuityCounter(next_cc);
1398  pmt->GetAsTSPackets(m_scratch, next_cc);
1399 
1400  for (size_t i = 0; i < m_scratch.size(); ++i)
1402 }
1403 
1405 {
1406  const uint pid = tspacket.PID();
1407 
1408  if (pid != 0x1fff)
1409  m_packet_count.fetchAndAddAcquire(1);
1410 
1411  // Check continuity counter
1412  uint old_cnt = m_continuity_counter[pid];
1413  if ((pid != 0x1fff) && !CheckCC(pid, tspacket.ContinuityCounter()))
1414  {
1415  int v = m_continuity_error_count.fetchAndAddRelaxed(1) + 1;
1416  double erate = v * 100.0 / m_packet_count.fetchAndAddRelaxed(0);
1417  LOG(VB_RECORD, LOG_WARNING, LOC +
1418  QString("PID 0x%1 discontinuity detected ((%2+1)%16!=%3) %4%")
1419  .arg(pid,0,16).arg(old_cnt,2)
1420  .arg(tspacket.ContinuityCounter(),2)
1421  .arg(erate));
1422  }
1423 
1424  // Only create fake keyframe[s] if there are no audio/video streams
1425  if (m_input_pmt && m_has_no_av)
1426  {
1427  FindOtherKeyframes(&tspacket);
1428  m_buffer_packets = false;
1429  }
1430  else if (m_record_mpts_only)
1431  {
1432  /* When recording the full, unfiltered, MPTS, trigger a write
1433  * every 0.5 seconds. Since the packets are unfiltered and
1434  * unprocessed we cannot wait for a keyframe to trigger the
1435  * writes. */
1436 
1437  static MythTimer s_timer;
1438 
1439  if (m_frames_seen_count++ == 0)
1440  s_timer.start();
1441 
1442  if (s_timer.elapsed() > 500) // 0.5 seconds
1443  {
1447  s_timer.addMSecs(-500);
1448  }
1449  }
1450  else if (m_stream_id[pid] == 0)
1451  {
1452  // Ignore this packet if the PID should be stripped
1453  return true;
1454  }
1455  else
1456  {
1457  // There are audio/video streams. Only write the packet
1458  // if audio/video key-frames have been found
1460  return true;
1461  }
1462 
1463  BufferedWrite(tspacket);
1464 
1465  return true;
1466 }
1467 
1469 {
1470  if (!m_ringBuffer)
1471  return true;
1472 
1473  uint streamType = m_stream_id[tspacket.PID()];
1474 
1475  if (tspacket.HasPayload() && tspacket.PayloadStart())
1476  {
1477  if (m_buffer_packets && m_first_keyframe >= 0 && !m_payload_buffer.empty())
1478  {
1479  // Flush the buffer
1480  if (m_ringBuffer)
1482  m_payload_buffer.clear();
1483  }
1484 
1485  // buffer packets until we know if this is a keyframe
1486  m_buffer_packets = true;
1487  }
1488 
1489  // Check for keyframes and count frames
1490  if (streamType == StreamID::H264Video)
1491  FindH264Keyframes(&tspacket);
1492  else if (streamType != 0)
1493  FindMPEG2Keyframes(&tspacket);
1494  else
1495  LOG(VB_RECORD, LOG_ERR, LOC +
1496  "ProcessVideoTSPacket: unknown stream type!");
1497 
1498  return ProcessAVTSPacket(tspacket);
1499 }
1500 
1502 {
1503  if (!m_ringBuffer)
1504  return true;
1505 
1506  if (tspacket.HasPayload() && tspacket.PayloadStart())
1507  {
1508  if (m_buffer_packets && m_first_keyframe >= 0 && !m_payload_buffer.empty())
1509  {
1510  // Flush the buffer
1511  if (m_ringBuffer)
1513  m_payload_buffer.clear();
1514  }
1515 
1516  // buffer packets until we know if this is a keyframe
1517  m_buffer_packets = true;
1518  }
1519 
1520  FindAudioKeyframes(&tspacket);
1521  return ProcessAVTSPacket(tspacket);
1522 }
1523 
1526 {
1527  // Sync recording start to first keyframe
1529  {
1530  if (m_buffer_packets)
1531  BufferedWrite(tspacket);
1532  return true;
1533  }
1534 
1535  const uint pid = tspacket.PID();
1536 
1537  if (pid != 0x1fff)
1538  m_packet_count.fetchAndAddAcquire(1);
1539 
1540  // Check continuity counter
1541  uint old_cnt = m_continuity_counter[pid];
1542  if ((pid != 0x1fff) && !CheckCC(pid, tspacket.ContinuityCounter()))
1543  {
1544  int v = m_continuity_error_count.fetchAndAddRelaxed(1) + 1;
1545  double erate = v * 100.0 / m_packet_count.fetchAndAddRelaxed(0);
1546  LOG(VB_RECORD, LOG_WARNING, LOC +
1547  QString("A/V PID 0x%1 discontinuity detected ((%2+1)%16!=%3) %4%")
1548  .arg(pid,0,16).arg(old_cnt).arg(tspacket.ContinuityCounter())
1549  .arg(erate,5,'f',2));
1550  }
1551 
1552  if (!(m_pid_status[pid] & kPayloadStartSeen))
1553  {
1555  LOG(VB_RECORD, LOG_INFO, LOC +
1556  QString("PID 0x%1 Found Payload Start").arg(pid,0,16));
1557  }
1558 
1559  BufferedWrite(tspacket);
1560 
1561  return true;
1562 }
1563 
1565 {
1567  recq->AddTSStatistics(
1568  m_continuity_error_count.fetchAndAddRelaxed(0),
1569  m_packet_count.fetchAndAddRelaxed(0));
1570  return recq;
1571 }
1572 
1573 /* vim: set expandtab tabstop=4 shiftwidth=4: */
H264Parser m_h264_parser
Definition: dtvrecorder.h:153
Used to access the data of a Transport Stream packet.
Definition: tspacket.h:166
bool m_pes_synced
Definition: dtvrecorder.h:151
Group of Pictures (GOP) start code.
Definition: mpegtables.h:62
virtual void SetOption(const QString &name, const QString &value)
Set an specific option.
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:194
FrameRate m_td_tick_framerate
Definition: dtvrecorder.h:202
int restart(void)
Returns milliseconds elapsed since last start() or restart() and resets the count.
Definition: mythtimer.cpp:62
ProgramAssociationTable * m_input_pat
PAT on input side.
Definition: dtvrecorder.h:173
frm_pos_map_t m_durationMap
Definition: recorderbase.h:349
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
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:212
bool m_wait_for_keyframe_option
Wait for the a GOP/SEQ-start before sending data.
Definition: dtvrecorder.h:156
unsigned char m_continuity_counter[0x1fff+1]
Definition: dtvrecorder.h:183
void AudioCodecChange(AVCodecID aCodec)
Note a change in audio codec.
bool onKeyFrameStart(void) const
Definition: H264Parser.h:155
QAtomicInt m_timeOfLatestDataPacketInterval
Definition: recorderbase.h:367
frm_pos_map_t m_positionMap
Definition: recorderbase.h:347
uint32_t addBytes(const uint8_t *bytes, const uint32_t byte_count, const uint64_t stream_offset)
Definition: H264Parser.cpp:398
virtual void InitStreamData(void)
static const FrameRate frameRateMap[16]
void WriterFlush(void)
Calls ThreadedFileWriter::Flush(void)
bool FindOtherKeyframes(const TSPacket *tspacket)
Non-Audio/Video data.
QString toString(void) const
Definition: recorderbase.h:44
int m_minimum_recording_quality
Definition: dtvrecorder.h:187
bool CheckCC(uint pid, uint cc)
Definition: dtvrecorder.h:216
double m_video_frame_rate
Definition: recorderbase.h:315
unsigned long long m_frames_written_count
Definition: dtvrecorder.h:196
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:176
virtual bool IsListeningPID(uint pid) const
unsigned int m_video_bytes_remaining
Definition: dtvrecorder.h:143
bool m_record_mpts
Definition: dtvrecorder.h:179
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
ISO 13818-1 private tables & ITU H.222.0.
Definition: mpegtables.h:143
virtual ~DTVRecorder()
Definition: dtvrecorder.cpp:60
virtual void SetCAMPMT(const ProgramMapTable *)
Definition: dtvrecorder.h:127
unsigned int ContinuityCounter(void) const
Definition: tspacket.h:87
void HandleSingleProgramPAT(ProgramAssociationTable *pat, bool insert) override
bool m_buffer_packets
Definition: dtvrecorder.h:167
uint getNum(void) const
Definition: recorderbase.h:42
void Reset(void) override
Reset the recorder to the startup state.
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
bool m_use_pts
Definition: dtvrecorder.h:188
QDateTime m_timeOfFirstData
Definition: recorderbase.h:365
void HandlePMT(uint progNum, const ProgramMapTable *) override
uint aspectRatio(void) const
Computes aspect ratio from picture size and sample aspect ratio.
static const unsigned char kPayloadStartSeen
Definition: dtvrecorder.h:213
uint32_t m_start_code
Definition: dtvrecorder.h:137
void SetContinuityCounter(unsigned int cc)
Definition: tspacket.h:148
static guint32 * tmp
Definition: goom_core.c:35
bool m_has_no_av
Definition: dtvrecorder.h:176
uint PCRPID(void) const
stream that contains program clock reference.
Definition: mpegtables.h:690
QMutex m_statisticsLock
Definition: recorderbase.h:363
int m_progressive_sequence
Definition: dtvrecorder.h:147
AVCodecID m_primaryAudioCodec
Definition: recorderbase.h:309
virtual void SetStreamData(MPEGStreamData *data)
QDateTime m_ts_first_dt[256]
Definition: dtvrecorder.h:192
unsigned int AFCOffset(void) const
Definition: tspacket.h:208
Sequence (SEQ) start code contains frame size, aspect ratio and fps.
Definition: mpegtables.h:53
bool PayloadStart(void) const
Definition: tspacket.h:67
void SetTotalFrames(uint64_t total_frames)
Note the total frames in the recordedmark table.
ProgramMapTable * m_input_pmt
PMT on input side.
Definition: dtvrecorder.h:175
unsigned long long m_frames_seen_count
Definition: dtvrecorder.h:195
static bool IsVideo(uint type)
Returns true iff video is an MPEG1/2/3, H264 or open cable video stream.
Definition: mpegtables.h:165
uint StreamType(uint i) const
Definition: mpegtables.h:702
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)
virtual QString GetSIStandard(void) const
Definition: dtvrecorder.h:126
AVContainer m_containerFormat
Definition: recorderbase.h:307
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
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:141
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:75
SMPTE 421M video codec (aka VC1) in Blu-Ray.
Definition: mpegtables.h:118
ISO 11172-3.
Definition: mpegtables.h:121
bool HasPayload(void) const
Definition: tspacket.h:94
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
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:73
int64_t m_ts_last[256]
Definition: dtvrecorder.h:190
ISO 23008-2 & ITU H.265 (aka HEVC, Ultra HD)
Definition: mpegtables.h:116
MPEGStreamData * m_stream_data
Definition: dtvrecorder.h:164
QDateTime m_timeOfLatestData
Definition: recorderbase.h:368
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 *)
uint64_t m_ts_count[256]
Definition: dtvrecorder.h:189
QAtomicInt m_packet_count
Definition: dtvrecorder.h:193
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
uint64_t m_td_tick_count
Definition: dtvrecorder.h:201
bool m_seen_sps
Definition: dtvrecorder.h:152
int64_t m_ts_first[256]
Definition: dtvrecorder.h:191
virtual void SetRecordingStatus(RecStatus::Type status, const QString &file, int line)
bool FindAudioKeyframes(const TSPacket *tspacket)
ISO 13818-3.
Definition: mpegtables.h:122
void SendMythSystemRecEvent(const QString &msg, const RecordingInfo *pginfo)
unsigned int uint
Definition: compat.h:140
bool onFrameStart(void) const
Definition: H264Parser.h:154
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
QTime m_audio_timer
Definition: dtvrecorder.h:136
QAtomicInt m_continuity_error_count
Definition: dtvrecorder.h:194
MPEGStreamData * GetStreamData(void) const
Definition: dtvrecorder.h:60
MythTimer m_timeOfLatestDataTimer
Definition: recorderbase.h:369
bool m_music_choice
Definition: dtvrecorder.h:206
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 m_other_bytes_remaining
Definition: dtvrecorder.h:144
unsigned int m_audio_bytes_remaining
Definition: dtvrecorder.h:142
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
QString m_recording_type
Definition: dtvrecorder.h:133
unsigned long long m_last_keyframe_seen
Definition: dtvrecorder.h:141
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:114
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
FrameRate m_frameRate
Definition: recorderbase.h:321
static int64_t extract_timestamp(const uint8_t *bufptr, int bytes_left, int pts_or_dts)
double m_td_base
Definition: dtvrecorder.h:200
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
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:220
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:439
vector< unsigned char > m_payload_buffer
Definition: dtvrecorder.h:168
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
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.
virtual RecordingQuality * GetRecordingQuality(const RecordingInfo *) const
Returns a report about the current recordings quality.
virtual bool CheckForRingBufferSwitch(void)
If requested, switch to new RingBuffer/ProgramInfo objects.
int m_repeat_pict
Definition: dtvrecorder.h:148
void GetAsTSPackets(vector< TSPacket > &output, uint cc) const
Returns payload only PESPacket as series of TSPackets.
Definition: pespacket.cpp:104
unsigned char m_pid_status[0x1fff+1]
Definition: dtvrecorder.h:182
uint FindPID(uint progNum) const
Definition: mpegtables.h:619
unsigned long long m_last_gop_seen
Definition: dtvrecorder.h:139
vector< TSPacket > m_scratch
Definition: dtvrecorder.h:184
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.
bool m_record_mpts_only
Definition: dtvrecorder.h:180
frm_pos_map_t m_durationMapDelta
Definition: recorderbase.h:350
unsigned long long m_last_seq_seen
Definition: dtvrecorder.h:140
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 IsVideo(uint i, const QString &sistandard) const
Returns true iff the stream at index i is a video stream.
Definition: mpegtables.cpp:514
uint pictureHeight(void) const
Definition: H264Parser.h:158
double m_total_duration
Definition: dtvrecorder.h:197
uint pictureWidth(void) const
Definition: H264Parser.h:157
void VideoCodecChange(AVCodecID vCodec)
Note a change in video codec.
QString m_error
non-empty iff irrecoverable recording error detected
Definition: dtvrecorder.h:162
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
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
uint32_t GetTimeScale(void) const
Definition: H264Parser.h:192
uint64_t keyframeAUstreamOffset(void) const
Definition: H264Parser.h:170
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
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 m_has_written_other_keyframe
Definition: dtvrecorder.h:158
Last MPEG-1/2 video stream (w/ext hdr)
Definition: mpegtables.h:79
void ClearPositionMap(MarkTypes type) const
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
QMutex m_positionMapLock
Definition: recorderbase.h:346
void HandleSingleProgramPMT(ProgramMapTable *pmt, bool insert) override
ISO 11172-2 (aka MPEG-1)
Definition: mpegtables.h:112
QMutex m_pid_lock
Definition: dtvrecorder.h:171
int m_first_keyframe
Definition: dtvrecorder.h:138
bool IsAudio(uint i, const QString &sistandard) const
Returns true iff the stream at index i is an audio stream.
Definition: mpegtables.cpp:536
unsigned char m_stream_id[0x1fff+1]
Definition: dtvrecorder.h:181
uint getDen(void) const
Definition: recorderbase.h:43
uint ProgramPID(uint i) const
Definition: mpegtables.h:610
void AddTSStatistics(int continuity_error_count, int packet_count)
uint m_videoHeight
Definition: recorderbase.h:319
Always MPEG-2??
Definition: mpegtables.h:117