MythTV master
tv_rec.cpp
Go to the documentation of this file.
1// C headers
2#include <chrono> // for milliseconds
3#include <cstdio>
4#include <cstdlib>
5#include <cstring>
6#include <sched.h> // for sched_yield
7#include <thread> // for sleep_for
8#include <utility>
9
10// MythTV headers
11
12#include "libmythbase/compat.h"
13#include "libmythbase/mythconfig.h"
16#include "libmythbase/mythdb.h"
20
21#include "cardutil.h"
22#include "channelgroup.h"
23#include "eitscanner.h"
24#include "io/mythmediabuffer.h"
25#include "jobqueue.h"
26#include "livetvchain.h"
27#include "mpeg/atscstreamdata.h"
28#include "mpeg/atsctables.h"
29#include "mpeg/dvbstreamdata.h"
30#include "mythsystemevent.h"
31#include "osd.h"
33#include "programinfo.h"
39#include "recorders/vboxutils.h"
40#include "recordingprofile.h"
41#include "recordingrule.h"
42#include "sourceutil.h"
43#include "tv_rec.h"
44#include "tvremoteutil.h"
45
46#define DEBUG_CHANNEL_PREFIX 0
48#define LOC QString("TVRec[%1]: ").arg(m_inputId)
49#define LOC2 QString("TVRec[%1]: ").arg(inputid) // for static functions
50
51QReadWriteLock TVRec::s_inputsLock;
52QMap<uint,TVRec*> TVRec::s_inputs;
53QMutex TVRec::s_eitLock;
54
55static bool is_dishnet_eit(uint inputid);
56static int init_jobs(const RecordingInfo *rec, RecordingProfile &profile,
57 bool on_host, bool transcode_bfr_comm, bool on_line_comm);
59static std::chrono::seconds eit_start_rand(uint inputId, std::chrono::seconds eitTransportTimeout);
60
85TVRec::TVRec(int inputid)
86 // Various threads
87 : m_eventThread(new MThread("TVRecEvent", this)),
88 // Configuration variables from setup routines
89 m_inputId(inputid)
90{
91 s_inputs[m_inputId] = this;
92}
93
94bool TVRec::CreateChannel(const QString &startchannel,
95 bool enter_power_save_mode)
96{
97 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("CreateChannel(%1)")
98 .arg(startchannel));
99 // If this recorder is a child and its parent is not in error, we
100 // do not need nor want to set the channel.
101 bool setchan = true;
102 if (m_parentId)
103 {
104 TVRec *parentTV = GetTVRec(m_parentId);
105 if (parentTV && parentTV->GetState() != kState_Error)
106 setchan = false;
107 }
109 this, m_genOpt, m_dvbOpt, m_fwOpt,
110 startchannel, enter_power_save_mode, m_rbFileExt, setchan);
111
112#if CONFIG_VBOX
113 if (m_genOpt.m_inputType == "VBOX")
114 {
115 if (!CardUtil::IsVBoxPresent(m_inputId))
116 {
117 // VBOX presence failed, recorder is marked errored
118 LOG(VB_GENERAL, LOG_ERR, LOC +
119 QString("CreateChannel(%1) failed due to VBOX not responding "
120 "to network check on inputid [%2]")
121 .arg(startchannel).arg(m_inputId));
122 m_channel = nullptr;
123 }
124 }
125#endif
126
127#if CONFIG_SATIP
128 if (m_genOpt.m_inputType == "SATIP")
129 {
130 if (!CardUtil::IsSatIPPresent(m_inputId))
131 {
132 // SatIP box presence failed, recorder is marked errored
133 LOG(VB_GENERAL, LOG_ERR, LOC +
134 QString("CreateChannel(%1) failed due to SatIP box not responding "
135 "to network check on inputid [%2]")
136 .arg(startchannel).arg(m_inputId));
137 m_channel = nullptr;
138 }
139 }
140#endif
141
142 if (!m_channel)
143 {
144 SetFlags(kFlagErrored, __FILE__, __LINE__);
145 return false;
146 }
147
148 return true;
149}
150
156bool TVRec::Init(void)
157{
158 QMutexLocker lock(&m_stateChangeLock);
159
161 {
162 LOG(VB_CHANNEL, LOG_ERR, LOC +
163 QString("Failed to GetDevices for input %1")
164 .arg(m_inputId));
165 return false;
166 }
167
169
170 // Configure the Channel instance
171 QString startchannel = CardUtil::GetStartChannel(m_inputId);
172 if (startchannel.isEmpty())
173 return false;
174 if (!CreateChannel(startchannel, true))
175 {
176 LOG(VB_CHANNEL, LOG_ERR, LOC +
177 QString("Failed to create channel instance for %1")
178 .arg(startchannel));
179 return false;
180 }
181
182 // All conflicting inputs for this input
183 if (m_parentId == 0)
184 {
186 }
187
188 m_transcodeFirst = gCoreContext->GetBoolSetting("AutoTranscodeBeforeAutoCommflag", false);
189 m_earlyCommFlag = gCoreContext->GetBoolSetting("AutoCommflagWhileRecording", false);
190 m_runJobOnHostOnly = gCoreContext->GetBoolSetting("JobsRunOnRecordHost", false);
191 m_eitTransportTimeout = gCoreContext->GetDurSetting<std::chrono::minutes>("EITTransportTimeout", 5min);
192 if (m_eitTransportTimeout < 15s)
194 m_eitCrawlIdleStart = gCoreContext->GetDurSetting<std::chrono::seconds>("EITCrawIdleStart", 60s);
195 m_eitScanPeriod = gCoreContext->GetDurSetting<std::chrono::minutes>("EITScanPeriod", 15min);
196 if (m_eitScanPeriod < 5min)
197 m_eitScanPeriod = 5min;
198 m_audioSampleRateDB = gCoreContext->GetNumSetting("AudioSampleRate");
199 m_overRecordSecNrml = gCoreContext->GetDurSetting<std::chrono::seconds>("RecordOverTime");
200 m_overRecordSecCat = gCoreContext->GetDurSetting<std::chrono::minutes>("CategoryOverTime");
201 m_overRecordCategory= gCoreContext->GetSetting("OverTimeCategory");
202
204
206
207 return true;
208}
209
215{
216 s_inputs.remove(m_inputId);
217
219 {
220 ClearFlags(kFlagRunMainLoop, __FILE__, __LINE__);
222 delete m_eventThread;
223 m_eventThread = nullptr;
224 }
225
226 if (m_channel)
227 {
228 delete m_channel;
229 m_channel = nullptr;
230 }
231}
232
234{
235 LOG(VB_RECORD, LOG_INFO, LOC + "TeardownAll");
236
238
239 if (m_scanner)
240 {
241 delete m_scanner;
242 m_scanner = nullptr;
243 }
244
246
247 SetRingBuffer(nullptr);
248}
249
251{
252 QMutexLocker locker(&m_triggerEventLoopLock);
254 m_triggerEventLoopWait.wakeAll();
255}
256
264{
265 if (m_changeState)
267 return m_internalState;
268}
269
278{
279 QMutexLocker lock(&m_stateChangeLock);
280
281 ProgramInfo *tmppginfo = nullptr;
282
284 {
285 tmppginfo = new ProgramInfo(*m_curRecording);
287 }
288 else
289 {
290 tmppginfo = new ProgramInfo();
291 }
292 tmppginfo->SetInputID(m_inputId);
293
294 return tmppginfo;
295}
296
311void TVRec::RecordPending(const ProgramInfo *rcinfo, std::chrono::seconds secsleft,
312 bool hasLater)
313{
314 QMutexLocker statelock(&m_stateChangeLock);
315 QMutexLocker pendlock(&m_pendingRecLock);
316
317 if (secsleft < 0s)
318 {
319 LOG(VB_RECORD, LOG_INFO, LOC + "Pending recording revoked on " +
320 QString("inputid [%1]").arg(rcinfo->GetInputID()));
321
322 PendingMap::iterator it = m_pendingRecordings.find(rcinfo->GetInputID());
323 if (it != m_pendingRecordings.end())
324 {
325 (*it).m_ask = false;
326 (*it).m_doNotAsk = true;
327 (*it).m_canceled = true;
328 }
329 return;
330 }
331
332 LOG(VB_RECORD, LOG_INFO, LOC +
333 QString("RecordPending on inputid [%1]").arg(rcinfo->GetInputID()));
334
335 PendingInfo pending;
336 pending.m_info = new ProgramInfo(*rcinfo);
337 pending.m_recordingStart = MythDate::current().addSecs(secsleft.count());
338 pending.m_hasLaterShowing = hasLater;
339 pending.m_ask = true;
340 pending.m_doNotAsk = false;
341
342 m_pendingRecordings[rcinfo->GetInputID()] = pending;
343
344 // If this isn't a recording for this instance to make, we are done
345 if (rcinfo->GetInputID() != m_inputId)
346 return;
347
348 // We also need to check our input groups
349 std::vector<uint> inputids = CardUtil::GetConflictingInputs(rcinfo->GetInputID());
350
351 m_pendingRecordings[rcinfo->GetInputID()].m_possibleConflicts = inputids;
352
353 pendlock.unlock();
354 statelock.unlock();
355 for (uint inputid : inputids)
356 RemoteRecordPending(inputid, rcinfo, secsleft, hasLater);
357 statelock.relock();
358 pendlock.relock();
359}
360
365{
368 delete old_rec;
369}
370
374QDateTime TVRec::GetRecordEndTime(const ProgramInfo *pi) const
375{
376 bool spcat = (!m_overRecordCategory.isEmpty() &&
378 std::chrono::seconds secs = (spcat) ? m_overRecordSecCat : m_overRecordSecNrml;
379 return pi->GetRecordingEndTime().addSecs(secs.count());
380}
381
388{
389 QMutexLocker pendlock(&m_pendingRecLock);
390 LOG(VB_RECORD, LOG_INFO, LOC +
391 QString("CancelNextRecording(%1) -- begin").arg(cancel));
392
393 PendingMap::iterator it = m_pendingRecordings.find(m_inputId);
394 if (it == m_pendingRecordings.end())
395 {
396 LOG(VB_RECORD, LOG_INFO, LOC + QString("CancelNextRecording(%1) -- "
397 "error, unknown recording").arg(cancel));
398 return;
399 }
400
401 if (cancel)
402 {
403 std::vector<unsigned int> &inputids = (*it).m_possibleConflicts;
404 for (uint inputid : inputids)
405 {
406 LOG(VB_RECORD, LOG_INFO, LOC +
407 QString("CancelNextRecording -- inputid 0x%1")
408 .arg((uint64_t)inputid,0,16));
409
410 pendlock.unlock();
411 RemoteRecordPending(inputid, (*it).m_info, -1s, false);
412 pendlock.relock();
413 }
414
415 LOG(VB_RECORD, LOG_INFO, LOC +
416 QString("CancelNextRecording -- inputid [%1]")
417 .arg(m_inputId));
418
419 RecordPending((*it).m_info, -1s, false);
420 }
421 else
422 {
423 (*it).m_canceled = false;
424 }
425
426 LOG(VB_RECORD, LOG_INFO, LOC +
427 QString("CancelNextRecording(%1) -- end").arg(cancel));
428}
429
438{
439 RecordingInfo ri1(*pginfo);
442 RecordingInfo *rcinfo = &ri1;
443
444 LOG(VB_RECORD, LOG_INFO, LOC + QString("StartRecording(%1)")
446
447 QMutexLocker lock(&m_stateChangeLock);
448
451
452 // Flush out any pending state changes
454
455 // We need to do this check early so we don't cancel an overrecord
456 // that we're trying to extend.
460 {
461 int post_roll_seconds = m_curRecording->GetRecordingEndTime()
462 .secsTo(m_recordEndTime);
463
468
470 .addSecs(post_roll_seconds);
471
472 QString msg = QString("updating recording: %1 %2 %3 %4")
473 .arg(m_curRecording->GetTitle(),
474 QString::number(m_curRecording->GetChanID()),
477 LOG(VB_RECORD, LOG_INFO, LOC + msg);
478
479 ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
480
483 }
484
485 bool cancelNext = false;
486 PendingInfo pendinfo;
487
488 m_pendingRecLock.lock();
489 PendingMap::iterator it = m_pendingRecordings.find(m_inputId);
490 if (it != m_pendingRecordings.end())
491 {
492 (*it).m_ask = (*it).m_doNotAsk = false;
493 cancelNext = (*it).m_canceled;
494 }
495 m_pendingRecLock.unlock();
496
497 // Flush out events...
499
500 // Rescan pending recordings since the event loop may have deleted
501 // a stale entry. If this happens the info pointer will not be valid
502 // since the HandlePendingRecordings loop will have deleted it.
503 m_pendingRecLock.lock();
505 bool has_pending = (it != m_pendingRecordings.end());
506 if (has_pending)
507 pendinfo = *it;
508 m_pendingRecLock.unlock();
509
510 // If the needed input is in a shared input group, and we are
511 // not canceling the recording anyway, check other recorders
512 if (!cancelNext && has_pending && !pendinfo.m_possibleConflicts.empty())
513 {
514 LOG(VB_RECORD, LOG_INFO, LOC +
515 "Checking input group recorders - begin");
516 std::vector<unsigned int> &inputids = pendinfo.m_possibleConflicts;
517
518 uint mplexid = 0;
519 uint chanid = 0;
520 uint sourceid = 0;
521 std::vector<unsigned int> inputids2;
522 std::vector<TVState> states;
523
524 // Stop remote recordings if needed
525 for (uint inputid : inputids)
526 {
527 InputInfo busy_input;
528 bool is_busy = RemoteIsBusy(inputid, busy_input);
529
530 if (is_busy && !sourceid)
531 {
532 mplexid = pendinfo.m_info->QueryMplexID();
533 chanid = pendinfo.m_info->GetChanID();
534 sourceid = pendinfo.m_info->GetSourceID();
535 }
536
537 if (is_busy &&
538 ((sourceid != busy_input.m_sourceId) ||
539 (mplexid != busy_input.m_mplexId) ||
540 ((mplexid == 0 || mplexid == 32767) &&
541 chanid != busy_input.m_chanId)))
542 {
543 states.push_back((TVState) RemoteGetState(inputid));
544 inputids2.push_back(inputid);
545 }
546 }
547
548 bool ok = true;
549 for (uint i = 0; (i < inputids2.size()) && ok; i++)
550 {
551 LOG(VB_RECORD, LOG_INFO, LOC +
552 QString("Attempting to stop input [%1] in state %2")
553 .arg(inputids2[i]).arg(StateToString(states[i])));
554
555 bool success = RemoteStopRecording(inputids2[i]);
556 if (success)
557 {
558 uint state = RemoteGetState(inputids2[i]);
559 LOG(VB_GENERAL, LOG_INFO, LOC + QString("a [%1]: %2")
560 .arg(inputids2[i]).arg(StateToString((TVState)state)));
561 success = (kState_None == state);
562 }
563
564 // If we managed to stop LiveTV recording, restart playback..
565 if (success && states[i] == kState_WatchingLiveTV)
566 {
567 QString message = QString("QUIT_LIVETV %1").arg(inputids2[i]);
568 MythEvent me(message);
570 }
571
572 LOG(VB_RECORD, LOG_INFO, LOC +
573 QString("Stopping recording on [%1], %2") .arg(inputids2[i])
574 .arg(success ? "succeeded" : "failed"));
575
576 ok &= success;
577 }
578
579 // If we failed to stop the remote recordings, don't record
580 if (!ok)
581 {
583 cancelNext = true;
584 }
585
586 inputids.clear();
587
588 LOG(VB_RECORD, LOG_INFO, LOC + "Checking input group recorders - done");
589 }
590
591 bool did_switch = false;
592 if (!cancelNext && (GetState() == kState_RecordingOnly))
593 {
595 did_switch = (nullptr != ri2);
596 if (did_switch)
597 {
598 // Make sure scheduler is allowed to end this recording
599 ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
600
602 }
603 else
604 {
605 // If in post-roll, end recording
606 m_stateChangeLock.unlock();
608 m_stateChangeLock.lock();
609 }
610 }
611
612 if (!cancelNext && (GetState() == kState_None))
613 {
614 if (m_tvChain)
615 {
616 QString message = QString("LIVETV_EXITED");
617 MythEvent me(message, m_tvChain->GetID());
620 m_tvChain = nullptr;
621 }
622
624
625 // Tell event loop to begin recording.
626 m_curRecording = new RecordingInfo(*rcinfo);
631
632 // Make sure scheduler is allowed to end this recording
633 ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
634
637 else
638 LOG(VB_RECORD, LOG_WARNING, LOC + "Still failing.");
640 }
641 else if (!cancelNext && (GetState() == kState_WatchingLiveTV))
642 {
646
647 // We want the frontend to change channel for recording
648 // and disable the UI for channel change, PiP, etc.
649
650 QString message = QString("LIVETV_WATCH %1 1").arg(m_inputId);
651 QStringList prog;
652 rcinfo->ToStringList(prog);
653 MythEvent me(message, prog);
655 }
656 else if (!did_switch)
657 {
658 QString msg = QString("Wanted to record: %1 %2 %3 %4\n\t\t\t")
659 .arg(rcinfo->GetTitle(),
660 QString::number(rcinfo->GetChanID()),
663
664 if (cancelNext)
665 {
666 msg += "But a user has canceled this recording";
668 }
669 else
670 {
671 msg += QString("But the current state is: %1")
674 }
675
677 {
678 msg += QString("\n\t\t\tCurrently recording: %1 %2 %3 %4")
679 .arg(m_curRecording->GetTitle(),
680 QString::number(m_curRecording->GetChanID()),
683 }
684
685 LOG(VB_GENERAL, LOG_INFO, LOC + msg);
686 }
687
688 for (const auto & pend : std::as_const(m_pendingRecordings))
689 delete pend.m_info;
690 m_pendingRecordings.clear();
691
692 if (!did_switch)
693 {
695
696 QMutexLocker locker(&m_pendingRecLock);
697 if ((m_curRecording) &&
702 {
703 SetRecordingStatus(RecStatus::Failed, __LINE__, true);
704 }
705 return m_recStatus;
706 }
707
708 return GetRecordingStatus();
709}
710
712{
713 QMutexLocker pendlock(&m_pendingRecLock);
714 return m_recStatus;
715}
716
718 RecStatus::Type new_status, int line, bool have_lock)
719{
720 RecStatus::Type old_status { RecStatus::Unknown };
721 if (have_lock)
722 {
723 old_status = m_recStatus;
724 m_recStatus = new_status;
725 }
726 else
727 {
728 m_pendingRecLock.lock();
729 old_status = m_recStatus;
730 m_recStatus = new_status;
731 m_pendingRecLock.unlock();
732 }
733
734 LOG(VB_RECORD, LOG_INFO, LOC +
735 QString("SetRecordingStatus(%1->%2) on line %3")
736 .arg(RecStatus::toString(old_status, kSingleRecord),
738 QString::number(line)));
739}
740
747void TVRec::StopRecording(bool killFile)
748{
750 {
751 QMutexLocker lock(&m_stateChangeLock);
752 if (killFile)
753 SetFlags(kFlagKillRec, __FILE__, __LINE__);
754 else if (m_curRecording)
755 {
756 QDateTime now = MythDate::current(true);
757 if (now < m_curRecording->GetDesiredEndTime())
759 }
761 // wait for state change to take effect
764
766 }
767}
768
775{
776 return (state == kState_RecordingOnly ||
777 state == kState_WatchingLiveTV);
778}
779
785{
786 return (state == kState_WatchingPreRecorded);
787}
788
795{
796 if (StateIsRecording(state))
797 return kState_None;
798
799 LOG(VB_GENERAL, LOG_ERR, LOC +
800 QString("Unknown state in RemoveRecording: %1")
801 .arg(StateToString(state)));
802 return kState_Error;
803}
804
811{
812 if (StateIsPlaying(state))
813 {
814 if (state == kState_WatchingPreRecorded)
815 return kState_None;
817 }
818
819 QString msg = "Unknown state in RemovePlaying: %1";
820 LOG(VB_GENERAL, LOG_ERR, LOC + msg.arg(StateToString(state)));
821
822 return kState_Error;
823}
824
831{
832 if (!curRec)
833 return;
834
836 LOG(VB_RECORD, LOG_INFO, LOC + QString("StartedRecording(%1) fn(%2)")
837 .arg(curRec->MakeUniqueKey(), curRec->GetPathname()));
838
839 if (curRec->IsCommercialFree())
841
842 AutoRunInitType t = (curRec->GetRecordingGroup() == "LiveTV") ?
844 InitAutoRunJobs(curRec, t, nullptr, __LINE__);
845
846 SendMythSystemRecEvent("REC_STARTED", curRec);
847}
848
857{
858 if (!curRec)
859 return;
860
861 // Make sure the recording group is up to date
862 const QString recgrp = curRec->QueryRecordingGroup();
863 curRec->SetRecordingGroup(recgrp);
864
865 bool is_good = true;
866 if (recq)
867 {
868 LOG((recq->IsDamaged()) ? VB_GENERAL : VB_RECORD, LOG_INFO,
869 LOC + QString("FinishedRecording(%1) %2 recq:\n%3")
870 .arg(curRec->MakeUniqueKey(),
871 (recq->IsDamaged()) ? "damaged" : "good",
872 recq->toStringXML()));
873 is_good = !recq->IsDamaged();
874 delete recq;
875 recq = nullptr;
876 }
877
878 RecStatus::Type ors = curRec->GetRecordingStatus();
879 // Set the final recording status
882 else if (curRec->GetRecordingStatus() != RecStatus::Recorded)
885 is_good &= (curRec->GetRecordingStatus() == RecStatus::Recorded);
886
887 // Figure out if this was already done for this recording
888 bool was_finished = false;
889 static QMutex s_finRecLock;
890 static QHash<QString,QDateTime> s_finRecMap;
891 {
892 QMutexLocker locker(&s_finRecLock);
893 QDateTime now = MythDate::current();
894 QDateTime expired = now.addSecs(-5LL * 60);
895 QHash<QString,QDateTime>::iterator it = s_finRecMap.begin();
896 while (it != s_finRecMap.end())
897 {
898 if ((*it) < expired)
899 it = s_finRecMap.erase(it);
900 else
901 ++it;
902 }
903 QString key = curRec->MakeUniqueKey();
904 it = s_finRecMap.find(key);
905 if (it != s_finRecMap.end())
906 was_finished = true;
907 else
908 s_finRecMap[key] = now;
909 }
910
911 // Print something informative to the log
912 LOG(VB_RECORD, LOG_INFO, LOC +
913 QString("FinishedRecording(%1) %2 quality"
914 "\n\t\t\ttitle: %3\n\t\t\t"
915 "in recgroup: %4 status: %5:%6 %7 %8")
916 .arg(curRec->MakeUniqueKey(),
917 is_good ? "Good" : "Bad",
918 curRec->GetTitle(),
919 recgrp,
922 HasFlags(kFlagDummyRecorderRunning)?"is_dummy":"not_dummy",
923 was_finished?"already_finished":"finished_now"));
924
925 // This has already been called on this recording..
926 if (was_finished)
927 return;
928
929 // Notify the frontend watching live tv that this file is final
930 if (m_tvChain)
932
933 // if this is a dummy recorder, do no more..
935 {
936 curRec->FinishedRecording(true); // so end time is updated
937 SendMythSystemRecEvent("REC_FINISHED", curRec);
938 return;
939 }
940
941 // Get the width and set the videoprops
942 MarkTypes aspectRatio = curRec->QueryAverageAspectRatio();
943 uint avg_height = curRec->QueryAverageHeight();
944 bool progressive = curRec->QueryAverageScanProgressive();
945
946 uint16_t flags {VID_UNKNOWN};
947 if (avg_height > 2000)
948 flags |= VID_4K;
949 else if (avg_height > 1000)
950 flags |= VID_1080;
951 else if (avg_height > 700)
952 flags |= VID_720;
953 if (progressive)
954 flags |= VID_PROGRESSIVE;
955 if (!is_good)
956 flags |= VID_DAMAGED;
957 if ((aspectRatio == MARK_ASPECT_16_9) ||
958 (aspectRatio == MARK_ASPECT_2_21_1))
959 flags |= VID_WIDESCREEN;
960
961 curRec->SaveVideoProperties
962 (VID_4K | VID_1080 | VID_720 | VID_DAMAGED |
963 VID_WIDESCREEN | VID_PROGRESSIVE, flags);
964
965 // Make sure really short recordings have positive run time.
966 if (curRec->GetRecordingEndTime() <= curRec->GetRecordingStartTime())
967 {
968 curRec->SetRecordingEndTime(
969 curRec->GetRecordingStartTime().addSecs(60));
970 }
971
972 // HACK Temporary hack, ensure we've loaded the recording file info, do it now
973 // so that it contains the final filesize information
974 if (!curRec->GetRecordingFile())
975 curRec->LoadRecordingFile();
976
977 // Generate a preview
978 uint64_t fsize = curRec->GetFilesize();
979 if (curRec->IsLocal() && (fsize >= 1000) &&
981 {
983 }
984
985 // store recording in recorded table
986 curRec->FinishedRecording(!is_good || (recgrp == "LiveTV"));
987
988 // send out UPDATE_RECORDING_STATUS message
989 LOG(VB_RECORD, LOG_INFO, LOC +
990 QString("FinishedRecording -- UPDATE_RECORDING_STATUS: %1")
991 .arg(RecStatus::toString(is_good ? curRec->GetRecordingStatus()
993 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
994 .arg(curRec->GetInputID())
995 .arg(curRec->GetChanID())
997 .arg(is_good ? curRec->GetRecordingStatus() : RecStatus::Failed)
1000
1001 // send out REC_FINISHED message
1002 SendMythSystemRecEvent("REC_FINISHED", curRec);
1003
1004 // send out DONE_RECORDING message
1005 auto secsSince = MythDate::secsInPast(curRec->GetRecordingStartTime());
1006 QString message = QString("DONE_RECORDING %1 %2 %3")
1007 .arg(m_inputId).arg(secsSince.count()).arg(GetFramesWritten());
1008 MythEvent me2(message);
1009 gCoreContext->dispatch(me2);
1010
1011 // Handle JobQueue
1012 QHash<QString,int>::iterator autoJob =
1013 m_autoRunJobs.find(curRec->MakeUniqueKey());
1014 if (autoJob == m_autoRunJobs.end())
1015 {
1016 LOG(VB_GENERAL, LOG_INFO,
1017 "autoRunJobs not initialized until FinishedRecording()");
1019 (recgrp == "LiveTV") ? kAutoRunNone : kAutoRunProfile;
1020 InitAutoRunJobs(curRec, t, nullptr, __LINE__);
1021 autoJob = m_autoRunJobs.find(curRec->MakeUniqueKey());
1022 }
1023 LOG(VB_JOBQUEUE, LOG_INFO, QString("AutoRunJobs 0x%1").arg(*autoJob,0,16));
1024 if ((recgrp == "LiveTV") || (fsize < 1000) ||
1025 (curRec->GetRecordingStatus() != RecStatus::Recorded) ||
1026 (curRec->GetRecordingStartTime().secsTo(
1027 MythDate::current()) < 120))
1028 {
1031 }
1032 if (*autoJob != JOB_NONE)
1033 JobQueue::QueueRecordingJobs(*curRec, *autoJob);
1034 m_autoRunJobs.erase(autoJob);
1035}
1036
1037// NOLINTBEGIN(cppcoreguidelines-macro-usage)
1038#define TRANSITION(ASTATE,BSTATE) \
1039 ((m_internalState == (ASTATE)) && (m_desiredNextState == (BSTATE)))
1040#define SET_NEXT() do { nextState = m_desiredNextState; changed = true; } while(false)
1041#define SET_LAST() do { nextState = m_internalState; changed = true; } while(false)
1042// NOLINTEND(cppcoreguidelines-macro-usage)
1043
1052{
1053 TVState nextState = m_internalState;
1054
1055 bool changed = false;
1056
1057 QString transMsg = QString(" %1 to %2")
1059
1061 {
1062 LOG(VB_GENERAL, LOG_ERR, LOC +
1063 "HandleStateChange(): Null transition" + transMsg);
1064 m_changeState = false;
1065 return;
1066 }
1067
1068 // Stop EIT scanning on this input before any tuning,
1069 // to avoid race condition with it's tuning requests.
1071 {
1072 LOG(VB_EIT, LOG_INFO, LOC + QString("Stop EIT scan on input %1").arg(GetInputId()));
1073
1075 ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1077 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
1078 }
1079
1080 // Stop EIT scanning on all conflicting inputs so that
1081 // the tuner card is available for a new tuning request.
1082 // Conflicting inputs are inputs that have independent video sources
1083 // but that share a tuner card, such as a DVB-S/S2 tuner card that
1084 // connects to multiple satellites with a DiSEqC switch.
1085 if (m_scanner && !m_eitInputs.empty())
1086 {
1087 s_inputsLock.lockForRead();
1088 s_eitLock.lock();
1089 for (auto input : m_eitInputs)
1090 {
1091 auto *tv_rec = s_inputs.value(input);
1092 if (tv_rec && tv_rec->m_scanner && tv_rec->HasFlags(kFlagEITScannerRunning))
1093 {
1094 LOG(VB_EIT, LOG_INFO, LOC +
1095 QString("Stop EIT scan active on conflicting input %1")
1096 .arg(input));
1097 tv_rec->m_scanner->StopActiveScan();
1098 tv_rec->ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1099 tv_rec->TuningShutdowns(TuningRequest(kFlagNoRec));
1101 tv_rec->m_eitScanStartTime = MythDate::current().addSecs(secs.count());
1102 }
1103 }
1104 s_eitLock.unlock();
1105 s_inputsLock.unlock();
1106 }
1107
1108 // Handle different state transitions
1110 {
1112 SET_NEXT();
1113 }
1115 {
1117 SET_NEXT();
1118 }
1120 {
1121 SetPseudoLiveTVRecording(nullptr);
1122
1123 SET_NEXT();
1124 }
1126 {
1127 SetPseudoLiveTVRecording(nullptr);
1129 SET_NEXT();
1130 }
1132 {
1135 (GetFlags()&kFlagKillRec)));
1136 SET_NEXT();
1137 }
1138
1139 QString msg = (changed) ? "Changing from" : "Unknown state transition:";
1140 LOG(VB_GENERAL, LOG_INFO, LOC + msg + transMsg);
1141
1142 // update internal state variable
1143 m_internalState = nextState;
1144 m_changeState = false;
1145
1147 {
1149 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
1150 }
1151 else
1152 {
1153 m_eitScanStartTime = MythDate::current().addYears(1);
1154 }
1155}
1156#undef TRANSITION
1157#undef SET_NEXT
1158#undef SET_LAST
1159
1164{
1165 QMutexLocker lock(&m_stateChangeLock);
1166 m_desiredNextState = nextState;
1167 m_changeState = true;
1168 WakeEventLoop();
1169}
1170
1186{
1187 LOG(VB_RECORD, LOG_INFO, LOC + QString("TeardownRecorder(%1)")
1188 .arg((request_flags & kFlagKillRec) ? "kFlagKillRec" : ""));
1189
1190 m_pauseNotify = false;
1191 m_isPip = false;
1192
1194 {
1197 delete m_recorderThread;
1198 m_recorderThread = nullptr;
1199 }
1201 __FILE__, __LINE__);
1202
1203 RecordingQuality *recq = nullptr;
1204 if (m_recorder)
1205 {
1206 if (GetV4LChannel())
1207 m_channel->SetFd(-1);
1208
1210
1211 QMutexLocker locker(&m_stateChangeLock);
1212 delete m_recorder;
1213 m_recorder = nullptr;
1214 }
1215
1216 if (m_buffer)
1217 {
1218 LOG(VB_FILE, LOG_INFO, LOC + "calling StopReads()");
1220 }
1221
1222 if (m_curRecording)
1223 {
1224 if (!!(request_flags & kFlagKillRec))
1226
1228
1230 delete m_curRecording;
1231 m_curRecording = nullptr;
1232 }
1233
1234 m_pauseNotify = true;
1235
1236 if (GetDTVChannel())
1238}
1239
1241{
1242 return dynamic_cast<DTVRecorder*>(m_recorder);
1243}
1244
1246{
1247 if (m_channel &&
1248 ((m_genOpt.m_inputType == "DVB" && m_dvbOpt.m_dvbOnDemand) ||
1249 m_genOpt.m_inputType == "FREEBOX" ||
1250 m_genOpt.m_inputType == "VBOX" ||
1251 m_genOpt.m_inputType == "HDHOMERUN" ||
1252 m_genOpt.m_inputType == "EXTERNAL" ||
1254 {
1255 m_channel->Close();
1256 }
1257}
1258
1260{
1261 return dynamic_cast<DTVChannel*>(m_channel);
1262}
1263
1265{
1266#if CONFIG_V4L2
1267 return dynamic_cast<V4LChannel*>(m_channel);
1268#else
1269 return nullptr;
1270#endif // CONFIG_V4L2
1271}
1272
1273// Check if EIT is enabled for the video source connected to this input
1274static bool get_use_eit(uint inputid)
1275{
1277 query.prepare(
1278 "SELECT SUM(useeit) "
1279 "FROM videosource, capturecard "
1280 "WHERE videosource.sourceid = capturecard.sourceid AND"
1281 " capturecard.cardid = :INPUTID");
1282 query.bindValue(":INPUTID", inputid);
1283
1284 if (!query.exec() || !query.isActive())
1285 {
1286 MythDB::DBError("get_use_eit", query);
1287 return false;
1288 }
1289 if (query.next())
1290 return query.value(0).toBool();
1291 return false;
1292}
1293
1294static bool is_dishnet_eit(uint inputid)
1295{
1297 query.prepare(
1298 "SELECT SUM(dishnet_eit) "
1299 "FROM videosource, capturecard "
1300 "WHERE videosource.sourceid = capturecard.sourceid AND"
1301 " capturecard.cardid = :INPUTID");
1302 query.bindValue(":INPUTID", inputid);
1303
1304 if (!query.exec() || !query.isActive())
1305 {
1306 MythDB::DBError("is_dishnet_eit", query);
1307 return false;
1308 }
1309 if (query.next())
1310 return query.value(0).toBool();
1311 return false;
1312}
1313
1314// Highest capturecard instance number including multirec instances
1315static int get_highest_input(void)
1316{
1318 query.prepare(
1319 "SELECT MAX(cardid) "
1320 "FROM capturecard ");
1321
1322 if (!query.exec() || !query.isActive())
1323 {
1324 MythDB::DBError("highest_input", query);
1325 return -1;
1326 }
1327 if (query.next())
1328 return query.value(0).toInt();
1329 return -1;
1330}
1331
1332static std::chrono::seconds eit_start_rand(uint inputId, std::chrono::seconds eitTransportTimeout)
1333{
1334 // Randomize start time a bit
1335 auto timeout = std::chrono::seconds(MythRandom(0, eitTransportTimeout.count() / 3));
1336
1337 // Use the highest input number and the current input number
1338 // to distribute the scan start evenly over eitTransportTimeout
1339 int highest_input = get_highest_input();
1340 if (highest_input > 0)
1341 timeout += eitTransportTimeout * inputId / highest_input;
1342
1343 return timeout;
1344}
1345
1347void TVRec::run(void)
1348{
1349 QMutexLocker lock(&m_stateChangeLock);
1350 SetFlags(kFlagRunMainLoop, __FILE__, __LINE__);
1351 ClearFlags(kFlagExitPlayer | kFlagFinishRecording, __FILE__, __LINE__);
1352
1353 // Check whether we should use the EITScanner in this TVRec instance
1354 if (CardUtil::IsEITCapable(m_genOpt.m_inputType) && // Card type capable of receiving EIT?
1355 (!GetDTVChannel() || GetDTVChannel()->IsMaster()) && // Card is master and not a multirec instance
1356 (m_dvbOpt.m_dvbEitScan || get_use_eit(m_inputId))) // EIT is selected for card OR EIT is selected for video source
1357 {
1360 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
1361 }
1362 else
1363 {
1364 m_eitScanStartTime = MythDate::current().addYears(10);
1365 }
1366
1367 while (HasFlags(kFlagRunMainLoop))
1368 {
1369 // If there is a state change queued up, do it...
1370 if (m_changeState)
1371 {
1374 __FILE__, __LINE__);
1375 }
1376
1377 // Quick exit on fatal errors.
1378 if (IsErrored())
1379 {
1380 LOG(VB_GENERAL, LOG_ERR, LOC +
1381 "RunTV encountered fatal error, exiting event thread.");
1382 ClearFlags(kFlagRunMainLoop, __FILE__, __LINE__);
1383 TeardownAll();
1384 return;
1385 }
1386
1387 // Handle any tuning events.. Blindly grabbing the lock here
1388 // can sometimes cause a deadlock with Init() while it waits
1389 // to make sure this thread starts. Until a better solution
1390 // is found, don't run HandleTuning unless we can safely get
1391 // the lock.
1392 if (s_inputsLock.tryLockForRead())
1393 {
1394 HandleTuning();
1395 s_inputsLock.unlock();
1396 }
1397
1398 // Tell frontends about pending recordings
1400
1401 // If we are recording a program, check if the recording is
1402 // over or someone has asked us to finish the recording.
1403 // Add an extra 60 seconds to the recording end time if we
1404 // might want a back to back recording.
1405 QDateTime recEnd = (!m_pendingRecordings.empty()) ?
1406 m_recordEndTime.addSecs(60) : m_recordEndTime;
1407 if ((GetState() == kState_RecordingOnly) &&
1408 (MythDate::current() > recEnd ||
1410 {
1412 ClearFlags(kFlagFinishRecording, __FILE__, __LINE__);
1413 }
1414
1415 if (m_curRecording)
1416 {
1418
1419 if (m_recorder)
1420 {
1422
1423 // Check for recorder errors
1424 if (m_recorder->IsErrored())
1425 {
1427
1429 {
1430 QString message = QString("QUIT_LIVETV %1").arg(m_inputId);
1431 MythEvent me(message);
1433 }
1434 else
1435 {
1437 }
1438 }
1439 }
1440 }
1441
1442 // Check for the end of the current program..
1444 {
1445 QDateTime now = MythDate::current();
1446 bool has_finish = HasFlags(kFlagFinishRecording);
1447 bool has_rec = m_pseudoLiveTVRecording;
1448 bool enable_ui = true;
1449
1450 m_pendingRecLock.lock();
1451 bool rec_soon = m_pendingRecordings.contains(m_inputId);
1452 m_pendingRecLock.unlock();
1453
1454 if (has_rec && (has_finish || (now > m_recordEndTime)))
1455 {
1456 SetPseudoLiveTVRecording(nullptr);
1457 }
1458 else if (!has_rec && !rec_soon && m_curRecording &&
1460 {
1461 if (!m_switchingBuffer)
1462 {
1463 LOG(VB_RECORD, LOG_INFO, LOC +
1464 "Switching Buffer (" +
1465 QString("!has_rec(%1) && ").arg(has_rec) +
1466 QString("!rec_soon(%1) && (").arg(rec_soon) +
1467 MythDate::toString(now, MythDate::ISODate) + " >= " +
1469 QString("(%1) ))")
1470 .arg(now >= m_curRecording->GetScheduledEndTime()));
1471
1472 m_switchingBuffer = true;
1473
1475 false, true);
1476 }
1477 else
1478 {
1479 LOG(VB_RECORD, LOG_INFO, "Waiting for ringbuffer switch");
1480 }
1481 }
1482 else
1483 {
1484 enable_ui = false;
1485 }
1486
1487 if (enable_ui)
1488 {
1489 LOG(VB_RECORD, LOG_INFO, LOC + "Enabling Full LiveTV UI.");
1490 QString message = QString("LIVETV_WATCH %1 0").arg(m_inputId);
1491 MythEvent me(message);
1493 }
1494 }
1495
1496 // Check for ExitPlayer flag, and if set change to a non-watching
1497 // state (either kState_RecordingOnly or kState_None).
1499 {
1504 ClearFlags(kFlagExitPlayer, __FILE__, __LINE__);
1505 }
1506
1507 // Start active EIT scan
1508 bool conflicting_input = false;
1509 if (m_scanner && m_channel &&
1511 {
1513 {
1514 LOG(VB_EIT, LOG_INFO, LOC +
1515 QString("EIT scanning disabled for input %1")
1516 .arg(GetInputId()));
1517 m_eitScanStartTime = MythDate::current().addYears(10);
1518 }
1519 else if (!get_use_eit(GetInputId()))
1520 {
1521 LOG(VB_EIT, LOG_INFO, LOC +
1522 QString("EIT scanning disabled for video source %1")
1523 .arg(GetSourceID()));
1524 m_eitScanStartTime = MythDate::current().addYears(10);
1525 }
1526 else
1527 {
1528 LOG(VB_EIT, LOG_INFO, LOC +
1529 QString("EIT scanning enabled for input %1 connected to video source %2 '%3'")
1531
1532 // Check if another card in the same input group is busy recording.
1533 // This could be either a virtual DVB-device or a second tuner on a single card.
1534 s_inputsLock.lockForRead();
1535 s_eitLock.lock();
1536 bool allow_eit = true;
1537 std::vector<uint> inputids = CardUtil::GetConflictingInputs(m_inputId);
1538 InputInfo busy_input;
1539 for (uint i = 0; i < inputids.size() && allow_eit; ++i)
1540 allow_eit = !RemoteIsBusy(inputids[i], busy_input);
1541
1542 // Check if another card in the same input group is busy with an EIT scan.
1543 // We cannot start an EIT scan on this input if there is already an EIT scan
1544 // running on a conflicting real input.
1545 // Note that EIT scans never run on virtual inputs.
1546 if (allow_eit)
1547 {
1548 for (auto input : inputids)
1549 {
1550 auto *tv_rec = s_inputs.value(input);
1551 if (tv_rec && tv_rec->m_scanner)
1552 {
1553 conflicting_input = true;
1554 if (tv_rec->HasFlags(kFlagEITScannerRunning))
1555 {
1556 LOG(VB_EIT, LOG_INFO, LOC +
1557 QString("EIT scan on conflicting input %1").arg(input));
1558 allow_eit = false;
1559 busy_input.m_inputId = tv_rec->m_inputId;
1560 break;
1561 }
1562 }
1563 }
1564 }
1565
1566 if (allow_eit)
1567 {
1568 LOG(VB_EIT, LOG_INFO, LOC +
1569 QString("Start EIT active scan on input %1")
1570 .arg(m_inputId));
1572 SetFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1573 m_eitScanStartTime = MythDate::current().addYears(1);
1574 if (conflicting_input)
1576 else
1577 m_eitScanStopTime = MythDate::current().addYears(1);
1578 }
1579 else
1580 {
1581 const int seconds_postpone = 300;
1582 LOG(VB_EIT, LOG_INFO, LOC +
1583 QString("Postponing EIT scan on input %1 for %2 seconds because input %3 is busy")
1584 .arg(m_inputId).arg(seconds_postpone).arg(busy_input.m_inputId));
1585 m_eitScanStartTime = m_eitScanStartTime.addSecs(seconds_postpone);
1586 }
1587 s_eitLock.unlock();
1588 s_inputsLock.unlock();
1589 }
1590 }
1591
1592
1593 // Stop active EIT scan and allow start of the EIT scan on one of the conflicting real inputs.
1595 {
1596 LOG(VB_EIT, LOG_INFO, LOC +
1597 QString("Stop EIT scan on input %1 to allow scan on a conflicting input")
1598 .arg(GetInputId()));
1599
1601 ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1603
1605 secs += m_eitScanPeriod;
1606 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
1607 }
1608
1609 // We should be no more than a few thousand milliseconds,
1610 // as the end recording code does not have a trigger...
1611 // NOTE: If you change anything here, make sure that
1612 // WaitforEventThreadSleep() will still work...
1613 if (m_tuningRequests.empty() && !m_changeState)
1614 {
1615 lock.unlock(); // stateChangeLock
1616
1617 {
1618 QMutexLocker locker(&m_triggerEventSleepLock);
1620 m_triggerEventSleepWait.wakeAll();
1621 }
1622
1623 sched_yield();
1624
1625 {
1626 QMutexLocker locker(&m_triggerEventLoopLock);
1627 // We check triggerEventLoopSignal because it is possible
1628 // that WakeEventLoop() was called since we
1629 // unlocked the stateChangeLock
1631 {
1633 &m_triggerEventLoopLock, 1000 /* ms */);
1634 }
1636 }
1637
1638 lock.relock(); // stateChangeLock
1639 }
1640 }
1641
1642 if (GetState() != kState_None)
1643 {
1646 }
1647
1648 TeardownAll();
1649}
1650
1656bool TVRec::WaitForEventThreadSleep(bool wake, std::chrono::milliseconds time)
1657{
1658 bool ok = false;
1659 MythTimer t;
1660 t.start();
1661
1662 while (!ok && (t.elapsed() < time))
1663 {
1664 MythTimer t2;
1665 t2.start();
1666
1667 if (wake)
1668 WakeEventLoop();
1669
1670 m_stateChangeLock.unlock();
1671
1672 sched_yield();
1673
1674 {
1675 QMutexLocker locker(&m_triggerEventSleepLock);
1679 }
1680
1681 m_stateChangeLock.lock();
1682
1683 // verify that we were triggered.
1684 ok = (m_tuningRequests.empty() && !m_changeState);
1685
1686 std::chrono::milliseconds te = t2.elapsed();
1687 if (!ok && te < 10ms)
1688 std::this_thread::sleep_for(10ms - te);
1689 }
1690 return ok;
1691}
1692
1694{
1695 QMutexLocker pendlock(&m_pendingRecLock);
1696
1697 for (auto it = m_pendingRecordings.begin(); it != m_pendingRecordings.end();)
1698 {
1699 if (MythDate::current() > (*it).m_recordingStart.addSecs(30))
1700 {
1701 LOG(VB_RECORD, LOG_INFO, LOC + "Deleting stale pending recording " +
1702 QString("[%1] '%2'")
1703 .arg((*it).m_info->GetInputID())
1704 .arg((*it).m_info->GetTitle()));
1705
1706 delete (*it).m_info;
1707 it = m_pendingRecordings.erase(it);
1708 }
1709 else
1710 {
1711 it++;
1712 }
1713 }
1714
1715 if (m_pendingRecordings.empty())
1716 return;
1717
1718 // Make sure EIT scan is stopped so it does't interfere
1720 {
1721 LOG(VB_CHANNEL, LOG_INFO,
1722 LOC + "Stopping active EIT scan for pending recording.");
1724 }
1725
1726 // If we have a pending recording and AskAllowRecording
1727 // or DoNotAskAllowRecording is set and the frontend is
1728 // ready send an ASK_RECORDING query to frontend.
1729
1730 bool has_rec = false;
1731 auto it = m_pendingRecordings.begin();
1732 if ((1 == m_pendingRecordings.size()) &&
1733 (*it).m_ask &&
1734 ((*it).m_info->GetInputID() == m_inputId) &&
1736 {
1738 has_rec = m_pseudoLiveTVRecording &&
1740 (*it).m_recordingStart);
1741 }
1742
1743 for (it = m_pendingRecordings.begin(); it != m_pendingRecordings.end(); ++it)
1744 {
1745 if (!(*it).m_ask && !(*it).m_doNotAsk)
1746 continue;
1747
1748 auto timeuntil = ((*it).m_doNotAsk) ?
1749 -1s: MythDate::secsInFuture((*it).m_recordingStart);
1750
1751 if (has_rec)
1752 (*it).m_canceled = true;
1753
1754 QString query = QString("ASK_RECORDING %1 %2 %3 %4")
1755 .arg(m_inputId)
1756 .arg(timeuntil.count())
1757 .arg(has_rec ? 1 : 0)
1758 .arg((*it).m_hasLaterShowing ? 1 : 0);
1759
1760 LOG(VB_GENERAL, LOG_INFO, LOC + query);
1761
1762 QStringList msg;
1763 (*it).m_info->ToStringList(msg);
1764 MythEvent me(query, msg);
1766
1767 (*it).m_ask = (*it).m_doNotAsk = false;
1768 }
1769}
1770
1772 uint &parentid,
1773 GeneralDBOptions &gen_opts,
1774 DVBDBOptions &dvb_opts,
1775 FireWireDBOptions &firewire_opts)
1776{
1777 int testnum = 0;
1778 QString test;
1779
1781 query.prepare(
1782 "SELECT videodevice, vbidevice, audiodevice, "
1783 " audioratelimit, cardtype, "
1784 " skipbtaudio, signal_timeout, channel_timeout, "
1785 " dvb_wait_for_seqstart, "
1786 ""
1787 " dvb_on_demand, dvb_tuning_delay, dvb_eitscan,"
1788 ""
1789 " firewire_speed, firewire_model, firewire_connection, "
1790 " parentid "
1791 ""
1792 "FROM capturecard "
1793 "WHERE cardid = :INPUTID");
1794 query.bindValue(":INPUTID", inputid);
1795
1796 if (!query.exec() || !query.isActive())
1797 {
1798 MythDB::DBError("getdevices", query);
1799 return false;
1800 }
1801
1802 if (!query.next())
1803 return false;
1804
1805 // General options
1806 test = query.value(0).toString();
1807 if (!test.isEmpty())
1808 gen_opts.m_videoDev = test;
1809
1810 test = query.value(1).toString();
1811 if (!test.isEmpty())
1812 gen_opts.m_vbiDev = test;
1813
1814 test = query.value(2).toString();
1815 if (!test.isEmpty())
1816 gen_opts.m_audioDev = test;
1817
1818 gen_opts.m_audioSampleRate = std::max(testnum, query.value(3).toInt());
1819
1820 test = query.value(4).toString();
1821 if (!test.isEmpty())
1822 gen_opts.m_inputType = test;
1823
1824 gen_opts.m_skipBtAudio = query.value(5).toBool();
1825
1826 gen_opts.m_signalTimeout = (uint) std::max(query.value(6).toInt(), 0);
1827 gen_opts.m_channelTimeout = (uint) std::max(query.value(7).toInt(), 0);
1828
1829 // We should have at least 1000 ms to acquire tables...
1830 int table_timeout = ((int)gen_opts.m_channelTimeout -
1831 (int)gen_opts.m_signalTimeout);
1832 if (table_timeout < 1000)
1833 gen_opts.m_channelTimeout = gen_opts.m_signalTimeout + 1000;
1834
1835 gen_opts.m_waitForSeqstart = query.value(8).toBool();
1836
1837 // DVB options
1838 uint dvboff = 9;
1839 dvb_opts.m_dvbOnDemand = query.value(dvboff + 0).toBool();
1840 dvb_opts.m_dvbTuningDelay = std::chrono::milliseconds(query.value(dvboff + 1).toUInt());
1841 dvb_opts.m_dvbEitScan = query.value(dvboff + 2).toBool();
1842
1843 // Firewire options
1844 uint fireoff = dvboff + 3;
1845 firewire_opts.m_speed = query.value(fireoff + 0).toUInt();
1846
1847 test = query.value(fireoff + 1).toString();
1848 if (!test.isEmpty())
1849 firewire_opts.m_model = test;
1850
1851 firewire_opts.m_connection = query.value(fireoff + 2).toUInt();
1852
1853 parentid = query.value(15).toUInt();
1854
1855 return true;
1856}
1857
1858static void GetPidsToCache(DTVSignalMonitor *dtvMon, pid_cache_t &pid_cache)
1859{
1860 if (!dtvMon->GetATSCStreamData())
1861 return;
1862
1863 const MasterGuideTable *mgt = dtvMon->GetATSCStreamData()->GetCachedMGT();
1864 if (!mgt)
1865 return;
1866
1867 for (uint i = 0; i < mgt->TableCount(); ++i)
1868 {
1869 pid_cache_item_t item(mgt->TablePID(i), mgt->TableType(i));
1870 pid_cache.push_back(item);
1871 }
1872 dtvMon->GetATSCStreamData()->ReturnCachedTable(mgt);
1873}
1874
1875static bool ApplyCachedPids(DTVSignalMonitor *dtvMon, const DTVChannel* channel)
1876{
1877 pid_cache_t pid_cache;
1878 channel->GetCachedPids(pid_cache);
1879 bool vctpid_cached = false;
1880 for (const auto& pid : pid_cache)
1881 {
1882 if ((pid.GetTableID() == TableID::TVCT) ||
1883 (pid.GetTableID() == TableID::CVCT))
1884 {
1885 vctpid_cached = true;
1886 if (dtvMon->GetATSCStreamData())
1887 dtvMon->GetATSCStreamData()->AddListeningPID(pid.GetPID());
1888 }
1889 }
1890 return vctpid_cached;
1891}
1892
1909{
1910 LOG(VB_RECORD, LOG_INFO, LOC + "Setting up table monitoring.");
1911
1913 DTVChannel *dtvchan = GetDTVChannel();
1914 if (!sm || !dtvchan)
1915 {
1916 LOG(VB_GENERAL, LOG_ERR, LOC + "Setting up table monitoring.");
1917 return false;
1918 }
1919
1920 MPEGStreamData *sd = nullptr;
1921 if (GetDTVRecorder())
1922 {
1923 sd = GetDTVRecorder()->GetStreamData();
1924 sd->SetCaching(true);
1925 }
1926
1927 QString recording_type = "all";
1931 const StandardSetting *setting = profile.byName("recordingtype");
1932 if (setting)
1933 recording_type = setting->getValue();
1934
1935 const QString tuningmode = dtvchan->GetTuningMode();
1936
1937 // Check if this is an ATSC Channel
1938 int major = dtvchan->GetMajorChannel();
1939 int minor = dtvchan->GetMinorChannel();
1940 if ((minor > 0) && (tuningmode == "atsc"))
1941 {
1942 QString msg = QString("ATSC channel: %1_%2").arg(major).arg(minor);
1943 LOG(VB_RECORD, LOG_INFO, LOC + msg);
1944
1945 auto *asd = dynamic_cast<ATSCStreamData*>(sd);
1946 if (!asd)
1947 {
1948 sd = asd = new ATSCStreamData(major, minor, m_inputId);
1949 sd->SetCaching(true);
1950 if (GetDTVRecorder())
1952 }
1953
1954 asd->Reset();
1955 sm->SetStreamData(sd);
1956 sm->SetChannel(major, minor);
1957 sd->SetRecordingType(recording_type);
1958
1959 // Try to get pid of VCT from cache and
1960 // require MGT if we don't have VCT pid.
1961 if (!ApplyCachedPids(sm, dtvchan))
1963
1964 LOG(VB_RECORD, LOG_INFO, LOC +
1965 "Successfully set up ATSC table monitoring.");
1966 return true;
1967 }
1968
1969 // Check if this is an DVB channel
1970 int progNum = dtvchan->GetProgramNumber();
1971 if ((progNum >= 0) && (tuningmode == "dvb") && CardUtil::IsChannelReusable(m_genOpt.m_inputType))
1972 {
1973 int netid = dtvchan->GetOriginalNetworkID();
1974 int tsid = dtvchan->GetTransportID();
1975
1976 auto *dsd = dynamic_cast<DVBStreamData*>(sd);
1977 if (!dsd)
1978 {
1979 sd = dsd = new DVBStreamData(netid, tsid, progNum, m_inputId);
1980 sd->SetCaching(true);
1981 if (GetDTVRecorder())
1983 }
1984
1985 LOG(VB_RECORD, LOG_INFO, LOC +
1986 QString("DVB service_id %1 on net_id %2 tsid %3")
1987 .arg(progNum).arg(netid).arg(tsid));
1988
1990
1991 dsd->Reset();
1992 sm->SetStreamData(sd);
1993 sm->SetDVBService(netid, tsid, progNum);
1994 sd->SetRecordingType(recording_type);
1995
1999 sm->SetRotorTarget(1.0F);
2000
2001 if (EITscan)
2002 {
2004 sm->IgnoreEncrypted(true);
2005 }
2006
2007 LOG(VB_RECORD, LOG_INFO, LOC +
2008 "Successfully set up DVB table monitoring.");
2009 return true;
2010 }
2011
2012 // Check if this is an MPEG channel
2013 if (progNum >= 0)
2014 {
2015 if (!sd)
2016 {
2017 sd = new MPEGStreamData(progNum, m_inputId, true);
2018 sd->SetCaching(true);
2019 if (GetDTVRecorder())
2021 }
2022
2023 QString msg = QString("MPEG program number: %1").arg(progNum);
2024 LOG(VB_RECORD, LOG_INFO, LOC + msg);
2025
2027
2028 sd->Reset();
2029 sm->SetStreamData(sd);
2030 sm->SetProgramNumber(progNum);
2031 sd->SetRecordingType(recording_type);
2032
2036 sm->SetRotorTarget(1.0F);
2037
2038 if (EITscan)
2039 {
2041 sm->IgnoreEncrypted(true);
2042 }
2043
2044 LOG(VB_RECORD, LOG_INFO, LOC +
2045 "Successfully set up MPEG table monitoring.");
2046 return true;
2047 }
2048
2049 // If this is not an ATSC, DVB or MPEG channel then check to make sure
2050 // that we have permanent pidcache entries.
2051 bool ok = false;
2052 if (GetDTVChannel())
2053 {
2054 pid_cache_t pid_cache;
2055 GetDTVChannel()->GetCachedPids(pid_cache);
2056 for (auto item = pid_cache.cbegin(); !ok && item != pid_cache.cend(); ++item)
2057 ok |= item->IsPermanent();
2058 }
2059
2060 if (!ok)
2061 {
2062 QString msg = "No valid DTV info, ATSC maj(%1) min(%2), MPEG pn(%3)";
2063 LOG(VB_GENERAL, LOG_ERR, LOC + msg.arg(major).arg(minor).arg(progNum));
2064 }
2065 else
2066 {
2067 LOG(VB_RECORD, LOG_INFO, LOC +
2068 "Successfully set up raw pid monitoring.");
2069 }
2070
2071 return ok;
2072}
2073
2088bool TVRec::SetupSignalMonitor(bool tablemon, bool EITscan, bool notify)
2089{
2090 LOG(VB_RECORD, LOG_INFO, LOC + QString("SetupSignalMonitor(%1, %2)")
2091 .arg(tablemon).arg(notify));
2092
2093 // if it already exists, there no need to initialize it
2094 if (m_signalMonitor)
2095 return true;
2096
2097 // if there is no channel object we can't monitor it
2098 if (!m_channel)
2099 return false;
2100
2101 // nothing to monitor here either (DummyChannel)
2102 if (m_genOpt.m_inputType == "IMPORT" || m_genOpt.m_inputType == "DEMO")
2103 return true;
2104
2105 // make sure statics are initialized
2107
2110 m_channel, false);
2111
2112 if (m_signalMonitor)
2113 {
2114 LOG(VB_RECORD, LOG_INFO, LOC + "Signal monitor successfully created");
2115 // If this is a monitor for Digital TV, initialize table monitors
2116 if (GetDTVSignalMonitor() && tablemon &&
2117 !SetupDTVSignalMonitor(EITscan))
2118 {
2119 LOG(VB_GENERAL, LOG_ERR, LOC +
2120 "Failed to setup digital signal monitoring");
2121
2122 return false;
2123 }
2124
2130
2131 // Start the monitoring thread
2133 }
2134
2135 return true;
2136}
2137
2143{
2144 if (!m_signalMonitor)
2145 return;
2146
2147 LOG(VB_RECORD, LOG_INFO, LOC + "TeardownSignalMonitor() -- begin");
2148
2149 // If this is a DTV signal monitor, save any pids we know about.
2151 DTVChannel *dtvChan = GetDTVChannel();
2152 if (dtvMon && dtvChan)
2153 {
2154 pid_cache_t pid_cache;
2155 GetPidsToCache(dtvMon, pid_cache);
2156 if (!pid_cache.empty())
2157 dtvChan->SaveCachedPids(pid_cache);
2158 }
2159
2160 if (m_signalMonitor)
2161 {
2162 delete m_signalMonitor;
2163 m_signalMonitor = nullptr;
2164 }
2165
2166 LOG(VB_RECORD, LOG_INFO, LOC + "TeardownSignalMonitor() -- end");
2167}
2168
2180std::chrono::milliseconds TVRec::SetSignalMonitoringRate(std::chrono::milliseconds rate, int notifyFrontend)
2181{
2182 QString msg = "SetSignalMonitoringRate(%1, %2)";
2183 LOG(VB_RECORD, LOG_INFO, LOC +
2184 msg.arg(rate.count()).arg(notifyFrontend) + "-- start");
2185
2186 QMutexLocker lock(&m_stateChangeLock);
2187
2189 {
2190 LOG(VB_GENERAL, LOG_ERR, LOC +
2191 "Signal Monitoring is notsupported by your hardware.");
2192 return 0ms;
2193 }
2194
2196 {
2197 LOG(VB_GENERAL, LOG_ERR, LOC +
2198 "Signal can only be monitored in LiveTV Mode.");
2199 return 0ms;
2200 }
2201
2202 ClearFlags(kFlagRingBufferReady, __FILE__, __LINE__);
2203
2204 TuningRequest req = (rate > 0ms) ?
2207
2209
2210 // Wait for RingBuffer reset
2213 LOG(VB_RECORD, LOG_INFO, LOC +
2214 msg.arg(rate.count()).arg(notifyFrontend) + " -- end");
2215 return 1ms;
2216}
2217
2219{
2220 return dynamic_cast<DTVSignalMonitor*>(m_signalMonitor);
2221}
2222
2234bool TVRec::ShouldSwitchToAnotherInput(const QString& chanid) const
2235{
2236 QString msg("");
2238
2239 if (!query.isConnected())
2240 return false;
2241
2242 query.prepare("SELECT channel.channum, channel.callsign "
2243 "FROM channel "
2244 "WHERE channel.chanid = :CHANID");
2245 query.bindValue(":CHANID", chanid);
2246 if (!query.exec() || !query.next())
2247 {
2248 MythDB::DBError("ShouldSwitchToAnotherInput", query);
2249 return false;
2250 }
2251
2252 QString channelname = query.value(0).toString();
2253 QString callsign = query.value(1).toString();
2254
2255 query.prepare(
2256 "SELECT channel.channum "
2257 "FROM channel, capturecard "
2258 "WHERE deleted IS NULL AND "
2259 " ( channel.chanid = :CHANID OR "
2260 " ( channel.channum = :CHANNUM AND "
2261 " channel.callsign = :CALLSIGN ) "
2262 " ) AND "
2263 " channel.sourceid = capturecard.sourceid AND "
2264 " capturecard.cardid = :INPUTID");
2265 query.bindValue(":CHANID", chanid);
2266 query.bindValue(":CHANNUM", channelname);
2267 query.bindValue(":CALLSIGN", callsign);
2268 query.bindValue(":INPUTID", m_inputId);
2269
2270 if (!query.exec() || !query.isActive())
2271 {
2272 MythDB::DBError("ShouldSwitchToAnotherInput", query);
2273 }
2274 else if (query.size() > 0)
2275 {
2276 msg = "Found channel (%1) on current input[%2].";
2277 LOG(VB_RECORD, LOG_INFO, LOC + msg.arg(channelname).arg(m_inputId));
2278 return false;
2279 }
2280
2281 // We didn't find it on the current input, so now we check other inputs.
2282 query.prepare(
2283 "SELECT channel.channum, capturecard.cardid "
2284 "FROM channel, capturecard "
2285 "WHERE deleted IS NULL AND "
2286 " ( channel.chanid = :CHANID OR "
2287 " ( channel.channum = :CHANNUM AND "
2288 " channel.callsign = :CALLSIGN ) "
2289 " ) AND "
2290 " channel.sourceid = capturecard.sourceid AND "
2291 " capturecard.cardid != :INPUTID");
2292 query.bindValue(":CHANID", chanid);
2293 query.bindValue(":CHANNUM", channelname);
2294 query.bindValue(":CALLSIGN", callsign);
2295 query.bindValue(":INPUTID", m_inputId);
2296
2297 if (!query.exec() || !query.isActive())
2298 {
2299 MythDB::DBError("ShouldSwitchToAnotherInput", query);
2300 }
2301 else if (query.next())
2302 {
2303 msg = QString("Found channel (%1) on different input(%2).")
2304 .arg(query.value(0).toString(), query.value(1).toString());
2305 LOG(VB_RECORD, LOG_INFO, LOC + msg);
2306 return true;
2307 }
2308
2309 msg = QString("Did not find channel(%1) on any input.").arg(channelname);
2310 LOG(VB_RECORD, LOG_ERR, LOC + msg);
2311 return false;
2312}
2313
2324bool TVRec::CheckChannel(const QString& name) const
2325{
2326 if (!m_channel)
2327 return false;
2328
2329 return m_channel->CheckChannel(name);
2330}
2331
2335static QString add_spacer(const QString &channel, const QString &spacer)
2336{
2337 QString chan = channel;
2338 if ((chan.length() >= 2) && !spacer.isEmpty())
2339 return chan.left(chan.length()-1) + spacer + chan.right(1);
2340 return chan;
2341}
2342
2371 uint &complete_valid_channel_on_rec,
2372 bool &is_extra_char_useful,
2373 QString &needed_spacer) const
2374{
2375#if DEBUG_CHANNEL_PREFIX
2376 LOG(VB_GENERAL, LOG_DEBUG, QString("CheckChannelPrefix(%1)").arg(prefix));
2377#endif
2378
2379 static const std::array<const QString,5> s_spacers = { "", "_", "-", "#", "." };
2380
2382 QString basequery = QString(
2383 "SELECT channel.chanid, channel.channum, capturecard.cardid "
2384 "FROM channel, capturecard "
2385 "WHERE deleted IS NULL AND "
2386 " channel.channum LIKE '%1%' AND "
2387 " channel.sourceid = capturecard.sourceid");
2388
2389 const std::array<const QString,2> inputquery
2390 {
2391 QString(" AND capturecard.cardid = '%1'").arg(m_inputId),
2392 QString(" AND capturecard.cardid != '%1'").arg(m_inputId),
2393 };
2394
2395 std::vector<unsigned int> fchanid;
2396 std::vector<QString> fchannum;
2397 std::vector<unsigned int> finputid;
2398 std::vector<QString> fspacer;
2399
2400 for (const auto & str : inputquery)
2401 {
2402 for (const auto & spacer : s_spacers)
2403 {
2404 QString qprefix = add_spacer(
2405 prefix, (spacer == "_") ? "\\_" : spacer);
2406 query.prepare(basequery.arg(qprefix) + str);
2407
2408 if (!query.exec() || !query.isActive())
2409 {
2410 MythDB::DBError("checkchannel -- locate channum", query);
2411 }
2412 else if (query.size())
2413 {
2414 while (query.next())
2415 {
2416 fchanid.push_back(query.value(0).toUInt());
2417 fchannum.push_back(query.value(1).toString());
2418 finputid.push_back(query.value(2).toUInt());
2419 fspacer.emplace_back(spacer);
2420#if DEBUG_CHANNEL_PREFIX
2421 LOG(VB_GENERAL, LOG_DEBUG,
2422 QString("(%1,%2) Adding %3 rec %4")
2423 .arg(i).arg(j).arg(query.value(1).toString(),6)
2424 .arg(query.value(2).toUInt()));
2425#endif
2426 }
2427 }
2428
2429 if (prefix.length() < 2)
2430 break;
2431 }
2432 }
2433
2434 // Now process the lists for the info we need...
2435 is_extra_char_useful = false;
2436 complete_valid_channel_on_rec = 0;
2437 needed_spacer.clear();
2438
2439 if (fchanid.empty())
2440 return false;
2441
2442 if (fchanid.size() == 1) // Unique channel...
2443 {
2444 needed_spacer = fspacer[0];
2445 bool nc = (fchannum[0] != add_spacer(prefix, fspacer[0]));
2446
2447 complete_valid_channel_on_rec = (nc) ? 0 : finputid[0];
2448 is_extra_char_useful = nc;
2449 return true;
2450 }
2451
2452 // If we get this far there is more than one channel
2453 // sharing the prefix we were given.
2454
2455 // Is an extra characher useful for disambiguation?
2456 is_extra_char_useful = false;
2457 for (uint i = 0; (i < fchannum.size()) && !is_extra_char_useful; i++)
2458 {
2459 is_extra_char_useful = (fchannum[i] != add_spacer(prefix, fspacer[i]));
2460#if DEBUG_CHANNEL_PREFIX
2461 LOG(VB_GENERAL, LOG_DEBUG, QString("is_extra_char_useful(%1!=%2): %3")
2462 .arg(fchannum[i]).arg(add_spacer(prefix, fspacer[i]))
2463 .arg(is_extra_char_useful));
2464#endif
2465 }
2466
2467 // Are any of the channels complete w/o spacer?
2468 // If so set complete_valid_channel_on_rec,
2469 // with a preference for our inputid.
2470 for (size_t i = 0; i < fchannum.size(); i++)
2471 {
2472 if (fchannum[i] == prefix)
2473 {
2474 complete_valid_channel_on_rec = finputid[i];
2475 if (finputid[i] == m_inputId)
2476 break;
2477 }
2478 }
2479
2480 if (complete_valid_channel_on_rec != 0)
2481 return true;
2482
2483 // Add a spacer, if one is needed to select a valid channel.
2484 bool spacer_needed = true;
2485 for (uint i = 0; (i < fspacer.size() && spacer_needed); i++)
2486 spacer_needed = !fspacer[i].isEmpty();
2487 if (spacer_needed)
2488 needed_spacer = fspacer[0];
2489
2490 // If it isn't useful to wait for more characters,
2491 // then try to commit to any true match immediately.
2492 for (size_t i = 0; i < ((is_extra_char_useful) ? 0 : fchanid.size()); i++)
2493 {
2494 if (fchannum[i] == add_spacer(prefix, fspacer[i]))
2495 {
2496 needed_spacer = fspacer[i];
2497 complete_valid_channel_on_rec = finputid[i];
2498 return true;
2499 }
2500 }
2501
2502 return true;
2503}
2504
2506 const QString &channum)
2507{
2508 if (!m_recorder)
2509 return false;
2510
2511 QString videoFilters = ChannelUtil::GetVideoFilters(sourceid, channum);
2512 if (!videoFilters.isEmpty())
2513 {
2514 m_recorder->SetVideoFilters(videoFilters);
2515 return true;
2516 }
2517
2518 return false;
2519}
2520
2526{
2527 return ((m_recorder && m_recorder->IsRecording()) ||
2529}
2530
2536bool TVRec::IsBusy(InputInfo *busy_input, std::chrono::seconds time_buffer) const
2537{
2538 InputInfo dummy;
2539 if (!busy_input)
2540 busy_input = &dummy;
2541
2542 busy_input->Clear();
2543
2544 if (!m_channel)
2545 return false;
2546
2547 if (!m_channel->GetInputID())
2548 return false;
2549
2550 uint chanid = 0;
2551
2552 if (GetState() != kState_None)
2553 {
2554 busy_input->m_inputId = m_channel->GetInputID();
2555 chanid = m_channel->GetChanID();
2556 }
2557
2558 PendingInfo pendinfo;
2559 bool has_pending = false;
2560 {
2561 m_pendingRecLock.lock();
2562 PendingMap::const_iterator it = m_pendingRecordings.find(m_inputId);
2563 has_pending = (it != m_pendingRecordings.end());
2564 if (has_pending)
2565 pendinfo = *it;
2566 m_pendingRecLock.unlock();
2567 }
2568
2569 if (!busy_input->m_inputId && has_pending)
2570 {
2571 auto timeLeft = MythDate::secsInFuture(pendinfo.m_recordingStart);
2572
2573 if (timeLeft <= time_buffer)
2574 {
2575 QString channum;
2576 QString input;
2577 if (pendinfo.m_info->QueryTuningInfo(channum, input))
2578 {
2579 busy_input->m_inputId = m_channel->GetInputID();
2580 chanid = pendinfo.m_info->GetChanID();
2581 }
2582 }
2583 }
2584
2585 if (busy_input->m_inputId)
2586 {
2587 CardUtil::GetInputInfo(*busy_input);
2588 busy_input->m_chanId = chanid;
2589 busy_input->m_mplexId = ChannelUtil::GetMplexID(busy_input->m_chanId);
2590 busy_input->m_mplexId =
2591 (32767 == busy_input->m_mplexId) ? 0 : busy_input->m_mplexId;
2592 }
2593
2594 return busy_input->m_inputId != 0U;
2595}
2596
2597
2605{
2606 QMutexLocker lock(&m_stateChangeLock);
2607
2608 if (m_recorder)
2609 return m_recorder->GetFrameRate();
2610 return -1.0F;
2611}
2612
2620{
2621 QMutexLocker lock(&m_stateChangeLock);
2622
2623 if (m_recorder)
2624 return m_recorder->GetFramesWritten();
2625 return -1;
2626}
2627
2635{
2636 QMutexLocker lock(&m_stateChangeLock);
2637
2638 if (m_buffer)
2639 return m_buffer->GetWritePosition();
2640 return -1;
2641}
2642
2650int64_t TVRec::GetKeyframePosition(uint64_t desired) const
2651{
2652 QMutexLocker lock(&m_stateChangeLock);
2653
2654 if (m_recorder)
2655 return m_recorder->GetKeyframePosition(desired);
2656 return -1;
2657}
2658
2668 int64_t start, int64_t end, frm_pos_map_t &map) const
2669{
2670 QMutexLocker lock(&m_stateChangeLock);
2671
2672 if (m_recorder)
2673 return m_recorder->GetKeyframePositions(start, end, map);
2674
2675 return false;
2676}
2677
2679 int64_t start, int64_t end, frm_pos_map_t &map) const
2680{
2681 QMutexLocker lock(&m_stateChangeLock);
2682
2683 if (m_recorder)
2684 return m_recorder->GetKeyframeDurations(start, end, map);
2685
2686 return false;
2687}
2688
2694long long TVRec::GetMaxBitrate(void) const
2695{
2696 long long bitrate = 0;
2697 if (m_genOpt.m_inputType == "MPEG")
2698 { // NOLINT(bugprone-branch-clone)
2699 bitrate = 10080000LL; // use DVD max bit rate
2700 }
2701 else if (m_genOpt.m_inputType == "HDPVR")
2702 {
2703 bitrate = 20200000LL; // Peak bit rate for HD-PVR
2704 }
2706 {
2707 bitrate = 22200000LL; // 1080i
2708 }
2709 else // frame grabber
2710 {
2711 bitrate = 10080000LL; // use DVD max bit rate, probably too big
2712 }
2713
2714 return bitrate;
2715}
2716
2722void TVRec::SpawnLiveTV(LiveTVChain *newchain, bool pip, QString startchan)
2723{
2724 QMutexLocker lock(&m_stateChangeLock);
2725
2726 m_tvChain = newchain;
2727 m_tvChain->IncrRef(); // mark it for TVRec use
2729
2730 QString hostprefix = MythCoreContext::GenMythURL(
2733
2734 m_tvChain->SetHostPrefix(hostprefix);
2736
2737 m_isPip = pip;
2738 m_liveTVStartChannel = std::move(startchan);
2739
2740 // Change to WatchingLiveTV
2742 // Wait for state change to take effect
2744
2745 // Make sure StartRecording can't steal our tuner
2746 SetFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2747}
2748
2753{
2754 if (m_tvChain)
2755 return m_tvChain->GetID();
2756 return "";
2757}
2758
2768{
2769 QMutexLocker lock(&m_stateChangeLock);
2770
2772 return; // already stopped
2773
2774 if (!m_curRecording)
2775 return;
2776
2777 const QString recgrp = m_curRecording->QueryRecordingGroup();
2779
2780 if (recgrp != "LiveTV" && !m_pseudoLiveTVRecording)
2781 {
2782 // User wants this recording to continue
2784 }
2785 else if (recgrp == "LiveTV" && m_pseudoLiveTVRecording)
2786 {
2787 // User wants to abandon scheduled recording
2788 SetPseudoLiveTVRecording(nullptr);
2789 }
2790}
2791
2802{
2803 if (!m_channel)
2804 return;
2805
2806 // Notify scheduler of the recording.
2807 // + set up recording so it can be resumed
2808 rec->SetInputID(m_inputId);
2810
2811 if (rec->GetRecordingRuleType() == kNotRecording)
2812 {
2815 }
2816
2817 // + remove any end offset which would mismatch the live session
2818 rec->GetRecordingRule()->m_endOffset = 0;
2819
2820 // + save RecStatus::Inactive recstatus to so that a reschedule call
2821 // doesn't start recording this on another input before we
2822 // send the SCHEDULER_ADD_RECORDING message to the scheduler.
2824 rec->AddHistory(false);
2825
2826 // + save RecordingRule so that we get a recordid
2827 // (don't allow RescheduleMatch(), avoiding unneeded reschedule)
2828 rec->GetRecordingRule()->Save(false);
2829
2830 // + save recordid to recorded entry
2831 rec->ApplyRecordRecID();
2832
2833 // + set proper recstatus (saved later)
2835
2836 // + pass proginfo to scheduler and reschedule
2837 QStringList prog;
2838 rec->ToStringList(prog);
2839 MythEvent me("SCHEDULER_ADD_RECORDING", prog);
2841
2842 // Allow scheduler to end this recording before post-roll,
2843 // if it has another recording for this recorder.
2844 ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2845}
2846
2848 RecordingProfile *recpro, int line)
2849{
2850 if (kAutoRunProfile == t)
2851 {
2853 if (!recpro)
2854 {
2855 LoadProfile(nullptr, rec, profile);
2856 recpro = &profile;
2857 }
2859 init_jobs(rec, *recpro, m_runJobOnHostOnly,
2861 }
2862 else
2863 {
2865 }
2866 LOG(VB_JOBQUEUE, LOG_INFO,
2867 QString("InitAutoRunJobs for %1, line %2 -> 0x%3")
2868 .arg(rec->MakeUniqueKey()).arg(line)
2869 .arg(m_autoRunJobs[rec->MakeUniqueKey()],0,16));
2870}
2871
2883void TVRec::SetLiveRecording([[maybe_unused]] int recording)
2884{
2885 LOG(VB_GENERAL, LOG_INFO, LOC +
2886 QString("SetLiveRecording(%1)").arg(recording));
2887 QMutexLocker locker(&m_stateChangeLock);
2888
2890 bool was_rec = m_pseudoLiveTVRecording;
2892 if (was_rec && !m_pseudoLiveTVRecording)
2893 {
2894 LOG(VB_GENERAL, LOG_INFO, LOC + "SetLiveRecording() -- cancel");
2895 // cancel -- 'recording' should be 0 or -1
2896 SetFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2898 InitAutoRunJobs(m_curRecording, kAutoRunNone, nullptr, __LINE__);
2899 }
2900 else if (!was_rec && m_pseudoLiveTVRecording)
2901 {
2902 LOG(VB_GENERAL, LOG_INFO, LOC + "SetLiveRecording() -- record");
2903 // record -- 'recording' should be 1 or -1
2904
2905 // If the last recording was flagged for keeping
2906 // in the frontend, then add the recording rule
2907 // so that transcode, commfrag, etc can be run.
2912 InitAutoRunJobs(m_curRecording, kAutoRunProfile, nullptr, __LINE__);
2913 }
2914
2915 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
2916 .arg(m_curRecording->GetInputID())
2917 .arg(m_curRecording->GetChanID())
2919 .arg(recstat)
2921
2923}
2924
2930{
2931 QMutexLocker lock(&m_stateChangeLock);
2932 LOG(VB_RECORD, LOG_INFO, LOC +
2933 QString("StopLiveTV(void) curRec: 0x%1 pseudoRec: 0x%2")
2934 .arg((uint64_t)m_curRecording,0,16)
2935 .arg((uint64_t)m_pseudoLiveTVRecording,0,16));
2936
2938 return;
2939
2940 bool hadPseudoLiveTVRec = m_pseudoLiveTVRecording;
2942
2943 if (!hadPseudoLiveTVRec && m_pseudoLiveTVRecording)
2945
2946 // Figure out next state and if needed recording end time.
2947 TVState next_state = kState_None;
2949 {
2951 next_state = kState_RecordingOnly;
2952 }
2953
2954 // Change to the appropriate state
2955 ChangeState(next_state);
2956
2957 // Wait for state change to take effect...
2959
2960 // We are done with the tvchain...
2961 if (m_tvChain)
2962 {
2963 m_tvChain->DecrRef();
2964 }
2965 m_tvChain = nullptr;
2966}
2967
2977{
2978 QMutexLocker lock(&m_stateChangeLock);
2979
2980 if (!m_recorder)
2981 {
2982 LOG(VB_GENERAL, LOG_ERR, LOC +
2983 "PauseRecorder() called with no recorder");
2984 return;
2985 }
2986
2987 m_recorder->Pause();
2988}
2989
2996{
2997 if (m_pauseNotify)
2998 WakeEventLoop();
2999}
3000
3004void TVRec::ToggleChannelFavorite(const QString& changroupname)
3005{
3006 QMutexLocker lock(&m_stateChangeLock);
3007
3008 if (!m_channel)
3009 return;
3010
3011 // Get current channel id...
3012 uint sourceid = m_channel->GetSourceID();
3013 QString channum = m_channel->GetChannelName();
3014 uint chanid = ChannelUtil::GetChanID(sourceid, channum);
3015
3016 if (!chanid)
3017 {
3018 LOG(VB_GENERAL, LOG_ERR, LOC +
3019 QString("Channel: \'%1\' was not found in the database.\n"
3020 "\t\tMost likely, the 'starting channel' for this "
3021 "Input Connection is invalid.\n"
3022 "\t\tCould not toggle favorite.").arg(channum));
3023 return;
3024 }
3025
3026 int changrpid = ChannelGroup::GetChannelGroupId(changroupname);
3027 if (changrpid <1)
3028 {
3029 LOG(VB_RECORD, LOG_ERR, LOC +
3030 QString("ToggleChannelFavorite: Invalid channel group name %1,")
3031 .arg(changroupname));
3032 }
3033 else
3034 {
3035 bool result = ChannelGroup::ToggleChannel(chanid, changrpid, true);
3036
3037 if (!result)
3038 LOG(VB_RECORD, LOG_ERR, LOC + "Unable to toggle channel favorite.");
3039 else
3040 {
3041 LOG(VB_RECORD, LOG_INFO, LOC +
3042 QString("Toggled channel favorite.channum %1, chan group %2")
3043 .arg(channum, changroupname));
3044 }
3045 }
3046}
3047
3054{
3055 QMutexLocker lock(&m_stateChangeLock);
3056 if (!m_channel)
3057 return -1;
3058
3059 int ret = m_channel->GetPictureAttribute(attr);
3060
3061 return (ret < 0) ? -1 : ret / 655;
3062}
3063
3072 PictureAttribute attr,
3073 bool direction)
3074{
3075 QMutexLocker lock(&m_stateChangeLock);
3076 if (!m_channel)
3077 return -1;
3078
3079 int ret = m_channel->ChangePictureAttribute(type, attr, direction);
3080
3081 return (ret < 0) ? -1 : ret / 655;
3082}
3083
3087QString TVRec::GetInput(void) const
3088{
3089 if (m_channel)
3090 return m_channel->GetInputName();
3091 return {};
3092}
3093
3098{
3099 if (m_channel)
3100 return m_channel->GetSourceID();
3101 return 0;
3102}
3103
3112QString TVRec::SetInput(QString input)
3113{
3114 QMutexLocker lock(&m_stateChangeLock);
3115 QString origIn = input;
3116 LOG(VB_RECORD, LOG_INFO, LOC + "SetInput(" + input + ") -- begin");
3117
3118 if (!m_channel)
3119 {
3120 LOG(VB_RECORD, LOG_INFO, LOC + "SetInput() -- end no channel class");
3121 return {};
3122 }
3123
3124 LOG(VB_RECORD, LOG_INFO, LOC + "SetInput(" + origIn + ":" + input +
3125 ") -- end nothing to do");
3126 return input;
3127}
3128
3138void TVRec::SetChannel(const QString& name, uint requestType)
3139{
3140 QMutexLocker locker1(&m_setChannelLock);
3141 QMutexLocker locker2(&m_stateChangeLock);
3142
3143 LOG(VB_CHANNEL, LOG_INFO, LOC +
3144 QString("SetChannel(%1) -- begin").arg(name));
3145
3146 // Detect tuning request type if needed
3147 if (requestType & kFlagDetect)
3148 {
3150 requestType = m_lastTuningRequest.m_flags & (kFlagRec | kFlagNoRec);
3151 }
3152
3153 // Clear the RingBuffer reset flag, in case we wait for a reset below
3154 ClearFlags(kFlagRingBufferReady, __FILE__, __LINE__);
3155
3156 // Clear out any EITScan channel change requests
3157 auto it = m_tuningRequests.begin();
3158 while (it != m_tuningRequests.end())
3159 {
3160 if ((*it).m_flags & kFlagEITScan)
3161 it = m_tuningRequests.erase(it);
3162 else
3163 ++it;
3164 }
3165
3166 // Actually add the tuning request to the queue, and
3167 // then wait for it to start tuning
3168 m_tuningRequests.enqueue(TuningRequest(requestType, name));
3170
3171 // If we are using a recorder, wait for a RingBuffer reset
3172 if (requestType & kFlagRec)
3173 {
3176 }
3177 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("SetChannel(%1) -- end").arg(name));
3178}
3179
3187bool TVRec::QueueEITChannelChange(const QString &name)
3188{
3189 LOG(VB_CHANNEL, LOG_INFO, LOC +
3190 QString("QueueEITChannelChange(%1)").arg(name));
3191
3192 bool ok = false;
3193 if (m_setChannelLock.tryLock())
3194 {
3195 if (m_stateChangeLock.tryLock())
3196 {
3197 if (m_tuningRequests.empty())
3198 {
3200 ok = true;
3201 }
3202 m_stateChangeLock.unlock();
3203 }
3204 m_setChannelLock.unlock();
3205 }
3206
3207 LOG(VB_CHANNEL, LOG_DEBUG, LOC +
3208 QString("QueueEITChannelChange(%1) %2")
3209 .arg(name, ok ? "done" : "failed"));
3210
3211 return ok;
3212}
3213
3215 QString &title, QString &subtitle,
3216 QString &desc, QString &category,
3217 QString &starttime, QString &endtime,
3218 QString &callsign, QString &iconpath,
3219 QString &channum, uint &sourceChanid,
3220 QString &seriesid, QString &programid)
3221{
3222 QString compare = "<=";
3223 QString sortorder = "desc";
3224 uint chanid = 0;
3225
3226 if (sourceChanid)
3227 {
3228 chanid = sourceChanid;
3229
3230 if (BROWSE_UP == direction)
3232 else if (BROWSE_DOWN == direction)
3234 else if (BROWSE_FAVORITE == direction)
3235 {
3236 chanid = m_channel->GetNextChannel(
3238 }
3239 else if (BROWSE_LEFT == direction)
3240 {
3241 compare = "<";
3242 }
3243 else if (BROWSE_RIGHT == direction)
3244 {
3245 compare = ">";
3246 sortorder = "asc";
3247 }
3248 }
3249
3250 if (!chanid)
3251 {
3252 if (BROWSE_SAME == direction)
3254 else if (BROWSE_UP == direction)
3255 chanid = m_channel->GetNextChannel(channum, CHANNEL_DIRECTION_UP);
3256 else if (BROWSE_DOWN == direction)
3258 else if (BROWSE_FAVORITE == direction)
3259 {
3260 chanid = m_channel->GetNextChannel(channum,
3262 }
3263 else if (BROWSE_LEFT == direction)
3264 {
3266 compare = "<";
3267 }
3268 else if (BROWSE_RIGHT == direction)
3269 {
3271 compare = ">";
3272 sortorder = "asc";
3273 }
3274 }
3275
3276 QString querystr = QString(
3277 "SELECT title, subtitle, description, category, "
3278 " starttime, endtime, callsign, icon, "
3279 " channum, seriesid, programid "
3280 "FROM program, channel "
3281 "WHERE program.chanid = channel.chanid AND "
3282 " channel.chanid = :CHANID AND "
3283 " starttime %1 :STARTTIME "
3284 "ORDER BY starttime %2 "
3285 "LIMIT 1").arg(compare, sortorder);
3286
3288 query.prepare(querystr);
3289 query.bindValue(":CHANID", chanid);
3290 query.bindValue(":STARTTIME", starttime);
3291
3292 // Clear everything now in case either query fails.
3293 title = subtitle = desc = category = "";
3294 starttime = endtime = callsign = iconpath = "";
3295 channum = seriesid = programid = "";
3296 sourceChanid = 0;
3297
3298 // Try to get the program info
3299 if (!query.exec() && !query.isActive())
3300 {
3301 MythDB::DBError("GetNextProgram -- get program info", query);
3302 }
3303 else if (query.next())
3304 {
3305 title = query.value(0).toString();
3306 subtitle = query.value(1).toString();
3307 desc = query.value(2).toString();
3308 category = query.value(3).toString();
3309 starttime = query.value(4).toString();
3310 endtime = query.value(5).toString();
3311 callsign = query.value(6).toString();
3312 iconpath = query.value(7).toString();
3313 channum = query.value(8).toString();
3314 seriesid = query.value(9).toString();
3315 programid = query.value(10).toString();
3316 sourceChanid = chanid;
3317 return;
3318 }
3319
3320 // Couldn't get program info, so get the channel info instead
3321 query.prepare(
3322 "SELECT channum, callsign, icon "
3323 "FROM channel "
3324 "WHERE chanid = :CHANID");
3325 query.bindValue(":CHANID", chanid);
3326
3327 if (!query.exec() || !query.isActive())
3328 {
3329 MythDB::DBError("GetNextProgram -- get channel info", query);
3330 }
3331 else if (query.next())
3332 {
3333 sourceChanid = chanid;
3334 channum = query.value(0).toString();
3335 callsign = query.value(1).toString();
3336 iconpath = query.value(2).toString();
3337 }
3338}
3339
3340bool TVRec::GetChannelInfo(uint &chanid, uint &sourceid,
3341 QString &callsign, QString &channum,
3342 QString &channame, QString &xmltvid) const
3343{
3344 callsign.clear();
3345 channum.clear();
3346 channame.clear();
3347 xmltvid.clear();
3348
3349 if ((!chanid || !sourceid) && !m_channel)
3350 return false;
3351
3352 if (!chanid)
3353 chanid = (uint) std::max(m_channel->GetChanID(), 0);
3354
3355 if (!sourceid)
3356 sourceid = m_channel->GetSourceID();
3357
3359 query.prepare(
3360 "SELECT callsign, channum, name, xmltvid "
3361 "FROM channel "
3362 "WHERE chanid = :CHANID");
3363 query.bindValue(":CHANID", chanid);
3364 if (!query.exec() || !query.isActive())
3365 {
3366 MythDB::DBError("GetChannelInfo", query);
3367 return false;
3368 }
3369
3370 if (!query.next())
3371 return false;
3372
3373 callsign = query.value(0).toString();
3374 channum = query.value(1).toString();
3375 channame = query.value(2).toString();
3376 xmltvid = query.value(3).toString();
3377
3378 return true;
3379}
3380
3381bool TVRec::SetChannelInfo(uint chanid, uint sourceid,
3382 const QString& oldchannum,
3383 const QString& callsign, const QString& channum,
3384 const QString& channame, const QString& xmltvid)
3385{
3386 if (!chanid || !sourceid || channum.isEmpty())
3387 return false;
3388
3390 query.prepare(
3391 "UPDATE channel "
3392 "SET callsign = :CALLSIGN, "
3393 " channum = :CHANNUM, "
3394 " name = :CHANNAME, "
3395 " xmltvid = :XMLTVID "
3396 "WHERE chanid = :CHANID AND "
3397 " sourceid = :SOURCEID");
3398 query.bindValue(":CALLSIGN", callsign);
3399 query.bindValue(":CHANNUM", channum);
3400 query.bindValue(":CHANNAME", channame);
3401 query.bindValue(":XMLTVID", xmltvid);
3402 query.bindValue(":CHANID", chanid);
3403 query.bindValue(":SOURCEID", sourceid);
3404
3405 if (!query.exec())
3406 {
3407 MythDB::DBError("SetChannelInfo", query);
3408 return false;
3409 }
3410
3411 if (m_channel)
3412 m_channel->Renumber(sourceid, oldchannum, channum);
3413
3414 return true;
3415}
3416
3417void TVRec::SetChannelTimeout(std::chrono::milliseconds timeout)
3418{
3420 LOG(VB_CHANNEL, LOG_INFO, LOC +
3421 QString("Override tune timeout: %1ms")
3423}
3424
3429{
3430 QMutexLocker lock(&m_stateChangeLock);
3431
3432 MythMediaBuffer *oldbuffer = m_buffer;
3433 m_buffer = Buffer;
3434
3435 if (oldbuffer && (oldbuffer != Buffer))
3436 {
3438 ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3439 delete oldbuffer;
3440 }
3441
3442 m_switchingBuffer = false;
3443}
3444
3446{
3447 LOG(VB_GENERAL, LOG_INFO, LOC + "RingBufferChanged()");
3448
3449 QMutexLocker lock(&m_stateChangeLock);
3450
3451 if (pginfo)
3452 {
3453 if (m_curRecording)
3454 {
3457 delete m_curRecording;
3458 }
3460 m_curRecording = new RecordingInfo(*pginfo);
3463 }
3464
3466}
3467
3469 QString &input) const
3470{
3471 QString channum;
3472
3473 if (request.m_program)
3474 {
3475 request.m_program->QueryTuningInfo(channum, input);
3476 return channum;
3477 }
3478
3479 channum = request.m_channel;
3480 input = request.m_input;
3481
3482 // If this is Live TV startup, we need a channel...
3483 if (channum.isEmpty() && (request.m_flags & kFlagLiveTV))
3484 {
3485 if (!m_liveTVStartChannel.isEmpty())
3486 channum = m_liveTVStartChannel;
3487 else
3488 {
3491 }
3492 }
3493 if (request.m_flags & kFlagLiveTV)
3494 m_channel->Init(channum, false);
3495
3496 if (m_channel && !channum.isEmpty() && (channum.indexOf("NextChannel") >= 0))
3497 {
3498 // FIXME This is just horrible
3499 int dir = channum.right(channum.length() - 12).toInt();
3500 uint chanid = m_channel->GetNextChannel(0, static_cast<ChannelChangeDirection>(dir));
3501 channum = ChannelUtil::GetChanNum(chanid);
3502 }
3503
3504 return channum;
3505}
3506
3508{
3509 if ((request.m_flags & kFlagAntennaAdjust) || request.m_input.isEmpty() ||
3511 {
3512 return false;
3513 }
3514
3515 uint sourceid = m_channel->GetSourceID();
3516 QString oldchannum = m_channel->GetChannelName();
3517 QString newchannum = request.m_channel;
3518
3519 if (ChannelUtil::IsOnSameMultiplex(sourceid, newchannum, oldchannum))
3520 {
3522 auto *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
3523
3524 if (atsc)
3525 {
3526 uint major = 0;
3527 uint minor = 0;
3528 ChannelUtil::GetATSCChannel(sourceid, newchannum, major, minor);
3529
3530 if (minor && atsc->HasChannel(major, minor))
3531 {
3532 request.m_majorChan = major;
3533 request.m_minorChan = minor;
3534 return true;
3535 }
3536 }
3537
3538 if (mpeg)
3539 {
3540 uint progNum = ChannelUtil::GetProgramNumber(sourceid, newchannum);
3541 if (mpeg->HasProgram(progNum))
3542 {
3543 request.m_progNum = progNum;
3544 return true;
3545 }
3546 }
3547 }
3548
3549 return false;
3550}
3551
3560{
3561 if (!m_tuningRequests.empty())
3562 {
3563 TuningRequest request = m_tuningRequests.front();
3564 LOG(VB_RECORD, LOG_INFO, LOC +
3565 "HandleTuning Request: " + request.toString());
3566
3567 QString input;
3568 request.m_channel = TuningGetChanNum(request, input);
3569 request.m_input = input;
3570
3571 if (TuningOnSameMultiplex(request))
3572 LOG(VB_CHANNEL, LOG_INFO, LOC + "On same multiplex");
3573
3574 TuningShutdowns(request);
3575
3576 // The dequeue isn't safe to do until now because we
3577 // release the stateChangeLock to teardown a recorder
3579
3580 // Now we start new stuff
3581 if (request.m_flags & (kFlagRecording|kFlagLiveTV|
3583 {
3584 if (!m_recorder)
3585 {
3586 LOG(VB_RECORD, LOG_INFO, LOC +
3587 "No recorder yet, calling TuningFrequency");
3588 TuningFrequency(request);
3589 }
3590 else
3591 {
3592 LOG(VB_RECORD, LOG_INFO, LOC + "Waiting for recorder pause..");
3593 SetFlags(kFlagWaitingForRecPause, __FILE__, __LINE__);
3594 }
3595 }
3596 m_lastTuningRequest = request;
3597 }
3598
3600 {
3601 if (!m_recorder || !m_recorder->IsPaused())
3602 return;
3603
3604 ClearFlags(kFlagWaitingForRecPause, __FILE__, __LINE__);
3605 LOG(VB_RECORD, LOG_INFO, LOC +
3606 "Recorder paused, calling TuningFrequency");
3608 }
3609
3610 MPEGStreamData *streamData = nullptr;
3612 {
3613 streamData = TuningSignalCheck();
3614 if (streamData == nullptr)
3615 return;
3616 }
3617
3619 {
3620 if (m_recorder)
3622 else
3623 TuningNewRecorder(streamData);
3624
3625 // If we got this far it is safe to set a new starting channel...
3626 if (m_channel)
3628 }
3629}
3630
3636{
3637 LOG(VB_RECORD, LOG_INFO, LOC + QString("TuningShutdowns(%1)")
3638 .arg(request.toString()));
3639
3640 if (m_scanner && !(request.m_flags & kFlagEITScan) &&
3642 {
3644 ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
3646 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
3647 }
3648
3649 if (m_scanner && !request.IsOnSameMultiplex())
3651
3653 {
3654 MPEGStreamData *sd = nullptr;
3655 if (GetDTVSignalMonitor())
3658 ClearFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3659
3660 // Delete StreamData if it is not in use by the recorder.
3661 MPEGStreamData *rec_sd = nullptr;
3662 if (GetDTVRecorder())
3663 rec_sd = GetDTVRecorder()->GetStreamData();
3664 if (sd && (sd != rec_sd))
3665 delete sd;
3666 }
3668 ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3669
3670 // At this point any waits are canceled.
3671
3672 if (request.m_flags & kFlagNoRec)
3673 {
3675 {
3677 ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3679 }
3680
3682 (m_curRecording &&
3685 {
3686 m_stateChangeLock.unlock();
3687 TeardownRecorder(request.m_flags);
3688 m_stateChangeLock.lock();
3689 }
3690 // At this point the recorders are shut down
3691
3692 CloseChannel();
3693 // At this point the channel is shut down
3694 }
3695
3696 if (m_buffer && (request.m_flags & kFlagKillRingBuffer))
3697 {
3698 LOG(VB_RECORD, LOG_INFO, LOC + "Tearing down RingBuffer");
3699 SetRingBuffer(nullptr);
3700 // At this point the ringbuffer is shut down
3701 }
3702
3703 // Clear pending actions from last request
3704 ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
3705}
3706
3725{
3726 LOG(VB_GENERAL, LOG_INFO, LOC + QString("TuningFrequency(%1)")
3727 .arg(request.toString()));
3728
3729 DTVChannel *dtvchan = GetDTVChannel();
3730 if (dtvchan)
3731 {
3732 MPEGStreamData *mpeg = nullptr;
3733
3734 if (GetDTVRecorder())
3736
3737 // Tune with SI table standard (dvb, atsc, mpeg) from database, see issue #452
3739
3740 const QString tuningmode = (HasFlags(kFlagEITScannerRunning)) ?
3741 dtvchan->GetSIStandard() :
3742 dtvchan->GetSuggestedTuningMode(
3744
3745 dtvchan->SetTuningMode(tuningmode);
3746
3747 if (request.m_minorChan && (tuningmode == "atsc"))
3748 {
3749 auto *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
3750 if (atsc)
3751 atsc->SetDesiredChannel(request.m_majorChan, request.m_minorChan);
3752 }
3753 else if (request.m_progNum >= 0)
3754 {
3755 if (mpeg)
3756 mpeg->SetDesiredProgram(request.m_progNum);
3757 }
3758 }
3759
3760 if (request.IsOnSameMultiplex())
3761 {
3762 // Update the channel number for SwitchLiveTVRingBuffer (called from
3763 // TuningRestartRecorder). This ensures that the livetvchain will be
3764 // updated with the new channel number
3765 if (m_channel)
3766 {
3768 m_channel->GetChannelName(), request.m_channel );
3769 }
3770
3771 QStringList slist;
3772 slist<<"message"<<QObject::tr("On known multiplex...");
3773 MythEvent me(QString("SIGNAL %1").arg(m_inputId), slist);
3775
3776 SetFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3777 return;
3778 }
3779
3780 QString channum = request.m_channel;
3781
3782 bool ok1 = true;
3783 if (m_channel)
3784 {
3785 m_channel->Open();
3786 if (!channum.isEmpty())
3787 ok1 = m_channel->SetChannelByString(channum);
3788 else
3789 ok1 = false;
3790 }
3791
3792 if (!ok1)
3793 {
3794 if (!(request.m_flags & kFlagLiveTV) || !(request.m_flags & kFlagEITScan))
3795 {
3796 if (m_curRecording)
3798
3799 LOG(VB_GENERAL, LOG_ERR, LOC +
3800 QString("Failed to set channel to %1. Reverting to kState_None")
3801 .arg(channum));
3804 else
3806 return;
3807 }
3808
3809 LOG(VB_GENERAL, LOG_ERR, LOC +
3810 QString("Failed to set channel to %1.").arg(channum));
3811 }
3812
3813 bool mpts_only = GetDTVChannel() &&
3814 GetDTVChannel()->GetFormat().compare("MPTS") == 0;
3815 if (mpts_only)
3816 {
3817 // Not using a signal monitor, so just set the status to recording
3819 if (m_curRecording)
3820 {
3822 }
3823 }
3824
3825
3826 bool livetv = (request.m_flags & kFlagLiveTV) != 0U;
3827 bool antadj = (request.m_flags & kFlagAntennaAdjust) != 0U;
3828 bool use_sm = !mpts_only && SignalMonitor::IsRequired(m_genOpt.m_inputType);
3829 bool use_dr = use_sm && (livetv || antadj);
3830 bool has_dummy = false;
3831
3832 if (use_dr)
3833 {
3834 // We need there to be a ringbuffer for these modes
3835 bool ok2 = false;
3837 m_pseudoLiveTVRecording = nullptr;
3838
3839 m_tvChain->SetInputType("DUMMY");
3840
3841 if (!m_buffer)
3842 ok2 = CreateLiveTVRingBuffer(channum);
3843 else
3844 ok2 = SwitchLiveTVRingBuffer(channum, true, false);
3846
3848
3849 if (!ok2)
3850 {
3851 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RingBuffer 1");
3852 return;
3853 }
3854
3855 has_dummy = true;
3856 }
3857
3858 // Start signal monitoring for devices capable of monitoring
3859 if (use_sm)
3860 {
3861 LOG(VB_RECORD, LOG_INFO, LOC + "Starting Signal Monitor");
3862 bool error = false;
3863 if (!SetupSignalMonitor(
3864 !antadj, (request.m_flags & kFlagEITScan) != 0U, livetv || antadj))
3865 {
3866 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to setup signal monitor");
3867 if (m_signalMonitor)
3868 {
3869 delete m_signalMonitor;
3870 m_signalMonitor = nullptr;
3871 }
3872
3873 // pretend the signal monitor is running to prevent segfault
3874 SetFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3875 ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3876 error = true;
3877 }
3878
3879 if (m_signalMonitor)
3880 {
3881 if (request.m_flags & kFlagEITScan)
3882 {
3884 SetVideoStreamsRequired(0);
3886 }
3887
3888 SetFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3889 ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3890 if (!antadj)
3891 {
3892 QDateTime expire = MythDate::current();
3893
3894 SetFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3895 if (m_curRecording)
3896 {
3898 // If startRecordingDeadline is passed, this
3899 // recording is marked as failed, so the scheduler
3900 // can try another showing.
3902 expire.addMSecs(m_genOpt.m_channelTimeout);
3904 expire.addMSecs(m_genOpt.m_channelTimeout * 2 / 3);
3905 // Keep trying to record this showing (even if it
3906 // has been marked as failed) until the scheduled
3907 // end time.
3909 m_curRecording->GetRecordingEndTime().addSecs(-10);
3910
3911 LOG(VB_CHANNEL, LOG_DEBUG, LOC +
3912 QString("Pre-fail start deadline: %1 "
3913 "Start recording deadline: %2 "
3914 "Good signal deadline: %3")
3915 .arg(m_preFailDeadline.toLocalTime()
3916 .toString("hh:mm:ss.zzz"),
3917 m_startRecordingDeadline.toLocalTime()
3918 .toString("hh:mm:ss.zzz"),
3919 m_signalMonitorDeadline.toLocalTime()
3920 .toString("hh:mm:ss.zzz")));
3921 }
3922 else
3923 {
3925 expire.addMSecs(m_genOpt.m_channelTimeout);
3926 }
3928
3929 //System Event TUNING_TIMEOUT deadline
3931 m_signalEventCmdSent = false;
3932 }
3933 }
3934
3935 if (has_dummy && m_buffer)
3936 {
3937 // Make sure recorder doesn't point to bogus ringbuffer before
3938 // it is potentially restarted without a new ringbuffer, if
3939 // the next channel won't tune and the user exits LiveTV.
3940 if (m_recorder)
3941 m_recorder->SetRingBuffer(nullptr);
3942
3943 SetFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3944 LOG(VB_RECORD, LOG_INFO, "DummyDTVRecorder -- started");
3945 SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
3946 }
3947
3948 // if we had problems starting the signal monitor,
3949 // we don't want to start the recorder...
3950 if (error)
3951 return;
3952 }
3953
3954 // Request a recorder, if the command is a recording command
3955 ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3956 if (request.m_flags & kFlagRec && !antadj)
3957 SetFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3958}
3959
3968{
3969 RecStatus::Type newRecStatus = RecStatus::Unknown;
3970 bool keep_trying = false;
3971 QDateTime current_time = MythDate::current();
3972
3973 if ((m_signalMonitor->IsErrored() || current_time > m_signalEventCmdTimeout) &&
3975 {
3976 gCoreContext->SendSystemEvent(QString("TUNING_SIGNAL_TIMEOUT CARDID %1")
3977 .arg(m_inputId));
3978 m_signalEventCmdSent = true;
3979 }
3980
3982 {
3983 LOG(VB_RECORD, LOG_INFO, LOC + "TuningSignalCheck: Good signal");
3984 if (m_curRecording && (current_time > m_startRecordingDeadline))
3985 {
3986 newRecStatus = RecStatus::Failing;
3987 m_curRecording->SaveVideoProperties(VID_DAMAGED, VID_DAMAGED);
3988
3989 QString desc = tr("Good signal seen after %1 ms")
3991 m_startRecordingDeadline.msecsTo(current_time));
3992 QString title = m_curRecording->GetTitle();
3993 if (!m_curRecording->GetSubtitle().isEmpty())
3994 title += " - " + m_curRecording->GetSubtitle();
3995
3997 "Recording", title,
3998 tr("See 'Tuning timeout' in mythtv-setup "
3999 "for this input."));
4001
4002 LOG(VB_GENERAL, LOG_WARNING, LOC +
4003 QString("It took longer than %1 ms to get a signal lock. "
4004 "Keeping status of '%2'")
4006 .arg(RecStatus::toString(newRecStatus, kSingleRecord)));
4007 LOG(VB_GENERAL, LOG_WARNING, LOC +
4008 "See 'Tuning timeout' in mythtv-setup for this input");
4009 }
4010 else
4011 {
4012 newRecStatus = RecStatus::Recording;
4013 }
4014 }
4015 else if (m_signalMonitor->IsErrored() || current_time > m_signalMonitorDeadline)
4016 {
4017 LOG(VB_GENERAL, LOG_ERR, LOC + "TuningSignalCheck: SignalMonitor " +
4018 (m_signalMonitor->IsErrored() ? "failed" : "timed out"));
4019
4020 ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4021 newRecStatus = RecStatus::Failed;
4022
4024 {
4026 }
4027 }
4028 else if (m_curRecording && !m_reachedPreFail && current_time > m_preFailDeadline)
4029 {
4030 LOG(VB_GENERAL, LOG_ERR, LOC +
4031 "TuningSignalCheck: Hit pre-fail timeout");
4032 SendMythSystemRecEvent("REC_PREFAIL", m_curRecording);
4033 m_reachedPreFail = true;
4034 return nullptr;
4035 }
4037 current_time > m_startRecordingDeadline)
4038 {
4039 newRecStatus = RecStatus::Failing;
4041 keep_trying = true;
4042
4043 SendMythSystemRecEvent("REC_FAILING", m_curRecording);
4044
4045 QString desc = tr("Taking more than %1 ms to get a lock.")
4047 QString title = m_curRecording->GetTitle();
4048 if (!m_curRecording->GetSubtitle().isEmpty())
4049 title += " - " + m_curRecording->GetSubtitle();
4050
4052 "Recording", title,
4053 tr("See 'Tuning timeout' in mythtv-setup "
4054 "for this input."));
4055 mn.SetDuration(30s);
4057
4058 LOG(VB_GENERAL, LOG_WARNING, LOC +
4059 QString("TuningSignalCheck: taking more than %1 ms to get a lock. "
4060 "marking this recording as '%2'.")
4062 .arg(RecStatus::toString(newRecStatus, kSingleRecord)));
4063 LOG(VB_GENERAL, LOG_WARNING, LOC +
4064 "See 'Tuning timeout' in mythtv-setup for this input");
4065 }
4066 else
4067 {
4068 if (m_signalMonitorCheckCnt) // Don't flood log file
4070 else
4071 {
4072 LOG(VB_RECORD, LOG_INFO, LOC +
4073 QString("TuningSignalCheck: Still waiting. Will timeout @ %1")
4074 .arg(m_signalMonitorDeadline.toLocalTime()
4075 .toString("hh:mm:ss.zzz")));
4077 }
4078 return nullptr;
4079 }
4080
4081 SetRecordingStatus(newRecStatus, __LINE__);
4082
4083 if (m_curRecording)
4084 {
4085 m_curRecording->SetRecordingStatus(newRecStatus);
4086 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
4087 .arg(m_curRecording->GetInputID())
4088 .arg(m_curRecording->GetChanID())
4090 .arg(newRecStatus)
4093 }
4094
4095 if (keep_trying)
4096 return nullptr;
4097
4098 // grab useful data from DTV signal monitor before we kill it...
4099 MPEGStreamData *streamData = nullptr;
4100 if (GetDTVSignalMonitor())
4101 streamData = GetDTVSignalMonitor()->GetStreamData();
4102
4104 {
4105 // shut down signal monitoring
4107 ClearFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
4108 }
4109 ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
4110
4111 if (streamData)
4112 {
4113 auto *dsd = dynamic_cast<DVBStreamData*>(streamData);
4114 if (dsd)
4116 if (m_scanner)
4117 {
4118 if (get_use_eit(GetInputId()))
4119 {
4121 }
4122 else
4123 {
4124 LOG(VB_EIT, LOG_INFO, LOC +
4125 QString("EIT scanning disabled for video source %1")
4126 .arg(GetSourceID())); }
4127 }
4128 }
4129
4130 return streamData;
4131}
4132
4134 bool on_host, bool transcode_bfr_comm, bool on_line_comm)
4135{
4136 if (!rec)
4137 return 0; // no jobs for Live TV recordings..
4138
4139 int jobs = 0; // start with no jobs
4140
4141 // grab standard jobs flags from program info
4143
4144 // disable commercial flagging on PBS, BBC, etc.
4145 if (rec->IsCommercialFree())
4147
4148 // disable transcoding if the profile does not allow auto transcoding
4149 const StandardSetting *autoTrans = profile.byName("autotranscode");
4150 if ((!autoTrans) || (autoTrans->getValue().toInt() == 0))
4152
4153 bool ml = JobQueue::JobIsInMask(JOB_METADATA, jobs);
4154 if (ml)
4155 {
4156 // When allowed, metadata lookup should occur at the
4157 // start of a recording to make the additional info
4158 // available immediately (and for use in future jobs).
4159 QString host = (on_host) ? gCoreContext->GetHostName() : "";
4161 rec->GetChanID(),
4162 rec->GetRecordingStartTime(), "", "",
4163 host, JOB_LIVE_REC);
4164
4165 // don't do regular metadata lookup, we won't need it.
4167 }
4168
4169 // is commercial flagging enabled, and is on-line comm flagging enabled?
4170 bool rt = JobQueue::JobIsInMask(JOB_COMMFLAG, jobs) && on_line_comm;
4171 // also, we either need transcoding to be disabled or
4172 // we need to be allowed to commercial flag before transcoding?
4174 !transcode_bfr_comm;
4175 if (rt)
4176 {
4177 // queue up real-time (i.e. on-line) commercial flagging.
4178 QString host = (on_host) ? gCoreContext->GetHostName() : "";
4180 rec->GetChanID(),
4181 rec->GetRecordingStartTime(), "", "",
4182 host, JOB_LIVE_REC);
4183
4184 // don't do regular comm flagging, we won't need it.
4186 }
4187
4188 return jobs;
4189}
4190
4191QString TVRec::LoadProfile(void *tvchain, RecordingInfo *rec,
4193{
4194 // Determine the correct recording profile.
4195 // In LiveTV mode use "Live TV" profile, otherwise use the
4196 // recording's specified profile. If the desired profile can't
4197 // be found, fall back to the "Default" profile for input type.
4198 QString profileName = "Live TV";
4199 if (!tvchain && rec)
4200 profileName = rec->GetRecordingRule()->m_recProfile;
4201
4202 QString profileRequested = profileName;
4203
4204 if (profile.loadByType(profileName, m_genOpt.m_inputType,
4206 {
4207 LOG(VB_RECORD, LOG_INFO, LOC +
4208 QString("Using profile '%1' to record")
4209 .arg(profileName));
4210 }
4211 else
4212 {
4213 profileName = "Default";
4214 if (profile.loadByType(profileName, m_genOpt.m_inputType, m_genOpt.m_videoDev))
4215 {
4216 LOG(VB_RECORD, LOG_INFO, LOC +
4217 QString("Profile '%1' not found, using "
4218 "fallback profile '%2' to record")
4219 .arg(profileRequested, profileName));
4220 }
4221 else
4222 {
4223 LOG(VB_RECORD, LOG_ERR, LOC +
4224 QString("Profile '%1' not found, and unable "
4225 "to load fallback profile '%2'. Results "
4226 "may be unpredicable")
4227 .arg(profileRequested, profileName));
4228 }
4229 }
4230
4231 return profileName;
4232}
4233
4238 RecordingInfo **rec,
4240 bool had_dummyrec)
4241{
4242 if (m_tvChain)
4243 {
4244 bool ok = false;
4245 if (!m_buffer)
4246 {
4248 SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4249 }
4250 else
4251 {
4253 true, !had_dummyrec && m_recorder);
4254 }
4255 if (!ok)
4256 {
4257 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RingBuffer 2");
4258 return false;
4259 }
4260 *rec = m_curRecording; // new'd in Create/SwitchLiveTVRingBuffer()
4261 }
4262
4264 {
4265 bool write = m_genOpt.m_inputType != "IMPORT";
4266 QString pathname = (*rec)->GetPathname();
4267 LOG(VB_GENERAL, LOG_INFO, LOC + QString("rec->GetPathname(): '%1'")
4268 .arg(pathname));
4270 if (!m_buffer->IsOpen() && write)
4271 {
4272 LOG(VB_GENERAL, LOG_ERR, LOC +
4273 QString("RingBuffer '%1' not open...")
4274 .arg(pathname));
4275 SetRingBuffer(nullptr);
4276 ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
4277 return false;
4278 }
4279 }
4280
4281 if (!m_buffer)
4282 {
4283 LOG(VB_GENERAL, LOG_ERR, LOC +
4284 QString("Failed to start recorder! ringBuffer is NULL\n"
4285 "\t\t\t\t Tuning request was %1\n")
4287
4288 if (HasFlags(kFlagLiveTV))
4289 {
4290 QString message = QString("QUIT_LIVETV %1").arg(m_inputId);
4291 MythEvent me(message);
4293 }
4294 return false;
4295 }
4296
4297 if (m_channel && m_genOpt.m_inputType == "MJPEG")
4298 m_channel->Close(); // Needed because of NVR::MJPEGInit()
4299
4300 LOG(VB_GENERAL, LOG_INFO, LOC + "TuningNewRecorder - CreateRecorder()");
4302
4303 if (m_recorder)
4304 {
4307 if (m_recorder->IsErrored())
4308 {
4309 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialize recorder!");
4310 delete m_recorder;
4311 m_recorder = nullptr;
4312 }
4313 }
4314
4315 if (!m_recorder)
4316 {
4317 LOG(VB_GENERAL, LOG_ERR, LOC +
4318 QString("Failed to start recorder!\n"
4319 "\t\t\t\t Tuning request was %1\n")
4321
4322 if (HasFlags(kFlagLiveTV))
4323 {
4324 QString message = QString("QUIT_LIVETV %1").arg(m_inputId);
4325 MythEvent me(message);
4327 }
4329 if (m_tvChain)
4330 (*rec) = nullptr;
4331 return false;
4332 }
4333
4334 if (*rec)
4335 m_recorder->SetRecording(*rec);
4336
4337 if (GetDTVRecorder() && streamData)
4338 {
4339 const StandardSetting *setting = profile.byName("recordingtype");
4340 if (setting)
4341 streamData->SetRecordingType(setting->getValue());
4342 GetDTVRecorder()->SetStreamData(streamData);
4343 }
4344
4345 if (m_channel && m_genOpt.m_inputType == "MJPEG")
4346 m_channel->Open(); // Needed because of NVR::MJPEGInit()
4347
4348 // Setup for framebuffer capture devices..
4349 if (m_channel)
4350 {
4353 }
4354
4355 if (GetV4LChannel())
4356 {
4358 CloseChannel();
4359 }
4360
4361 m_recorderThread = new MThread("RecThread", m_recorder);
4363
4364 // Wait for recorder to start.
4365 m_stateChangeLock.unlock();
4366 while (!m_recorder->IsRecording() && !m_recorder->IsErrored())
4367 std::this_thread::sleep_for(5us);
4368 m_stateChangeLock.lock();
4369
4370 if (GetV4LChannel())
4372
4373 SetFlags(kFlagRecorderRunning | kFlagRingBufferReady, __FILE__, __LINE__);
4374
4375 ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4376
4377 //workaround for failed import recordings, no signal monitor means we never
4378 //go to recording state and the status here seems to override the status
4379 //set in the importrecorder and backend via setrecordingstatus
4380 if (m_genOpt.m_inputType == "IMPORT")
4381 {
4383 if (m_curRecording)
4385 }
4386
4387 return true;
4388}
4389
4394{
4395 LOG(VB_RECORD, LOG_INFO, LOC + "Starting Recorder");
4396
4397 bool had_dummyrec = false;
4399 {
4401 ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
4403 had_dummyrec = true;
4404 }
4405
4407
4410
4411 if (TuningNewRecorderReal(streamData, &rec, profile, had_dummyrec))
4412 return;
4413
4414 SetRecordingStatus(RecStatus::Failed, __LINE__, true);
4416
4417 if (rec)
4418 {
4419 // Make sure the scheduler knows...
4421 LOG(VB_RECORD, LOG_INFO, LOC +
4422 QString("TuningNewRecorder -- UPDATE_RECORDING_STATUS: %1")
4424 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
4425 .arg(rec->GetInputID())
4426 .arg(rec->GetChanID())
4428 .arg(RecStatus::Failed)
4431 }
4432
4433 if (m_tvChain)
4434 delete rec;
4435}
4436
4441{
4442 LOG(VB_RECORD, LOG_INFO, LOC + "Restarting Recorder");
4443
4444 bool had_dummyrec = false;
4445
4446 if (m_curRecording)
4447 {
4450 }
4451
4453 {
4454 ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
4455 had_dummyrec = true;
4456 }
4457
4458 SwitchLiveTVRingBuffer(m_channel->GetChannelName(), true, !had_dummyrec);
4459
4460 if (had_dummyrec)
4461 {
4463 ProgramInfo *progInfo = m_tvChain->GetProgramAt(-1);
4464 RecordingInfo recinfo(*progInfo);
4465 delete progInfo;
4466 recinfo.SetInputID(m_inputId);
4467 m_recorder->SetRecording(&recinfo);
4468 }
4469 m_recorder->Reset();
4470
4471 // Set file descriptor of channel from recorder for V4L
4472 if (GetV4LChannel())
4474
4475 // Some recorders unpause on Reset, others do not...
4477
4479 {
4481 QString msg1 = QString("Recording: %1 %2 %3 %4")
4482 .arg(rcinfo1->GetTitle(), QString::number(rcinfo1->GetChanID()),
4485 ProgramInfo *rcinfo2 = m_tvChain->GetProgramAt(-1);
4486 QString msg2 = QString("Recording: %1 %2 %3 %4")
4487 .arg(rcinfo2->GetTitle(), QString::number(rcinfo2->GetChanID()),
4490 delete rcinfo2;
4491 LOG(VB_RECORD, LOG_INFO, LOC + "Pseudo LiveTV recording starting." +
4492 "\n\t\t\t" + msg1 + "\n\t\t\t" + msg2);
4493
4496
4498
4499 InitAutoRunJobs(m_curRecording, kAutoRunProfile, nullptr, __LINE__);
4500 }
4501
4502 ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4503}
4504
4505void TVRec::SetFlags(uint f, const QString & file, int line)
4506{
4507 QMutexLocker lock(&m_stateChangeLock);
4508 m_stateFlags |= f;
4509 LOG(VB_RECORD, LOG_INFO, LOC + QString("SetFlags(%1) -> %2 @ %3:%4")
4510 .arg(FlagToString(f), FlagToString(m_stateFlags), file, QString::number(line)));
4511 WakeEventLoop();
4512}
4513
4514void TVRec::ClearFlags(uint f, const QString & file, int line)
4515{
4516 QMutexLocker lock(&m_stateChangeLock);
4517 m_stateFlags &= ~f;
4518 LOG(VB_RECORD, LOG_INFO, LOC + QString("ClearFlags(%1) -> %2 @ %3:%4")
4519 .arg(FlagToString(f), FlagToString(m_stateFlags), file, QString::number(line)));
4520 WakeEventLoop();
4521}
4522
4524{
4525 QString msg("");
4526
4527 // General flags
4528 if (kFlagFrontendReady & f)
4529 msg += "FrontendReady,";
4530 if (kFlagRunMainLoop & f)
4531 msg += "RunMainLoop,";
4532 if (kFlagExitPlayer & f)
4533 msg += "ExitPlayer,";
4534 if (kFlagFinishRecording & f)
4535 msg += "FinishRecording,";
4536 if (kFlagErrored & f)
4537 msg += "Errored,";
4539 msg += "CancelNextRecording,";
4540
4541 // Tuning flags
4542 if ((kFlagRec & f) == kFlagRec)
4543 msg += "REC,";
4544 else
4545 {
4546 if (kFlagLiveTV & f)
4547 msg += "LiveTV,";
4548 if (kFlagRecording & f)
4549 msg += "Recording,";
4550 }
4551 if ((kFlagNoRec & f) == kFlagNoRec)
4552 msg += "NOREC,";
4553 else
4554 {
4555 if (kFlagEITScan & f)
4556 msg += "EITScan,";
4557 if (kFlagCloseRec & f)
4558 msg += "CloseRec,";
4559 if (kFlagKillRec & f)
4560 msg += "KillRec,";
4561 if (kFlagAntennaAdjust & f)
4562 msg += "AntennaAdjust,";
4563 }
4565 msg += "PENDINGACTIONS,";
4566 else
4567 {
4569 msg += "WaitingForRecPause,";
4570 if (kFlagWaitingForSignal & f)
4571 msg += "WaitingForSignal,";
4573 msg += "NeedToStartRecorder,";
4574 if (kFlagKillRingBuffer & f)
4575 msg += "KillRingBuffer,";
4576 }
4577 if ((kFlagAnyRunning & f) == kFlagAnyRunning)
4578 msg += "ANYRUNNING,";
4579 else
4580 {
4582 msg += "SignalMonitorRunning,";
4583 if (kFlagEITScannerRunning & f)
4584 msg += "EITScannerRunning,";
4586 msg += "ANYRECRUNNING,";
4587 else
4588 {
4590 msg += "DummyRecorderRunning,";
4591 if (kFlagRecorderRunning & f)
4592 msg += "RecorderRunning,";
4593 }
4594 }
4595 if (kFlagRingBufferReady & f)
4596 msg += "RingBufferReady,";
4597
4598 if (msg.isEmpty())
4599 msg = QString("0x%1").arg(f,0,16);
4600
4601 return msg;
4602}
4603
4605{
4606 QMutexLocker lock(&m_nextLiveTVDirLock);
4607
4608 bool found = !m_nextLiveTVDir.isEmpty();
4609 if (!found && m_triggerLiveTVDir.wait(&m_nextLiveTVDirLock, 500))
4610 {
4611 found = !m_nextLiveTVDir.isEmpty();
4612 }
4613
4614 return found;
4615}
4616
4618{
4619 QMutexLocker lock(&m_nextLiveTVDirLock);
4620
4621 m_nextLiveTVDir = std::move(dir);
4622 m_triggerLiveTVDir.wakeAll();
4623}
4624
4627 const QString & channum)
4628{
4629 LOG(VB_RECORD, LOG_INFO, LOC + "GetProgramRingBufferForLiveTV()");
4630 if (!m_channel || !m_tvChain || !pginfo || !Buffer)
4631 return false;
4632
4633 m_nextLiveTVDirLock.lock();
4634 m_nextLiveTVDir.clear();
4635 m_nextLiveTVDirLock.unlock();
4636
4637 // Dispatch this early, the response can take a while.
4638 MythEvent me(QString("QUERY_NEXT_LIVETV_DIR %1").arg(m_inputId));
4640
4641 uint sourceid = m_channel->GetSourceID();
4642 int chanid = ChannelUtil::GetChanID(sourceid, channum);
4643
4644 if (chanid < 0)
4645 {
4646 // Test setups might have zero channels
4647 if (m_genOpt.m_inputType == "IMPORT" || m_genOpt.m_inputType == "DEMO")
4648 chanid = 9999;
4649 else
4650 {
4651 LOG(VB_GENERAL, LOG_ERR, LOC +
4652 QString("Channel: \'%1\' was not found in the database.\n"
4653 "\t\tMost likely, the 'starting channel' for this "
4654 "Input Connection is invalid.\n"
4655 "\t\tCould not start livetv.").arg(channum));
4656 return false;
4657 }
4658 }
4659
4660 auto hoursMax =
4661 gCoreContext->GetDurSetting<std::chrono::hours>("MaxHoursPerLiveTVRecording", 8h);
4662 if (hoursMax <= 0h)
4663 hoursMax = 8h;
4664
4665 RecordingInfo *prog = nullptr;
4668 else
4669 {
4670 prog = new RecordingInfo(
4671 chanid, MythDate::current(true), true, hoursMax);
4672 }
4673
4674 prog->SetInputID(m_inputId);
4675
4676 if (prog->GetRecordingStartTime() == prog->GetRecordingEndTime())
4677 {
4678 LOG(VB_GENERAL, LOG_ERR, LOC + "GetProgramRingBufferForLiveTV()"
4679 "\n\t\t\tProgramInfo is invalid."
4680 "\n" + prog->toString());
4681 prog->SetScheduledEndTime(prog->GetRecordingStartTime().addSecs(3600));
4683
4684 prog->SetChanID(chanid);
4685 }
4686
4689
4690 prog->SetStorageGroup("LiveTV");
4691
4693 {
4694 QMutexLocker lock(&m_nextLiveTVDirLock);
4696 }
4697 else
4698 {
4699 StorageGroup sgroup("LiveTV", gCoreContext->GetHostName());
4700 prog->SetPathname(sgroup.FindNextDirMostFree());
4701 }
4702
4704 prog->SetRecordingGroup("LiveTV");
4705
4706 StartedRecording(prog);
4707
4708 *Buffer = MythMediaBuffer::Create(prog->GetPathname(), true);
4709 if (!(*Buffer) || !(*Buffer)->IsOpen())
4710 {
4711 LOG(VB_GENERAL, LOG_ERR, LOC + QString("RingBuffer '%1' not open...")
4712 .arg(prog->GetPathname()));
4713
4714 delete *Buffer;
4715 delete prog;
4716
4717 return false;
4718 }
4719
4720 *pginfo = prog;
4721 return true;
4722}
4723
4724bool TVRec::CreateLiveTVRingBuffer(const QString & channum)
4725{
4726 LOG(VB_RECORD, LOG_INFO, LOC + QString("CreateLiveTVRingBuffer(%1)")
4727 .arg(channum));
4728
4729 RecordingInfo *pginfo = nullptr;
4730 MythMediaBuffer *buffer = nullptr;
4731
4732 if (!m_channel ||
4733 !m_channel->CheckChannel(channum))
4734 {
4736 return false;
4737 }
4738
4739 if (!GetProgramRingBufferForLiveTV(&pginfo, &buffer, channum))
4740 {
4741 ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
4743 LOG(VB_GENERAL, LOG_ERR, LOC +
4744 QString("CreateLiveTVRingBuffer(%1) failed").arg(channum));
4745 return false;
4746 }
4747
4748 SetRingBuffer(buffer);
4749
4753
4754 bool discont = (m_tvChain->TotalSize() > 0);
4756 m_channel->GetInputName(), discont);
4757
4758 if (m_curRecording)
4759 {
4761 delete m_curRecording;
4762 }
4763
4764 m_curRecording = pginfo;
4766
4767 return true;
4768}
4769
4770bool TVRec::SwitchLiveTVRingBuffer(const QString & channum,
4771 bool discont, bool set_rec)
4772{
4773 QString msg;
4774 if (m_curRecording)
4775 {
4776 msg = QString(" curRec(%1) curRec.size(%2)")
4778 .arg(m_curRecording->GetFilesize());
4779 }
4780 LOG(VB_RECORD, LOG_INFO, LOC +
4781 QString("SwitchLiveTVRingBuffer(discont %1, set_next_rec %2)")
4782 .arg(discont).arg(set_rec) + msg);
4783
4784 RecordingInfo *pginfo = nullptr;
4785 MythMediaBuffer *buffer = nullptr;
4786
4787 if (!m_channel ||
4788 !m_channel->CheckChannel(channum))
4789 {
4791 return false;
4792 }
4793
4794 if (!GetProgramRingBufferForLiveTV(&pginfo, &buffer, channum))
4795 {
4797 return false;
4798 }
4799
4800 QString oldinputtype = m_tvChain->GetInputType(-1);
4801
4802 pginfo->MarkAsInUse(true, kRecorderInUseID);
4807 m_channel->GetInputName(), discont);
4808
4809 if (set_rec && m_recorder)
4810 {
4811 m_recorder->SetNextRecording(pginfo, buffer);
4812 if (discont)
4814 delete pginfo;
4815 SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4816 }
4817 else if (!set_rec)
4818 {
4819 // dummy recordings are finished before this
4820 // is called and other recordings must be finished..
4821 if (m_curRecording && oldinputtype != "DUMMY")
4822 {
4825 delete m_curRecording;
4826 }
4827 m_curRecording = pginfo;
4828 SetRingBuffer(buffer);
4829 }
4830 else
4831 {
4832 delete buffer;
4833 }
4834
4835 return true;
4836}
4837
4839{
4840 LOG(VB_RECORD, LOG_INFO, LOC + "SwitchRecordingRingBuffer()");
4841
4843 {
4844 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4845 "already switching.");
4846 return nullptr;
4847 }
4848
4849 if (!m_recorder)
4850 {
4851 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4852 "invalid recorder.");
4853 return nullptr;
4854 }
4855
4856 if (!m_curRecording)
4857 {
4858 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4859 "invalid recording.");
4860 return nullptr;
4861 }
4862
4863 if (rcinfo.GetChanID() != m_curRecording->GetChanID())
4864 {
4865 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4866 "Not the same channel.");
4867 return nullptr;
4868 }
4869
4870 auto *ri = new RecordingInfo(rcinfo);
4872
4873 QString pn = LoadProfile(nullptr, ri, profile);
4874
4875 if (pn != m_recProfileName)
4876 {
4877 LOG(VB_RECORD, LOG_ERR, LOC +
4878 QString("SwitchRecordingRingBuffer() -> "
4879 "cannot switch profile '%1' to '%2'")
4880 .arg(m_recProfileName, pn));
4881 return nullptr;
4882 }
4883
4885
4886 ri->MarkAsInUse(true, kRecorderInUseID);
4887 StartedRecording(ri);
4888
4889 bool write = m_genOpt.m_inputType != "IMPORT";
4890 MythMediaBuffer *buffer = MythMediaBuffer::Create(ri->GetPathname(), write);
4891 if (!buffer || !buffer->IsOpen())
4892 {
4893 delete buffer;
4894 ri->SetRecordingStatus(RecStatus::Failed);
4895 FinishedRecording(ri, nullptr);
4896 ri->MarkAsInUse(false, kRecorderInUseID);
4897 delete ri;
4898 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer() -> "
4899 "Failed to create new RB.");
4900 return nullptr;
4901 }
4902
4903 m_recorder->SetNextRecording(ri, buffer);
4904 SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4906 m_switchingBuffer = true;
4907 ri->SetRecordingStatus(RecStatus::Recording);
4908 LOG(VB_RECORD, LOG_INFO, LOC + "SwitchRecordingRingBuffer -> done");
4909 return ri;
4910}
4911
4913{
4914 QMap<uint,TVRec*>::const_iterator it = s_inputs.constFind(inputid);
4915 if (it == s_inputs.constEnd())
4916 return nullptr;
4917 return *it;
4918}
4919
4921{
4922 LOG(VB_RECORD, LOG_INFO, LOC + QString("enable:%1").arg(enable));
4923
4924 if (m_scanner != nullptr)
4925 {
4926 if (enable)
4927 {
4929 && m_eitScanStartTime > MythDate::current().addYears(9))
4930 {
4932 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
4933 }
4934 }
4935 else
4936 {
4937 m_eitScanStartTime = MythDate::current().addYears(10);
4939 {
4941 ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
4942 }
4943 }
4944 }
4945}
4946
4947QString TuningRequest::toString(void) const
4948{
4949 return QString("Program(%1) channel(%2) input(%3) flags(%4)")
4950 .arg(m_program == nullptr ? "NULL" : m_program->toString(),
4951 m_channel.isEmpty() ? "<empty>" : m_channel,
4952 m_input.isEmpty() ? "<empty>" : m_input,
4954}
4955
4956#if CONFIG_DVB
4957#include "recorders/dvbchannel.h"
4959{
4960 // Some DVB devices munge the PMT and/or PAT so the CRC check fails.
4961 // We need to tell the stream data class to not check the CRC on
4962 // these devices. This can cause segfaults.
4963 auto * dvb = dynamic_cast<DVBChannel*>(c);
4964 if (dvb != nullptr)
4965 s->SetIgnoreCRC(dvb->HasCRCBug());
4966}
4967#else
4969#endif // CONFIG_DVB
4970
4971/* vim: set expandtab tabstop=4 shiftwidth=4: */
Overall structure.
std::vector< pid_cache_item_t > pid_cache_t
Definition: channelutil.h:43
Encapsulates data about ATSC stream and emits events for most tables.
const MasterGuideTable * GetCachedMGT(bool current=true) const
void SetDesiredChannel(int major, int minor)
static bool GetInputInfo(InputInfo &input, std::vector< uint > *groupids=nullptr)
Definition: cardutil.cpp:1704
static QString GetStartChannel(uint inputid)
Definition: cardutil.cpp:1798
static bool IsEITCapable(const QString &rawtype)
Definition: cardutil.h:172
static QString GetInputName(uint inputid)
Definition: cardutil.cpp:1779
static bool IsV4L(const QString &rawtype)
Definition: cardutil.h:147
static uint GetSourceID(uint inputid)
Definition: cardutil.cpp:1954
static bool IsEncoder(const QString &rawtype)
Definition: cardutil.h:137
static std::vector< uint > GetConflictingInputs(uint inputid)
Definition: cardutil.cpp:2247
static bool IsChannelReusable(const QString &rawtype)
Definition: cardutil.h:224
Abstract class providing a generic interface to tuning hardware.
Definition: channelbase.h:32
virtual QString GetInputName(void) const
Definition: channelbase.h:69
virtual int ChangePictureAttribute(PictureAdjustType, PictureAttribute, bool)
Definition: channelbase.h:95
virtual uint GetSourceID(void) const
Definition: channelbase.h:71
virtual bool Open(void)=0
Opens the channel changing hardware for use.
virtual uint GetNextChannel(uint chanid, ChannelChangeDirection direction) const
virtual void StoreInputChannels(void)
Saves current channel as the default channel for the current input.
virtual void Close(void)=0
Closes the channel changing hardware to use.
virtual bool IsOpen(void) const =0
Reports whether channel is already open.
virtual QString GetChannelName(void) const
Definition: channelbase.h:64
virtual void Renumber(uint sourceid, const QString &oldChanNum, const QString &newChanNum)
Changes a channum if we have it cached anywhere.
bool CheckChannel(const QString &channum) const
virtual int GetPictureAttribute(PictureAttribute) const
Definition: channelbase.h:94
virtual int GetChanID(void) const
virtual void SetFd(int fd)
Sets file descriptor.
Definition: channelbase.h:55
virtual bool Init(QString &startchannel, bool setchan)
Definition: channelbase.cpp:67
virtual int GetInputID(void) const
Definition: channelbase.h:67
static ChannelBase * CreateChannel(TVRec *tvrec, const GeneralDBOptions &genOpt, const DVBDBOptions &dvbOpt, const FireWireDBOptions &fwOpt, const QString &startchannel, bool enter_power_save_mode, QString &rbFileExt, bool setchan)
virtual bool InitPictureAttributes(void)
Definition: channelbase.h:93
virtual bool SetChannelByString(const QString &chan)=0
static bool ToggleChannel(uint chanid, int changrpid, bool delete_chan)
static int GetChannelGroupId(const QString &changroupname)
static uint GetMplexID(uint sourceid, const QString &channum)
static int GetChanID(int db_mplexid, int service_transport_id, int major_channel, int minor_channel, int program_number)
static int GetProgramNumber(uint sourceid, const QString &channum)
Definition: channelutil.h:198
static QString GetVideoFilters(uint sourceid, const QString &channum)
Definition: channelutil.h:200
static bool IsOnSameMultiplex(uint srcid, const QString &new_channum, const QString &old_channum)
static QString GetChanNum(int chan_id)
Returns the channel-number string of the given channel.
static bool GetATSCChannel(uint sourceid, const QString &channum, uint &major, uint &minor)
Class providing a generic interface to digital tuning hardware.
Definition: dtvchannel.h:34
int GetProgramNumber(void) const
Returns program number in PAT, -1 if unknown.
Definition: dtvchannel.h:89
void SaveCachedPids(const pid_cache_t &pid_cache) const
Saves MPEG PIDs to cache to database.
Definition: dtvchannel.cpp:107
QString GetTuningMode(void) const
Returns tuning mode last set by SetTuningMode().
Definition: dtvchannel.cpp:73
uint GetTransportID(void) const
Returns DVB transport_stream_id, 0 if unknown.
Definition: dtvchannel.h:105
QString GetSIStandard(void) const
Returns PSIP table standard: MPEG, DVB, ATSC, or OpenCable.
Definition: dtvchannel.cpp:45
void SetTuningMode(const QString &tuning_mode)
Sets tuning mode: "mpeg", "dvb", "atsc", etc.
Definition: dtvchannel.cpp:87
QString GetFormat(void)
Definition: dtvchannel.h:46
uint GetMajorChannel(void) const
Returns major channel, 0 if unknown.
Definition: dtvchannel.h:93
uint GetMinorChannel(void) const
Returns minor channel, 0 if unknown.
Definition: dtvchannel.h:97
void GetCachedPids(pid_cache_t &pid_cache) const
Returns cached MPEG PIDs for last tuned channel.
Definition: dtvchannel.cpp:97
QString GetSuggestedTuningMode(bool is_live_tv) const
Returns suggested tuning mode: "mpeg", "dvb", or "atsc".
Definition: dtvchannel.cpp:57
virtual bool EnterPowerSavingMode(void)
Enters power saving mode if the card supports it.
Definition: dtvchannel.h:65
uint GetOriginalNetworkID(void) const
Returns DVB original_network_id, 0 if unknown.
Definition: dtvchannel.h:101
This is a specialization of RecorderBase used to handle MPEG-2, MPEG-4, MPEG-4 AVC,...
Definition: dtvrecorder.h:35
MPEGStreamData * GetStreamData(void) const
Definition: dtvrecorder.h:58
virtual void SetStreamData(MPEGStreamData *data)
This class is intended to detect the presence of needed tables.
void SetDVBService(uint network_id, uint transport_id, int service_id)
void AddFlags(uint64_t _flags) override
ATSCStreamData * GetATSCStreamData()
Returns the ATSC stream data if it exists.
void SetChannel(int major, int minor)
virtual void SetRotorTarget(float)
Sets rotor target pos from 0.0 to 1.0.
void IgnoreEncrypted(bool ignore)
MPEGStreamData * GetStreamData()
Returns the MPEG stream data if it exists.
void SetProgramNumber(int progNum)
virtual void SetStreamData(MPEGStreamData *data)
Sets the MPEG stream data for DTVSignalMonitor to use, and connects the table signals to the monitor.
Provides interface to the tuning hardware when using DVB drivers.
Definition: dvbchannel.h:31
bool m_dvbOnDemand
Definition: tv_rec.h:85
std::chrono::milliseconds m_dvbTuningDelay
Definition: tv_rec.h:86
bool m_dvbEitScan
Definition: tv_rec.h:87
void SetDishNetEIT(bool use_dishnet_eit)
Acts as glue between ChannelBase, EITSource and EITHelper.
Definition: eitscanner.h:29
void StopEITEventProcessing(void)
Stops inserting Event Information Tables into DB.
Definition: eitscanner.cpp:221
void StartEITEventProcessing(ChannelBase *channel, EITSource *eitSource)
Start inserting Event Information Tables from the multiplex we happen to be tuned to into the databas...
Definition: eitscanner.cpp:188
void StartActiveScan(TVRec *rec, std::chrono::seconds max_seconds_per_multiplex)
Start active EIT scan.
Definition: eitscanner.cpp:253
void StopActiveScan(void)
Stop active EIT scan.
Definition: eitscanner.cpp:333
QString m_model
Definition: tv_rec.h:97
int m_connection
Definition: tv_rec.h:96
bool m_skipBtAudio
Definition: tv_rec.h:74
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
uint m_channelTimeout
Definition: tv_rec.h:76
QString m_videoDev
Definition: tv_rec.h:69
QString m_audioDev
Definition: tv_rec.h:71
uint m_signalTimeout
Definition: tv_rec.h:75
uint m_chanId
chanid restriction if applicable
Definition: inputinfo.h:51
uint m_inputId
unique key in DB for this input
Definition: inputinfo.h:49
uint m_sourceId
associated channel listings source
Definition: inputinfo.h:48
uint m_mplexId
mplexid restriction if applicable
Definition: inputinfo.h:50
virtual void Clear(void)
Definition: inputinfo.cpp:6
static bool QueueRecordingJobs(const RecordingInfo &recinfo, int jobTypes=JOB_NONE)
Definition: jobqueue.cpp:496
static void RemoveJobsFromMask(int jobs, int &mask)
Definition: jobqueue.h:202
static void AddJobsToMask(int jobs, int &mask)
Definition: jobqueue.h:201
static bool JobIsInMask(int job, int mask)
Definition: jobqueue.h:198
static bool QueueJob(int jobType, uint chanid, const QDateTime &recstartts, const QString &args="", const QString &comment="", QString host="", int flags=0, int status=JOB_QUEUED, QDateTime schedruntime=QDateTime())
Definition: jobqueue.cpp:518
static bool JobIsNotInMask(int job, int mask)
Definition: jobqueue.h:199
Keeps track of recordings in a current LiveTV instance.
Definition: livetvchain.h:33
int TotalSize(void) const
QString GetID(void) const
Definition: livetvchain.h:54
void FinishedRecording(ProgramInfo *pginfo)
void ReloadAll(const QStringList &data=QStringList())
void SetInputType(const QString &type)
Definition: livetvchain.cpp:52
ProgramInfo * GetProgramAt(int at) const
Returns program at the desired location.
void SetHostPrefix(const QString &prefix)
Definition: livetvchain.cpp:47
QString GetInputType(int pos=-1) const
void AppendNewProgram(ProgramInfo *pginfo, const QString &channum, const QString &inputname, bool discont)
Definition: livetvchain.cpp:63
Encapsulates data about MPEG stream and emits events for each table.
void SetRecordingType(const QString &recording_type)
virtual void ReturnCachedTable(const PSIPTable *psip) const
void SetIgnoreCRC(bool haveCRCbug)
virtual void Reset(void)
void SetVideoStreamsRequired(uint num)
void SetCaching(bool cacheTables)
virtual void AddListeningPID(uint pid, PIDPriority priority=kPIDPriorityNormal)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838
QVariant value(int i) const
Definition: mythdbcon.h:204
int size(void) const
Definition: mythdbcon.h:214
bool isActive(void) const
Definition: mythdbcon.h:215
bool isConnected(void) const
Only updated once during object creation.
Definition: mythdbcon.h:137
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:49
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:281
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:298
This table tells the decoder on which PIDs to find other tables, and their sizes and each table's cur...
Definition: atsctables.h:81
uint TableType(uint i) const
Definition: atsctables.h:125
uint TableCount() const
Definition: atsctables.h:121
uint TablePID(uint i) const
Definition: atsctables.h:133
QString GetHostName(void)
QString GetSetting(const QString &key, const QString &defaultval="")
void SendSystemEvent(const QString &msg)
int GetBackendServerPort(void)
Returns the locally defined backend control port.
void dispatch(const MythEvent &event)
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
int GetNumSetting(const QString &key, int defaultval=0)
void SendEvent(const MythEvent &event)
std::enable_if_t< std::chrono::__is_duration< T >::value, T > GetDurSetting(const QString &key, T defaultval=T::zero())
bool GetBoolSetting(const QString &key, bool defaultval=false)
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
T dequeue()
Removes item from front of list and returns a copy. O(1).
Definition: mythdeque.h:31
void enqueue(const T &d)
Adds item to the back of the list. O(1).
Definition: mythdeque.h:41
This class is used as a container for messages.
Definition: mythevent.h:17
long long GetWritePosition(void) const
Returns how far into a ThreadedFileWriter file we have written.
virtual bool IsOpen(void) const =0
static MythMediaBuffer * Create(const QString &Filename, bool Write, bool UseReadAhead=true, std::chrono::milliseconds Timeout=kDefaultOpenTimeout, bool StreamOnly=false)
Creates a RingBuffer instance.
static const Type kCheck
static const Type kError
void SetDuration(std::chrono::seconds Duration)
Contains a duration during which the notification will be displayed for. The duration is informative ...
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:14
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:91
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
std::vector< uint > m_possibleConflicts
Definition: tv_rec.h:137
ProgramInfo * m_info
Definition: tv_rec.h:131
bool m_hasLaterShowing
Definition: tv_rec.h:133
QDateTime m_recordingStart
Definition: tv_rec.h:132
bool m_doNotAsk
Definition: tv_rec.h:136
bool m_ask
Definition: tv_rec.h:135
static void GetPreviewImage(const ProgramInfo &pginfo, const QString &token)
Submit a request for the generation of a preview image.
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
uint GetRecordingRuleID(void) const
Definition: programinfo.h:460
bool IsSameProgramWeakCheck(const ProgramInfo &other) const
Checks for duplicate using only title, chanid and startts.
QString toString(Verbosity v=kLongDescription, const QString &sep=":", const QString &grp="\"") const
bool QueryTuningInfo(QString &channum, QString &input) const
Returns the channel and input needed to record the program.
void UpdateInUseMark(bool force=false)
QString GetRecordingGroup(void) const
Definition: programinfo.h:427
void SaveAutoExpire(AutoExpireType autoExpire, bool updateDelete=false)
Set "autoexpire" field in "recorded" table to "autoExpire".
void SetRecordingRuleType(RecordingType type)
Definition: programinfo.h:593
uint GetRecordingID(void) const
Definition: programinfo.h:457
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:405
uint QueryMplexID(void) const
Queries multiplex any recording would be made on, zero if unknown.
void SetRecordingStatus(RecStatus::Type status)
Definition: programinfo.h:592
void SetRecordingGroup(const QString &group)
Definition: programinfo.h:539
uint GetSourceID(void) const
Definition: programinfo.h:473
void SetScheduledEndTime(const QDateTime &dt)
Definition: programinfo.h:536
void SetRecordingStartTime(const QDateTime &dt)
Definition: programinfo.h:537
void SaveVideoProperties(uint mask, uint video_property_flags)
QString GetTitle(void) const
Definition: programinfo.h:368
uint QueryAverageHeight(void) const
If present in recording this loads average height of the main video stream from database's stream mar...
void MarkAsInUse(bool inuse, const QString &usedFor="")
Tracks a recording's in use status, to prevent deletion and to allow the storage scheduler to perform...
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:412
bool QueryAverageScanProgressive(void) const
If present in recording this loads average video scan type of the main video stream from database's s...
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:398
bool IsLocal(void) const
Definition: programinfo.h:358
void SetChanID(uint _chanid)
Definition: programinfo.h:534
bool IsCommercialFree(void) const
Definition: programinfo.h:489
MarkTypes QueryAverageAspectRatio(void) const
void SetRecordingRuleID(uint id)
Definition: programinfo.h:550
QString MakeUniqueKey(void) const
Creates a unique string that can be used to identify an existing recording.
Definition: programinfo.h:346
QString GetPathname(void) const
Definition: programinfo.h:350
uint GetInputID(void) const
Definition: programinfo.h:474
void ToStringList(QStringList &list) const
Serializes ProgramInfo into a QStringList which can be passed over a socket.
void SetRecordingEndTime(const QDateTime &dt)
Definition: programinfo.h:538
void SetStorageGroup(const QString &group)
Definition: programinfo.h:542
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 SetInputID(uint id)
Definition: programinfo.h:552
void SaveCommFlagged(CommFlagStatus flag)
Set "commflagged" field in "recorded" table to "flag".
QString GetSubtitle(void) const
Definition: programinfo.h:370
QString GetCategory(void) const
Definition: programinfo.h:377
virtual void SetRecordingID(uint _recordedid)
Definition: programinfo.h:590
void SetPathname(const QString &pn)
RecordingType GetRecordingRuleType(void) const
Definition: programinfo.h:462
QString QueryRecordingGroup(void) const
Query recgroup from recorded.
static QString toString(RecStatus::Type recstatus, uint id)
Converts "recstatus" into a short (unreadable) string.
virtual void Reset(void)=0
Reset the recorder to the startup state.
virtual bool IsRecording(void)
Tells whether the StartRecorder() loop is running.
virtual void Pause(bool clear=true)
Pause tells recorder to pause, it should not block.
virtual void SetVideoFilters(QString &filters)=0
Tells recorder which filters to use.
virtual int GetVideoFd(void)=0
Returns file descriptor of recorder device.
virtual void Unpause(void)
Unpause tells recorder to unpause.
virtual bool CheckForRingBufferSwitch(void)
If requested, switch to new RingBuffer/ProgramInfo objects.
virtual void Initialize(void)=0
This is called between SetOptionsFromProfile() and run() to initialize any devices,...
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
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.
virtual RecordingQuality * GetRecordingQuality(const RecordingInfo *ri) const
Returns a report about the current recordings quality.
bool GetKeyframeDurations(long long start, long long end, frm_pos_map_t &map) const
virtual long long GetFramesWritten(void)=0
Returns number of frames written to disk.
long long GetKeyframePosition(long long desired) const
Returns closest keyframe position before the desired frame.
virtual bool IsPaused(bool holding_lock=false) const
Returns true iff recorder is paused.
void SavePositionMap(bool force=false, bool finished=false)
Save the seektable to the DB.
static RecorderBase * CreateRecorder(TVRec *tvrec, ChannelBase *channel, RecordingProfile &profile, const GeneralDBOptions &genOpt)
virtual bool IsErrored(void)=0
Tells us whether an unrecoverable error has been encountered.
void SetRingBuffer(MythMediaBuffer *Buffer)
Tells recorder to use an externally created ringbuffer.
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:36
void FinishedRecording(bool allowReRecord)
If not a premature stop, adds program to history of recorded programs.
void SetDesiredStartTime(const QDateTime &dt)
RecordingRule * GetRecordingRule(void)
Returns the "record" field, creating it if necessary.
void ApplyRecordRecGroupChange(const QString &newrecgroup)
Sets the recording group, both in this RecordingInfo and in the database.
int GetAutoRunJobs(void) const
Returns a bitmap of which jobs are attached to this RecordingInfo.
void AddHistory(bool resched=true, bool forcedup=false, bool future=false)
Adds recording history, creating "record" it if necessary.
void ApplyRecordRecID(void)
Sets recordid to match RecordingRule recordid.
void UpdateRecordingEnd(void)
Update information in the recorded table when the end-time of a recording is changed.
uint64_t GetFilesize(void) const override
void LoadRecordingFile()
void StartedRecording(const QString &ext)
Inserts this RecordingInfo into the database as an existing recording.
void SetDesiredEndTime(const QDateTime &dt)
RecordingFile * GetRecordingFile() const
bool IsDamaged(void) const
QString toStringXML(void) const
RecordingType m_type
AutoExpireType GetAutoExpire(void) const
Definition: recordingrule.h:62
QString m_recProfile
bool Save(bool sendSig=true)
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
virtual int IncrRef(void)
Increments reference count.
static void Init()
Initializes the some static constants needed by SignalMonitorValue.
void AddListener(SignalMonitorListener *listener)
static SignalMonitor * Init(const QString &cardtype, int db_cardnum, ChannelBase *channel, bool release_stream)
static const uint64_t kDVBSigMon_WaitForPos
Wait for rotor to complete turning the antenna.
static const uint64_t kDTVSigMon_WaitForSDT
bool IsErrored(void) const
Definition: signalmonitor.h:82
virtual bool HasExtraSlowTuning(void) const
Definition: signalmonitor.h:60
virtual bool IsAllGood(void) const
Definition: signalmonitor.h:81
static bool IsSupported(const QString &cardtype)
static const uint64_t kDTVSigMon_WaitForPMT
void SetUpdateRate(std::chrono::milliseconds msec)
Sets the number of milliseconds between signal monitoring attempts in the signal monitoring thread.
static const uint64_t kDTVSigMon_WaitForPAT
void SetNotifyFrontend(bool notify)
Enables or disables frontend notification of the current signal value.
Definition: signalmonitor.h:92
static const uint64_t kDTVSigMon_WaitForMGT
static bool IsRequired(const QString &cardtype)
Returns true iff the card type supports signal monitoring.
virtual void Start()
Start signal monitoring thread.
static QString GetSourceName(uint sourceid)
Definition: sourceutil.cpp:47
virtual QString getValue(void) const
QString FindNextDirMostFree(void)
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:142
bool SwitchLiveTVRingBuffer(const QString &channum, bool discont, bool set_rec)
Definition: tv_rec.cpp:4770
V4LChannel * GetV4LChannel(void)
Definition: tv_rec.cpp:1264
bool ShouldSwitchToAnotherInput(const QString &chanid) const
Checks if named channel exists on current tuner, or another tuner.
Definition: tv_rec.cpp:2234
bool GetChannelInfo(uint &chanid, uint &sourceid, QString &callsign, QString &channum, QString &channame, QString &xmltvid) const
Definition: tv_rec.cpp:3340
static const uint kFlagRecording
final result desired is a timed recording
Definition: tv_rec.h:453
static const uint kFlagAnyRecRunning
Definition: tv_rec.h:481
TuningQueue m_tuningRequests
Definition: tv_rec.h:396
uint m_parentId
Definition: tv_rec.h:373
bool SetupDTVSignalMonitor(bool EITscan)
Tells DTVSignalMonitor what channel to look for.
Definition: tv_rec.cpp:1908
int m_audioSampleRateDB
Definition: tv_rec.h:366
QHash< QString, int > m_autoRunJobs
Definition: tv_rec.h:413
bool GetKeyframePositions(int64_t start, int64_t end, frm_pos_map_t &map) const
Returns byte position in RingBuffer of a keyframes according to recorder.
Definition: tv_rec.cpp:2667
static bool GetDevices(uint inputid, uint &parentid, GeneralDBOptions &gen_opts, DVBDBOptions &dvb_opts, FireWireDBOptions &firewire_opts)
Definition: tv_rec.cpp:1771
void NotifySchedulerOfRecording(RecordingInfo *rec)
Tell scheduler about the recording.
Definition: tv_rec.cpp:2801
void PauseRecorder(void)
Tells "recorder" to pause, used for channel and input changes.
Definition: tv_rec.cpp:2976
void SetChannelTimeout(std::chrono::milliseconds timeout)
Definition: tv_rec.cpp:3417
DTVChannel * GetDTVChannel(void)
Definition: tv_rec.cpp:1259
uint GetFlags(void) const
Definition: tv_rec.h:244
bool CreateChannel(const QString &startchannel, bool enter_power_save_mode)
Definition: tv_rec.cpp:94
bool m_earlyCommFlag
Definition: tv_rec.h:361
void FinishedRecording(RecordingInfo *curRec, RecordingQuality *recq)
If not a premature stop, adds program to history of recorded programs.
Definition: tv_rec.cpp:856
QDateTime m_signalEventCmdTimeout
Definition: tv_rec.h:343
static const uint kFlagErrored
Definition: tv_rec.h:446
uint m_stateFlags
Definition: tv_rec.h:395
static QMutex s_eitLock
Definition: tv_rec.h:377
TVState RemoveRecording(TVState state) const
If "state" is kState_RecordingOnly or kState_WatchingLiveTV, returns a kState_None,...
Definition: tv_rec.cpp:794
bool SetVideoFiltersForChannel(uint sourceid, const QString &channum)
Definition: tv_rec.cpp:2505
void SetPseudoLiveTVRecording(RecordingInfo *pi)
Sets the pseudo LiveTV RecordingInfo.
Definition: tv_rec.cpp:364
static const uint kFlagEITScannerRunning
Definition: tv_rec.h:477
TVRec(int _inputid)
Performs instance initialization not requiring access to database.
Definition: tv_rec.cpp:85
bool SetChannelInfo(uint chanid, uint sourceid, const QString &oldchannum, const QString &callsign, const QString &channum, const QString &channame, const QString &xmltvid)
Definition: tv_rec.cpp:3381
QString GetChainID(void)
Get the chainid of the livetv instance.
Definition: tv_rec.cpp:2752
void TuningFrequency(const TuningRequest &request)
Performs initial tuning required for any tuning event.
Definition: tv_rec.cpp:3724
QMutex m_nextLiveTVDirLock
Definition: tv_rec.h:422
static const uint kFlagExitPlayer
Definition: tv_rec.h:444
void SetFlags(uint f, const QString &file, int line)
Definition: tv_rec.cpp:4505
static const uint kFlagFinishRecording
Definition: tv_rec.h:445
static const uint kFlagEITScan
final result desired is an EIT Scan
Definition: tv_rec.h:460
TVState m_desiredNextState
Definition: tv_rec.h:392
static bool StateIsRecording(TVState state)
Returns true if "state" is kState_RecordingOnly, or kState_WatchingLiveTV.
Definition: tv_rec.cpp:774
QDateTime m_recordEndTime
Definition: tv_rec.h:411
~TVRec(void) override
Stops the event and scanning threads and deletes any ChannelBase, RingBuffer, and RecorderBase instan...
Definition: tv_rec.cpp:214
void SetChannel(const QString &name, uint requestType=kFlagDetect)
Changes to a named channel on the current tuner.
Definition: tv_rec.cpp:3138
bool Init(void)
Performs instance initialization, returns true on success.
Definition: tv_rec.cpp:156
GeneralDBOptions m_genOpt
Definition: tv_rec.h:381
void TuningNewRecorder(MPEGStreamData *streamData)
Creates a recorder instance.
Definition: tv_rec.cpp:4393
EITScanner * m_scanner
Definition: tv_rec.h:341
RecordingInfo * m_curRecording
Definition: tv_rec.h:410
QMutex m_setChannelLock
Definition: tv_rec.h:388
bool m_changeState
Definition: tv_rec.h:393
bool QueueEITChannelChange(const QString &name)
Queues up a channel change for the EITScanner.
Definition: tv_rec.cpp:3187
QString m_nextLiveTVDir
Definition: tv_rec.h:421
DVBDBOptions m_dvbOpt
Definition: tv_rec.h:382
std::chrono::seconds m_eitTransportTimeout
Definition: tv_rec.h:364
QString m_liveTVStartChannel
Definition: tv_rec.h:424
QString m_overRecordCategory
Definition: tv_rec.h:369
ProgramInfo * GetRecording(void)
Allocates and returns a ProgramInfo for the current recording.
Definition: tv_rec.cpp:277
friend class TuningRequest
Definition: tv_rec.h:145
QWaitCondition m_triggerEventSleepWait
Definition: tv_rec.h:404
uint m_inputId
Definition: tv_rec.h:372
void SetNextLiveTVDir(QString dir)
Definition: tv_rec.cpp:4617
QString m_recProfileName
Definition: tv_rec.h:385
RecStatus::Type GetRecordingStatus(void) const
Definition: tv_rec.cpp:711
static const uint kFlagFrontendReady
Definition: tv_rec.h:442
static const uint kFlagKillRingBuffer
Definition: tv_rec.h:467
long long GetMaxBitrate(void) const
Returns the maximum bits per second this recorder can produce.
Definition: tv_rec.cpp:2694
RecStatus::Type StartRecording(ProgramInfo *pginfo)
Tells TVRec to Start recording the program "rcinfo" as soon as possible.
Definition: tv_rec.cpp:437
bool WaitForEventThreadSleep(bool wake=true, std::chrono::milliseconds time=std::chrono::milliseconds::max())
You MUST HAVE the stateChange-lock locked when you call this method!
Definition: tv_rec.cpp:1656
static const uint kFlagRecorderRunning
Definition: tv_rec.h:480
void ToggleChannelFavorite(const QString &changroupname)
Toggles whether the current channel should be on our favorites list.
Definition: tv_rec.cpp:3004
std::chrono::seconds m_eitCrawlIdleStart
Definition: tv_rec.h:363
void HandleStateChange(void)
Changes the internalState to the desiredNextState if possible.
Definition: tv_rec.cpp:1051
QRecursiveMutex m_pendingRecLock
Definition: tv_rec.h:390
void RecorderPaused(void)
This is a callback, called by the "recorder" instance when it has actually paused.
Definition: tv_rec.cpp:2995
void WakeEventLoop(void)
Definition: tv_rec.cpp:250
QDateTime m_eitScanStopTime
Definition: tv_rec.h:399
static const uint kFlagKillRec
close recorder, discard recording
Definition: tv_rec.h:464
QMutex m_triggerEventLoopLock
Definition: tv_rec.h:400
bool m_reachedRecordingDeadline
Definition: tv_rec.h:349
int ChangePictureAttribute(PictureAdjustType type, PictureAttribute attr, bool direction)
Returns current value [0,100] if it succeeds, -1 otherwise.
Definition: tv_rec.cpp:3071
TuningRequest m_lastTuningRequest
Definition: tv_rec.h:397
bool IsErrored(void) const
Returns true is "errored" is true, false otherwise.
Definition: tv_rec.h:237
QDateTime m_eitScanStartTime
Definition: tv_rec.h:398
RecordingInfo * SwitchRecordingRingBuffer(const RecordingInfo &rcinfo)
Definition: tv_rec.cpp:4838
long long GetFramesWritten(void)
Returns number of frames written to disk by recorder.
Definition: tv_rec.cpp:2619
void StartedRecording(RecordingInfo *curRec)
Inserts a "curRec" into the database.
Definition: tv_rec.cpp:830
AutoRunInitType
Definition: tv_rec.h:327
@ kAutoRunProfile
Definition: tv_rec.h:327
@ kAutoRunNone
Definition: tv_rec.h:327
QDateTime m_preFailDeadline
Definition: tv_rec.h:350
void TuningShutdowns(const TuningRequest &request)
This shuts down anything that needs to be shut down before handling the passed in tuning request.
Definition: tv_rec.cpp:3635
void RecordPending(const ProgramInfo *rcinfo, std::chrono::seconds secsleft, bool hasLater)
Tells TVRec "rcinfo" is the next pending recording.
Definition: tv_rec.cpp:311
bool CheckChannelPrefix(const QString &prefix, uint &complete_valid_channel_on_rec, bool &is_extra_char_useful, QString &needed_spacer) const
Checks a prefix against the channels in the DB.
Definition: tv_rec.cpp:2370
void StopLiveTV(void)
Tells TVRec to stop a "Live TV" recorder.
Definition: tv_rec.cpp:2929
void TeardownAll(void)
Definition: tv_rec.cpp:233
PendingMap m_pendingRecordings
Definition: tv_rec.h:417
void GetNextProgram(BrowseDirection direction, QString &title, QString &subtitle, QString &desc, QString &category, QString &starttime, QString &endtime, QString &callsign, QString &iconpath, QString &channum, uint &chanid, QString &seriesid, QString &programid)
Definition: tv_rec.cpp:3214
RecorderBase * m_recorder
Definition: tv_rec.h:338
long long GetFilePosition(void)
Returns total number of bytes written by RingBuffer.
Definition: tv_rec.cpp:2634
static const uint kFlagRunMainLoop
Definition: tv_rec.h:443
bool m_triggerEventSleepSignal
Definition: tv_rec.h:405
static const uint kFlagRingBufferReady
Definition: tv_rec.h:485
SignalMonitor * m_signalMonitor
Definition: tv_rec.h:340
std::chrono::seconds m_eitScanPeriod
Definition: tv_rec.h:365
void TeardownRecorder(uint request_flags)
Tears down the recorder.
Definition: tv_rec.cpp:1185
TVState m_internalState
Definition: tv_rec.h:391
bool IsBusy(InputInfo *busy_input=nullptr, std::chrono::seconds time_buffer=5s) const
Returns true if the recorder is busy, or will be within the next time_buffer seconds.
Definition: tv_rec.cpp:2536
void CheckForRecGroupChange(void)
Check if frontend changed the recording group.
Definition: tv_rec.cpp:2767
bool m_isPip
Definition: tv_rec.h:374
QString TuningGetChanNum(const TuningRequest &request, QString &input) const
Definition: tv_rec.cpp:3468
FireWireDBOptions m_fwOpt
Definition: tv_rec.h:383
void run(void) override
Event handling method, contains event loop.
Definition: tv_rec.cpp:1347
static const uint kFlagRec
Definition: tv_rec.h:456
QWaitCondition m_triggerEventLoopWait
Definition: tv_rec.h:401
static const uint kFlagAnyRunning
Definition: tv_rec.h:482
void SetRecordingStatus(RecStatus::Type new_status, int line, bool have_lock=false)
Definition: tv_rec.cpp:717
bool m_runJobOnHostOnly
Definition: tv_rec.h:362
void CancelNextRecording(bool cancel)
Tells TVRec to cancel the upcoming recording.
Definition: tv_rec.cpp:387
LiveTVChain * m_tvChain
Definition: tv_rec.h:427
QString m_rbFileExt
Definition: tv_rec.h:431
uint m_signalMonitorCheckCnt
Definition: tv_rec.h:348
MythMediaBuffer * m_buffer
Definition: tv_rec.h:430
static const uint kFlagLiveTV
final result desired is LiveTV recording
Definition: tv_rec.h:451
QWaitCondition m_triggerLiveTVDir
Definition: tv_rec.h:423
static const uint kFlagCancelNextRecording
Definition: tv_rec.h:447
void SetRingBuffer(MythMediaBuffer *Buffer)
Sets "ringBuffer", deleting any existing RingBuffer.
Definition: tv_rec.cpp:3428
QString GetInput(void) const
Returns current input.
Definition: tv_rec.cpp:3087
bool m_transcodeFirst
Definition: tv_rec.h:360
ChannelBase * m_channel
Definition: tv_rec.h:339
QDateTime m_startRecordingDeadline
Definition: tv_rec.h:346
static TVRec * GetTVRec(uint inputid)
Definition: tv_rec.cpp:4912
void SetLiveRecording(int recording)
Tells the Scheduler about changes to the recording status of the LiveTV recording.
Definition: tv_rec.cpp:2883
static QReadWriteLock s_inputsLock
Definition: tv_rec.h:434
TVState RemovePlaying(TVState state) const
Returns TVState that would remove the playing, but potentially keep recording if we are watching an i...
Definition: tv_rec.cpp:810
void RingBufferChanged(MythMediaBuffer *Buffer, RecordingInfo *pginfo, RecordingQuality *recq)
Definition: tv_rec.cpp:3445
static const uint kFlagDummyRecorderRunning
Definition: tv_rec.h:479
QRecursiveMutex m_stateChangeLock
Definition: tv_rec.h:389
RecordingInfo * m_pseudoLiveTVRecording
Definition: tv_rec.h:420
bool WaitForNextLiveTVDir(void)
Definition: tv_rec.cpp:4604
static const uint kFlagSignalMonitorRunning
Definition: tv_rec.h:476
static QString FlagToString(uint f)
Definition: tv_rec.cpp:4523
TVState GetState(void) const
Returns the TVState of the recorder.
Definition: tv_rec.cpp:263
bool CreateLiveTVRingBuffer(const QString &channum)
Definition: tv_rec.cpp:4724
static const uint kFlagPendingActions
Definition: tv_rec.h:473
bool m_triggerEventLoopSignal
Definition: tv_rec.h:402
void TuningRestartRecorder(void)
Restarts a stopped recorder or unpauses a paused recorder.
Definition: tv_rec.cpp:4440
DTVRecorder * GetDTVRecorder(void)
Definition: tv_rec.cpp:1240
bool m_reachedPreFail
Definition: tv_rec.h:351
int GetPictureAttribute(PictureAttribute attr)
Definition: tv_rec.cpp:3053
std::chrono::seconds m_overRecordSecNrml
Definition: tv_rec.h:367
bool SetupSignalMonitor(bool tablemon, bool EITscan, bool notify)
This creates a SignalMonitor instance and begins signal monitoring.
Definition: tv_rec.cpp:2088
QMutex m_triggerEventSleepLock
Definition: tv_rec.h:403
volatile bool m_switchingBuffer
Definition: tv_rec.h:406
static const uint kFlagAntennaAdjust
antenna adjusting mode (LiveTV without recording).
Definition: tv_rec.h:455
uint GetInputId(void) const
Returns the inputid.
Definition: tv_rec.h:233
void InitAutoRunJobs(RecordingInfo *rec, AutoRunInitType t, RecordingProfile *recpro, int line)
Definition: tv_rec.cpp:2847
static const uint kFlagNoRec
Definition: tv_rec.h:466
bool m_signalEventCmdSent
Definition: tv_rec.h:344
static const uint kFlagCloseRec
close recorder, keep recording
Definition: tv_rec.h:462
static const uint kFlagDetect
Definition: tv_rec.h:486
QDateTime m_signalMonitorDeadline
Definition: tv_rec.h:347
QDateTime GetRecordEndTime(const ProgramInfo *pi) const
Returns recording end time with proper post-roll.
Definition: tv_rec.cpp:374
RecStatus::Type m_recStatus
Definition: tv_rec.h:407
void HandlePendingRecordings(void)
Definition: tv_rec.cpp:1693
static QMap< uint, TVRec * > s_inputs
Definition: tv_rec.h:435
void SpawnLiveTV(LiveTVChain *newchain, bool pip, QString startchan)
Tells TVRec to spawn a "Live TV" recorder.
Definition: tv_rec.cpp:2722
void HandleTuning(void)
Handles all tuning events.
Definition: tv_rec.cpp:3559
void StopRecording(bool killFile=false)
Changes from a recording state to kState_None.
Definition: tv_rec.cpp:747
void EnableActiveScan(bool enable)
Definition: tv_rec.cpp:4920
std::chrono::milliseconds SetSignalMonitoringRate(std::chrono::milliseconds rate, int notifyFrontend=1)
Sets the signal monitoring rate.
Definition: tv_rec.cpp:2180
void ClearFlags(uint f, const QString &file, int line)
Definition: tv_rec.cpp:4514
bool IsReallyRecording(void)
Returns true if frontend can consider the recorder started.
Definition: tv_rec.cpp:2525
bool m_pauseNotify
Definition: tv_rec.h:394
int64_t GetKeyframePosition(uint64_t desired) const
Returns byte position in RingBuffer of a keyframe according to recorder.
Definition: tv_rec.cpp:2650
void CloseChannel(void)
Definition: tv_rec.cpp:1245
static constexpr std::chrono::milliseconds kSignalMonitoringRate
How many milliseconds the signal monitor should wait between checks.
Definition: tv_rec.h:439
static const uint kFlagNeedToStartRecorder
Definition: tv_rec.h:472
bool TuningOnSameMultiplex(TuningRequest &request)
Definition: tv_rec.cpp:3507
MThread * m_recorderThread
Recorder thread, runs RecorderBase::run().
Definition: tv_rec.h:357
std::chrono::seconds m_overRecordSecCat
Definition: tv_rec.h:368
bool CheckChannel(const QString &name) const
Checks if named channel exists on current tuner.
Definition: tv_rec.cpp:2324
static const uint kFlagWaitingForSignal
Definition: tv_rec.h:471
void TeardownSignalMonitor(void)
If a SignalMonitor instance exists, the monitoring thread is stopped and the instance is deleted.
Definition: tv_rec.cpp:2142
MPEGStreamData * TuningSignalCheck(void)
This checks if we have a channel lock.
Definition: tv_rec.cpp:3967
bool GetKeyframeDurations(int64_t start, int64_t end, frm_pos_map_t &map) const
Definition: tv_rec.cpp:2678
std::vector< uint > m_eitInputs
Definition: tv_rec.h:378
void ChangeState(TVState nextState)
Puts a state change on the nextState queue.
Definition: tv_rec.cpp:1163
bool GetProgramRingBufferForLiveTV(RecordingInfo **pginfo, MythMediaBuffer **Buffer, const QString &channum)
Definition: tv_rec.cpp:4625
bool TuningNewRecorderReal(MPEGStreamData *streamData, RecordingInfo **rec, RecordingProfile &profile, bool had_dummyrec)
Helper function for TVRec::TuningNewRecorder.
Definition: tv_rec.cpp:4237
MThread * m_eventThread
Event processing thread, runs TVRec::run().
Definition: tv_rec.h:355
bool HasFlags(uint f) const
Definition: tv_rec.h:287
float GetFramerate(void)
Returns recordering frame rate from the recorder.
Definition: tv_rec.cpp:2604
DTVSignalMonitor * GetDTVSignalMonitor(void)
Definition: tv_rec.cpp:2218
uint GetSourceID(void) const
Returns current source id.
Definition: tv_rec.cpp:3097
static const uint kFlagWaitingForRecPause
Definition: tv_rec.h:470
QString SetInput(QString input)
Changes to the specified input.
Definition: tv_rec.cpp:3112
static bool StateIsPlaying(TVState state)
Returns true if we are in any state associated with a player.
Definition: tv_rec.cpp:784
QString LoadProfile(void *tvchain, RecordingInfo *rec, RecordingProfile &profile) const
Definition: tv_rec.cpp:4191
int m_progNum
Definition: tv_rec.h:121
bool IsOnSameMultiplex(void) const
Definition: tv_rec.h:112
uint m_minorChan
Definition: tv_rec.h:120
uint m_flags
Definition: tv_rec.h:115
RecordingInfo * m_program
Definition: tv_rec.h:116
uint m_majorChan
Definition: tv_rec.h:119
QString toString(void) const
Definition: tv_rec.cpp:4947
QString m_channel
Definition: tv_rec.h:117
QString m_input
Definition: tv_rec.h:118
Implements tuning for TV cards using the V4L driver API, both versions 1 and 2.
Definition: v4lchannel.h:32
unsigned int uint
Definition: compat.h:60
#define minor(X)
Definition: compat.h:58
unsigned short uint16_t
Definition: iso6937tables.h:3
@ JOB_METADATA
Definition: jobqueue.h:80
@ JOB_NONE
Definition: jobqueue.h:75
@ JOB_COMMFLAG
Definition: jobqueue.h:79
@ JOB_TRANSCODE
Definition: jobqueue.h:78
@ JOB_LIVE_REC
Definition: jobqueue.h:61
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
Convenience inline random number generator functions.
void SendMythSystemRecEvent(const QString &msg, const RecordingInfo *pginfo)
std::chrono::seconds secsInPast(const QDateTime &past)
Definition: mythdate.cpp:212
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
@ ISODate
Default UTC.
Definition: mythdate.h:17
std::chrono::seconds secsInFuture(const QDateTime &future)
Definition: mythdate.cpp:217
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
uint32_t MythRandom()
generate 32 random bits
Definition: mythrandom.h:20
def error(message)
Definition: smolt.py:409
def write(text, progress=True)
Definition: mythburn.py:306
const QString kRecorderInUseID
MarkTypes
Definition: programtypes.h:46
@ MARK_ASPECT_2_21_1
Definition: programtypes.h:67
@ MARK_ASPECT_16_9
Definition: programtypes.h:66
@ kLiveTVAutoExpire
Definition: programtypes.h:196
@ COMM_FLAG_COMMFREE
Definition: programtypes.h:123
QMap< long long, long long > frm_pos_map_t
Frame # -> File offset map.
Definition: programtypes.h:44
@ kNotRecording
@ kSingleRecord
QString StateToString(TVState state)
Returns a human readable QString representing a TVState.
Definition: tv.cpp:11
BrowseDirection
Used to request ProgramInfo for channel browsing.
Definition: tv.h:41
@ BROWSE_SAME
Fetch browse information on current channel and time.
Definition: tv.h:43
@ BROWSE_RIGHT
Fetch information on current channel in the future.
Definition: tv.h:47
@ BROWSE_LEFT
Fetch information on current channel in the past.
Definition: tv.h:46
@ BROWSE_UP
Fetch information on previous channel.
Definition: tv.h:44
@ BROWSE_FAVORITE
Fetch information on the next favorite channel.
Definition: tv.h:48
@ BROWSE_DOWN
Fetch information on next channel.
Definition: tv.h:45
PictureAdjustType
Definition: tv.h:124
ChannelChangeDirection
ChannelChangeDirection is an enumeration of possible channel changing directions.
Definition: tv.h:32
@ CHANNEL_DIRECTION_SAME
Definition: tv.h:36
@ CHANNEL_DIRECTION_DOWN
Definition: tv.h:34
@ CHANNEL_DIRECTION_FAVORITE
Definition: tv.h:35
@ CHANNEL_DIRECTION_UP
Definition: tv.h:33
TVState
TVState is an enumeration of the states used by TV and TVRec.
Definition: tv.h:54
@ kState_None
None State, this is the initial state in both TV and TVRec, it indicates that we are ready to change ...
Definition: tv.h:61
@ kState_RecordingOnly
Recording Only is a TVRec only state for when we are recording a program, but there is no one current...
Definition: tv.h:87
@ kState_WatchingLiveTV
Watching LiveTV is the state for when we are watching a recording and the user has control over the c...
Definition: tv.h:66
@ kState_Error
Error State, if we ever try to enter this state errored is set.
Definition: tv.h:57
@ kState_WatchingPreRecorded
Watching Pre-recorded is a TV only state for when we are watching a pre-existing recording.
Definition: tv.h:70
@ kState_ChangingState
This is a placeholder state which we never actually enter, but is returned by GetState() when we are ...
Definition: tv.h:92
static std::chrono::seconds eit_start_rand(uint inputId, std::chrono::seconds eitTransportTimeout)
Definition: tv_rec.cpp:1332
#define LOC
Definition: tv_rec.cpp:48
static void GetPidsToCache(DTVSignalMonitor *dtvMon, pid_cache_t &pid_cache)
Definition: tv_rec.cpp:1858
static int get_highest_input(void)
Definition: tv_rec.cpp:1315
static void apply_broken_dvb_driver_crc_hack(ChannelBase *, MPEGStreamData *)
Definition: tv_rec.cpp:4968
static int init_jobs(const RecordingInfo *rec, RecordingProfile &profile, bool on_host, bool transcode_bfr_comm, bool on_line_comm)
Definition: tv_rec.cpp:4133
static bool get_use_eit(uint inputid)
Definition: tv_rec.cpp:1274
#define SET_NEXT()
Definition: tv_rec.cpp:1040
static QString add_spacer(const QString &channel, const QString &spacer)
Adds the spacer before the last character in chan.
Definition: tv_rec.cpp:2335
static bool ApplyCachedPids(DTVSignalMonitor *dtvMon, const DTVChannel *channel)
Definition: tv_rec.cpp:1875
static bool is_dishnet_eit(uint inputid)
Definition: tv_rec.cpp:1294
#define TRANSITION(ASTATE, BSTATE)
Definition: tv_rec.cpp:1038
bool RemoteStopRecording(uint inputid)
bool RemoteIsBusy(uint inputid, InputInfo &busy_input)
bool RemoteRecordPending(uint inputid, const ProgramInfo *pginfo, std::chrono::seconds secsleft, bool hasLater)
uint RemoteGetState(uint inputid)
PictureAttribute