MythTV  master
recorderbase.cpp
Go to the documentation of this file.
1 #include <algorithm> // for min
2 #include <cstdint>
3 using namespace std;
4 
5 #include "firewirerecorder.h"
6 #include "recordingprofile.h"
7 #include "firewirechannel.h"
8 #include "importrecorder.h"
9 #include "cetonrecorder.h"
10 #include "dummychannel.h"
11 #include "hdhrrecorder.h"
12 #include "iptvrecorder.h"
13 #include "mpegrecorder.h"
14 #include "recorderbase.h"
15 #include "cetonchannel.h"
16 #include "asirecorder.h"
17 #include "dvbrecorder.h"
18 #include "ExternalRecorder.h"
19 #include "hdhrchannel.h"
20 #include "iptvchannel.h"
21 #include "mythsystemevent.h"
22 #include "mythlogging.h"
23 #include "programinfo.h"
24 #include "asichannel.h"
25 #include "dtvchannel.h"
26 #include "dvbchannel.h"
27 #include "ExternalChannel.h"
28 #include "ringbuffer.h"
29 #include "cardutil.h"
30 #include "tv_rec.h"
31 #include "mythdate.h"
32 #if CONFIG_LIBMP3LAME
33 #include "NuppelVideoRecorder.h"
34 #endif
35 #if CONFIG_V4L2
36 #include "v4l2encrecorder.h"
37 #include "v4lchannel.h"
38 #endif
39 
40 #define TVREC_CARDNUM \
41  ((m_tvrec != nullptr) ? QString::number(m_tvrec->GetInputId()) : "NULL")
42 
43 #define LOC QString("RecBase[%1](%2): ") \
44  .arg(TVREC_CARDNUM).arg(m_videodevice)
45 
47 
49  : m_tvrec(rec)
50 {
52  QMutexLocker locker(avcodeclock);
53 #if 0
54  avcodec_init(); // init CRC's
55 #endif
56 }
57 
59 {
61  {
62  delete m_ringBuffer;
63  m_ringBuffer = nullptr;
64  }
65  SetRecording(nullptr);
66  if (m_nextRingBuffer)
67  {
68  QMutexLocker locker(&m_nextRingBufferLock);
69  delete m_nextRingBuffer;
70  m_nextRingBuffer = nullptr;
71  }
72  if (m_nextRecording)
73  {
74  delete m_nextRecording;
75  m_nextRecording = nullptr;
76  }
77 }
78 
80 {
81  if (VERBOSE_LEVEL_CHECK(VB_RECORD, LOG_INFO))
82  {
83  QString msg("");
84  if (rbuf)
85  msg = " '" + rbuf->GetFilename() + "'";
86  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetRingBuffer(0x%1)")
87  .arg((uint64_t)rbuf,0,16) + msg);
88  }
89  m_ringBuffer = rbuf;
90  m_weMadeBuffer = false;
91 }
92 
94 {
95  if (pginfo)
96  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetRecording(0x%1) title(%2)")
97  .arg((uint64_t)pginfo,0,16).arg(pginfo->GetTitle()));
98  else
99  LOG(VB_RECORD, LOG_INFO, LOC + "SetRecording(0x0)");
100 
101  ProgramInfo *oldrec = m_curRecording;
102  if (pginfo)
103  {
104  // NOTE: RecorderBase and TVRec do not share a single RecordingInfo
105  // instance which may lead to the possibility that changes made
106  // in the database by one are overwritten by the other
107  m_curRecording = new RecordingInfo(*pginfo);
108  // Compute an estimate of the actual progstart delay for setting the
109  // MARK_UTIL_PROGSTART mark. We can't reliably use
110  // m_curRecording->GetRecordingStartTime() because the scheduler rounds it
111  // to the nearest minute, so we use the current time instead.
116  recFile->Save();
117  }
118  else
119  m_curRecording = nullptr;
120 
121  delete oldrec;
122 }
123 
125 {
126  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetNextRecording(0x%1, 0x%2)")
127  .arg(reinterpret_cast<intptr_t>(ri),0,16)
128  .arg(reinterpret_cast<intptr_t>(rb),0,16));
129 
130  // First we do some of the time consuming stuff we can do now
131  SavePositionMap(true);
132  if (m_ringBuffer)
133  {
135  if (m_curRecording)
137  }
138 
139  // Then we set the next info
140  QMutexLocker locker(&m_nextRingBufferLock);
141  if (m_nextRecording)
142  {
143  delete m_nextRecording;
144  m_nextRecording = nullptr;
145  }
146  if (ri)
147  m_nextRecording = new RecordingInfo(*ri);
148 
149  delete m_nextRingBuffer;
150  m_nextRingBuffer = rb;
151 }
152 
153 void RecorderBase::SetOption(const QString &name, const QString &value)
154 {
155  if (name == "videocodec")
156  m_videocodec = value;
157  else if (name == "videodevice")
158  m_videodevice = value;
159  else if (name == "tvformat")
160  {
161  m_ntsc = false;
162  if (value.toLower() == "ntsc" || value.toLower() == "ntsc-jp")
163  { // NOLINT(bugprone-branch-clone)
164  m_ntsc = true;
165  SetFrameRate(29.97);
166  }
167  else if (value.toLower() == "pal-m")
168  SetFrameRate(29.97);
169  else if (value.toLower() == "atsc")
170  {
171  // Here we set the TV format values for ATSC. ATSC isn't really
172  // NTSC, but users who configure a non-ATSC-recorder as ATSC
173  // are far more likely to be using a mix of ATSC and NTSC than
174  // a mix of ATSC and PAL or SECAM. The atsc recorder itself
175  // does not care about these values, except in so much as tv_rec
176  // cares about m_video_frame_rate which should be neither 29.97
177  // nor 25.0, but based on the actual video.
178  m_ntsc = true;
179  SetFrameRate(29.97);
180  }
181  else
182  SetFrameRate(25.00);
183  }
184  else
185  {
186  LOG(VB_GENERAL, LOG_WARNING, LOC +
187  QString("SetOption(%1,%2): Option not recognized")
188  .arg(name).arg(value));
189  }
190 }
191 
192 void RecorderBase::SetOption(const QString &name, int value)
193 {
194  LOG(VB_GENERAL, LOG_ERR, LOC +
195  QString("SetOption(): Unknown int option: %1: %2")
196  .arg(name).arg(value));
197 }
198 
200 {
201  const StandardSetting *setting = profile->byName(name);
202  if (setting)
203  SetOption(name, setting->getValue().toInt());
204  else
205  LOG(VB_GENERAL, LOG_ERR, LOC +
206  QString("SetIntOption(...%1): Option not in profile.").arg(name));
207 }
208 
210 {
211  const StandardSetting *setting = profile->byName(name);
212  if (setting)
213  SetOption(name, setting->getValue());
214  else
215  LOG(VB_GENERAL, LOG_ERR, LOC +
216  QString("SetStrOption(...%1): Option not in profile.").arg(name));
217 }
218 
225 {
226  QMutexLocker locker(&m_pauseLock);
227  m_request_recording = false;
228  m_unpauseWait.wakeAll();
229  while (m_recording)
230  {
231  m_recordingWait.wait(&m_pauseLock, 100);
233  {
234  LOG(VB_GENERAL, LOG_ERR, LOC +
235  "Programmer Error: Recorder started while we were in "
236  "StopRecording");
237  m_request_recording = false;
238  }
239  }
240 }
241 
244 {
245  QMutexLocker locker(&m_pauseLock);
246  return m_recording;
247 }
248 
251 {
252  QMutexLocker locker(&m_pauseLock);
253  return m_request_recording;
254 }
255 
264 {
265  (void) clear;
266  QMutexLocker locker(&m_pauseLock);
267  m_request_pause = true;
268 }
269 
275 {
276  QMutexLocker locker(&m_pauseLock);
277  m_request_pause = false;
278  m_unpauseWait.wakeAll();
279 }
280 
282 bool RecorderBase::IsPaused(bool holding_lock) const
283 {
284  if (!holding_lock)
285  m_pauseLock.lock();
286  bool ret = m_paused;
287  if (!holding_lock)
288  m_pauseLock.unlock();
289  return ret;
290 }
291 
299 {
300  MythTimer t;
301  t.start();
302 
303  QMutexLocker locker(&m_pauseLock);
304  while (!IsPaused(true) && m_request_pause)
305  {
306  int wait = timeout - t.elapsed();
307  if (wait <= 0)
308  return false;
309  m_pauseWait.wait(&m_pauseLock, wait);
310  }
311  return true;
312 }
313 
327 {
328  QMutexLocker locker(&m_pauseLock);
329  if (m_request_pause)
330  {
331  if (!IsPaused(true))
332  {
333  m_paused = true;
334  m_pauseWait.wakeAll();
335  if (m_tvrec)
337  }
338 
340  }
341 
342  if (!m_request_pause && IsPaused(true))
343  {
344  m_paused = false;
345  m_unpauseWait.wakeAll();
346  }
347 
348  return IsPaused(true);
349 }
350 
352 {
353  bool did_switch = false;
354 
355  m_nextRingBufferLock.lock();
356 
357  RecordingQuality *recq = nullptr;
358 
359  if (m_nextRingBuffer)
360  {
361  FinishRecording();
362 
363  recq = GetRecordingQuality(nullptr);
364 
365  ResetForNewFile();
366 
368  m_frameRate = FrameRate(0);
369 
372 
373  m_nextRingBuffer = nullptr;
374  m_nextRecording = nullptr;
375 
376  StartNewFile();
377  did_switch = true;
378  }
379  m_nextRingBufferLock.unlock();
380 
381  if (recq && m_tvrec)
382  {
383  // This call will free recq.
385  }
386  else
387  {
388  delete recq;
389  }
390 
392  return did_switch;
393 }
394 
396  const QString& file, int line)
397 {
399  {
400  LOG(VB_RECORD, LOG_INFO,
401  QString("Modifying recording status from %1 to %2 at %3:%4")
403  .arg(RecStatus::toString(status, kSingleRecord)).arg(file).arg(line));
404 
406 
407  if (status == RecStatus::Failing)
408  {
410  SendMythSystemRecEvent("REC_FAILING", m_curRecording);
411  }
412 
413  MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
414  .arg(m_curRecording->GetInputID())
415  .arg(m_curRecording->GetChanID())
417  .arg(status)
419  gCoreContext->dispatch(me);
420  }
421 }
422 
424 {
425  QMutexLocker locker(&m_statisticsLock);
426  m_timeOfFirstData = QDateTime();
427  m_timeOfFirstDataIsSet.fetchAndStoreRelaxed(0);
428  m_timeOfLatestData = QDateTime();
429  m_timeOfLatestDataCount.fetchAndStoreRelaxed(0);
430  m_timeOfLatestDataPacketInterval.fetchAndStoreRelaxed(2000);
431  m_recordingGaps.clear();
432 }
433 
435 {
436  if (m_curRecording)
437  {
438  if (m_primaryVideoCodec == AV_CODEC_ID_H264)
440 
442  if (recFile)
443  {
444  // Container
446 
447  // Video
448  recFile->m_videoCodec = ff_codec_id_string(m_primaryVideoCodec);
450  {
451  case MARK_ASPECT_1_1 :
452  recFile->m_videoAspectRatio = 1.0;
453  break;
454  case MARK_ASPECT_4_3:
455  recFile->m_videoAspectRatio = 1.33333333333;
456  break;
457  case MARK_ASPECT_16_9:
458  recFile->m_videoAspectRatio = 1.77777777777;
459  break;
460  case MARK_ASPECT_2_21_1:
461  recFile->m_videoAspectRatio = 2.21;
462  break;
463  default:
464  recFile->m_videoAspectRatio = (double)m_videoAspect / 1000000.0;
465  break;
466  }
467  QSize resolution(m_curRecording->QueryAverageWidth(),
469  recFile->m_videoResolution = resolution;
470  recFile->m_videoFrameRate = (double)m_curRecording->QueryAverageFrameRate() / 1000.0;
471 
472  // Audio
473  recFile->m_audioCodec = ff_codec_id_string(m_primaryAudioCodec);
474 
475  recFile->Save();
476  }
477  else
478  LOG(VB_GENERAL, LOG_CRIT, "RecordingFile object is NULL. No video file metadata can be stored");
479 
480  SavePositionMap(true, true); // Save Position Map only, not file size
481 
482  if (m_ringBuffer)
484  }
485 
486  LOG(VB_GENERAL, LOG_NOTICE, QString("Finished Recording: "
487  "Container: %7 "
488  "Video Codec: %1 (%2x%3 A/R: %4 %5fps) "
489  "Audio Codec: %6")
490  .arg(avcodec_get_name(m_primaryVideoCodec))
491  .arg(m_videoWidth)
492  .arg(m_videoHeight)
493  .arg(m_videoAspect)
494  .arg(GetFrameRate())
495  .arg(avcodec_get_name(m_primaryAudioCodec))
497 }
498 
500  const RecordingInfo *r) const
501 {
502  QMutexLocker locker(&m_statisticsLock);
503  if (r && m_curRecording &&
505  {
508  }
509  return new RecordingQuality(
512 }
513 
514 long long RecorderBase::GetKeyframePosition(long long desired) const
515 {
516  QMutexLocker locker(&m_positionMapLock);
517  long long ret = -1;
518 
519  if (m_positionMap.empty())
520  return ret;
521 
522  // find closest exact or previous keyframe position...
523  frm_pos_map_t::const_iterator it = m_positionMap.lowerBound(desired);
524  if (it == m_positionMap.end())
525  ret = *m_positionMap.begin();
526  else if ((it.key() == desired) ||
527  (--it != m_positionMap.end()))
528  ret = *it;
529 
530  return ret;
531 }
532 
534  long long start, long long end, frm_pos_map_t &map) const
535 {
536  map.clear();
537 
538  QMutexLocker locker(&m_positionMapLock);
539  if (m_positionMap.empty())
540  return true;
541 
542  frm_pos_map_t::const_iterator it = m_positionMap.lowerBound(start);
543  end = (end < 0) ? INT64_MAX : end;
544  for (; (it != m_positionMap.end()) &&
545  (it.key() <= end); ++it)
546  map[it.key()] = *it;
547 
548  LOG(VB_GENERAL, LOG_DEBUG, LOC +
549  QString("GetKeyframePositions(%1,%2,#%3) out of %4")
550  .arg(start).arg(end).arg(map.size()).arg(m_positionMap.size()));
551 
552  return true;
553 }
554 
556  long long start, long long end, frm_pos_map_t &map) const
557 {
558  map.clear();
559 
560  QMutexLocker locker(&m_positionMapLock);
561  if (m_durationMap.empty())
562  return true;
563 
564  frm_pos_map_t::const_iterator it = m_durationMap.lowerBound(start);
565  end = (end < 0) ? INT64_MAX : end;
566  for (; (it != m_durationMap.end()) &&
567  (it.key() <= end); ++it)
568  map[it.key()] = *it;
569 
570  LOG(VB_GENERAL, LOG_DEBUG, LOC +
571  QString("GetKeyframeDurations(%1,%2,#%3) out of %4")
572  .arg(start).arg(end).arg(map.size()).arg(m_durationMap.size()));
573 
574  return true;
575 }
576 
585 void RecorderBase::SavePositionMap(bool force, bool finished)
586 {
587  bool needToSave = force;
588  m_positionMapLock.lock();
589 
590  bool has_delta = !m_positionMapDelta.empty();
591  // set pm_elapsed to a fake large value if the timer hasn't yet started
592  uint pm_elapsed = (m_positionMapTimer.isRunning()) ?
594  // save on every 1.5 seconds if in the first few frames of a recording
595  needToSave |= (m_positionMap.size() < 30) &&
596  has_delta && (pm_elapsed >= 1500);
597  // save every 10 seconds later on
598  needToSave |= has_delta && (pm_elapsed >= 10000);
599  // Assume that m_durationMapDelta is the same size as
600  // m_positionMapDelta and implicitly use the same logic about when
601  // to same m_durationMapDelta.
602 
603  if (m_curRecording && needToSave)
604  {
606  if (has_delta)
607  {
608  // copy the delta map because most times we are called it will be in
609  // another thread and we don't want to lock the main recorder thread
610  // which is populating the delta map
612  m_positionMapDelta.clear();
613  frm_pos_map_t durationDeltaCopy(m_durationMapDelta);
614  m_durationMapDelta.clear();
615  m_positionMapLock.unlock();
616 
618  m_curRecording->SavePositionMapDelta(durationDeltaCopy,
620 
621  TryWriteProgStartMark(durationDeltaCopy);
622  }
623  else
624  {
625  m_positionMapLock.unlock();
626  }
627 
628  if (m_ringBuffer && !finished) // Finished Recording will update the final size for us
629  {
631  }
632  }
633  else
634  {
635  m_positionMapLock.unlock();
636  }
637 
638  // Make sure a ringbuffer switch is checked at least every 3
639  // seconds. Otherwise, this check is only performed on keyframes,
640  // and if there is a problem with the input we may never see one
641  // again, resulting in a wedged recording.
642  if (!finished && m_ringBufferCheckTimer.isRunning() &&
644  {
646  LOG(VB_RECORD, LOG_WARNING, LOC +
647  "Ringbuffer was switched due to timeout instead of keyframe.");
648  }
649 }
650 
652 {
653  // Note: all log strings contain "progstart mark" for searching.
654  if (m_estimatedProgStartMS <= 0)
655  {
656  // Do nothing because no progstart mark is needed.
657  LOG(VB_RECORD, LOG_DEBUG,
658  QString("No progstart mark needed because delta=%1")
659  .arg(m_estimatedProgStartMS));
660  return;
661  }
662  frm_pos_map_t::const_iterator last_it = durationDeltaCopy.end();
663  --last_it;
664  long long bookmarkFrame = 0;
665  LOG(VB_RECORD, LOG_DEBUG,
666  QString("durationDeltaCopy.begin() = (%1,%2)")
667  .arg(durationDeltaCopy.begin().key())
668  .arg(durationDeltaCopy.begin().value()));
669  if (m_estimatedProgStartMS > *last_it)
670  {
671  // Do nothing because we haven't reached recstartts yet.
672  LOG(VB_RECORD, LOG_DEBUG,
673  QString("No progstart mark yet because estimatedProgStartMS=%1 "
674  "and *last_it=%2")
675  .arg(m_estimatedProgStartMS).arg(*last_it));
676  }
678  m_estimatedProgStartMS < *durationDeltaCopy.begin())
679  {
680  // Set progstart mark @ lastSavedKeyframe
681  LOG(VB_RECORD, LOG_DEBUG,
682  QString("Set progstart mark=%1 because %2<=%3<%4")
684  .arg(m_estimatedProgStartMS).arg(*durationDeltaCopy.begin()));
685  bookmarkFrame = m_lastSavedKeyframe;
686  }
687  else if (*durationDeltaCopy.begin() <= m_estimatedProgStartMS &&
688  m_estimatedProgStartMS < *last_it)
689  {
690  frm_pos_map_t::const_iterator upper_it = durationDeltaCopy.begin();
691  for (; upper_it != durationDeltaCopy.end(); ++upper_it)
692  {
693  if (*upper_it > m_estimatedProgStartMS)
694  {
695  --upper_it;
696  // Set progstart mark @ upper_it.key()
697  LOG(VB_RECORD, LOG_DEBUG,
698  QString("Set progstart mark=%1 because "
699  "estimatedProgStartMS=%2 and upper_it.value()=%3")
700  .arg(upper_it.key()).arg(m_estimatedProgStartMS)
701  .arg(upper_it.value()));
702  bookmarkFrame = upper_it.key();
703  break;
704  }
705  }
706  }
707  else
708  {
709  // do nothing
710  LOG(VB_RECORD, LOG_DEBUG, "No progstart mark due to fallthrough");
711  }
712  if (bookmarkFrame)
713  {
714  frm_dir_map_t progStartMap;
715  progStartMap[bookmarkFrame] = MARK_UTIL_PROGSTART;
717  }
718  m_lastSavedKeyframe = last_it.key();
719  m_lastSavedDuration = last_it.value();
720  LOG(VB_RECORD, LOG_DEBUG,
721  QString("Setting lastSavedKeyframe=%1 lastSavedDuration=%2 "
722  "for progstart mark calculations")
724 }
725 
726 void RecorderBase::AspectChange(uint aspect, long long frame)
727 {
729  uint customAspect = 0;
730  if (aspect == ASPECT_1_1 || aspect >= ASPECT_CUSTOM)
731  {
732  if (aspect > 0x0F)
733  customAspect = aspect;
734  else if (m_videoWidth && m_videoHeight)
735  customAspect = m_videoWidth * 1000000 / m_videoHeight;
736 
737  mark = (customAspect) ? MARK_ASPECT_CUSTOM : mark;
738  }
739  if (aspect == ASPECT_4_3)
741  if (aspect == ASPECT_16_9)
743  if (aspect == ASPECT_2_21_1)
745 
746  // Populate the recordfile table as early as possible, the best
747  // value will be determined when the recording completes.
750  {
752  switch (m_videoAspect)
753  {
754  case ASPECT_1_1 :
755  recFile->m_videoAspectRatio = 1.0;
756  break;
757  case ASPECT_4_3:
758  recFile->m_videoAspectRatio = 1.33333333333;
759  break;
760  case ASPECT_16_9:
761  recFile->m_videoAspectRatio = 1.77777777777;
762  break;
763  case ASPECT_2_21_1:
764  recFile->m_videoAspectRatio = 2.21;
765  break;
766  default:
767  recFile->m_videoAspectRatio = (double)m_videoAspect / 1000000.0;
768  break;
769  }
770  recFile->Save();
771  }
772 
773  if (m_curRecording)
774  m_curRecording->SaveAspect(frame, mark, customAspect);
775 }
776 
777 void RecorderBase::ResolutionChange(uint width, uint height, long long frame)
778 {
779  if (m_curRecording)
780  {
781  // Populate the recordfile table as early as possible, the best value
782  // value will be determined when the recording completes.
785  {
786  m_curRecording->GetRecordingFile()->m_videoResolution = QSize(width, height);
788  }
789  m_curRecording->SaveResolution(frame, width, height);
790  }
791 }
792 
793 void RecorderBase::FrameRateChange(uint framerate, long long frame)
794 {
795  if (m_curRecording)
796  {
797  // Populate the recordfile table as early as possible, the average
798  // value will be determined when the recording completes.
800  {
801  m_curRecording->GetRecordingFile()->m_videoFrameRate = (double)framerate / 1000.0;
803  }
804  m_curRecording->SaveFrameRate(frame, framerate);
805  }
806 }
807 
808 void RecorderBase::VideoCodecChange(AVCodecID vCodec)
809 {
811  {
812  m_curRecording->GetRecordingFile()->m_videoCodec = ff_codec_id_string(vCodec);
814  }
815 }
816 
817 void RecorderBase::AudioCodecChange(AVCodecID aCodec)
818 {
820  {
821  m_curRecording->GetRecordingFile()->m_audioCodec = ff_codec_id_string(aCodec);
823  }
824 }
825 
826 void RecorderBase::SetDuration(uint64_t duration)
827 {
828  if (m_curRecording)
830 }
831 
832 void RecorderBase::SetTotalFrames(uint64_t total_frames)
833 {
834  if (m_curRecording)
835  m_curRecording->SaveTotalFrames(total_frames);
836 }
837 
838 
840  TVRec *tvrec,
841  ChannelBase *channel,
843  const GeneralDBOptions &genOpt)
844 {
845  if (!channel)
846  return nullptr;
847 
848  RecorderBase *recorder = nullptr;
849  if (genOpt.m_inputType == "MPEG")
850  { // NOLINTNEXTLINE(bugprone-branch-clone)
851 #ifdef USING_IVTV
852  recorder = new MpegRecorder(tvrec);
853 #endif // USING_IVTV
854  }
855 #ifdef USING_HDPVR
856  else if (genOpt.m_inputType == "HDPVR")
857  {
858  recorder = new MpegRecorder(tvrec);
859  }
860 #endif // USING_HDPVR
861 #ifdef USING_V4L2
862  else if (genOpt.m_inputType == "V4L2ENC")
863  {
864  if (dynamic_cast<V4LChannel*>(channel))
865  recorder = new V4L2encRecorder(tvrec, dynamic_cast<V4LChannel*>(channel));
866  }
867 #endif
868 #ifdef USING_FIREWIRE
869  else if (genOpt.m_inputType == "FIREWIRE")
870  {
871  if (dynamic_cast<FirewireChannel*>(channel))
872  recorder = new FirewireRecorder(tvrec, dynamic_cast<FirewireChannel*>(channel));
873  }
874 #endif // USING_FIREWIRE
875 #ifdef USING_HDHOMERUN
876  else if (genOpt.m_inputType == "HDHOMERUN")
877  {
878  if (dynamic_cast<HDHRChannel*>(channel))
879  {
880  recorder = new HDHRRecorder(tvrec, dynamic_cast<HDHRChannel*>(channel));
881  recorder->SetBoolOption("wait_for_seqstart", genOpt.m_waitForSeqstart);
882  }
883  }
884 #endif // USING_HDHOMERUN
885 #ifdef USING_CETON
886  else if (genOpt.m_inputType == "CETON")
887  {
888  if (dynamic_cast<CetonChannel*>(channel))
889  {
890  recorder = new CetonRecorder(tvrec, dynamic_cast<CetonChannel*>(channel));
891  recorder->SetBoolOption("wait_for_seqstart", genOpt.m_waitForSeqstart);
892  }
893  }
894 #endif // USING_CETON
895 #ifdef USING_DVB
896  else if (genOpt.m_inputType == "DVB")
897  {
898  if (dynamic_cast<DVBChannel*>(channel))
899  {
900  recorder = new DVBRecorder(tvrec, dynamic_cast<DVBChannel*>(channel));
901  recorder->SetBoolOption("wait_for_seqstart", genOpt.m_waitForSeqstart);
902  }
903  }
904 #endif // USING_DVB
905 #ifdef USING_IPTV
906  else if (genOpt.m_inputType == "FREEBOX")
907  {
908  if (dynamic_cast<IPTVChannel*>(channel))
909  {
910  recorder = new IPTVRecorder(tvrec, dynamic_cast<IPTVChannel*>(channel));
911  recorder->SetOption("mrl", genOpt.m_videoDev);
912  }
913  }
914 #endif // USING_IPTV
915 #ifdef USING_VBOX
916  else if (genOpt.m_inputType == "VBOX")
917  {
918  if (dynamic_cast<IPTVChannel*>(channel))
919  recorder = new IPTVRecorder(tvrec, dynamic_cast<IPTVChannel*>(channel));
920  }
921 #endif // USING_VBOX
922 #ifdef USING_ASI
923  else if (genOpt.m_inputType == "ASI")
924  {
925  if (dynamic_cast<ASIChannel*>(channel))
926  {
927  recorder = new ASIRecorder(tvrec, dynamic_cast<ASIChannel*>(channel));
928  recorder->SetBoolOption("wait_for_seqstart", genOpt.m_waitForSeqstart);
929  }
930  }
931 #endif // USING_ASI
932  else if (genOpt.m_inputType == "IMPORT")
933  {
934  recorder = new ImportRecorder(tvrec);
935  }
936  else if (genOpt.m_inputType == "DEMO")
937  {
938 #ifdef USING_IVTV
939  recorder = new MpegRecorder(tvrec);
940 #else
941  recorder = new ImportRecorder(tvrec);
942 #endif
943  }
944 #if CONFIG_LIBMP3LAME && defined(USING_V4L2)
945  else if (CardUtil::IsV4L(genOpt.m_inputType))
946  {
947  // V4L/MJPEG/GO7007 from here on
948  recorder = new NuppelVideoRecorder(tvrec, channel);
949  recorder->SetBoolOption("skipbtaudio", genOpt.m_skipBtAudio);
950  }
951 #endif // USING_V4L2
952  else if (genOpt.m_inputType == "EXTERNAL")
953  {
954  if (dynamic_cast<ExternalChannel*>(channel))
955  recorder = new ExternalRecorder(tvrec, dynamic_cast<ExternalChannel*>(channel));
956  }
957 
958  if (recorder)
959  {
960  recorder->SetOptionsFromProfile(&profile,
961  genOpt.m_videoDev, genOpt.m_audioDev, genOpt.m_vbiDev);
962  // Override the samplerate defined in the profile if this card
963  // was configured with a fixed rate.
964  if (genOpt.m_audioSampleRate)
965  recorder->SetOption("samplerate", genOpt.m_audioSampleRate);
966  }
967  else
968  {
969  QString msg = "Need %1 recorder, but compiled without %2 support!";
970  msg = msg.arg(genOpt.m_inputType).arg(genOpt.m_inputType);
971  LOG(VB_GENERAL, LOG_ERR,
972  "RecorderBase::CreateRecorder() Error, " + msg);
973  }
974 
975  return recorder;
976 }
977 
978 /* vim: set expandtab tabstop=4 shiftwidth=4: */
This is a specialization of DTVRecorder used to handle streams from V4L2 recorders.
long long m_lastSavedDuration
Definition: recorderbase.h:356
virtual void SetOption(const QString &name, const QString &value)
Set an specific option.
int restart(void)
Returns milliseconds elapsed since last start() or restart() and resets the count.
Definition: mythtimer.cpp:62
uint GetInputID(void) const
Definition: programinfo.h:455
frm_pos_map_t m_durationMap
Definition: recorderbase.h:349
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
QString m_videocodec
Definition: recorderbase.h:310
long long GetKeyframePosition(long long desired) const
Returns closest keyframe position before the desired frame.
QMutex m_nextRingBufferLock
Definition: recorderbase.h:339
void AudioCodecChange(AVCodecID aCodec)
Note a change in audio codec.
void SavePositionMap(bool force=false, bool finished=false)
Save the seektable to the DB.
long long m_lastSavedKeyframe
Definition: recorderbase.h:355
QAtomicInt m_timeOfLatestDataPacketInterval
Definition: recorderbase.h:367
frm_pos_map_t m_positionMap
Definition: recorderbase.h:347
RecordingInfo * m_nextRecording
Definition: recorderbase.h:341
This is a specialization of DTVRecorder used to handle streams from DVB drivers.
Definition: dvbrecorder.h:21
void TryWriteProgStartMark(const frm_pos_map_t &durationDeltaCopy)
void WriterFlush(void)
Calls ThreadedFileWriter::Flush(void)
QSize m_videoResolution
Definition: recordingfile.h:50
void RingBufferChanged(RingBuffer *, RecordingInfo *, RecordingQuality *)
Definition: tv_rec.cpp:3374
QDateTime GetDesiredEndTime(void) const
bool m_weMadeBuffer
Definition: recorderbase.h:305
QMutex m_pauseLock
Definition: recorderbase.h:326
bool m_skipBtAudio
Definition: tv_rec.h:74
virtual void StartNewFile(void)
Definition: recorderbase.h:267
QString m_videoDev
Definition: tv_rec.h:69
QString GetFilename(void) const
Returns name of file used by this RingBuffer.
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:34
MarkTypes QueryAverageAspectRatio(void) const
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: lang.c:20
virtual bool PauseAndWait(int timeout=100)
If m_request_pause is true, sets pause and blocks up to timeout milliseconds or until unpaused,...
void AspectChange(uint aspect, long long frame)
Note a change in aspect ratio in the recordedmark table.
bool GetKeyframeDurations(long long start, long long end, frm_pos_map_t &) const
QDateTime m_timeOfFirstData
Definition: recorderbase.h:365
RecordingFile * GetRecordingFile() const
void SetDesiredEndTime(const QDateTime &dt)
QString m_audioCodec
Definition: recordingfile.h:54
int m_audioSampleRate
Definition: tv_rec.h:73
QMutex m_statisticsLock
Definition: recorderbase.h:363
bool m_request_recording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:332
bool isRunning(void) const
Returns true if start() or restart() has been called at least once since construction and since any c...
Definition: mythtimer.cpp:134
void SaveFilesize(uint64_t fsize) override
Sets recording file size in database, and sets "filesize" field.
void SaveTotalFrames(int64_t frames)
Store the Total Frames at frame 0 in the recordedmarkup table.
ImportRecorder imports files, creating a seek map and other stuff that MythTV likes to have for recor...
virtual QString getValue(void) const
AVCodecID m_primaryAudioCodec
Definition: recorderbase.h:309
QMap< long long, long long > frm_pos_map_t
Frame # -> File offset map.
Definition: programtypes.h:46
This is a specialization of DTVRecorder used to handle DVB and ATSC streams from a firewire input.
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:382
virtual bool IsPaused(bool holding_lock=false) const
Returns true iff recorder is paused.
QString MakeUniqueKey(void) const
Creates a unique string that can be used to identify an existing recording.
Definition: programinfo.h:331
QWaitCondition m_unpauseWait
Definition: recorderbase.h:330
MarkTypes
Definition: programtypes.h:48
void SetTotalFrames(uint64_t total_frames)
Note the total frames in the recordedmark table.
Holds information on recordings and videos.
Definition: programinfo.h:66
uint QueryAverageHeight(void) const
If present in recording this loads average height of the main video stream from database's stream mar...
RingBuffer * m_nextRingBuffer
Definition: recorderbase.h:340
RingBuffer * m_ringBuffer
Definition: recorderbase.h:304
void SetDuration(uint64_t duration)
Note the total duration in the recordedmark table.
virtual void FinishRecording(void)
This class is used as a container for messages.
Definition: mythevent.h:16
AVContainer m_containerFormat
Definition: recorderbase.h:307
QString m_audioDev
Definition: tv_rec.h:71
QString m_videoCodec
Definition: recordingfile.h:49
void SetFrameRate(double rate)
Sets the video frame rate.
Definition: recorderbase.h:75
QWaitCondition m_pauseWait
Definition: recorderbase.h:329
MarkTypes m_positionMapType
Definition: recorderbase.h:345
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:141
void SaveVideoProperties(uint mask, uint video_property_flags)
static void clear(SettingsMap &cache, SettingsMap &overrides, const QString &myKey)
Definition: mythdb.cpp:830
uint QueryAverageWidth(void) const
If present in recording this loads average width of the main video stream from database's stream mark...
QAtomicInt m_timeOfLatestDataCount
Definition: recorderbase.h:366
#define LOC
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
Definition: mythlogging.h:24
bool m_waitForSeqstart
Definition: tv_rec.h:77
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QDateTime m_timeOfLatestData
Definition: recorderbase.h:368
static QString toString(Type, uint id)
Converts "recstatus" into a short (unreadable) string.
Definition: recStatus.cpp:39
virtual ~RecorderBase()
void RecorderPaused(void)
This is a callback, called by the "recorder" instance when it has actually paused.
Definition: tv_rec.cpp:2935
static RecorderBase * CreateRecorder(TVRec *tvrec, ChannelBase *channel, RecordingProfile &profile, const GeneralDBOptions &genOpt)
qint64 m_estimatedProgStartMS
Definition: recorderbase.h:354
double m_videoFrameRate
Definition: recordingfile.h:52
MythTimer m_ringBufferCheckTimer
Definition: recorderbase.h:342
virtual void SetRecordingStatus(RecStatus::Type status, const QString &file, int line)
void SendMythSystemRecEvent(const QString &msg, const RecordingInfo *pginfo)
void SaveAspect(uint64_t frame, MarkTypes type, uint customAspect)
Store aspect ratio of a frame in the recordedmark table.
unsigned int uint
Definition: compat.h:140
QWaitCondition m_recordingWait
Definition: recorderbase.h:335
void SetRecordingStatus(RecStatus::Type status)
Definition: programinfo.h:565
void FrameRateChange(uint framerate, long long frame)
Note a change in video frame rate in the recordedmark table.
RecorderBase(TVRec *rec)
double m_videoAspectRatio
Definition: recordingfile.h:51
bool m_recording
True while recording is actually being performed.
Definition: recorderbase.h:334
Holds information on a recording file and it's video and audio streams.
Definition: recordingfile.h:29
virtual void Unpause(void)
Unpause tells recorder to unpause.
This is the abstract base class for supporting recorder hardware.
Definition: recorderbase.h:66
void dispatch(const MythEvent &event)
void SaveResolution(uint64_t frame, uint width, uint height)
Store the Resolution at frame in the recordedmarkup table.
uint QueryAverageFrameRate(void) const
If present in recording this loads average frame rate of the main video stream from database's stream...
void SaveFrameRate(uint64_t frame, uint framerate)
Store the Frame Rate at frame in the recordedmarkup table.
RecordingGaps m_recordingGaps
Definition: recorderbase.h:370
This is a specialization of DTVRecorder used to handle streams from ASI drivers.
Definition: asirecorder.h:55
AVCodecID m_primaryVideoCodec
Definition: recorderbase.h:308
FrameRate m_frameRate
Definition: recorderbase.h:321
bool m_request_pause
Definition: recorderbase.h:327
long long GetWritePosition(void) const
Returns how far into a ThreadedFileWriter file we have written.
virtual bool IsRecording(void)
Tells whether the StartRecorder() loop is running.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
TVRec * m_tvrec
Definition: recorderbase.h:303
void ResolutionChange(uint width, uint height, long long frame)
Note a change in video size in the recordedmark table.
Abstract class providing a generic interface to tuning hardware.
Definition: channelbase.h:31
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:439
void SaveMarkupMap(const frm_dir_map_t &, MarkTypes type=MARK_ALL, int64_t min_frame=-1, int64_t max_frame=-1) const
QMutex * avcodeclock
This global variable is used to makes certain calls to avlib threadsafe.
uint m_videoAspect
Definition: recorderbase.h:317
static const uint kTimeOfLatestDataIntervalTarget
timeOfLatest update interval target in milliseconds.
Definition: recorderbase.h:372
int elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:90
QString m_inputType
Definition: tv_rec.h:72
AVContainer m_containerFormat
Definition: recordingfile.h:47
void SetRecording(const RecordingInfo *pginfo)
Changes the Recording from the one set initially with SetOptionsFromProfile().
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:364
virtual void ResetForNewFile(void)=0
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.
This is a specialization of DTVRecorder used to handle streams from External 'blackbox' recorders.
frm_pos_map_t m_positionMapDelta
Definition: recorderbase.h:348
long long GetRealFileSize(void) const
Returns the size of the file we are reading/writing, or -1 if the query fails.
Definition: ringbuffer.cpp:497
void SetIntOption(RecordingProfile *profile, const QString &name)
Convenience function used to set integer options from a profile.
MythTimer m_positionMapTimer
Definition: recorderbase.h:351
virtual void Pause(bool clear=true)
Pause tells recorder to pause, it should not block.
frm_pos_map_t m_durationMapDelta
Definition: recorderbase.h:350
QDateTime GetRecordingEndTime(void) const
Approximate time the recording should have ended, did end, or is intended to end.
Definition: programinfo.h:404
RemoteEncoder * recorder
void SetNextRecording(const RecordingInfo *, RingBuffer *)
Sets next recording info, to be applied as soon as practical.
virtual bool WaitForPause(int timeout=1000)
WaitForPause blocks until recorder is actually paused, or timeout milliseconds elapse.
QString m_videodevice
Definition: recorderbase.h:311
RecordingInfo * m_curRecording
Definition: recorderbase.h:323
virtual void StopRecording(void)
StopRecording() signals to the recorder that it should stop recording and exit cleanly.
Default UTC.
Definition: mythdate.h:14
QMap< uint64_t, MarkTypes > frm_dir_map_t
Frame # -> Mark map.
Definition: programtypes.h:81
Implements a file/stream reader/writer.
void VideoCodecChange(AVCodecID vCodec)
Note a change in video codec.
virtual void ClearStatistics(void)
deprecated, it is only 1:1 sample aspect ratio
Definition: programtypes.h:65
void SetStrOption(RecordingProfile *profile, const QString &name)
Convenience function used to set QString options from a profile.
static QString AVContainerToString(AVContainer format)
QString m_vbiDev
Definition: tv_rec.h:70
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
bool GetKeyframePositions(long long start, long long end, frm_pos_map_t &) const
QAtomicInt m_timeOfFirstDataIsSet
Definition: recorderbase.h:364
QMutex m_positionMapLock
Definition: recorderbase.h:346
static bool IsV4L(const QString &rawtype)
Definition: cardutil.h:131
double GetFrameRate(void) const
Returns the latest frame rate.
Definition: recorderbase.h:226
void SetDesiredStartTime(const QDateTime &dt)
QDateTime GetDesiredStartTime(void) const
void SaveTotalDuration(int64_t duration)
Store the Total Duration at frame 0 in the recordedmarkup table.
void SetRingBuffer(RingBuffer *rbuf)
Tells recorder to use an externally created ringbuffer.
void SavePositionMapDelta(frm_pos_map_t &, MarkTypes type) const
uint m_videoHeight
Definition: recorderbase.h:319
virtual bool IsRecordingRequested(void)
Tells us if StopRecording() has been called.