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  {
97  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetRecording(0x%1) title(%2)")
98  .arg((uint64_t)pginfo,0,16).arg(pginfo->GetTitle()));
99  }
100  else
101  {
102  LOG(VB_RECORD, LOG_INFO, LOC + "SetRecording(0x0)");
103  }
104 
105  ProgramInfo *oldrec = m_curRecording;
106  if (pginfo)
107  {
108  // NOTE: RecorderBase and TVRec do not share a single RecordingInfo
109  // instance which may lead to the possibility that changes made
110  // in the database by one are overwritten by the other
111  m_curRecording = new RecordingInfo(*pginfo);
112  // Compute an estimate of the actual progstart delay for setting the
113  // MARK_UTIL_PROGSTART mark. We can't reliably use
114  // m_curRecording->GetRecordingStartTime() because the scheduler rounds it
115  // to the nearest minute, so we use the current time instead.
120  recFile->Save();
121  }
122  else
123  m_curRecording = nullptr;
124 
125  delete oldrec;
126 }
127 
129 {
130  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetNextRecording(0x%1, 0x%2)")
131  .arg(reinterpret_cast<intptr_t>(ri),0,16)
132  .arg(reinterpret_cast<intptr_t>(rb),0,16));
133 
134  // First we do some of the time consuming stuff we can do now
135  SavePositionMap(true);
136  if (m_ringBuffer)
137  {
139  if (m_curRecording)
141  }
142 
143  // Then we set the next info
144  QMutexLocker locker(&m_nextRingBufferLock);
145  if (m_nextRecording)
146  {
147  delete m_nextRecording;
148  m_nextRecording = nullptr;
149  }
150  if (ri)
151  m_nextRecording = new RecordingInfo(*ri);
152 
153  delete m_nextRingBuffer;
154  m_nextRingBuffer = rb;
155 }
156 
157 void RecorderBase::SetOption(const QString &name, const QString &value)
158 {
159  if (name == "videocodec")
160  m_videocodec = value;
161  else if (name == "videodevice")
162  m_videodevice = value;
163  else if (name == "tvformat")
164  {
165  m_ntsc = false;
166  if (value.toLower() == "ntsc" || value.toLower() == "ntsc-jp")
167  { // NOLINT(bugprone-branch-clone)
168  m_ntsc = true;
169  SetFrameRate(29.97);
170  }
171  else if (value.toLower() == "pal-m")
172  SetFrameRate(29.97);
173  else if (value.toLower() == "atsc")
174  {
175  // Here we set the TV format values for ATSC. ATSC isn't really
176  // NTSC, but users who configure a non-ATSC-recorder as ATSC
177  // are far more likely to be using a mix of ATSC and NTSC than
178  // a mix of ATSC and PAL or SECAM. The atsc recorder itself
179  // does not care about these values, except in so much as tv_rec
180  // cares about m_videoFrameRate which should be neither 29.97
181  // nor 25.0, but based on the actual video.
182  m_ntsc = true;
183  SetFrameRate(29.97);
184  }
185  else
186  SetFrameRate(25.00);
187  }
188  else
189  {
190  LOG(VB_GENERAL, LOG_WARNING, LOC +
191  QString("SetOption(%1,%2): Option not recognized")
192  .arg(name).arg(value));
193  }
194 }
195 
196 void RecorderBase::SetOption(const QString &name, int value)
197 {
198  LOG(VB_GENERAL, LOG_ERR, LOC +
199  QString("SetOption(): Unknown int option: %1: %2")
200  .arg(name).arg(value));
201 }
202 
204 {
205  const StandardSetting *setting = profile->byName(name);
206  if (setting)
207  SetOption(name, setting->getValue().toInt());
208  else
209  LOG(VB_GENERAL, LOG_ERR, LOC +
210  QString("SetIntOption(...%1): Option not in profile.").arg(name));
211 }
212 
214 {
215  const StandardSetting *setting = profile->byName(name);
216  if (setting)
217  SetOption(name, setting->getValue());
218  else
219  LOG(VB_GENERAL, LOG_ERR, LOC +
220  QString("SetStrOption(...%1): Option not in profile.").arg(name));
221 }
222 
229 {
230  QMutexLocker locker(&m_pauseLock);
231  m_requestRecording = false;
232  m_unpauseWait.wakeAll();
233  while (m_recording)
234  {
235  m_recordingWait.wait(&m_pauseLock, 100);
236  if (m_requestRecording)
237  {
238  LOG(VB_GENERAL, LOG_ERR, LOC +
239  "Programmer Error: Recorder started while we were in "
240  "StopRecording");
241  m_requestRecording = false;
242  }
243  }
244 }
245 
248 {
249  QMutexLocker locker(&m_pauseLock);
250  return m_recording;
251 }
252 
255 {
256  QMutexLocker locker(&m_pauseLock);
257  return m_requestRecording;
258 }
259 
268 {
269  (void) clear;
270  QMutexLocker locker(&m_pauseLock);
271  m_requestPause = true;
272 }
273 
279 {
280  QMutexLocker locker(&m_pauseLock);
281  m_requestPause = false;
282  m_unpauseWait.wakeAll();
283 }
284 
286 bool RecorderBase::IsPaused(bool holding_lock) const
287 {
288  if (!holding_lock)
289  m_pauseLock.lock();
290  bool ret = m_paused;
291  if (!holding_lock)
292  m_pauseLock.unlock();
293  return ret;
294 }
295 
303 {
304  MythTimer t;
305  t.start();
306 
307  QMutexLocker locker(&m_pauseLock);
308  while (!IsPaused(true) && m_requestPause)
309  {
310  int wait = timeout - t.elapsed();
311  if (wait <= 0)
312  return false;
313  m_pauseWait.wait(&m_pauseLock, wait);
314  }
315  return true;
316 }
317 
331 {
332  QMutexLocker locker(&m_pauseLock);
333  if (m_requestPause)
334  {
335  if (!IsPaused(true))
336  {
337  m_paused = true;
338  m_pauseWait.wakeAll();
339  if (m_tvrec)
341  }
342 
344  }
345 
346  if (!m_requestPause && IsPaused(true))
347  {
348  m_paused = false;
349  m_unpauseWait.wakeAll();
350  }
351 
352  return IsPaused(true);
353 }
354 
356 {
357  bool did_switch = false;
358 
359  m_nextRingBufferLock.lock();
360 
361  RecordingQuality *recq = nullptr;
362 
363  if (m_nextRingBuffer)
364  {
365  FinishRecording();
366 
367  recq = GetRecordingQuality(nullptr);
368 
369  ResetForNewFile();
370 
372  m_frameRate = FrameRate(0);
373 
376 
377  m_nextRingBuffer = nullptr;
378  m_nextRecording = nullptr;
379 
380  StartNewFile();
381  did_switch = true;
382  }
383  m_nextRingBufferLock.unlock();
384 
385  if (recq && m_tvrec)
386  {
387  // This call will free recq.
389  }
390  else
391  {
392  delete recq;
393  }
394 
396  return did_switch;
397 }
398 
400  const QString& file, int line)
401 {
403  {
404  LOG(VB_RECORD, LOG_INFO,
405  QString("Modifying recording status from %1 to %2 at %3:%4")
407  .arg(RecStatus::toString(status, kSingleRecord)).arg(file).arg(line));
408 
410 
411  if (status == RecStatus::Failing)
412  {
414  SendMythSystemRecEvent("REC_FAILING", m_curRecording);
415  }
416 
417  MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
418  .arg(m_curRecording->GetInputID())
419  .arg(m_curRecording->GetChanID())
421  .arg(status)
423  gCoreContext->dispatch(me);
424  }
425 }
426 
428 {
429  QMutexLocker locker(&m_statisticsLock);
430  m_timeOfFirstData = QDateTime();
431  m_timeOfFirstDataIsSet.fetchAndStoreRelaxed(0);
432  m_timeOfLatestData = QDateTime();
433  m_timeOfLatestDataCount.fetchAndStoreRelaxed(0);
434  m_timeOfLatestDataPacketInterval.fetchAndStoreRelaxed(2000);
435  m_recordingGaps.clear();
436 }
437 
439 {
440  if (m_curRecording)
441  {
442  if (m_primaryVideoCodec == AV_CODEC_ID_H264)
444 
446  if (recFile)
447  {
448  // Container
450 
451  // Video
452  recFile->m_videoCodec = ff_codec_id_string(m_primaryVideoCodec);
454  {
455  case MARK_ASPECT_1_1 :
456  recFile->m_videoAspectRatio = 1.0;
457  break;
458  case MARK_ASPECT_4_3:
459  recFile->m_videoAspectRatio = 1.33333333333;
460  break;
461  case MARK_ASPECT_16_9:
462  recFile->m_videoAspectRatio = 1.77777777777;
463  break;
464  case MARK_ASPECT_2_21_1:
465  recFile->m_videoAspectRatio = 2.21;
466  break;
467  default:
468  recFile->m_videoAspectRatio = (double)m_videoAspect / 1000000.0;
469  break;
470  }
471  QSize resolution(m_curRecording->QueryAverageWidth(),
473  recFile->m_videoResolution = resolution;
474  recFile->m_videoFrameRate = (double)m_curRecording->QueryAverageFrameRate() / 1000.0;
475 
476  // Audio
477  recFile->m_audioCodec = ff_codec_id_string(m_primaryAudioCodec);
478 
479  recFile->Save();
480  }
481  else
482  LOG(VB_GENERAL, LOG_CRIT, "RecordingFile object is NULL. No video file metadata can be stored");
483 
484  SavePositionMap(true, true); // Save Position Map only, not file size
485 
486  if (m_ringBuffer)
488  }
489 
490  LOG(VB_GENERAL, LOG_NOTICE, QString("Finished Recording: "
491  "Container: %7 "
492  "Video Codec: %1 (%2x%3 A/R: %4 %5fps) "
493  "Audio Codec: %6")
494  .arg(avcodec_get_name(m_primaryVideoCodec))
495  .arg(m_videoWidth)
496  .arg(m_videoHeight)
497  .arg(m_videoAspect)
498  .arg(GetFrameRate())
499  .arg(avcodec_get_name(m_primaryAudioCodec))
501 }
502 
504  const RecordingInfo *r) const
505 {
506  QMutexLocker locker(&m_statisticsLock);
507  if (r && m_curRecording &&
509  {
512  }
513  return new RecordingQuality(
516 }
517 
518 long long RecorderBase::GetKeyframePosition(long long desired) const
519 {
520  QMutexLocker locker(&m_positionMapLock);
521  long long ret = -1;
522 
523  if (m_positionMap.empty())
524  return ret;
525 
526  // find closest exact or previous keyframe position...
527  frm_pos_map_t::const_iterator it = m_positionMap.lowerBound(desired);
528  if (it == m_positionMap.end())
529  ret = *m_positionMap.begin();
530  else if ((it.key() == desired) ||
531  (--it != m_positionMap.end()))
532  ret = *it;
533 
534  return ret;
535 }
536 
538  long long start, long long end, frm_pos_map_t &map) const
539 {
540  map.clear();
541 
542  QMutexLocker locker(&m_positionMapLock);
543  if (m_positionMap.empty())
544  return true;
545 
546  frm_pos_map_t::const_iterator it = m_positionMap.lowerBound(start);
547  end = (end < 0) ? INT64_MAX : end;
548  for (; (it != m_positionMap.end()) &&
549  (it.key() <= end); ++it)
550  map[it.key()] = *it;
551 
552  LOG(VB_GENERAL, LOG_DEBUG, LOC +
553  QString("GetKeyframePositions(%1,%2,#%3) out of %4")
554  .arg(start).arg(end).arg(map.size()).arg(m_positionMap.size()));
555 
556  return true;
557 }
558 
560  long long start, long long end, frm_pos_map_t &map) const
561 {
562  map.clear();
563 
564  QMutexLocker locker(&m_positionMapLock);
565  if (m_durationMap.empty())
566  return true;
567 
568  frm_pos_map_t::const_iterator it = m_durationMap.lowerBound(start);
569  end = (end < 0) ? INT64_MAX : end;
570  for (; (it != m_durationMap.end()) &&
571  (it.key() <= end); ++it)
572  map[it.key()] = *it;
573 
574  LOG(VB_GENERAL, LOG_DEBUG, LOC +
575  QString("GetKeyframeDurations(%1,%2,#%3) out of %4")
576  .arg(start).arg(end).arg(map.size()).arg(m_durationMap.size()));
577 
578  return true;
579 }
580 
589 void RecorderBase::SavePositionMap(bool force, bool finished)
590 {
591  bool needToSave = force;
592  m_positionMapLock.lock();
593 
594  bool has_delta = !m_positionMapDelta.empty();
595  // set pm_elapsed to a fake large value if the timer hasn't yet started
596  uint pm_elapsed = (m_positionMapTimer.isRunning()) ?
598  // save on every 1.5 seconds if in the first few frames of a recording
599  needToSave |= (m_positionMap.size() < 30) &&
600  has_delta && (pm_elapsed >= 1500);
601  // save every 10 seconds later on
602  needToSave |= has_delta && (pm_elapsed >= 10000);
603  // Assume that m_durationMapDelta is the same size as
604  // m_positionMapDelta and implicitly use the same logic about when
605  // to same m_durationMapDelta.
606 
607  if (m_curRecording && needToSave)
608  {
610  if (has_delta)
611  {
612  // copy the delta map because most times we are called it will be in
613  // another thread and we don't want to lock the main recorder thread
614  // which is populating the delta map
616  m_positionMapDelta.clear();
617  frm_pos_map_t durationDeltaCopy(m_durationMapDelta);
618  m_durationMapDelta.clear();
619  m_positionMapLock.unlock();
620 
622  m_curRecording->SavePositionMapDelta(durationDeltaCopy,
624 
625  TryWriteProgStartMark(durationDeltaCopy);
626  }
627  else
628  {
629  m_positionMapLock.unlock();
630  }
631 
632  if (m_ringBuffer && !finished) // Finished Recording will update the final size for us
633  {
635  }
636  }
637  else
638  {
639  m_positionMapLock.unlock();
640  }
641 
642  // Make sure a ringbuffer switch is checked at least every 3
643  // seconds. Otherwise, this check is only performed on keyframes,
644  // and if there is a problem with the input we may never see one
645  // again, resulting in a wedged recording.
646  if (!finished && m_ringBufferCheckTimer.isRunning() &&
648  {
650  LOG(VB_RECORD, LOG_WARNING, LOC +
651  "Ringbuffer was switched due to timeout instead of keyframe.");
652  }
653 }
654 
656 {
657  // Note: all log strings contain "progstart mark" for searching.
658  if (m_estimatedProgStartMS <= 0)
659  {
660  // Do nothing because no progstart mark is needed.
661  LOG(VB_RECORD, LOG_DEBUG,
662  QString("No progstart mark needed because delta=%1")
663  .arg(m_estimatedProgStartMS));
664  return;
665  }
666  frm_pos_map_t::const_iterator last_it = durationDeltaCopy.end();
667  --last_it;
668  long long bookmarkFrame = 0;
669  LOG(VB_RECORD, LOG_DEBUG,
670  QString("durationDeltaCopy.begin() = (%1,%2)")
671  .arg(durationDeltaCopy.begin().key())
672  .arg(durationDeltaCopy.begin().value()));
673  if (m_estimatedProgStartMS > *last_it)
674  {
675  // Do nothing because we haven't reached recstartts yet.
676  LOG(VB_RECORD, LOG_DEBUG,
677  QString("No progstart mark yet because estimatedProgStartMS=%1 "
678  "and *last_it=%2")
679  .arg(m_estimatedProgStartMS).arg(*last_it));
680  }
682  m_estimatedProgStartMS < *durationDeltaCopy.begin())
683  {
684  // Set progstart mark @ lastSavedKeyframe
685  LOG(VB_RECORD, LOG_DEBUG,
686  QString("Set progstart mark=%1 because %2<=%3<%4")
688  .arg(m_estimatedProgStartMS).arg(*durationDeltaCopy.begin()));
689  bookmarkFrame = m_lastSavedKeyframe;
690  }
691  else if (*durationDeltaCopy.begin() <= m_estimatedProgStartMS &&
692  m_estimatedProgStartMS < *last_it)
693  {
694  frm_pos_map_t::const_iterator upper_it = durationDeltaCopy.begin();
695  for (; upper_it != durationDeltaCopy.end(); ++upper_it)
696  {
697  if (*upper_it > m_estimatedProgStartMS)
698  {
699  --upper_it;
700  // Set progstart mark @ upper_it.key()
701  LOG(VB_RECORD, LOG_DEBUG,
702  QString("Set progstart mark=%1 because "
703  "estimatedProgStartMS=%2 and upper_it.value()=%3")
704  .arg(upper_it.key()).arg(m_estimatedProgStartMS)
705  .arg(upper_it.value()));
706  bookmarkFrame = upper_it.key();
707  break;
708  }
709  }
710  }
711  else
712  {
713  // do nothing
714  LOG(VB_RECORD, LOG_DEBUG, "No progstart mark due to fallthrough");
715  }
716  if (bookmarkFrame)
717  {
718  frm_dir_map_t progStartMap;
719  progStartMap[bookmarkFrame] = MARK_UTIL_PROGSTART;
721  }
722  m_lastSavedKeyframe = last_it.key();
723  m_lastSavedDuration = last_it.value();
724  LOG(VB_RECORD, LOG_DEBUG,
725  QString("Setting lastSavedKeyframe=%1 lastSavedDuration=%2 "
726  "for progstart mark calculations")
728 }
729 
730 void RecorderBase::AspectChange(uint aspect, long long frame)
731 {
733  uint customAspect = 0;
734  if (aspect == ASPECT_1_1 || aspect >= ASPECT_CUSTOM)
735  {
736  if (aspect > 0x0F)
737  customAspect = aspect;
738  else if (m_videoWidth && m_videoHeight)
739  customAspect = m_videoWidth * 1000000 / m_videoHeight;
740 
741  mark = (customAspect) ? MARK_ASPECT_CUSTOM : mark;
742  }
743  if (aspect == ASPECT_4_3)
745  if (aspect == ASPECT_16_9)
747  if (aspect == ASPECT_2_21_1)
749 
750  // Populate the recordfile table as early as possible, the best
751  // value will be determined when the recording completes.
754  {
756  switch (m_videoAspect)
757  {
758  case ASPECT_1_1 :
759  recFile->m_videoAspectRatio = 1.0;
760  break;
761  case ASPECT_4_3:
762  recFile->m_videoAspectRatio = 1.33333333333;
763  break;
764  case ASPECT_16_9:
765  recFile->m_videoAspectRatio = 1.77777777777;
766  break;
767  case ASPECT_2_21_1:
768  recFile->m_videoAspectRatio = 2.21;
769  break;
770  default:
771  recFile->m_videoAspectRatio = (double)m_videoAspect / 1000000.0;
772  break;
773  }
774  recFile->Save();
775  }
776 
777  if (m_curRecording)
778  m_curRecording->SaveAspect(frame, mark, customAspect);
779 }
780 
781 void RecorderBase::ResolutionChange(uint width, uint height, long long frame)
782 {
783  if (m_curRecording)
784  {
785  // Populate the recordfile table as early as possible, the best value
786  // value will be determined when the recording completes.
789  {
790  m_curRecording->GetRecordingFile()->m_videoResolution = QSize(width, height);
792  }
793  m_curRecording->SaveResolution(frame, width, height);
794  }
795 }
796 
797 void RecorderBase::FrameRateChange(uint framerate, long long frame)
798 {
799  if (m_curRecording)
800  {
801  // Populate the recordfile table as early as possible, the average
802  // value will be determined when the recording completes.
804  {
805  m_curRecording->GetRecordingFile()->m_videoFrameRate = (double)framerate / 1000.0;
807  }
808  m_curRecording->SaveFrameRate(frame, framerate);
809  }
810 }
811 
812 void RecorderBase::VideoCodecChange(AVCodecID vCodec)
813 {
815  {
816  m_curRecording->GetRecordingFile()->m_videoCodec = ff_codec_id_string(vCodec);
818  }
819 }
820 
821 void RecorderBase::AudioCodecChange(AVCodecID aCodec)
822 {
824  {
825  m_curRecording->GetRecordingFile()->m_audioCodec = ff_codec_id_string(aCodec);
827  }
828 }
829 
830 void RecorderBase::SetDuration(uint64_t duration)
831 {
832  if (m_curRecording)
834 }
835 
836 void RecorderBase::SetTotalFrames(uint64_t total_frames)
837 {
838  if (m_curRecording)
839  m_curRecording->SaveTotalFrames(total_frames);
840 }
841 
842 
844  TVRec *tvrec,
845  ChannelBase *channel,
847  const GeneralDBOptions &genOpt)
848 {
849  if (!channel)
850  return nullptr;
851 
852  RecorderBase *recorder = nullptr;
853  if (genOpt.m_inputType == "MPEG")
854  { // NOLINTNEXTLINE(bugprone-branch-clone)
855 #ifdef USING_IVTV
856  recorder = new MpegRecorder(tvrec);
857 #endif // USING_IVTV
858  }
859 #ifdef USING_HDPVR
860  else if (genOpt.m_inputType == "HDPVR")
861  {
862  recorder = new MpegRecorder(tvrec);
863  }
864 #endif // USING_HDPVR
865 #ifdef USING_V4L2
866  else if (genOpt.m_inputType == "V4L2ENC")
867  {
868  if (dynamic_cast<V4LChannel*>(channel))
869  recorder = new V4L2encRecorder(tvrec, dynamic_cast<V4LChannel*>(channel));
870  }
871 #endif
872 #ifdef USING_FIREWIRE
873  else if (genOpt.m_inputType == "FIREWIRE")
874  {
875  if (dynamic_cast<FirewireChannel*>(channel))
876  recorder = new FirewireRecorder(tvrec, dynamic_cast<FirewireChannel*>(channel));
877  }
878 #endif // USING_FIREWIRE
879 #ifdef USING_HDHOMERUN
880  else if (genOpt.m_inputType == "HDHOMERUN")
881  {
882  if (dynamic_cast<HDHRChannel*>(channel))
883  {
884  recorder = new HDHRRecorder(tvrec, dynamic_cast<HDHRChannel*>(channel));
885  recorder->SetBoolOption("wait_for_seqstart", genOpt.m_waitForSeqstart);
886  }
887  }
888 #endif // USING_HDHOMERUN
889 #ifdef USING_CETON
890  else if (genOpt.m_inputType == "CETON")
891  {
892  if (dynamic_cast<CetonChannel*>(channel))
893  {
894  recorder = new CetonRecorder(tvrec, dynamic_cast<CetonChannel*>(channel));
895  recorder->SetBoolOption("wait_for_seqstart", genOpt.m_waitForSeqstart);
896  }
897  }
898 #endif // USING_CETON
899 #ifdef USING_DVB
900  else if (genOpt.m_inputType == "DVB")
901  {
902  if (dynamic_cast<DVBChannel*>(channel))
903  {
904  recorder = new DVBRecorder(tvrec, dynamic_cast<DVBChannel*>(channel));
905  recorder->SetBoolOption("wait_for_seqstart", genOpt.m_waitForSeqstart);
906  }
907  }
908 #endif // USING_DVB
909 #ifdef USING_IPTV
910  else if (genOpt.m_inputType == "FREEBOX")
911  {
912  if (dynamic_cast<IPTVChannel*>(channel))
913  {
914  recorder = new IPTVRecorder(tvrec, dynamic_cast<IPTVChannel*>(channel));
915  recorder->SetOption("mrl", genOpt.m_videoDev);
916  }
917  }
918 #endif // USING_IPTV
919 #ifdef USING_VBOX
920  else if (genOpt.m_inputType == "VBOX")
921  {
922  if (dynamic_cast<IPTVChannel*>(channel))
923  recorder = new IPTVRecorder(tvrec, dynamic_cast<IPTVChannel*>(channel));
924  }
925 #endif // USING_VBOX
926 #ifdef USING_ASI
927  else if (genOpt.m_inputType == "ASI")
928  {
929  if (dynamic_cast<ASIChannel*>(channel))
930  {
931  recorder = new ASIRecorder(tvrec, dynamic_cast<ASIChannel*>(channel));
932  recorder->SetBoolOption("wait_for_seqstart", genOpt.m_waitForSeqstart);
933  }
934  }
935 #endif // USING_ASI
936  else if (genOpt.m_inputType == "IMPORT")
937  {
938  recorder = new ImportRecorder(tvrec);
939  }
940  else if (genOpt.m_inputType == "DEMO")
941  {
942 #ifdef USING_IVTV
943  recorder = new MpegRecorder(tvrec);
944 #else
945  recorder = new ImportRecorder(tvrec);
946 #endif
947  }
948 #if CONFIG_LIBMP3LAME && defined(USING_V4L2)
949  else if (CardUtil::IsV4L(genOpt.m_inputType))
950  {
951  // V4L/MJPEG/GO7007 from here on
952  recorder = new NuppelVideoRecorder(tvrec, channel);
953  recorder->SetBoolOption("skipbtaudio", genOpt.m_skipBtAudio);
954  }
955 #endif // USING_V4L2
956  else if (genOpt.m_inputType == "EXTERNAL")
957  {
958  if (dynamic_cast<ExternalChannel*>(channel))
959  recorder = new ExternalRecorder(tvrec, dynamic_cast<ExternalChannel*>(channel));
960  }
961 
962  if (recorder)
963  {
964  recorder->SetOptionsFromProfile(&profile,
965  genOpt.m_videoDev, genOpt.m_audioDev, genOpt.m_vbiDev);
966  // Override the samplerate defined in the profile if this card
967  // was configured with a fixed rate.
968  if (genOpt.m_audioSampleRate)
969  recorder->SetOption("samplerate", genOpt.m_audioSampleRate);
970  }
971  else
972  {
973  QString msg = "Need %1 recorder, but compiled without %2 support!";
974  msg = msg.arg(genOpt.m_inputType).arg(genOpt.m_inputType);
975  LOG(VB_GENERAL, LOG_ERR,
976  "RecorderBase::CreateRecorder() Error, " + msg);
977  }
978 
979  return recorder;
980 }
981 
982 /* 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:457
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
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:75
virtual void StartNewFile(void)
Definition: recorderbase.h:267
QString m_videoDev
Definition: tv_rec.h:70
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
void SavePositionMapDelta(frm_pos_map_t &posMap, MarkTypes type) 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_requestPause 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.
virtual RecordingQuality * GetRecordingQuality(const RecordingInfo *ri) const
Returns a report about the current recordings quality.
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:74
QMutex m_statisticsLock
Definition: recorderbase.h:363
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:384
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:333
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:67
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:72
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:142
void SaveVideoProperties(uint mask, uint video_property_flags)
static void clear(SettingsMap &cache, SettingsMap &overrides, const QString &myKey)
Definition: mythdb.cpp:846
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:78
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QDateTime m_timeOfLatestData
Definition: recorderbase.h:368
bool m_requestRecording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:332
void RecorderPaused(void)
This is a callback, called by the "recorder" instance when it has actually paused.
Definition: tv_rec.cpp:2942
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)
bool m_requestPause
Definition: recorderbase.h:327
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:567
void SaveMarkupMap(const frm_dir_map_t &marks, MarkTypes type=MARK_ALL, int64_t min_frame=-1, int64_t max_frame=-1) const
void FrameRateChange(uint framerate, long long frame)
Note a change in video frame rate in the recordedmark table.
void SetNextRecording(const RecordingInfo *ri, RingBuffer *rb)
Sets next recording info, to be applied as soon as practical.
RecorderBase(TVRec *rec)
double m_videoAspectRatio
Definition: recordingfile.h:51
~RecorderBase() override
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
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:441
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
bool GetKeyframeDurations(long long start, long long end, frm_pos_map_t &map) const
QString m_inputType
Definition: tv_rec.h:73
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:366
virtual void ResetForNewFile(void)=0
bool GetKeyframePositions(long long start, long long end, frm_pos_map_t &map) const
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.
void RingBufferChanged(RingBuffer *rb, RecordingInfo *pginfo, RecordingQuality *recq)
Definition: tv_rec.cpp:3383
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:406
RemoteEncoder * recorder
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:71
static QString toString(Type recstatus, uint id)
Converts "recstatus" into a short (unreadable) string.
Definition: recStatus.cpp:39
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
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.
uint m_videoHeight
Definition: recorderbase.h:319
virtual bool IsRecordingRequested(void)
Tells us if StopRecording() has been called.