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"
9
10#include "firewirerecorder.h"
11#include "recordingprofile.h"
12#include "firewirechannel.h"
13#include "importrecorder.h"
14#include "cetonrecorder.h"
15#include "dummychannel.h"
16#include "hdhrrecorder.h"
17#include "iptvrecorder.h"
18#include "mpegrecorder.h"
19#include "recorderbase.h"
20#include "cetonchannel.h"
21#include "asirecorder.h"
22#include "dvbrecorder.h"
23#include "ExternalRecorder.h"
24#include "hdhrchannel.h"
25#include "iptvchannel.h"
26#include "mythsystemevent.h"
27#include "asichannel.h"
28#include "dtvchannel.h"
29#include "dvbchannel.h"
30#include "satipchannel.h"
31#include "satiprecorder.h"
32#include "ExternalChannel.h"
33#include "io/mythmediabuffer.h"
34#include "cardutil.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 m_videocodec = value;
158 else if (name == "videodevice")
159 m_videodevice = value;
160 else if (name == "tvformat")
161 {
162 m_ntsc = false;
163 if (value.toLower() == "ntsc" || value.toLower() == "ntsc-jp")
164 { // NOLINT(bugprone-branch-clone)
165 m_ntsc = true;
166 SetFrameRate(29.97);
167 }
168 else if (value.toLower() == "pal-m")
169 {
170 SetFrameRate(29.97);
171 }
172 else if (value.toLower() == "atsc")
173 {
174 // Here we set the TV format values for ATSC. ATSC isn't really
175 // NTSC, but users who configure a non-ATSC-recorder as ATSC
176 // are far more likely to be using a mix of ATSC and NTSC than
177 // a mix of ATSC and PAL or SECAM. The atsc recorder itself
178 // does not care about these values, except in so much as tv_rec
179 // cares about m_videoFrameRate which should be neither 29.97
180 // nor 25.0, but based on the actual video.
181 m_ntsc = true;
182 SetFrameRate(29.97);
183 }
184 else
185 {
186 SetFrameRate(25.00);
187 }
188 }
189 else
190 {
191 LOG(VB_GENERAL, LOG_WARNING, LOC +
192 QString("SetOption(%1,%2): Option not recognized")
193 .arg(name, value));
194 }
195}
196
197void RecorderBase::SetOption(const QString &name, int value)
198{
199 LOG(VB_GENERAL, LOG_ERR, LOC +
200 QString("SetOption(): Unknown int option: %1: %2")
201 .arg(name).arg(value));
202}
203
205{
206 const StandardSetting *setting = profile->byName(name);
207 if (setting)
208 SetOption(name, setting->getValue().toInt());
209 else
210 LOG(VB_GENERAL, LOG_ERR, LOC +
211 QString("SetIntOption(...%1): Option not in profile.").arg(name));
212}
213
215{
216 const StandardSetting *setting = profile->byName(name);
217 if (setting)
218 SetOption(name, setting->getValue());
219 else
220 LOG(VB_GENERAL, LOG_ERR, LOC +
221 QString("SetStrOption(...%1): Option not in profile.").arg(name));
222}
223
230{
231 QMutexLocker locker(&m_pauseLock);
232 m_requestRecording = false;
233 m_unpauseWait.wakeAll();
234 while (m_recording)
235 {
236 m_recordingWait.wait(&m_pauseLock, 100);
238 {
239 LOG(VB_GENERAL, LOG_ERR, LOC +
240 "Programmer Error: Recorder started while we were in "
241 "StopRecording");
242 m_requestRecording = false;
243 }
244 }
245}
246
249{
250 QMutexLocker locker(&m_pauseLock);
251 return m_recording;
252}
253
256{
257 QMutexLocker locker(&m_pauseLock);
258 return m_requestRecording;
259}
260
268void RecorderBase::Pause([[maybe_unused]] bool clear)
269{
270 QMutexLocker locker(&m_pauseLock);
271 m_requestPause = true;
272}
273
279{
280 QMutexLocker locker(&m_pauseLock);
281 m_requestPause = false;
282 m_unpauseWait.wakeAll();
283}
284
286bool RecorderBase::IsPaused(bool holding_lock) const
287{
288 if (!holding_lock)
289 m_pauseLock.lock();
290 bool ret = m_paused;
291 if (!holding_lock)
292 m_pauseLock.unlock();
293 return ret;
294}
295
303bool RecorderBase::WaitForPause(std::chrono::milliseconds timeout)
304{
305 MythTimer t;
306 t.start();
307
308 QMutexLocker locker(&m_pauseLock);
309 while (!IsPaused(true) && m_requestPause)
310 {
311 std::chrono::milliseconds wait = timeout - t.elapsed();
312 if (wait <= 0ms)
313 return false;
314 m_pauseWait.wait(&m_pauseLock, wait.count());
315 }
316 return true;
317}
318
331bool RecorderBase::PauseAndWait(std::chrono::milliseconds timeout)
332{
333 QMutexLocker locker(&m_pauseLock);
334 if (m_requestPause)
335 {
336 if (!IsPaused(true))
337 {
338 m_paused = true;
339 m_pauseWait.wakeAll();
340 if (m_tvrec)
342 }
343
344 m_unpauseWait.wait(&m_pauseLock, timeout.count());
345 }
346
347 if (!m_requestPause && IsPaused(true))
348 {
349 m_paused = false;
350 m_unpauseWait.wakeAll();
351 }
352
353 return IsPaused(true);
354}
355
357{
358 bool did_switch = false;
359
361
362 RecordingQuality *recq = nullptr;
363
365 {
367
368 recq = GetRecordingQuality(nullptr);
369
371
374
377
378 m_nextRingBuffer = nullptr;
379 m_nextRecording = nullptr;
380
381 StartNewFile();
382 did_switch = true;
383 }
384 m_nextRingBufferLock.unlock();
385
386 if (recq && m_tvrec)
387 {
388 // This call will free recq.
390 }
391 else
392 {
393 delete recq;
394 }
395
397 return did_switch;
398}
399
401 const QString& file, int line)
402{
404 {
405 LOG(VB_RECORD, LOG_INFO,
406 QString("Modifying recording status from %1 to %2 at %3:%4")
409 file,
410 QString::number(line)));
411
413
414 if (status == RecStatus::Failing)
415 {
416 m_curRecording->SaveVideoProperties(VID_DAMAGED, VID_DAMAGED);
418 }
419
420 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
424 .arg(status)
427 }
428}
429
431{
432 QMutexLocker locker(&m_statisticsLock);
433 m_timeOfFirstData = QDateTime();
434 m_timeOfFirstDataIsSet.fetchAndStoreRelaxed(0);
435 m_timeOfLatestData = QDateTime();
436 m_timeOfLatestDataCount.fetchAndStoreRelaxed(0);
437 m_timeOfLatestDataPacketInterval.fetchAndStoreRelaxed(2000);
438 m_recordingGaps.clear();
439}
440
442{
443 if (m_curRecording)
444 {
445 if (m_primaryVideoCodec == AV_CODEC_ID_H264)
446 m_curRecording->SaveVideoProperties(VID_AVC, VID_AVC);
447 else if (m_primaryVideoCodec == AV_CODEC_ID_H265)
448 m_curRecording->SaveVideoProperties(VID_HEVC, VID_HEVC);
449 else if (m_primaryVideoCodec == AV_CODEC_ID_MPEG2VIDEO)
450 m_curRecording->SaveVideoProperties(VID_MPEG2, VID_MPEG2);
451
453 if (recFile)
454 {
455 // Container
457
458 // Video
459 recFile->m_videoCodec = avcodec_get_name(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;
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(m_curRecording->QueryAverageWidth(),
480 recFile->m_videoResolution = resolution;
481 recFile->m_videoFrameRate = (double)m_curRecording->QueryAverageFrameRate() / 1000.0;
482
483 // Audio
484 recFile->m_audioCodec = avcodec_get_name(m_primaryAudioCodec);
485
486 recFile->Save();
487 }
488 else
489 {
490 LOG(VB_GENERAL, LOG_CRIT, "RecordingFile object is NULL. No video file metadata can be stored");
491 }
492
493 SavePositionMap(true, true); // Save Position Map only, not file size
494
495 if (m_ringBuffer)
497 }
498
499 LOG(VB_GENERAL, LOG_NOTICE, QString("Finished Recording: "
500 "Container: %7 "
501 "Video Codec: %1 (%2x%3 A/R: %4 %5fps) "
502 "Audio Codec: %6")
503 .arg(avcodec_get_name(m_primaryVideoCodec))
504 .arg(m_videoWidth)
505 .arg(m_videoHeight)
506 .arg(m_videoAspect)
507 .arg(GetFrameRate())
508 .arg(avcodec_get_name(m_primaryAudioCodec),
510}
511
513 const RecordingInfo *r) const
514{
515 QMutexLocker locker(&m_statisticsLock);
516 if (r && m_curRecording &&
518 {
521 }
522 return new RecordingQuality(
525}
526
527long long RecorderBase::GetKeyframePosition(long long desired) const
528{
529 QMutexLocker locker(&m_positionMapLock);
530
531 if (m_positionMap.empty())
532 return -1;
533
534 // find closest exact or previous keyframe position...
535 frm_pos_map_t::const_iterator it = m_positionMap.lowerBound(desired);
536 if (it == m_positionMap.end())
537 return *m_positionMap.begin();
538 if (it.key() == desired)
539 return *it;
540
541 it--;
542 if (it != m_positionMap.end())
543 return *it;
544
545 return -1;
546}
547
549 long long start, long long end, frm_pos_map_t &map) const
550{
551 map.clear();
552
553 QMutexLocker locker(&m_positionMapLock);
554 if (m_positionMap.empty())
555 return true;
556
557 frm_pos_map_t::const_iterator it = m_positionMap.lowerBound(start);
558 end = (end < 0) ? INT64_MAX : end;
559 for (; (it != m_positionMap.end()) &&
560 (it.key() <= end); ++it)
561 map[it.key()] = *it;
562
563 LOG(VB_GENERAL, LOG_DEBUG, LOC +
564 QString("GetKeyframePositions(%1,%2,#%3) out of %4")
565 .arg(start).arg(end).arg(map.size()).arg(m_positionMap.size()));
566
567 return true;
568}
569
571 long long start, long long end, frm_pos_map_t &map) const
572{
573 map.clear();
574
575 QMutexLocker locker(&m_positionMapLock);
576 if (m_durationMap.empty())
577 return true;
578
579 frm_pos_map_t::const_iterator it = m_durationMap.lowerBound(start);
580 end = (end < 0) ? INT64_MAX : end;
581 for (; (it != m_durationMap.end()) &&
582 (it.key() <= end); ++it)
583 map[it.key()] = *it;
584
585 LOG(VB_GENERAL, LOG_DEBUG, LOC +
586 QString("GetKeyframeDurations(%1,%2,#%3) out of %4")
587 .arg(start).arg(end).arg(map.size()).arg(m_durationMap.size()));
588
589 return true;
590}
591
600void RecorderBase::SavePositionMap(bool force, bool finished)
601{
602 bool needToSave = force;
603 m_positionMapLock.lock();
604
605 bool has_delta = !m_positionMapDelta.empty();
606 // set pm_elapsed to a fake large value if the timer hasn't yet started
607 std::chrono::milliseconds pm_elapsed = (m_positionMapTimer.isRunning()) ?
608 m_positionMapTimer.elapsed() : std::chrono::milliseconds::max();
609 // save on every 1.5 seconds if in the first few frames of a recording
610 needToSave |= (m_positionMap.size() < 30) &&
611 has_delta && (pm_elapsed >= 1.5s);
612 // save every 10 seconds later on
613 needToSave |= has_delta && (pm_elapsed >= 10s);
614 // Assume that m_durationMapDelta is the same size as
615 // m_positionMapDelta and implicitly use the same logic about when
616 // to same m_durationMapDelta.
617
618 if (m_curRecording && needToSave)
619 {
621 if (has_delta)
622 {
623 // copy the delta map because most times we are called it will be in
624 // another thread and we don't want to lock the main recorder thread
625 // which is populating the delta map
627 m_positionMapDelta.clear();
628 frm_pos_map_t durationDeltaCopy(m_durationMapDelta);
629 m_durationMapDelta.clear();
630 m_positionMapLock.unlock();
631
633 m_curRecording->SavePositionMapDelta(durationDeltaCopy,
635
636 TryWriteProgStartMark(durationDeltaCopy);
637 }
638 else
639 {
640 m_positionMapLock.unlock();
641 }
642
643 if (m_ringBuffer && !finished) // Finished Recording will update the final size for us
644 {
646 }
647 }
648 else
649 {
650 m_positionMapLock.unlock();
651 }
652
653 // Make sure a ringbuffer switch is checked at least every 3
654 // seconds. Otherwise, this check is only performed on keyframes,
655 // and if there is a problem with the input we may never see one
656 // again, resulting in a wedged recording.
657 if (!finished && m_ringBufferCheckTimer.isRunning() &&
659 {
661 LOG(VB_RECORD, LOG_WARNING, LOC +
662 "Ringbuffer was switched due to timeout instead of keyframe.");
663 }
664}
665
667{
668 // Note: all log strings contain "progstart mark" for searching.
669 if (m_estimatedProgStartMS <= 0)
670 {
671 // Do nothing because no progstart mark is needed.
672 LOG(VB_RECORD, LOG_DEBUG,
673 QString("No progstart mark needed because delta=%1")
675 return;
676 }
677 frm_pos_map_t::const_iterator first_it = durationDeltaCopy.begin();
678 if (first_it == durationDeltaCopy.end())
679 {
680 LOG(VB_RECORD, LOG_DEBUG, "No progstart mark because map is empty");
681 return;
682 }
683 frm_pos_map_t::const_iterator last_it = durationDeltaCopy.end();
684 --last_it;
685 long long bookmarkFrame = 0;
686 long long first_time { first_it.value() };
687 long long last_time { last_it.value() };
688 LOG(VB_RECORD, LOG_DEBUG,
689 QString("durationDeltaCopy.begin() = (%1,%2)")
690 .arg(first_it.key())
691 .arg(first_it.value()));
692 if (m_estimatedProgStartMS > last_time)
693 {
694 // Do nothing because we haven't reached recstartts yet.
695 LOG(VB_RECORD, LOG_DEBUG,
696 QString("No progstart mark yet because estimatedProgStartMS=%1 "
697 "and *last_it=%2")
698 .arg(m_estimatedProgStartMS).arg(last_time));
699 }
701 m_estimatedProgStartMS < first_time)
702 {
703 // Set progstart mark @ lastSavedKeyframe
704 LOG(VB_RECORD, LOG_DEBUG,
705 QString("Set progstart mark=%1 because %2<=%3<%4")
707 .arg(m_estimatedProgStartMS).arg(first_time));
708 bookmarkFrame = m_lastSavedKeyframe;
709 }
710 else if (first_time <= m_estimatedProgStartMS &&
711 m_estimatedProgStartMS < last_time)
712 {
713 frm_pos_map_t::const_iterator upper_it = first_it;
714 for (; upper_it != durationDeltaCopy.end(); ++upper_it)
715 {
716 if (*upper_it > m_estimatedProgStartMS)
717 {
718 --upper_it;
719 // Set progstart mark @ upper_it.key()
720 LOG(VB_RECORD, LOG_DEBUG,
721 QString("Set progstart mark=%1 because "
722 "estimatedProgStartMS=%2 and upper_it.value()=%3")
723 .arg(upper_it.key()).arg(m_estimatedProgStartMS)
724 .arg(upper_it.value()));
725 bookmarkFrame = upper_it.key();
726 break;
727 }
728 }
729 }
730 else
731 {
732 // do nothing
733 LOG(VB_RECORD, LOG_DEBUG, "No progstart mark due to fallthrough");
734 }
735 if (bookmarkFrame)
736 {
737 frm_dir_map_t progStartMap;
738 progStartMap[bookmarkFrame] = MARK_UTIL_PROGSTART;
740 }
741 m_lastSavedKeyframe = last_it.key();
742 m_lastSavedDuration = last_it.value();
743 LOG(VB_RECORD, LOG_DEBUG,
744 QString("Setting lastSavedKeyframe=%1 lastSavedDuration=%2 "
745 "for progstart mark calculations")
747}
748
749void RecorderBase::AspectChange(uint aspect, long long frame)
750{
752 uint customAspect = 0;
753 if (aspect == ASPECT_1_1 || aspect >= ASPECT_CUSTOM)
754 {
755 if (aspect > 0x0F)
756 customAspect = aspect;
757 else if (m_videoWidth && m_videoHeight)
758 customAspect = m_videoWidth * 1000000 / m_videoHeight;
759
760 mark = (customAspect) ? MARK_ASPECT_CUSTOM : mark;
761 }
762 if (aspect == ASPECT_4_3)
763 mark = MARK_ASPECT_4_3;
764 if (aspect == ASPECT_16_9)
765 mark = MARK_ASPECT_16_9;
766 if (aspect == ASPECT_2_21_1)
767 mark = MARK_ASPECT_2_21_1;
768
769 // Populate the recordfile table as early as possible, the best
770 // value will be determined when the recording completes.
773 {
775 switch (m_videoAspect)
776 {
777 case ASPECT_1_1 :
778 recFile->m_videoAspectRatio = 1.0;
779 break;
780 case ASPECT_4_3:
781 recFile->m_videoAspectRatio = 1.33333333333;
782 break;
783 case ASPECT_16_9:
784 recFile->m_videoAspectRatio = 1.77777777777;
785 break;
786 case ASPECT_2_21_1:
787 recFile->m_videoAspectRatio = 2.21;
788 break;
789 default:
790 recFile->m_videoAspectRatio = (double)m_videoAspect / 1000000.0;
791 break;
792 }
793 recFile->Save();
794 }
795
796 if (m_curRecording)
797 m_curRecording->SaveAspect(frame, mark, customAspect);
798}
799
800void RecorderBase::ResolutionChange(uint width, uint height, long long frame)
801{
802 if (m_curRecording)
803 {
804 // Populate the recordfile table as early as possible, the best value
805 // value will be determined when the recording completes.
808 {
809 m_curRecording->GetRecordingFile()->m_videoResolution = QSize(width, height);
811 }
812 m_curRecording->SaveResolution(frame, width, height);
813 }
814}
815
816void RecorderBase::FrameRateChange(uint framerate, uint64_t frame)
817{
818 if (m_curRecording)
819 {
820 // Populate the recordfile table as early as possible, the average
821 // value will be determined when the recording completes.
823 {
824 m_curRecording->GetRecordingFile()->m_videoFrameRate = (double)framerate / 1000.0;
826 }
827 m_curRecording->SaveFrameRate(frame, framerate);
828 }
829}
830
832{
833 if (m_curRecording)
835}
836
837void RecorderBase::VideoCodecChange(AVCodecID vCodec)
838{
840 {
841 m_curRecording->GetRecordingFile()->m_videoCodec = avcodec_get_name(vCodec);
843 }
844}
845
846void RecorderBase::AudioCodecChange(AVCodecID aCodec)
847{
849 {
850 m_curRecording->GetRecordingFile()->m_audioCodec = avcodec_get_name(aCodec);
852 }
853}
854
855void RecorderBase::SetDuration(std::chrono::milliseconds duration)
856{
857 if (m_curRecording)
859}
860
861void RecorderBase::SetTotalFrames(uint64_t total_frames)
862{
863 if (m_curRecording)
864 m_curRecording->SaveTotalFrames(total_frames);
865}
866
867
869 TVRec *tvrec,
870 ChannelBase *channel,
872 const GeneralDBOptions &genOpt)
873{
874 if (!channel)
875 return nullptr;
876
877 RecorderBase *recorder = nullptr;
878 if (genOpt.m_inputType == "IMPORT")
879 {
880 recorder = new ImportRecorder(tvrec);
881 }
882 else if (genOpt.m_inputType == "EXTERNAL")
883 {
884 if (dynamic_cast<ExternalChannel*>(channel))
885 recorder = new ExternalRecorder(tvrec, dynamic_cast<ExternalChannel*>(channel));
886 }
887#if CONFIG_V4L2
888 else if ((genOpt.m_inputType == "MPEG") ||
889 (genOpt.m_inputType == "HDPVR") ||
890 (genOpt.m_inputType == "DEMO"))
891 {
892 recorder = new MpegRecorder(tvrec);
893 }
894 else if (genOpt.m_inputType == "V4L2ENC")
895 {
896 if (dynamic_cast<V4LChannel*>(channel))
897 recorder = new V4L2encRecorder(tvrec, dynamic_cast<V4LChannel*>(channel));
898 }
899#else
900 else if (genOpt.m_inputType == "DEMO")
901 {
902 recorder = new ImportRecorder(tvrec);
903 }
904#endif // CONFIG_V4L2
905#if CONFIG_FIREWIRE
906 else if (genOpt.m_inputType == "FIREWIRE")
907 {
908 if (dynamic_cast<FirewireChannel*>(channel))
909 recorder = new FirewireRecorder(tvrec, dynamic_cast<FirewireChannel*>(channel));
910 }
911#endif // CONFIG_FIREWIRE
912#if CONFIG_HDHOMERUN
913 else if (genOpt.m_inputType == "HDHOMERUN")
914 {
915 if (dynamic_cast<HDHRChannel*>(channel))
916 {
917 recorder = new HDHRRecorder(tvrec, dynamic_cast<HDHRChannel*>(channel));
918 recorder->SetBoolOption("wait_for_seqstart", genOpt.m_waitForSeqstart);
919 }
920 }
921#endif // CONFIG_HDHOMERUN
922#if CONFIG_CETON
923 else if (genOpt.m_inputType == "CETON")
924 {
925 if (dynamic_cast<CetonChannel*>(channel))
926 {
927 recorder = new CetonRecorder(tvrec, dynamic_cast<CetonChannel*>(channel));
928 recorder->SetBoolOption("wait_for_seqstart", genOpt.m_waitForSeqstart);
929 }
930 }
931#endif // CONFIG_CETON
932#if CONFIG_DVB
933 else if (genOpt.m_inputType == "DVB")
934 {
935 if (dynamic_cast<DVBChannel*>(channel))
936 {
937 recorder = new DVBRecorder(tvrec, dynamic_cast<DVBChannel*>(channel));
938 recorder->SetBoolOption("wait_for_seqstart", genOpt.m_waitForSeqstart);
939 }
940 }
941#endif // CONFIG_DVB
942#if CONFIG_IPTV
943 else if (genOpt.m_inputType == "FREEBOX")
944 {
945 if (dynamic_cast<IPTVChannel*>(channel))
946 {
947 recorder = new IPTVRecorder(tvrec, dynamic_cast<IPTVChannel*>(channel));
948 recorder->SetOption("mrl", genOpt.m_videoDev);
949 }
950 }
951#endif // CONFIG_IPTV
952#if CONFIG_VBOX
953 else if (genOpt.m_inputType == "VBOX")
954 {
955 if (dynamic_cast<IPTVChannel*>(channel))
956 recorder = new IPTVRecorder(tvrec, dynamic_cast<IPTVChannel*>(channel));
957 }
958#endif // CONFIG_VBOX
959#if CONFIG_SATIP
960 else if (genOpt.m_inputType == "SATIP")
961 {
962 if (dynamic_cast<SatIPChannel*>(channel))
963 recorder = new SatIPRecorder(tvrec, dynamic_cast<SatIPChannel*>(channel));
964 }
965#endif // CONFIG_SATIP
966#if CONFIG_ASI
967 else if (genOpt.m_inputType == "ASI")
968 {
969 if (dynamic_cast<ASIChannel*>(channel))
970 {
971 recorder = new ASIRecorder(tvrec, dynamic_cast<ASIChannel*>(channel));
972 recorder->SetBoolOption("wait_for_seqstart", genOpt.m_waitForSeqstart);
973 }
974 }
975#endif // CONFIG_ASI
976
977 if (recorder)
978 {
979 recorder->SetOptionsFromProfile(&profile,
980 genOpt.m_videoDev, genOpt.m_audioDev, genOpt.m_vbiDev);
981 // Override the samplerate defined in the profile if this card
982 // was configured with a fixed rate.
983 if (genOpt.m_audioSampleRate)
984 recorder->SetOption("samplerate", genOpt.m_audioSampleRate);
985 }
986 else
987 {
988 QString msg = "Need %1 recorder, but compiled without %2 support!";
989 msg = msg.arg(genOpt.m_inputType, genOpt.m_inputType);
990 LOG(VB_GENERAL, LOG_ERR,
991 "RecorderBase::CreateRecorder() Error, " + msg);
992 }
993
994 return recorder;
995}
996
997/* 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:73
int m_audioSampleRate
Definition: tv_rec.h:74
bool m_waitForSeqstart
Definition: tv_rec.h:78
QString m_vbiDev
Definition: tv_rec.h:71
QString m_videoDev
Definition: tv_rec.h:70
QString m_audioDev
Definition: tv_rec.h:72
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:68
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:373
void SetRecordingStatus(RecStatus::Type status)
Definition: programinfo.h:585
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:391
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:340
void SavePositionMapDelta(frm_pos_map_t &posMap, MarkTypes type) const
uint GetInputID(void) const
Definition: programinfo.h:467
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:451
QDateTime GetRecordingEndTime(void) const
Approximate time the recording should have ended, did end, or is intended to end.
Definition: programinfo.h:413
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:50
qint64 m_estimatedProgStartMS
Definition: recorderbase.h:341
virtual bool IsRecording(void)
Tells whether the StartRecorder() loop is running.
virtual void StartNewFile(void)
Definition: recorderbase.h:250
QAtomicInt m_timeOfLatestDataPacketInterval
Definition: recorderbase.h:354
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:294
QMutex m_pauseLock
Definition: recorderbase.h:313
uint m_videoHeight
Definition: recorderbase.h:306
bool m_requestPause
Definition: recorderbase.h:314
void SetFrameRate(double rate)
Sets the video frame rate.
Definition: recorderbase.h:58
QMutex m_nextRingBufferLock
Definition: recorderbase.h:326
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:334
AVCodecID m_primaryAudioCodec
Definition: recorderbase.h:296
uint m_videoAspect
Definition: recorderbase.h:304
frm_pos_map_t m_durationMapDelta
Definition: recorderbase.h:337
MarkTypes m_positionMapType
Definition: recorderbase.h:332
long long m_lastSavedDuration
Definition: recorderbase.h:343
QDateTime m_timeOfLatestData
Definition: recorderbase.h:355
QMutex m_statisticsLock
Definition: recorderbase.h:350
TVRec * m_tvrec
Definition: recorderbase.h:290
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:352
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:209
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:329
frm_pos_map_t m_positionMapDelta
Definition: recorderbase.h:335
virtual void FinishRecording(void)
bool m_weMadeBuffer
Definition: recorderbase.h:292
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:295
bool m_recording
True while recording is actually being performed.
Definition: recorderbase.h:321
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:336
QWaitCondition m_pauseWait
Definition: recorderbase.h:316
long long GetKeyframePosition(long long desired) const
Returns closest keyframe position before the desired frame.
long long m_lastSavedKeyframe
Definition: recorderbase.h:342
void SetStrOption(RecordingProfile *profile, const QString &name)
Convenience function used to set QString options from a profile.
MythMediaBuffer * m_ringBuffer
Definition: recorderbase.h:291
RecordingGaps m_recordingGaps
Definition: recorderbase.h:357
QString m_videodevice
Definition: recorderbase.h:298
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:308
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:338
~RecorderBase() override
QAtomicInt m_timeOfLatestDataCount
Definition: recorderbase.h:353
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:351
bool m_requestRecording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:319
QMutex m_positionMapLock
Definition: recorderbase.h:333
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:327
void AudioCodecChange(AVCodecID aCodec)
Note a change in audio codec.
void TryWriteProgStartMark(const frm_pos_map_t &durationDeltaCopy)
RecordingInfo * m_curRecording
Definition: recorderbase.h:310
static RecorderBase * CreateRecorder(TVRec *tvrec, ChannelBase *channel, RecordingProfile &profile, const GeneralDBOptions &genOpt)
QWaitCondition m_unpauseWait
Definition: recorderbase.h:317
virtual void ClearStatistics(void)
RecordingInfo * m_nextRecording
Definition: recorderbase.h:328
QString m_videocodec
Definition: recorderbase.h:297
QWaitCondition m_recordingWait
Definition: recorderbase.h:322
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:30
AVContainer m_containerFormat
Definition: recordingfile.h:47
double m_videoFrameRate
Definition: recordingfile.h:52
static QString AVContainerToString(AVContainer format)
QString m_videoCodec
Definition: recordingfile.h:49
QString m_audioCodec
Definition: recordingfile.h:54
double m_videoAspectRatio
Definition: recordingfile.h:51
QSize m_videoResolution
Definition: recordingfile.h:50
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:143
void RecorderPaused(void)
This is a callback, called by the "recorder" instance when it has actually paused.
Definition: tv_rec.cpp:2996
void RingBufferChanged(MythMediaBuffer *Buffer, RecordingInfo *pginfo, RecordingQuality *recq)
Definition: tv_rec.cpp:3438
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:32
unsigned int uint
Definition: freesurround.h:24
bool force
RemoteEncoder * recorder
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:949
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