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  {
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 &&
504  (r->MakeUniqueKey() == m_curRecording->MakeUniqueKey()))
505  {
506  m_curRecording->SetDesiredStartTime(r->GetDesiredStartTime());
507  m_curRecording->SetDesiredEndTime(r->GetDesiredEndTime());
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  ret = *it;
528  else if (--it != m_positionMap.end())
529  ret = *it;
530 
531  return ret;
532 }
533 
535  long long start, long long end, frm_pos_map_t &map) const
536 {
537  map.clear();
538 
539  QMutexLocker locker(&m_positionMapLock);
540  if (m_positionMap.empty())
541  return true;
542 
543  frm_pos_map_t::const_iterator it = m_positionMap.lowerBound(start);
544  end = (end < 0) ? INT64_MAX : end;
545  for (; (it != m_positionMap.end()) &&
546  (it.key() <= end); ++it)
547  map[it.key()] = *it;
548 
549  LOG(VB_GENERAL, LOG_DEBUG, LOC +
550  QString("GetKeyframePositions(%1,%2,#%3) out of %4")
551  .arg(start).arg(end).arg(map.size()).arg(m_positionMap.size()));
552 
553  return true;
554 }
555 
557  long long start, long long end, frm_pos_map_t &map) const
558 {
559  map.clear();
560 
561  QMutexLocker locker(&m_positionMapLock);
562  if (m_durationMap.empty())
563  return true;
564 
565  frm_pos_map_t::const_iterator it = m_durationMap.lowerBound(start);
566  end = (end < 0) ? INT64_MAX : end;
567  for (; (it != m_durationMap.end()) &&
568  (it.key() <= end); ++it)
569  map[it.key()] = *it;
570 
571  LOG(VB_GENERAL, LOG_DEBUG, LOC +
572  QString("GetKeyframeDurations(%1,%2,#%3) out of %4")
573  .arg(start).arg(end).arg(map.size()).arg(m_durationMap.size()));
574 
575  return true;
576 }
577 
586 void RecorderBase::SavePositionMap(bool force, bool finished)
587 {
588  bool needToSave = force;
589  m_positionMapLock.lock();
590 
591  bool has_delta = !m_positionMapDelta.empty();
592  // set pm_elapsed to a fake large value if the timer hasn't yet started
593  uint pm_elapsed = (m_positionMapTimer.isRunning()) ?
595  // save on every 1.5 seconds if in the first few frames of a recording
596  needToSave |= (m_positionMap.size() < 30) &&
597  has_delta && (pm_elapsed >= 1500);
598  // save every 10 seconds later on
599  needToSave |= has_delta && (pm_elapsed >= 10000);
600  // Assume that m_durationMapDelta is the same size as
601  // m_positionMapDelta and implicitly use the same logic about when
602  // to same m_durationMapDelta.
603 
604  if (m_curRecording && needToSave)
605  {
607  if (has_delta)
608  {
609  // copy the delta map because most times we are called it will be in
610  // another thread and we don't want to lock the main recorder thread
611  // which is populating the delta map
613  m_positionMapDelta.clear();
614  frm_pos_map_t durationDeltaCopy(m_durationMapDelta);
615  m_durationMapDelta.clear();
616  m_positionMapLock.unlock();
617 
619  m_curRecording->SavePositionMapDelta(durationDeltaCopy,
621 
622  TryWriteProgStartMark(durationDeltaCopy);
623  }
624  else
625  {
626  m_positionMapLock.unlock();
627  }
628 
629  if (m_ringBuffer && !finished) // Finished Recording will update the final size for us
630  {
632  }
633  }
634  else
635  {
636  m_positionMapLock.unlock();
637  }
638 
639  // Make sure a ringbuffer switch is checked at least every 3
640  // seconds. Otherwise, this check is only performed on keyframes,
641  // and if there is a problem with the input we may never see one
642  // again, resulting in a wedged recording.
643  if (!finished && m_ringBufferCheckTimer.isRunning() &&
645  {
647  LOG(VB_RECORD, LOG_WARNING, LOC +
648  "Ringbuffer was switched due to timeout instead of keyframe.");
649  }
650 }
651 
653 {
654  // Note: all log strings contain "progstart mark" for searching.
655  if (m_estimatedProgStartMS <= 0)
656  {
657  // Do nothing because no progstart mark is needed.
658  LOG(VB_RECORD, LOG_DEBUG,
659  QString("No progstart mark needed because delta=%1")
660  .arg(m_estimatedProgStartMS));
661  return;
662  }
663  frm_pos_map_t::const_iterator last_it = durationDeltaCopy.end();
664  --last_it;
665  long long bookmarkFrame = 0;
666  LOG(VB_RECORD, LOG_DEBUG,
667  QString("durationDeltaCopy.begin() = (%1,%2)")
668  .arg(durationDeltaCopy.begin().key())
669  .arg(durationDeltaCopy.begin().value()));
670  if (m_estimatedProgStartMS > *last_it)
671  {
672  // Do nothing because we haven't reached recstartts yet.
673  LOG(VB_RECORD, LOG_DEBUG,
674  QString("No progstart mark yet because estimatedProgStartMS=%1 "
675  "and *last_it=%2")
676  .arg(m_estimatedProgStartMS).arg(*last_it));
677  }
679  m_estimatedProgStartMS < *durationDeltaCopy.begin())
680  {
681  // Set progstart mark @ lastSavedKeyframe
682  LOG(VB_RECORD, LOG_DEBUG,
683  QString("Set progstart mark=%1 because %2<=%3<%4")
685  .arg(m_estimatedProgStartMS).arg(*durationDeltaCopy.begin()));
686  bookmarkFrame = m_lastSavedKeyframe;
687  }
688  else if (*durationDeltaCopy.begin() <= m_estimatedProgStartMS &&
689  m_estimatedProgStartMS < *last_it)
690  {
691  frm_pos_map_t::const_iterator upper_it = durationDeltaCopy.begin();
692  for (; upper_it != durationDeltaCopy.end(); ++upper_it)
693  {
694  if (*upper_it > m_estimatedProgStartMS)
695  {
696  --upper_it;
697  // Set progstart mark @ upper_it.key()
698  LOG(VB_RECORD, LOG_DEBUG,
699  QString("Set progstart mark=%1 because "
700  "estimatedProgStartMS=%2 and upper_it.value()=%3")
701  .arg(upper_it.key()).arg(m_estimatedProgStartMS)
702  .arg(upper_it.value()));
703  bookmarkFrame = upper_it.key();
704  break;
705  }
706  }
707  }
708  else
709  {
710  // do nothing
711  LOG(VB_RECORD, LOG_DEBUG, "No progstart mark due to fallthrough");
712  }
713  if (bookmarkFrame)
714  {
715  frm_dir_map_t progStartMap;
716  progStartMap[bookmarkFrame] = MARK_UTIL_PROGSTART;
718  }
719  m_lastSavedKeyframe = last_it.key();
720  m_lastSavedDuration = last_it.value();
721  LOG(VB_RECORD, LOG_DEBUG,
722  QString("Setting lastSavedKeyframe=%1 lastSavedDuration=%2 "
723  "for progstart mark calculations")
725 }
726 
727 void RecorderBase::AspectChange(uint aspect, long long frame)
728 {
730  uint customAspect = 0;
731  if (aspect == ASPECT_1_1 || aspect >= ASPECT_CUSTOM)
732  {
733  if (aspect > 0x0F)
734  customAspect = aspect;
735  else if (m_videoWidth && m_videoHeight)
736  customAspect = m_videoWidth * 1000000 / m_videoHeight;
737 
738  mark = (customAspect) ? MARK_ASPECT_CUSTOM : mark;
739  }
740  if (aspect == ASPECT_4_3)
742  if (aspect == ASPECT_16_9)
744  if (aspect == ASPECT_2_21_1)
746 
747  // Populate the recordfile table as early as possible, the best
748  // value will be determined when the recording completes.
751  {
753  switch (m_videoAspect)
754  {
755  case ASPECT_1_1 :
756  recFile->m_videoAspectRatio = 1.0;
757  break;
758  case ASPECT_4_3:
759  recFile->m_videoAspectRatio = 1.33333333333;
760  break;
761  case ASPECT_16_9:
762  recFile->m_videoAspectRatio = 1.77777777777;
763  break;
764  case ASPECT_2_21_1:
765  recFile->m_videoAspectRatio = 2.21;
766  break;
767  default:
768  recFile->m_videoAspectRatio = (double)m_videoAspect / 1000000.0;
769  break;
770  }
771  recFile->Save();
772  }
773 
774  if (m_curRecording)
775  m_curRecording->SaveAspect(frame, mark, customAspect);
776 }
777 
778 void RecorderBase::ResolutionChange(uint width, uint height, long long frame)
779 {
780  if (m_curRecording)
781  {
782  // Populate the recordfile table as early as possible, the best value
783  // value will be determined when the recording completes.
786  {
787  m_curRecording->GetRecordingFile()->m_videoResolution = QSize(width, height);
789  }
790  m_curRecording->SaveResolution(frame, width, height);
791  }
792 }
793 
794 void RecorderBase::FrameRateChange(uint framerate, long long frame)
795 {
796  if (m_curRecording)
797  {
798  // Populate the recordfile table as early as possible, the average
799  // value will be determined when the recording completes.
801  {
802  m_curRecording->GetRecordingFile()->m_videoFrameRate = (double)framerate / 1000.0;
804  }
805  m_curRecording->SaveFrameRate(frame, framerate);
806  }
807 }
808 
809 void RecorderBase::VideoCodecChange(AVCodecID vCodec)
810 {
812  {
813  m_curRecording->GetRecordingFile()->m_videoCodec = ff_codec_id_string(vCodec);
815  }
816 }
817 
818 void RecorderBase::AudioCodecChange(AVCodecID aCodec)
819 {
821  {
822  m_curRecording->GetRecordingFile()->m_audioCodec = ff_codec_id_string(aCodec);
824  }
825 }
826 
827 void RecorderBase::SetDuration(uint64_t duration)
828 {
829  if (m_curRecording)
831 }
832 
833 void RecorderBase::SetTotalFrames(uint64_t total_frames)
834 {
835  if (m_curRecording)
836  m_curRecording->SaveTotalFrames(total_frames);
837 }
838 
839 
841  TVRec *tvrec,
842  ChannelBase *channel,
843  const RecordingProfile &profile,
844  const GeneralDBOptions &genOpt)
845 {
846  if (!channel)
847  return nullptr;
848 
849  RecorderBase *recorder = nullptr;
850  if (genOpt.inputtype == "MPEG")
851  {
852 #ifdef USING_IVTV
853  recorder = new MpegRecorder(tvrec);
854 #endif // USING_IVTV
855  }
856  else if (genOpt.inputtype == "HDPVR")
857  {
858 #ifdef USING_HDPVR
859  recorder = new MpegRecorder(tvrec);
860 #endif // USING_HDPVR
861  }
862  else if (genOpt.inputtype == "V4L2ENC")
863  {
864 #ifdef USING_V4L2
865  if (dynamic_cast<V4LChannel*>(channel))
866  recorder = new V4L2encRecorder(tvrec, dynamic_cast<V4LChannel*>(channel));
867 #endif
868  }
869  else if (genOpt.inputtype == "FIREWIRE")
870  {
871 #ifdef USING_FIREWIRE
872  if (dynamic_cast<FirewireChannel*>(channel))
873  recorder = new FirewireRecorder(tvrec, dynamic_cast<FirewireChannel*>(channel));
874 #endif // USING_FIREWIRE
875  }
876  else if (genOpt.inputtype == "HDHOMERUN")
877  {
878 #ifdef USING_HDHOMERUN
879  if (dynamic_cast<HDHRChannel*>(channel))
880  {
881  recorder = new HDHRRecorder(tvrec, dynamic_cast<HDHRChannel*>(channel));
882  recorder->SetBoolOption("wait_for_seqstart", genOpt.wait_for_seqstart);
883  }
884 #endif // USING_HDHOMERUN
885  }
886  else if (genOpt.inputtype == "CETON")
887  {
888 #ifdef USING_CETON
889  if (dynamic_cast<CetonChannel*>(channel))
890  {
891  recorder = new CetonRecorder(tvrec, dynamic_cast<CetonChannel*>(channel));
892  recorder->SetBoolOption("wait_for_seqstart", genOpt.wait_for_seqstart);
893  }
894 #endif // USING_CETON
895  }
896  else if (genOpt.inputtype == "DVB")
897  {
898 #ifdef USING_DVB
899  if (dynamic_cast<DVBChannel*>(channel))
900  {
901  recorder = new DVBRecorder(tvrec, dynamic_cast<DVBChannel*>(channel));
902  recorder->SetBoolOption("wait_for_seqstart", genOpt.wait_for_seqstart);
903  }
904 #endif // USING_DVB
905  }
906  else if (genOpt.inputtype == "FREEBOX")
907  {
908 #ifdef USING_IPTV
909  if (dynamic_cast<IPTVChannel*>(channel))
910  {
911  recorder = new IPTVRecorder(tvrec, dynamic_cast<IPTVChannel*>(channel));
912  recorder->SetOption("mrl", genOpt.videodev);
913  }
914 #endif // USING_IPTV
915  }
916  else if (genOpt.inputtype == "VBOX")
917  {
918 #ifdef USING_VBOX
919  if (dynamic_cast<IPTVChannel*>(channel))
920  recorder = new IPTVRecorder(tvrec, dynamic_cast<IPTVChannel*>(channel));
921 #endif // USING_VBOX
922  }
923  else if (genOpt.inputtype == "ASI")
924  {
925 #ifdef USING_ASI
926  if (dynamic_cast<ASIChannel*>(channel))
927  {
928  recorder = new ASIRecorder(tvrec, dynamic_cast<ASIChannel*>(channel));
929  recorder->SetBoolOption("wait_for_seqstart", genOpt.wait_for_seqstart);
930  }
931 #endif // USING_ASI
932  }
933  else if (genOpt.inputtype == "IMPORT")
934  {
935  recorder = new ImportRecorder(tvrec);
936  }
937  else if (genOpt.inputtype == "DEMO")
938  {
939 #ifdef USING_IVTV
940  recorder = new MpegRecorder(tvrec);
941 #else
942  recorder = new ImportRecorder(tvrec);
943 #endif
944  }
945  else if (CardUtil::IsV4L(genOpt.inputtype))
946  {
947 #if CONFIG_LIBMP3LAME && defined(USING_V4L2)
948  // V4L/MJPEG/GO7007 from here on
949  recorder = new NuppelVideoRecorder(tvrec, channel);
950  recorder->SetBoolOption("skipbtaudio", genOpt.skip_btaudio);
951 #endif // USING_V4L2
952  }
953  else if (genOpt.inputtype == "EXTERNAL")
954  {
955  if (dynamic_cast<ExternalChannel*>(channel))
956  recorder = new ExternalRecorder(tvrec, dynamic_cast<ExternalChannel*>(channel));
957  }
958 
959  if (recorder)
960  {
961  recorder->SetOptionsFromProfile(
962  const_cast<RecordingProfile*>(&profile),
963  genOpt.videodev, genOpt.audiodev, genOpt.vbidev);
964  // Override the samplerate defined in the profile if this card
965  // was configured with a fixed rate.
966  if (genOpt.audiosamplerate)
967  recorder->SetOption("samplerate", genOpt.audiosamplerate);
968  }
969  else
970  {
971  QString msg = "Need %1 recorder, but compiled without %2 support!";
972  msg = msg.arg(genOpt.inputtype).arg(genOpt.inputtype);
973  LOG(VB_GENERAL, LOG_ERR,
974  "RecorderBase::CreateRecorder() Error, " + msg);
975  }
976 
977  return recorder;
978 }
979 
980 /* 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 audiosamplerate
Definition: tv_rec.h:79
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
static RecorderBase * CreateRecorder(TVRec *tvrec, ChannelBase *channel, const RecordingProfile &profile, const GeneralDBOptions &genOpt)
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:3368
bool m_weMadeBuffer
Definition: recorderbase.h:305
QMutex m_pauseLock
Definition: recorderbase.h:326
QString vbidev
Definition: tv_rec.h:76
virtual void StartNewFile(void)
Definition: recorderbase.h:267
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
QMap< uint64_t, MarkTypes > frm_dir_map_t
Frame # -> Mark map.
Definition: programtypes.h:81
unsigned int uint
Definition: compat.h:140
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
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.
unsigned char r
Definition: ParseText.cpp:329
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
bool wait_for_seqstart
Definition: tv_rec.h:83
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
bool skip_btaudio
Definition: tv_rec.h:80
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:150
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
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
unsigned char t
Definition: ParseText.cpp:329
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:2929
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.
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
const char * name
Definition: ParseText.cpp:328
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.
int elapsed(void) const
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:90
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
QString audiodev
Definition: tv_rec.h:77
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
QString inputtype
Definition: tv_rec.h:78
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.
Implements a file/stream reader/writer.
void VideoCodecChange(AVCodecID vCodec)
Note a change in video codec.
QMap< long long, long long > frm_pos_map_t
Frame # -> File offset map.
Definition: programtypes.h:46
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)
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
bool GetKeyframePositions(long long start, long long end, frm_pos_map_t &) const
QString videodev
Definition: tv_rec.h:75
Default UTC.
Definition: mythdate.h:14
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)
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.