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 "v4l2encrecorder.h"
15 #include "recorderbase.h"
16 #include "cetonchannel.h"
17 #include "asirecorder.h"
18 #include "dvbrecorder.h"
19 #include "ExternalRecorder.h"
20 #include "hdhrchannel.h"
21 #include "iptvchannel.h"
22 #include "mythsystemevent.h"
23 #include "mythlogging.h"
24 #include "programinfo.h"
25 #include "asichannel.h"
26 #include "dtvchannel.h"
27 #include "dvbchannel.h"
28 #include "v4lchannel.h"
29 #include "ExternalChannel.h"
30 #include "ringbuffer.h"
31 #include "cardutil.h"
32 #include "tv_rec.h"
33 #include "mythdate.h"
34 #if CONFIG_LIBMP3LAME
35 #include "NuppelVideoRecorder.h"
36 #endif
37 
38 #define TVREC_CARDNUM \
39  ((tvrec != nullptr) ? QString::number(tvrec->GetInputId()) : "NULL")
40 
41 #define LOC QString("RecBase[%1](%2): ") \
42  .arg(TVREC_CARDNUM).arg(videodevice)
43 
45 
47  : tvrec(rec), ringBuffer(nullptr),
48  weMadeBuffer(true),
49  m_containerFormat(formatUnknown),
50  m_primaryVideoCodec(AV_CODEC_ID_NONE),
51  m_primaryAudioCodec(AV_CODEC_ID_NONE),
52  videocodec("rtjpeg"),
53  ntsc(true), ntsc_framerate(true),
54  video_frame_rate(29.97),
55  m_videoAspect(0), m_videoHeight(0),
56  m_videoWidth(0), m_frameRate(0),
57  curRecording(nullptr),
58  request_pause(false), paused(false),
59  request_recording(false), recording(false),
60  nextRingBuffer(nullptr), nextRecording(nullptr),
61  positionMapType(MARK_GOP_BYFRAME),
62  estimatedProgStartMS(0), lastSavedKeyframe(0), lastSavedDuration(0)
63 {
65  QMutexLocker locker(avcodeclock);
66 #if 0
67  avcodec_init(); // init CRC's
68 #endif
69 }
70 
72 {
73  if (weMadeBuffer && ringBuffer)
74  {
75  delete ringBuffer;
76  ringBuffer = nullptr;
77  }
78  SetRecording(nullptr);
79  if (nextRingBuffer)
80  {
81  QMutexLocker locker(&nextRingBufferLock);
82  delete nextRingBuffer;
83  nextRingBuffer = nullptr;
84  }
85  if (nextRecording)
86  {
87  delete nextRecording;
88  nextRecording = nullptr;
89  }
90 }
91 
93 {
94  if (VERBOSE_LEVEL_CHECK(VB_RECORD, LOG_INFO))
95  {
96  QString msg("");
97  if (rbuf)
98  msg = " '" + rbuf->GetFilename() + "'";
99  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetRingBuffer(0x%1)")
100  .arg((uint64_t)rbuf,0,16) + msg);
101  }
102  ringBuffer = rbuf;
103  weMadeBuffer = false;
104 }
105 
107 {
108  if (pginfo)
109  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetRecording(0x%1) title(%2)")
110  .arg((uint64_t)pginfo,0,16).arg(pginfo->GetTitle()));
111  else
112  LOG(VB_RECORD, LOG_INFO, LOC + "SetRecording(0x0)");
113 
114  ProgramInfo *oldrec = curRecording;
115  if (pginfo)
116  {
117  // NOTE: RecorderBase and TVRec do not share a single RecordingInfo
118  // instance which may lead to the possibility that changes made
119  // in the database by one are overwritten by the other
120  curRecording = new RecordingInfo(*pginfo);
121  // Compute an estimate of the actual progstart delay for setting the
122  // MARK_UTIL_PROGSTART mark. We can't reliably use
123  // curRecording->GetRecordingStartTime() because the scheduler rounds it
124  // to the nearest minute, so we use the current time instead.
129  recFile->Save();
130  }
131  else
132  curRecording = nullptr;
133 
134  if (oldrec)
135  delete oldrec;
136 }
137 
139 {
140  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetNextRecording(0x%1, 0x%2)")
141  .arg(reinterpret_cast<intptr_t>(ri),0,16)
142  .arg(reinterpret_cast<intptr_t>(rb),0,16));
143 
144  // First we do some of the time consuming stuff we can do now
145  SavePositionMap(true);
146  if (ringBuffer)
147  {
149  if (curRecording)
151  }
152 
153  // Then we set the next info
154  QMutexLocker locker(&nextRingBufferLock);
155  if (nextRecording)
156  {
157  delete nextRecording;
158  nextRecording = nullptr;
159  }
160  if (ri)
161  nextRecording = new RecordingInfo(*ri);
162 
163  if (nextRingBuffer)
164  delete nextRingBuffer;
165  nextRingBuffer = rb;
166 }
167 
168 void RecorderBase::SetOption(const QString &name, const QString &value)
169 {
170  if (name == "videocodec")
171  videocodec = value;
172  else if (name == "videodevice")
173  videodevice = value;
174  else if (name == "tvformat")
175  {
176  ntsc = false;
177  if (value.toLower() == "ntsc" || value.toLower() == "ntsc-jp")
178  {
179  ntsc = true;
180  SetFrameRate(29.97);
181  }
182  else if (value.toLower() == "pal-m")
183  SetFrameRate(29.97);
184  else if (value.toLower() == "atsc")
185  {
186  // Here we set the TV format values for ATSC. ATSC isn't really
187  // NTSC, but users who configure a non-ATSC-recorder as ATSC
188  // are far more likely to be using a mix of ATSC and NTSC than
189  // a mix of ATSC and PAL or SECAM. The atsc recorder itself
190  // does not care about these values, except in so much as tv_rec
191  // cares anout video_frame_rate which should be neither 29.97
192  // nor 25.0, but based on the actual video.
193  ntsc = true;
194  SetFrameRate(29.97);
195  }
196  else
197  SetFrameRate(25.00);
198  }
199  else
200  {
201  LOG(VB_GENERAL, LOG_WARNING, LOC +
202  QString("SetOption(%1,%2): Option not recognized")
203  .arg(name).arg(value));
204  }
205 }
206 
207 void RecorderBase::SetOption(const QString &name, int value)
208 {
209  LOG(VB_GENERAL, LOG_ERR, LOC +
210  QString("SetOption(): Unknown int option: %1: %2")
211  .arg(name).arg(value));
212 }
213 
215 {
216  const StandardSetting *setting = profile->byName(name);
217  if (setting)
218  SetOption(name, setting->getValue().toInt());
219  else
220  LOG(VB_GENERAL, LOG_ERR, LOC +
221  QString("SetIntOption(...%1): Option not in profile.").arg(name));
222 }
223 
225 {
226  const StandardSetting *setting = profile->byName(name);
227  if (setting)
228  SetOption(name, setting->getValue());
229  else
230  LOG(VB_GENERAL, LOG_ERR, LOC +
231  QString("SetStrOption(...%1): Option not in profile.").arg(name));
232 }
233 
240 {
241  QMutexLocker locker(&pauseLock);
242  request_recording = false;
243  unpauseWait.wakeAll();
244  while (recording)
245  {
246  recordingWait.wait(&pauseLock, 100);
247  if (request_recording)
248  {
249  LOG(VB_GENERAL, LOG_ERR, LOC +
250  "Programmer Error: Recorder started while we were in "
251  "StopRecording");
252  request_recording = false;
253  }
254  }
255 }
256 
259 {
260  QMutexLocker locker(&pauseLock);
261  return recording;
262 }
263 
266 {
267  QMutexLocker locker(&pauseLock);
268  return request_recording;
269 }
270 
279 {
280  (void) clear;
281  QMutexLocker locker(&pauseLock);
282  request_pause = true;
283 }
284 
290 {
291  QMutexLocker locker(&pauseLock);
292  request_pause = false;
293  unpauseWait.wakeAll();
294 }
295 
297 bool RecorderBase::IsPaused(bool holding_lock) const
298 {
299  if (!holding_lock)
300  pauseLock.lock();
301  bool ret = paused;
302  if (!holding_lock)
303  pauseLock.unlock();
304  return ret;
305 }
306 
314 {
315  MythTimer t;
316  t.start();
317 
318  QMutexLocker locker(&pauseLock);
319  while (!IsPaused(true) && request_pause)
320  {
321  int wait = timeout - t.elapsed();
322  if (wait <= 0)
323  return false;
324  pauseWait.wait(&pauseLock, wait);
325  }
326  return true;
327 }
328 
342 {
343  QMutexLocker locker(&pauseLock);
344  if (request_pause)
345  {
346  if (!IsPaused(true))
347  {
348  paused = true;
349  pauseWait.wakeAll();
350  if (tvrec)
352  }
353 
354  unpauseWait.wait(&pauseLock, timeout);
355  }
356 
357  if (!request_pause && IsPaused(true))
358  {
359  paused = false;
360  unpauseWait.wakeAll();
361  }
362 
363  return IsPaused(true);
364 }
365 
367 {
368  nextRingBufferLock.lock();
369 
370  RecordingQuality *recq = nullptr;
371 
372  if (nextRingBuffer)
373  {
374  FinishRecording();
375 
376  recq = GetRecordingQuality(nullptr);
377 
378  ResetForNewFile();
379 
381  m_frameRate = FrameRate(0);
382 
385 
386  nextRingBuffer = nullptr;
387  nextRecording = nullptr;
388 
389  StartNewFile();
390  }
391  nextRingBufferLock.unlock();
392 
393  if (recq && tvrec)
394  {
395  // This call will free recq.
397  }
398  else
399  {
400  delete recq;
401  }
402 
404 }
405 
407  const QString& file, int line)
408 {
409  if (curRecording && curRecording->GetRecordingStatus() != status)
410  {
411  LOG(VB_RECORD, LOG_INFO,
412  QString("Modifying recording status from %1 to %2 at %3:%4")
414  .arg(RecStatus::toString(status, kSingleRecord)).arg(file).arg(line));
415 
417 
418  if (status == RecStatus::Failing)
419  {
421  SendMythSystemRecEvent("REC_FAILING", curRecording);
422  }
423 
424  MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
425  .arg(curRecording->GetInputID())
426  .arg(curRecording->GetChanID())
428  .arg(status)
430  gCoreContext->dispatch(me);
431  }
432 }
433 
435 {
436  QMutexLocker locker(&statisticsLock);
437  timeOfFirstData = QDateTime();
438  timeOfFirstDataIsSet.fetchAndStoreRelaxed(0);
439  timeOfLatestData = QDateTime();
440  timeOfLatestDataCount.fetchAndStoreRelaxed(0);
441  timeOfLatestDataPacketInterval.fetchAndStoreRelaxed(2000);
442  recordingGaps.clear();
443 }
444 
446 {
447  if (curRecording)
448  {
449  if (m_primaryVideoCodec == AV_CODEC_ID_H264)
451 
453  if (recFile)
454  {
455  // Container
457 
458  // Video
459  recFile->m_videoCodec = ff_codec_id_string(m_primaryVideoCodec);
461  {
462  case MARK_ASPECT_1_1 :
463  recFile->m_videoAspectRatio = 1.0;
464  break;
465  case MARK_ASPECT_4_3:
466  recFile->m_videoAspectRatio = 1.33333333333;
467  break;
468  case MARK_ASPECT_16_9:
469  recFile->m_videoAspectRatio = 1.77777777777;
470  break;
471  case MARK_ASPECT_2_21_1:
472  recFile->m_videoAspectRatio = 2.21;
473  break;
474  default:
475  recFile->m_videoAspectRatio = (double)m_videoAspect / 1000000.0;
476  break;
477  }
478  QSize resolution(curRecording->QueryAverageWidth(),
480  recFile->m_videoResolution = resolution;
481  recFile->m_videoFrameRate = (double)curRecording->QueryAverageFrameRate() / 1000.0;
482 
483  // Audio
484  recFile->m_audioCodec = ff_codec_id_string(m_primaryAudioCodec);
485 
486  recFile->Save();
487  }
488  else
489  LOG(VB_GENERAL, LOG_CRIT, "RecordingFile object is NULL. No video file metadata can be stored");
490 
491  SavePositionMap(true, true); // Save Position Map only, not file size
492 
493  if (ringBuffer)
495  }
496 
497  LOG(VB_GENERAL, LOG_NOTICE, QString("Finished Recording: "
498  "Container: %7 "
499  "Video Codec: %1 (%2x%3 A/R: %4 %5fps) "
500  "Audio Codec: %6")
501  .arg(avcodec_get_name(m_primaryVideoCodec))
502  .arg(m_videoWidth)
503  .arg(m_videoHeight)
504  .arg(m_videoAspect)
505  .arg(GetFrameRate())
506  .arg(avcodec_get_name(m_primaryAudioCodec))
508 }
509 
511  const RecordingInfo *r) const
512 {
513  QMutexLocker locker(&statisticsLock);
514  if (r && curRecording &&
515  (r->MakeUniqueKey() == curRecording->MakeUniqueKey()))
516  {
517  curRecording->SetDesiredStartTime(r->GetDesiredStartTime());
518  curRecording->SetDesiredEndTime(r->GetDesiredEndTime());
519  }
520  return new RecordingQuality(
523 }
524 
525 long long RecorderBase::GetKeyframePosition(long long desired) const
526 {
527  QMutexLocker locker(&positionMapLock);
528  long long ret = -1;
529 
530  if (positionMap.empty())
531  return ret;
532 
533  // find closest exact or previous keyframe position...
534  frm_pos_map_t::const_iterator it = positionMap.lowerBound(desired);
535  if (it == positionMap.end())
536  ret = *positionMap.begin();
537  else if (it.key() == desired)
538  ret = *it;
539  else if (--it != positionMap.end())
540  ret = *it;
541 
542  return ret;
543 }
544 
546  long long start, long long end, frm_pos_map_t &map) const
547 {
548  map.clear();
549 
550  QMutexLocker locker(&positionMapLock);
551  if (positionMap.empty())
552  return true;
553 
554  frm_pos_map_t::const_iterator it = positionMap.lowerBound(start);
555  end = (end < 0) ? INT64_MAX : end;
556  for (; (it != positionMap.end()) &&
557  (it.key() <= end); ++it)
558  map[it.key()] = *it;
559 
560  LOG(VB_GENERAL, LOG_DEBUG, LOC +
561  QString("GetKeyframePositions(%1,%2,#%3) out of %4")
562  .arg(start).arg(end).arg(map.size()).arg(positionMap.size()));
563 
564  return true;
565 }
566 
568  long long start, long long end, frm_pos_map_t &map) const
569 {
570  map.clear();
571 
572  QMutexLocker locker(&positionMapLock);
573  if (durationMap.empty())
574  return true;
575 
576  frm_pos_map_t::const_iterator it = durationMap.lowerBound(start);
577  end = (end < 0) ? INT64_MAX : end;
578  for (; (it != durationMap.end()) &&
579  (it.key() <= end); ++it)
580  map[it.key()] = *it;
581 
582  LOG(VB_GENERAL, LOG_DEBUG, LOC +
583  QString("GetKeyframeDurations(%1,%2,#%3) out of %4")
584  .arg(start).arg(end).arg(map.size()).arg(durationMap.size()));
585 
586  return true;
587 }
588 
597 void RecorderBase::SavePositionMap(bool force, bool finished)
598 {
599  bool needToSave = force;
600  positionMapLock.lock();
601 
602  bool has_delta = !positionMapDelta.empty();
603  // set pm_elapsed to a fake large value if the timer hasn't yet started
604  uint pm_elapsed = (positionMapTimer.isRunning()) ?
605  positionMapTimer.elapsed() : ~0;
606  // save on every 1.5 seconds if in the first few frames of a recording
607  needToSave |= (positionMap.size() < 30) &&
608  has_delta && (pm_elapsed >= 1500);
609  // save every 10 seconds later on
610  needToSave |= has_delta && (pm_elapsed >= 10000);
611  // Assume that durationMapDelta is the same size as
612  // positionMapDelta and implicitly use the same logic about when
613  // to same durationMapDelta.
614 
615  if (curRecording && needToSave)
616  {
618  if (has_delta)
619  {
620  // copy the delta map because most times we are called it will be in
621  // another thread and we don't want to lock the main recorder thread
622  // which is populating the delta map
623  frm_pos_map_t deltaCopy(positionMapDelta);
624  positionMapDelta.clear();
625  frm_pos_map_t durationDeltaCopy(durationMapDelta);
626  durationMapDelta.clear();
627  positionMapLock.unlock();
628 
630  curRecording->SavePositionMapDelta(durationDeltaCopy,
632 
633  TryWriteProgStartMark(durationDeltaCopy);
634  }
635  else
636  {
637  positionMapLock.unlock();
638  }
639 
640  if (ringBuffer && !finished) // Finished Recording will update the final size for us
641  {
643  }
644  }
645  else
646  {
647  positionMapLock.unlock();
648  }
649 
650  // Make sure a ringbuffer switch is checked at least every 60
651  // seconds. Otherwise, this check is only performed on keyframes,
652  // and if there is a problem with the input we may never see one
653  // again, resulting in a wedged recording.
654  if (!finished && ringBufferCheckTimer.isRunning() &&
655  ringBufferCheckTimer.elapsed() > 60000)
656  {
657  LOG(VB_RECORD, LOG_WARNING, LOC + "It has been over 60 seconds "
658  "since we've checked for a ringbuffer switch.");
660  }
661 }
662 
664 {
665  // Note: all log strings contain "progstart mark" for searching.
666  if (estimatedProgStartMS <= 0)
667  {
668  // Do nothing because no progstart mark is needed.
669  LOG(VB_RECORD, LOG_DEBUG,
670  QString("No progstart mark needed because delta=%1")
671  .arg(estimatedProgStartMS));
672  return;
673  }
674  frm_pos_map_t::const_iterator last_it = durationDeltaCopy.end();
675  --last_it;
676  long long bookmarkFrame = 0;
677  LOG(VB_RECORD, LOG_DEBUG,
678  QString("durationDeltaCopy.begin() = (%1,%2)")
679  .arg(durationDeltaCopy.begin().key())
680  .arg(durationDeltaCopy.begin().value()));
681  if (estimatedProgStartMS > *last_it)
682  {
683  // Do nothing because we haven't reached recstartts yet.
684  LOG(VB_RECORD, LOG_DEBUG,
685  QString("No progstart mark yet because estimatedProgStartMS=%1 "
686  "and *last_it=%2")
687  .arg(estimatedProgStartMS).arg(*last_it));
688  }
690  estimatedProgStartMS < *durationDeltaCopy.begin())
691  {
692  // Set progstart mark @ lastSavedKeyframe
693  LOG(VB_RECORD, LOG_DEBUG,
694  QString("Set progstart mark=%1 because %2<=%3<%4")
696  .arg(estimatedProgStartMS).arg(*durationDeltaCopy.begin()));
697  bookmarkFrame = lastSavedKeyframe;
698  }
699  else if (*durationDeltaCopy.begin() <= estimatedProgStartMS &&
700  estimatedProgStartMS < *last_it)
701  {
702  frm_pos_map_t::const_iterator upper_it = durationDeltaCopy.begin();
703  for (; upper_it != durationDeltaCopy.end(); ++upper_it)
704  {
705  if (*upper_it > estimatedProgStartMS)
706  {
707  --upper_it;
708  // Set progstart mark @ upper_it.key()
709  LOG(VB_RECORD, LOG_DEBUG,
710  QString("Set progstart mark=%1 because "
711  "estimatedProgStartMS=%2 and upper_it.value()=%3")
712  .arg(upper_it.key()).arg(estimatedProgStartMS)
713  .arg(upper_it.value()));
714  bookmarkFrame = upper_it.key();
715  break;
716  }
717  }
718  }
719  else
720  {
721  // do nothing
722  LOG(VB_RECORD, LOG_DEBUG, "No progstart mark due to fallthrough");
723  }
724  if (bookmarkFrame)
725  {
726  frm_dir_map_t progStartMap;
727  progStartMap[bookmarkFrame] = MARK_UTIL_PROGSTART;
729  }
730  lastSavedKeyframe = last_it.key();
731  lastSavedDuration = last_it.value();
732  LOG(VB_RECORD, LOG_DEBUG,
733  QString("Setting lastSavedKeyframe=%1 lastSavedDuration=%2 "
734  "for progstart mark calculations")
736 }
737 
738 void RecorderBase::AspectChange(uint aspect, long long frame)
739 {
741  uint customAspect = 0;
742  if (aspect == ASPECT_1_1 || aspect >= ASPECT_CUSTOM)
743  {
744  if (aspect > 0x0F)
745  customAspect = aspect;
746  else if (m_videoWidth && m_videoHeight)
747  customAspect = m_videoWidth * 1000000 / m_videoHeight;
748 
749  mark = (customAspect) ? MARK_ASPECT_CUSTOM : mark;
750  }
751  if (aspect == ASPECT_4_3)
753  if (aspect == ASPECT_16_9)
755  if (aspect == ASPECT_2_21_1)
757 
758  // Populate the recordfile table as early as possible, the best
759  // value will be determined when the recording completes.
762  {
764  switch (m_videoAspect)
765  {
766  case ASPECT_1_1 :
767  recFile->m_videoAspectRatio = 1.0;
768  break;
769  case ASPECT_4_3:
770  recFile->m_videoAspectRatio = 1.33333333333;
771  break;
772  case ASPECT_16_9:
773  recFile->m_videoAspectRatio = 1.77777777777;
774  break;
775  case ASPECT_2_21_1:
776  recFile->m_videoAspectRatio = 2.21;
777  break;
778  default:
779  recFile->m_videoAspectRatio = (double)m_videoAspect / 1000000.0;
780  break;
781  }
782  recFile->Save();
783  }
784 
785  if (curRecording)
786  curRecording->SaveAspect(frame, mark, customAspect);
787 }
788 
789 void RecorderBase::ResolutionChange(uint width, uint height, long long frame)
790 {
791  if (curRecording)
792  {
793  // Populate the recordfile table as early as possible, the best value
794  // value will be determined when the recording completes.
797  {
798  curRecording->GetRecordingFile()->m_videoResolution = QSize(width, height);
800  }
801  curRecording->SaveResolution(frame, width, height);
802  }
803 }
804 
805 void RecorderBase::FrameRateChange(uint framerate, long long frame)
806 {
807  if (curRecording)
808  {
809  // Populate the recordfile table as early as possible, the average
810  // value will be determined when the recording completes.
812  {
813  curRecording->GetRecordingFile()->m_videoFrameRate = (double)framerate / 1000.0;
815  }
816  curRecording->SaveFrameRate(frame, framerate);
817  }
818 }
819 
820 void RecorderBase::VideoCodecChange(AVCodecID vCodec)
821 {
823  {
824  curRecording->GetRecordingFile()->m_videoCodec = ff_codec_id_string(vCodec);
826  }
827 }
828 
829 void RecorderBase::AudioCodecChange(AVCodecID aCodec)
830 {
832  {
833  curRecording->GetRecordingFile()->m_audioCodec = ff_codec_id_string(aCodec);
835  }
836 }
837 
838 void RecorderBase::SetDuration(uint64_t duration)
839 {
840  if (curRecording)
841  curRecording->SaveTotalDuration(duration);
842 }
843 
844 void RecorderBase::SetTotalFrames(uint64_t total_frames)
845 {
846  if (curRecording)
847  curRecording->SaveTotalFrames(total_frames);
848 }
849 
850 
852  TVRec *tvrec,
853  ChannelBase *channel,
854  const RecordingProfile &profile,
855  const GeneralDBOptions &genOpt)
856 {
857  if (!channel)
858  return nullptr;
859 
860  RecorderBase *recorder = nullptr;
861  if (genOpt.inputtype == "MPEG")
862  {
863 #ifdef USING_IVTV
864  recorder = new MpegRecorder(tvrec);
865 #endif // USING_IVTV
866  }
867  else if (genOpt.inputtype == "HDPVR")
868  {
869 #ifdef USING_HDPVR
870  recorder = new MpegRecorder(tvrec);
871 #endif // USING_HDPVR
872  }
873  else if (genOpt.inputtype == "V4L2ENC")
874  {
875 #ifdef USING_V4L2
876  if (dynamic_cast<V4LChannel*>(channel))
877  recorder = new V4L2encRecorder(tvrec, dynamic_cast<V4LChannel*>(channel));
878 #endif
879  }
880  else if (genOpt.inputtype == "FIREWIRE")
881  {
882 #ifdef USING_FIREWIRE
883  if (dynamic_cast<FirewireChannel*>(channel))
884  recorder = new FirewireRecorder(tvrec, dynamic_cast<FirewireChannel*>(channel));
885 #endif // USING_FIREWIRE
886  }
887  else if (genOpt.inputtype == "HDHOMERUN")
888  {
889 #ifdef USING_HDHOMERUN
890  if (dynamic_cast<HDHRChannel*>(channel))
891  {
892  recorder = new HDHRRecorder(tvrec, dynamic_cast<HDHRChannel*>(channel));
893  recorder->SetOption("wait_for_seqstart", genOpt.wait_for_seqstart);
894  }
895 #endif // USING_HDHOMERUN
896  }
897  else if (genOpt.inputtype == "CETON")
898  {
899 #ifdef USING_CETON
900  if (dynamic_cast<CetonChannel*>(channel))
901  {
902  recorder = new CetonRecorder(tvrec, dynamic_cast<CetonChannel*>(channel));
903  recorder->SetOption("wait_for_seqstart", genOpt.wait_for_seqstart);
904  }
905 #endif // USING_CETON
906  }
907  else if (genOpt.inputtype == "DVB")
908  {
909 #ifdef USING_DVB
910  if (dynamic_cast<DVBChannel*>(channel))
911  {
912  recorder = new DVBRecorder(tvrec, dynamic_cast<DVBChannel*>(channel));
913  recorder->SetOption("wait_for_seqstart", genOpt.wait_for_seqstart);
914  }
915 #endif // USING_DVB
916  }
917  else if (genOpt.inputtype == "FREEBOX")
918  {
919 #ifdef USING_IPTV
920  if (dynamic_cast<IPTVChannel*>(channel))
921  {
922  recorder = new IPTVRecorder(tvrec, dynamic_cast<IPTVChannel*>(channel));
923  recorder->SetOption("mrl", genOpt.videodev);
924  }
925 #endif // USING_IPTV
926  }
927  else if (genOpt.inputtype == "VBOX")
928  {
929 #ifdef USING_VBOX
930  if (dynamic_cast<IPTVChannel*>(channel))
931  recorder = new IPTVRecorder(tvrec, dynamic_cast<IPTVChannel*>(channel));
932 #endif // USING_VBOX
933  }
934  else if (genOpt.inputtype == "ASI")
935  {
936 #ifdef USING_ASI
937  if (dynamic_cast<ASIChannel*>(channel))
938  {
939  recorder = new ASIRecorder(tvrec, dynamic_cast<ASIChannel*>(channel));
940  recorder->SetOption("wait_for_seqstart", genOpt.wait_for_seqstart);
941  }
942 #endif // USING_ASI
943  }
944  else if (genOpt.inputtype == "IMPORT")
945  {
947  }
948  else if (genOpt.inputtype == "DEMO")
949  {
950 #ifdef USING_IVTV
951  recorder = new MpegRecorder(tvrec);
952 #else
954 #endif
955  }
956  else if (CardUtil::IsV4L(genOpt.inputtype))
957  {
958 #if CONFIG_LIBMP3LAME && defined(USING_V4L2)
959  // V4L/MJPEG/GO7007 from here on
960  recorder = new NuppelVideoRecorder(tvrec, channel);
961  recorder->SetOption("skipbtaudio", genOpt.skip_btaudio);
962 #endif // USING_V4L2
963  }
964  else if (genOpt.inputtype == "EXTERNAL")
965  {
966  if (dynamic_cast<ExternalChannel*>(channel))
967  recorder = new ExternalRecorder(tvrec, dynamic_cast<ExternalChannel*>(channel));
968  }
969 
970  if (recorder)
971  {
972  recorder->SetOptionsFromProfile(
973  const_cast<RecordingProfile*>(&profile),
974  genOpt.videodev, genOpt.audiodev, genOpt.vbidev);
975  // Override the samplerate defined in the profile if this card
976  // was configured with a fixed rate.
977  if (genOpt.audiosamplerate)
978  recorder->SetOption("samplerate", genOpt.audiosamplerate);
979  }
980  else
981  {
982  QString msg = "Need %1 recorder, but compiled without %2 support!";
983  msg = msg.arg(genOpt.inputtype).arg(genOpt.inputtype);
984  LOG(VB_GENERAL, LOG_ERR,
985  "RecorderBase::CreateRecorder() Error, " + msg);
986  }
987 
988  return recorder;
989 }
990 
991 /* vim: set expandtab tabstop=4 shiftwidth=4: */
This is a specialization of DTVRecorder used to handle streams from V4L2 recorders.
int audiosamplerate
Definition: tv_rec.h:79
MythTimer ringBufferCheckTimer
Definition: recorderbase.h:335
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
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
RingBuffer * ringBuffer
Definition: recorderbase.h:297
long long GetKeyframePosition(long long desired) const
Returns closest keyframe position before the desired frame.
QWaitCondition unpauseWait
Definition: recorderbase.h:323
RecordingInfo * curRecording
Definition: recorderbase.h:316
static RecorderBase * CreateRecorder(TVRec *tvrec, ChannelBase *channel, const RecordingProfile &profile, const GeneralDBOptions &genOpt)
qint64 estimatedProgStartMS
Definition: recorderbase.h:347
void AudioCodecChange(AVCodecID aCodec)
Note a change in audio codec.
void SavePositionMap(bool force=false, bool finished=false)
Save the seektable to the DB.
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:91
void AspectChange(uint ratio, long long frame)
Note a change in aspect ratio in the recordedmark table.
QMutex pauseLock
Definition: recorderbase.h:319
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)
QDateTime timeOfFirstData
Definition: recorderbase.h:358
QSize m_videoResolution
Definition: recordingfile.h:50
void RingBufferChanged(RingBuffer *, RecordingInfo *, RecordingQuality *)
Definition: tv_rec.cpp:3405
frm_pos_map_t durationMap
Definition: recorderbase.h:342
QString vbidev
Definition: tv_rec.h:76
QWaitCondition pauseWait
Definition: recorderbase.h:322
virtual void StartNewFile(void)
Definition: recorderbase.h:260
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
RingBuffer * nextRingBuffer
Definition: recorderbase.h:333
MarkTypes QueryAverageAspectRatio(void) const
QWaitCondition recordingWait
Definition: recorderbase.h:328
QString videocodec
Definition: recorderbase.h:303
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 request_pause is true, sets pause and blocks up to timeout milliseconds or until unpaused,...
bool GetKeyframeDurations(long long start, long long end, frm_pos_map_t &) const
RecordingFile * GetRecordingFile() const
void SetDesiredEndTime(const QDateTime &dt)
QAtomicInt timeOfFirstDataIsSet
Definition: recorderbase.h:357
QString m_audioCodec
Definition: recordingfile.h:55
virtual void CheckForRingBufferSwitch(void)
If requested, switch to new RingBuffer/ProgramInfo objects.
QAtomicInt timeOfLatestDataCount
Definition: recorderbase.h:359
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:340
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:302
QMutex positionMapLock
Definition: recorderbase.h:339
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: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
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...
void SetDuration(uint64_t duration)
Note the total duration in the recordedmark table.
virtual void FinishRecording(void)
QDateTime timeOfLatestData
Definition: recorderbase.h:361
This class is used as a container for messages.
Definition: mythevent.h:15
AVContainer m_containerFormat
Definition: recorderbase.h:300
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
bool skip_btaudio
Definition: tv_rec.h:80
QString m_videoCodec
Definition: recordingfile.h:49
TVRec * tvrec
Definition: recorderbase.h:296
void SetFrameRate(double rate)
Sets the video frame rate.
Definition: recorderbase.h:75
bool recording
True while recording is actually being performed.
Definition: recorderbase.h:327
RecordingGaps recordingGaps
Definition: recorderbase.h:363
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
bool request_recording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:325
uint QueryAverageWidth(void) const
If present in recording this loads average width of the main video stream from database's stream mark...
#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:340
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:2966
double m_videoFrameRate
Definition: recordingfile.h:52
virtual void SetRecordingStatus(RecStatus::Type status, const QString &file, int line)
QMutex statisticsLock
Definition: recorderbase.h:356
virtual void SetOption(const QString &opt, const QString &value)
Set an specific option.
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.
void SetRecordingStatus(RecStatus::Type status)
Definition: programinfo.h:566
void FrameRateChange(uint framerate, long long frame)
Note a change in video frame rate in the recordedmark table.
MarkTypes positionMapType
Definition: recorderbase.h:338
RecorderBase(TVRec *rec)
double m_videoAspectRatio
Definition: recordingfile.h:51
Holds information on a recording file and it's video and audio streams.
Definition: recordingfile.h:29
const char * name
Definition: ParseText.cpp:339
virtual void Unpause(void)
Unpause tells recorder to unpause.
This is the abstract base class for supporting recorder hardware.
Definition: recorderbase.h:66
long long lastSavedDuration
Definition: recorderbase.h:349
RecordingInfo * nextRecording
Definition: recorderbase.h:334
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
This is a specialization of DTVRecorder used to handle streams from ASI drivers.
Definition: asirecorder.h:55
AVCodecID m_primaryVideoCodec
Definition: recorderbase.h:301
FrameRate m_frameRate
Definition: recorderbase.h:314
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
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:310
static const uint kTimeOfLatestDataIntervalTarget
timeOfLatest update interval target in milliseconds.
Definition: recorderbase.h:365
QString inputtype
Definition: tv_rec.h:78
QAtomicInt timeOfLatestDataPacketInterval
Definition: recorderbase.h:360
frm_pos_map_t positionMap
Definition: recorderbase.h:340
QMutex nextRingBufferLock
Definition: recorderbase.h:332
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
virtual RecordingQuality * GetRecordingQuality(const RecordingInfo *) const
Returns a report about the current recordings quality.
This is a specialization of DTVRecorder used to handle streams from External 'blackbox' recorders.
long long GetRealFileSize(void) const
Returns the size of the file we are reading/writing, or -1 if the query fails.
Definition: ringbuffer.cpp:532
void SetIntOption(RecordingProfile *profile, const QString &name)
Convenience function used to set integer options from a profile.
virtual void Pause(bool clear=true)
Pause tells recorder to pause, it should not block.
QDateTime GetRecordingEndTime(void) const
Approximate time the recording should have ended, did end, or is intended to end.
Definition: programinfo.h:406
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.
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.
MythTimer positionMapTimer
Definition: recorderbase.h:344
QMap< long long, long long > frm_pos_map_t
Frame # -> File offset map.
Definition: programtypes.h:46
frm_pos_map_t positionMapDelta
Definition: recorderbase.h:341
virtual void ClearStatistics(void)
frm_pos_map_t durationMapDelta
Definition: recorderbase.h:343
void SaveMarkupMap(const frm_dir_map_t &, MarkTypes type=MARK_ALL, int64_t min_frm=-1, int64_t max_frm=-1) const
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 videodevice
Definition: recorderbase.h:304
QString videodev
Definition: tv_rec.h:75
Default UTC.
Definition: mythdate.h:14
static bool IsV4L(const QString &rawtype)
Definition: cardutil.h:130
double GetFrameRate(void) const
Returns the latest frame rate.
Definition: recorderbase.h:219
void SetDesiredStartTime(const QDateTime &dt)
long long lastSavedKeyframe
Definition: recorderbase.h:348
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
bool request_pause
Definition: recorderbase.h:320
uint m_videoHeight
Definition: recorderbase.h:312
virtual bool IsRecordingRequested(void)
Tells us if StopRecording() has been called.