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