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