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
3421{
3422 QMutexLocker lock(&m_stateChangeLock);
3423
3424 MythMediaBuffer *oldbuffer = m_buffer;
3425 m_buffer = Buffer;
3426
3427 if (oldbuffer && (oldbuffer != Buffer))
3428 {
3430 ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3431 delete oldbuffer;
3432 }
3433
3434 m_switchingBuffer = false;
3435}
3436
3438{
3439 LOG(VB_GENERAL, LOG_INFO, LOC + "RingBufferChanged()");
3440
3441 QMutexLocker lock(&m_stateChangeLock);
3442
3443 if (pginfo)
3444 {
3445 if (m_curRecording)
3446 {
3449 delete m_curRecording;
3450 }
3452 m_curRecording = new RecordingInfo(*pginfo);
3455 }
3456
3458}
3459
3461 QString &input) const
3462{
3463 QString channum;
3464
3465 if (request.m_program)
3466 {
3467 request.m_program->QueryTuningInfo(channum, input);
3468 return channum;
3469 }
3470
3471 channum = request.m_channel;
3472 input = request.m_input;
3473
3474 // If this is Live TV startup, we need a channel...
3475 if (channum.isEmpty() && (request.m_flags & kFlagLiveTV))
3476 {
3477 if (!m_liveTVStartChannel.isEmpty())
3478 channum = m_liveTVStartChannel;
3479 else
3480 {
3483 }
3484 }
3485 if (request.m_flags & kFlagLiveTV)
3486 m_channel->Init(channum, false);
3487
3488 if (m_channel && !channum.isEmpty() && (channum.indexOf("NextChannel") >= 0))
3489 {
3490 // FIXME This is just horrible
3491 int dir = channum.right(channum.length() - 12).toInt();
3492 uint chanid = m_channel->GetNextChannel(0, static_cast<ChannelChangeDirection>(dir));
3493 channum = ChannelUtil::GetChanNum(chanid);
3494 }
3495
3496 return channum;
3497}
3498
3500{
3501 if ((request.m_flags & kFlagAntennaAdjust) || request.m_input.isEmpty() ||
3503 {
3504 return false;
3505 }
3506
3507 uint sourceid = m_channel->GetSourceID();
3508 QString oldchannum = m_channel->GetChannelName();
3509 QString newchannum = request.m_channel;
3510
3511 if (ChannelUtil::IsOnSameMultiplex(sourceid, newchannum, oldchannum))
3512 {
3514 auto *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
3515
3516 if (atsc)
3517 {
3518 uint major = 0;
3519 uint minor = 0;
3520 ChannelUtil::GetATSCChannel(sourceid, newchannum, major, minor);
3521
3522 if (minor && atsc->HasChannel(major, minor))
3523 {
3524 request.m_majorChan = major;
3525 request.m_minorChan = minor;
3526 return true;
3527 }
3528 }
3529
3530 if (mpeg)
3531 {
3532 uint progNum = ChannelUtil::GetProgramNumber(sourceid, newchannum);
3533 if (mpeg->HasProgram(progNum))
3534 {
3535 request.m_progNum = progNum;
3536 return true;
3537 }
3538 }
3539 }
3540
3541 return false;
3542}
3543
3552{
3553 if (!m_tuningRequests.empty())
3554 {
3555 TuningRequest request = m_tuningRequests.front();
3556 LOG(VB_RECORD, LOG_INFO, LOC +
3557 "HandleTuning Request: " + request.toString());
3558
3559 QString input;
3560 request.m_channel = TuningGetChanNum(request, input);
3561 request.m_input = input;
3562
3563 if (TuningOnSameMultiplex(request))
3564 LOG(VB_CHANNEL, LOG_INFO, LOC + "On same multiplex");
3565
3566 TuningShutdowns(request);
3567
3568 // The dequeue isn't safe to do until now because we
3569 // release the stateChangeLock to teardown a recorder
3571
3572 // Now we start new stuff
3573 if (request.m_flags & (kFlagRecording|kFlagLiveTV|
3575 {
3576 if (!m_recorder)
3577 {
3578 LOG(VB_RECORD, LOG_INFO, LOC +
3579 "No recorder yet, calling TuningFrequency");
3580 TuningFrequency(request);
3581 }
3582 else
3583 {
3584 LOG(VB_RECORD, LOG_INFO, LOC + "Waiting for recorder pause..");
3585 SetFlags(kFlagWaitingForRecPause, __FILE__, __LINE__);
3586 }
3587 }
3588 m_lastTuningRequest = request;
3589 }
3590
3592 {
3593 if (!m_recorder || !m_recorder->IsPaused())
3594 return;
3595
3596 ClearFlags(kFlagWaitingForRecPause, __FILE__, __LINE__);
3597 LOG(VB_RECORD, LOG_INFO, LOC +
3598 "Recorder paused, calling TuningFrequency");
3600 }
3601
3602 MPEGStreamData *streamData = nullptr;
3604 {
3605 streamData = TuningSignalCheck();
3606 if (streamData == nullptr)
3607 return;
3608 }
3609
3611 {
3612 if (m_recorder)
3614 else
3615 TuningNewRecorder(streamData);
3616
3617 // If we got this far it is safe to set a new starting channel...
3618 if (m_channel)
3620 }
3621}
3622
3628{
3629 LOG(VB_RECORD, LOG_INFO, LOC + QString("TuningShutdowns(%1)")
3630 .arg(request.toString()));
3631
3632 if (m_scanner && !(request.m_flags & kFlagEITScan) &&
3634 {
3636 ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
3638 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
3639 }
3640
3641 if (m_scanner && !request.IsOnSameMultiplex())
3643
3645 {
3646 MPEGStreamData *sd = nullptr;
3647 if (GetDTVSignalMonitor())
3650 ClearFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3651
3652 // Delete StreamData if it is not in use by the recorder.
3653 MPEGStreamData *rec_sd = nullptr;
3654 if (GetDTVRecorder())
3655 rec_sd = GetDTVRecorder()->GetStreamData();
3656 if (sd && (sd != rec_sd))
3657 delete sd;
3658 }
3660 ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3661
3662 // At this point any waits are canceled.
3663
3664 if (request.m_flags & kFlagNoRec)
3665 {
3667 {
3669 ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3671 }
3672
3674 (m_curRecording &&
3677 {
3678 m_stateChangeLock.unlock();
3679 TeardownRecorder(request.m_flags);
3680 m_stateChangeLock.lock();
3681 }
3682 // At this point the recorders are shut down
3683
3684 CloseChannel();
3685 // At this point the channel is shut down
3686 }
3687
3688 if (m_buffer && (request.m_flags & kFlagKillRingBuffer))
3689 {
3690 LOG(VB_RECORD, LOG_INFO, LOC + "Tearing down RingBuffer");
3691 SetRingBuffer(nullptr);
3692 // At this point the ringbuffer is shut down
3693 }
3694
3695 // Clear pending actions from last request
3696 ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
3697}
3698
3717{
3718 LOG(VB_GENERAL, LOG_INFO, LOC + QString("TuningFrequency(%1)")
3719 .arg(request.toString()));
3720
3721 DTVChannel *dtvchan = GetDTVChannel();
3722 if (dtvchan)
3723 {
3724 MPEGStreamData *mpeg = nullptr;
3725
3726 if (GetDTVRecorder())
3728
3729 // Tune with SI table standard (dvb, atsc, mpeg) from database, see issue #452
3731
3732 const QString tuningmode = (HasFlags(kFlagEITScannerRunning)) ?
3733 dtvchan->GetSIStandard() :
3734 dtvchan->GetSuggestedTuningMode(
3736
3737 dtvchan->SetTuningMode(tuningmode);
3738
3739 if (request.m_minorChan && (tuningmode == "atsc"))
3740 {
3741 auto *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
3742 if (atsc)
3743 atsc->SetDesiredChannel(request.m_majorChan, request.m_minorChan);
3744 }
3745 else if (request.m_progNum >= 0)
3746 {
3747 if (mpeg)
3748 mpeg->SetDesiredProgram(request.m_progNum);
3749 }
3750 }
3751
3752 if (request.IsOnSameMultiplex())
3753 {
3754 // Update the channel number for SwitchLiveTVRingBuffer (called from
3755 // TuningRestartRecorder). This ensures that the livetvchain will be
3756 // updated with the new channel number
3757 if (m_channel)
3758 {
3760 m_channel->GetChannelName(), request.m_channel );
3761 }
3762
3763 QStringList slist;
3764 slist<<"message"<<QObject::tr("On known multiplex...");
3765 MythEvent me(QString("SIGNAL %1").arg(m_inputId), slist);
3767
3768 SetFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3769 return;
3770 }
3771
3772 QString channum = request.m_channel;
3773
3774 bool ok1 = true;
3775 if (m_channel)
3776 {
3777 m_channel->Open();
3778 if (!channum.isEmpty())
3779 ok1 = m_channel->SetChannelByString(channum);
3780 else
3781 ok1 = false;
3782 }
3783
3784 if (!ok1)
3785 {
3786 if (!(request.m_flags & kFlagLiveTV) || !(request.m_flags & kFlagEITScan))
3787 {
3788 if (m_curRecording)
3790
3791 LOG(VB_GENERAL, LOG_ERR, LOC +
3792 QString("Failed to set channel to %1. Reverting to kState_None")
3793 .arg(channum));
3796 else
3798 return;
3799 }
3800
3801 LOG(VB_GENERAL, LOG_ERR, LOC +
3802 QString("Failed to set channel to %1.").arg(channum));
3803 }
3804
3805 bool mpts_only = GetDTVChannel() &&
3806 GetDTVChannel()->GetFormat().compare("MPTS") == 0;
3807 if (mpts_only)
3808 {
3809 // Not using a signal monitor, so just set the status to recording
3811 if (m_curRecording)
3812 {
3814 }
3815 }
3816
3817
3818 bool livetv = (request.m_flags & kFlagLiveTV) != 0U;
3819 bool antadj = (request.m_flags & kFlagAntennaAdjust) != 0U;
3820 bool use_sm = !mpts_only && SignalMonitor::IsRequired(m_genOpt.m_inputType);
3821 bool use_dr = use_sm && (livetv || antadj);
3822 bool has_dummy = false;
3823
3824 if (use_dr)
3825 {
3826 // We need there to be a ringbuffer for these modes
3827 bool ok2 = false;
3829 m_pseudoLiveTVRecording = nullptr;
3830
3831 m_tvChain->SetInputType("DUMMY");
3832
3833 if (!m_buffer)
3834 ok2 = CreateLiveTVRingBuffer(channum);
3835 else
3836 ok2 = SwitchLiveTVRingBuffer(channum, true, false);
3838
3840
3841 if (!ok2)
3842 {
3843 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RingBuffer 1");
3844 return;
3845 }
3846
3847 has_dummy = true;
3848 }
3849
3850 // Start signal monitoring for devices capable of monitoring
3851 if (use_sm)
3852 {
3853 LOG(VB_RECORD, LOG_INFO, LOC + "Starting Signal Monitor");
3854 bool error = false;
3855 if (!SetupSignalMonitor(
3856 !antadj, (request.m_flags & kFlagEITScan) != 0U, livetv || antadj))
3857 {
3858 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to setup signal monitor");
3859 if (m_signalMonitor)
3860 {
3861 delete m_signalMonitor;
3862 m_signalMonitor = nullptr;
3863 }
3864
3865 // pretend the signal monitor is running to prevent segfault
3866 SetFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3867 ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3868 error = true;
3869 }
3870
3871 if (m_signalMonitor)
3872 {
3873 if (request.m_flags & kFlagEITScan)
3874 {
3876 SetVideoStreamsRequired(0);
3878 }
3879
3880 SetFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3881 ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3882 if (!antadj)
3883 {
3884 QDateTime expire = MythDate::current();
3885
3886 SetFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3887 if (m_curRecording)
3888 {
3890 // If startRecordingDeadline is passed, this
3891 // recording is marked as failed, so the scheduler
3892 // can try another showing.
3894 expire.addMSecs(m_genOpt.m_channelTimeout);
3896 expire.addMSecs(m_genOpt.m_channelTimeout * 2 / 3);
3897 // Keep trying to record this showing (even if it
3898 // has been marked as failed) until the scheduled
3899 // end time.
3901 m_curRecording->GetRecordingEndTime().addSecs(-10);
3902
3903 LOG(VB_CHANNEL, LOG_DEBUG, LOC +
3904 QString("Pre-fail start deadline: %1 "
3905 "Start recording deadline: %2 "
3906 "Good signal deadline: %3")
3907 .arg(m_preFailDeadline.toLocalTime()
3908 .toString("hh:mm:ss.zzz"),
3909 m_startRecordingDeadline.toLocalTime()
3910 .toString("hh:mm:ss.zzz"),
3911 m_signalMonitorDeadline.toLocalTime()
3912 .toString("hh:mm:ss.zzz")));
3913 }
3914 else
3915 {
3917 expire.addMSecs(m_genOpt.m_channelTimeout);
3918 }
3920
3921 //System Event TUNING_TIMEOUT deadline
3923 m_signalEventCmdSent = false;
3924 }
3925 }
3926
3927 if (has_dummy && m_buffer)
3928 {
3929 // Make sure recorder doesn't point to bogus ringbuffer before
3930 // it is potentially restarted without a new ringbuffer, if
3931 // the next channel won't tune and the user exits LiveTV.
3932 if (m_recorder)
3933 m_recorder->SetRingBuffer(nullptr);
3934
3935 SetFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3936 LOG(VB_RECORD, LOG_INFO, "DummyDTVRecorder -- started");
3937 SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
3938 }
3939
3940 // if we had problems starting the signal monitor,
3941 // we don't want to start the recorder...
3942 if (error)
3943 return;
3944 }
3945
3946 // Request a recorder, if the command is a recording command
3947 ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3948 if (request.m_flags & kFlagRec && !antadj)
3949 SetFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3950}
3951
3960{
3961 RecStatus::Type newRecStatus = RecStatus::Unknown;
3962 bool keep_trying = false;
3963 QDateTime current_time = MythDate::current();
3964
3965 if ((m_signalMonitor->IsErrored() || current_time > m_signalEventCmdTimeout) &&
3967 {
3968 gCoreContext->SendSystemEvent(QString("TUNING_SIGNAL_TIMEOUT CARDID %1")
3969 .arg(m_inputId));
3970 m_signalEventCmdSent = true;
3971 }
3972
3974 {
3975 LOG(VB_RECORD, LOG_INFO, LOC + "TuningSignalCheck: Good signal");
3976 if (m_curRecording && (current_time > m_startRecordingDeadline))
3977 {
3978 newRecStatus = RecStatus::Failing;
3979 m_curRecording->SaveVideoProperties(VID_DAMAGED, VID_DAMAGED);
3980
3981 QString desc = tr("Good signal seen after %1 ms")
3983 m_startRecordingDeadline.msecsTo(current_time));
3984 QString title = m_curRecording->GetTitle();
3985 if (!m_curRecording->GetSubtitle().isEmpty())
3986 title += " - " + m_curRecording->GetSubtitle();
3987
3989 "Recording", title,
3990 tr("See 'Tuning timeout' in mythtv-setup "
3991 "for this input."));
3993
3994 LOG(VB_GENERAL, LOG_WARNING, LOC +
3995 QString("It took longer than %1 ms to get a signal lock. "
3996 "Keeping status of '%2'")
3998 .arg(RecStatus::toString(newRecStatus, kSingleRecord)));
3999 LOG(VB_GENERAL, LOG_WARNING, LOC +
4000 "See 'Tuning timeout' in mythtv-setup for this input");
4001 }
4002 else
4003 {
4004 newRecStatus = RecStatus::Recording;
4005 }
4006 }
4007 else if (m_signalMonitor->IsErrored() || current_time > m_signalMonitorDeadline)
4008 {
4009 LOG(VB_GENERAL, LOG_ERR, LOC + "TuningSignalCheck: SignalMonitor " +
4010 (m_signalMonitor->IsErrored() ? "failed" : "timed out"));
4011
4012 ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4013 newRecStatus = RecStatus::Failed;
4014
4016 {
4018 }
4019 }
4020 else if (m_curRecording && !m_reachedPreFail && current_time > m_preFailDeadline)
4021 {
4022 LOG(VB_GENERAL, LOG_ERR, LOC +
4023 "TuningSignalCheck: Hit pre-fail timeout");
4024 SendMythSystemRecEvent("REC_PREFAIL", m_curRecording);
4025 m_reachedPreFail = true;
4026 return nullptr;
4027 }
4029 current_time > m_startRecordingDeadline)
4030 {
4031 newRecStatus = RecStatus::Failing;
4033 keep_trying = true;
4034
4035 SendMythSystemRecEvent("REC_FAILING", m_curRecording);
4036
4037 QString desc = tr("Taking more than %1 ms to get a lock.")
4039 QString title = m_curRecording->GetTitle();
4040 if (!m_curRecording->GetSubtitle().isEmpty())
4041 title += " - " + m_curRecording->GetSubtitle();
4042
4044 "Recording", title,
4045 tr("See 'Tuning timeout' in mythtv-setup "
4046 "for this input."));
4047 mn.SetDuration(30s);
4049
4050 LOG(VB_GENERAL, LOG_WARNING, LOC +
4051 QString("TuningSignalCheck: taking more than %1 ms to get a lock. "
4052 "marking this recording as '%2'.")
4054 .arg(RecStatus::toString(newRecStatus, kSingleRecord)));
4055 LOG(VB_GENERAL, LOG_WARNING, LOC +
4056 "See 'Tuning timeout' in mythtv-setup for this input");
4057 }
4058 else
4059 {
4060 if (m_signalMonitorCheckCnt) // Don't flood log file
4062 else
4063 {
4064 LOG(VB_RECORD, LOG_INFO, LOC +
4065 QString("TuningSignalCheck: Still waiting. Will timeout @ %1")
4066 .arg(m_signalMonitorDeadline.toLocalTime()
4067 .toString("hh:mm:ss.zzz")));
4069 }
4070 return nullptr;
4071 }
4072
4073 SetRecordingStatus(newRecStatus, __LINE__);
4074
4075 if (m_curRecording)
4076 {
4077 m_curRecording->SetRecordingStatus(newRecStatus);
4078 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
4079 .arg(m_curRecording->GetInputID())
4080 .arg(m_curRecording->GetChanID())
4082 .arg(newRecStatus)
4085 }
4086
4087 if (keep_trying)
4088 return nullptr;
4089
4090 // grab useful data from DTV signal monitor before we kill it...
4091 MPEGStreamData *streamData = nullptr;
4092 if (GetDTVSignalMonitor())
4093 streamData = GetDTVSignalMonitor()->GetStreamData();
4094
4096 {
4097 // shut down signal monitoring
4099 ClearFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
4100 }
4101 ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
4102
4103 if (streamData)
4104 {
4105 auto *dsd = dynamic_cast<DVBStreamData*>(streamData);
4106 if (dsd)
4108 if (m_scanner)
4109 {
4110 if (get_use_eit(GetInputId()))
4111 {
4113 }
4114 else
4115 {
4116 LOG(VB_EIT, LOG_INFO, LOC +
4117 QString("EIT scanning disabled for video source %1")
4118 .arg(GetSourceID())); }
4119 }
4120 }
4121
4122 return streamData;
4123}
4124
4126 bool on_host, bool transcode_bfr_comm, bool on_line_comm)
4127{
4128 if (!rec)
4129 return 0; // no jobs for Live TV recordings..
4130
4131 int jobs = 0; // start with no jobs
4132
4133 // grab standard jobs flags from program info
4135
4136 // disable commercial flagging on PBS, BBC, etc.
4137 if (rec->IsCommercialFree())
4139
4140 // disable transcoding if the profile does not allow auto transcoding
4141 const StandardSetting *autoTrans = profile.byName("autotranscode");
4142 if ((!autoTrans) || (autoTrans->getValue().toInt() == 0))
4144
4145 bool ml = JobQueue::JobIsInMask(JOB_METADATA, jobs);
4146 if (ml)
4147 {
4148 // When allowed, metadata lookup should occur at the
4149 // start of a recording to make the additional info
4150 // available immediately (and for use in future jobs).
4151 QString host = (on_host) ? gCoreContext->GetHostName() : "";
4153 rec->GetChanID(),
4154 rec->GetRecordingStartTime(), "", "",
4155 host, JOB_LIVE_REC);
4156
4157 // don't do regular metadata lookup, we won't need it.
4159 }
4160
4161 // is commercial flagging enabled, and is on-line comm flagging enabled?
4162 bool rt = JobQueue::JobIsInMask(JOB_COMMFLAG, jobs) && on_line_comm;
4163 // also, we either need transcoding to be disabled or
4164 // we need to be allowed to commercial flag before transcoding?
4166 !transcode_bfr_comm;
4167 if (rt)
4168 {
4169 // queue up real-time (i.e. on-line) commercial flagging.
4170 QString host = (on_host) ? gCoreContext->GetHostName() : "";
4172 rec->GetChanID(),
4173 rec->GetRecordingStartTime(), "", "",
4174 host, JOB_LIVE_REC);
4175
4176 // don't do regular comm flagging, we won't need it.
4178 }
4179
4180 return jobs;
4181}
4182
4183QString TVRec::LoadProfile(void *tvchain, RecordingInfo *rec,
4185{
4186 // Determine the correct recording profile.
4187 // In LiveTV mode use "Live TV" profile, otherwise use the
4188 // recording's specified profile. If the desired profile can't
4189 // be found, fall back to the "Default" profile for input type.
4190 QString profileName = "Live TV";
4191 if (!tvchain && rec)
4192 profileName = rec->GetRecordingRule()->m_recProfile;
4193
4194 QString profileRequested = profileName;
4195
4196 if (profile.loadByType(profileName, m_genOpt.m_inputType,
4198 {
4199 LOG(VB_RECORD, LOG_INFO, LOC +
4200 QString("Using profile '%1' to record")
4201 .arg(profileName));
4202 }
4203 else
4204 {
4205 profileName = "Default";
4206 if (profile.loadByType(profileName, m_genOpt.m_inputType, m_genOpt.m_videoDev))
4207 {
4208 LOG(VB_RECORD, LOG_INFO, LOC +
4209 QString("Profile '%1' not found, using "
4210 "fallback profile '%2' to record")
4211 .arg(profileRequested, profileName));
4212 }
4213 else
4214 {
4215 LOG(VB_RECORD, LOG_ERR, LOC +
4216 QString("Profile '%1' not found, and unable "
4217 "to load fallback profile '%2'. Results "
4218 "may be unpredicable")
4219 .arg(profileRequested, profileName));
4220 }
4221 }
4222
4223 return profileName;
4224}
4225
4230 RecordingInfo **rec,
4232 bool had_dummyrec)
4233{
4234 if (m_tvChain)
4235 {
4236 bool ok = false;
4237 if (!m_buffer)
4238 {
4240 SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4241 }
4242 else
4243 {
4245 true, !had_dummyrec && m_recorder);
4246 }
4247 if (!ok)
4248 {
4249 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RingBuffer 2");
4250 return false;
4251 }
4252 *rec = m_curRecording; // new'd in Create/SwitchLiveTVRingBuffer()
4253 }
4254
4256 {
4257 bool write = m_genOpt.m_inputType != "IMPORT";
4258 QString pathname = (*rec)->GetPathname();
4259 LOG(VB_GENERAL, LOG_INFO, LOC + QString("rec->GetPathname(): '%1'")
4260 .arg(pathname));
4262 if (!m_buffer->IsOpen() && write)
4263 {
4264 LOG(VB_GENERAL, LOG_ERR, LOC +
4265 QString("RingBuffer '%1' not open...")
4266 .arg(pathname));
4267 SetRingBuffer(nullptr);
4268 ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
4269 return false;
4270 }
4271 }
4272
4273 if (!m_buffer)
4274 {
4275 LOG(VB_GENERAL, LOG_ERR, LOC +
4276 QString("Failed to start recorder! ringBuffer is NULL\n"
4277 "\t\t\t\t Tuning request was %1\n")
4279
4280 if (HasFlags(kFlagLiveTV))
4281 {
4282 QString message = QString("QUIT_LIVETV %1").arg(m_inputId);
4283 MythEvent me(message);
4285 }
4286 return false;
4287 }
4288
4289 if (m_channel && m_genOpt.m_inputType == "MJPEG")
4290 m_channel->Close(); // Needed because of NVR::MJPEGInit()
4291
4292 LOG(VB_GENERAL, LOG_INFO, LOC + "TuningNewRecorder - CreateRecorder()");
4294
4295 if (m_recorder)
4296 {
4299 if (m_recorder->IsErrored())
4300 {
4301 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialize recorder!");
4302 delete m_recorder;
4303 m_recorder = nullptr;
4304 }
4305 }
4306
4307 if (!m_recorder)
4308 {
4309 LOG(VB_GENERAL, LOG_ERR, LOC +
4310 QString("Failed to start recorder!\n"
4311 "\t\t\t\t Tuning request was %1\n")
4313
4314 if (HasFlags(kFlagLiveTV))
4315 {
4316 QString message = QString("QUIT_LIVETV %1").arg(m_inputId);
4317 MythEvent me(message);
4319 }
4321 if (m_tvChain)
4322 (*rec) = nullptr;
4323 return false;
4324 }
4325
4326 if (*rec)
4327 m_recorder->SetRecording(*rec);
4328
4329 if (GetDTVRecorder() && streamData)
4330 {
4331 const StandardSetting *setting = profile.byName("recordingtype");
4332 if (setting)
4333 streamData->SetRecordingType(setting->getValue());
4334 GetDTVRecorder()->SetStreamData(streamData);
4335 }
4336
4337 if (m_channel && m_genOpt.m_inputType == "MJPEG")
4338 m_channel->Open(); // Needed because of NVR::MJPEGInit()
4339
4340 // Setup for framebuffer capture devices..
4341 if (m_channel)
4342 {
4345 }
4346
4347 if (GetV4LChannel())
4348 {
4350 CloseChannel();
4351 }
4352
4353 m_recorderThread = new MThread("RecThread", m_recorder);
4355
4356 // Wait for recorder to start.
4357 m_stateChangeLock.unlock();
4358 while (!m_recorder->IsRecording() && !m_recorder->IsErrored())
4359 std::this_thread::sleep_for(5us);
4360 m_stateChangeLock.lock();
4361
4362 if (GetV4LChannel())
4364
4365 SetFlags(kFlagRecorderRunning | kFlagRingBufferReady, __FILE__, __LINE__);
4366
4367 ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4368
4369 //workaround for failed import recordings, no signal monitor means we never
4370 //go to recording state and the status here seems to override the status
4371 //set in the importrecorder and backend via setrecordingstatus
4372 if (m_genOpt.m_inputType == "IMPORT")
4373 {
4375 if (m_curRecording)
4377 }
4378
4379 return true;
4380}
4381
4386{
4387 LOG(VB_RECORD, LOG_INFO, LOC + "Starting Recorder");
4388
4389 bool had_dummyrec = false;
4391 {
4393 ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
4395 had_dummyrec = true;
4396 }
4397
4399
4402
4403 if (TuningNewRecorderReal(streamData, &rec, profile, had_dummyrec))
4404 return;
4405
4406 SetRecordingStatus(RecStatus::Failed, __LINE__, true);
4408
4409 if (rec)
4410 {
4411 // Make sure the scheduler knows...
4413 LOG(VB_RECORD, LOG_INFO, LOC +
4414 QString("TuningNewRecorder -- UPDATE_RECORDING_STATUS: %1")
4416 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
4417 .arg(rec->GetInputID())
4418 .arg(rec->GetChanID())
4420 .arg(RecStatus::Failed)
4423 }
4424
4425 if (m_tvChain)
4426 delete rec;
4427}
4428
4433{
4434 LOG(VB_RECORD, LOG_INFO, LOC + "Restarting Recorder");
4435
4436 bool had_dummyrec = false;
4437
4438 if (m_curRecording)
4439 {
4442 }
4443
4445 {
4446 ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
4447 had_dummyrec = true;
4448 }
4449
4450 SwitchLiveTVRingBuffer(m_channel->GetChannelName(), true, !had_dummyrec);
4451
4452 if (had_dummyrec)
4453 {
4455 ProgramInfo *progInfo = m_tvChain->GetProgramAt(-1);
4456 RecordingInfo recinfo(*progInfo);
4457 delete progInfo;
4458 recinfo.SetInputID(m_inputId);
4459 m_recorder->SetRecording(&recinfo);
4460 }
4461 m_recorder->Reset();
4462
4463 // Set file descriptor of channel from recorder for V4L
4464 if (GetV4LChannel())
4466
4467 // Some recorders unpause on Reset, others do not...
4469
4471 {
4473 QString msg1 = QString("Recording: %1 %2 %3 %4")
4474 .arg(rcinfo1->GetTitle(), QString::number(rcinfo1->GetChanID()),
4477 ProgramInfo *rcinfo2 = m_tvChain->GetProgramAt(-1);
4478 QString msg2 = QString("Recording: %1 %2 %3 %4")
4479 .arg(rcinfo2->GetTitle(), QString::number(rcinfo2->GetChanID()),
4482 delete rcinfo2;
4483 LOG(VB_RECORD, LOG_INFO, LOC + "Pseudo LiveTV recording starting." +
4484 "\n\t\t\t" + msg1 + "\n\t\t\t" + msg2);
4485
4488
4490
4491 InitAutoRunJobs(m_curRecording, kAutoRunProfile, nullptr, __LINE__);
4492 }
4493
4494 ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4495}
4496
4497void TVRec::SetFlags(uint f, const QString & file, int line)
4498{
4499 QMutexLocker lock(&m_stateChangeLock);
4500 m_stateFlags |= f;
4501 LOG(VB_RECORD, LOG_INFO, LOC + QString("SetFlags(%1) -> %2 @ %3:%4")
4502 .arg(FlagToString(f), FlagToString(m_stateFlags), file, QString::number(line)));
4503 WakeEventLoop();
4504}
4505
4506void TVRec::ClearFlags(uint f, const QString & file, int line)
4507{
4508 QMutexLocker lock(&m_stateChangeLock);
4509 m_stateFlags &= ~f;
4510 LOG(VB_RECORD, LOG_INFO, LOC + QString("ClearFlags(%1) -> %2 @ %3:%4")
4511 .arg(FlagToString(f), FlagToString(m_stateFlags), file, QString::number(line)));
4512 WakeEventLoop();
4513}
4514
4516{
4517 QString msg("");
4518
4519 // General flags
4520 if (kFlagFrontendReady & f)
4521 msg += "FrontendReady,";
4522 if (kFlagRunMainLoop & f)
4523 msg += "RunMainLoop,";
4524 if (kFlagExitPlayer & f)
4525 msg += "ExitPlayer,";
4526 if (kFlagFinishRecording & f)
4527 msg += "FinishRecording,";
4528 if (kFlagErrored & f)
4529 msg += "Errored,";
4531 msg += "CancelNextRecording,";
4532
4533 // Tuning flags
4534 if ((kFlagRec & f) == kFlagRec)
4535 msg += "REC,";
4536 else
4537 {
4538 if (kFlagLiveTV & f)
4539 msg += "LiveTV,";
4540 if (kFlagRecording & f)
4541 msg += "Recording,";
4542 }
4543 if ((kFlagNoRec & f) == kFlagNoRec)
4544 msg += "NOREC,";
4545 else
4546 {
4547 if (kFlagEITScan & f)
4548 msg += "EITScan,";
4549 if (kFlagCloseRec & f)
4550 msg += "CloseRec,";
4551 if (kFlagKillRec & f)
4552 msg += "KillRec,";
4553 if (kFlagAntennaAdjust & f)
4554 msg += "AntennaAdjust,";
4555 }
4557 msg += "PENDINGACTIONS,";
4558 else
4559 {
4561 msg += "WaitingForRecPause,";
4562 if (kFlagWaitingForSignal & f)
4563 msg += "WaitingForSignal,";
4565 msg += "NeedToStartRecorder,";
4566 if (kFlagKillRingBuffer & f)
4567 msg += "KillRingBuffer,";
4568 }
4569 if ((kFlagAnyRunning & f) == kFlagAnyRunning)
4570 msg += "ANYRUNNING,";
4571 else
4572 {
4574 msg += "SignalMonitorRunning,";
4575 if (kFlagEITScannerRunning & f)
4576 msg += "EITScannerRunning,";
4578 msg += "ANYRECRUNNING,";
4579 else
4580 {
4582 msg += "DummyRecorderRunning,";
4583 if (kFlagRecorderRunning & f)
4584 msg += "RecorderRunning,";
4585 }
4586 }
4587 if (kFlagRingBufferReady & f)
4588 msg += "RingBufferReady,";
4589
4590 if (msg.isEmpty())
4591 msg = QString("0x%1").arg(f,0,16);
4592
4593 return msg;
4594}
4595
4597{
4598 QMutexLocker lock(&m_nextLiveTVDirLock);
4599
4600 bool found = !m_nextLiveTVDir.isEmpty();
4601 if (!found && m_triggerLiveTVDir.wait(&m_nextLiveTVDirLock, 500))
4602 {
4603 found = !m_nextLiveTVDir.isEmpty();
4604 }
4605
4606 return found;
4607}
4608
4610{
4611 QMutexLocker lock(&m_nextLiveTVDirLock);
4612
4613 m_nextLiveTVDir = std::move(dir);
4614 m_triggerLiveTVDir.wakeAll();
4615}
4616
4619 const QString & channum)
4620{
4621 LOG(VB_RECORD, LOG_INFO, LOC + "GetProgramRingBufferForLiveTV()");
4622 if (!m_channel || !m_tvChain || !pginfo || !Buffer)
4623 return false;
4624
4625 m_nextLiveTVDirLock.lock();
4626 m_nextLiveTVDir.clear();
4627 m_nextLiveTVDirLock.unlock();
4628
4629 // Dispatch this early, the response can take a while.
4630 MythEvent me(QString("QUERY_NEXT_LIVETV_DIR %1").arg(m_inputId));
4632
4633 uint sourceid = m_channel->GetSourceID();
4634 int chanid = ChannelUtil::GetChanID(sourceid, channum);
4635
4636 if (chanid < 0)
4637 {
4638 // Test setups might have zero channels
4639 if (m_genOpt.m_inputType == "IMPORT" || m_genOpt.m_inputType == "DEMO")
4640 chanid = 9999;
4641 else
4642 {
4643 LOG(VB_GENERAL, LOG_ERR, LOC +
4644 QString("Channel: \'%1\' was not found in the database.\n"
4645 "\t\tMost likely, the 'starting channel' for this "
4646 "Input Connection is invalid.\n"
4647 "\t\tCould not start livetv.").arg(channum));
4648 return false;
4649 }
4650 }
4651
4652 auto hoursMax =
4653 gCoreContext->GetDurSetting<std::chrono::hours>("MaxHoursPerLiveTVRecording", 8h);
4654 if (hoursMax <= 0h)
4655 hoursMax = 8h;
4656
4657 RecordingInfo *prog = nullptr;
4660 else
4661 {
4662 prog = new RecordingInfo(
4663 chanid, MythDate::current(true), true, hoursMax);
4664 }
4665
4666 prog->SetInputID(m_inputId);
4667
4668 if (prog->GetRecordingStartTime() == prog->GetRecordingEndTime())
4669 {
4670 LOG(VB_GENERAL, LOG_ERR, LOC + "GetProgramRingBufferForLiveTV()"
4671 "\n\t\t\tProgramInfo is invalid."
4672 "\n" + prog->toString());
4673 prog->SetScheduledEndTime(prog->GetRecordingStartTime().addSecs(3600));
4675
4676 prog->SetChanID(chanid);
4677 }
4678
4681
4682 prog->SetStorageGroup("LiveTV");
4683
4685 {
4686 QMutexLocker lock(&m_nextLiveTVDirLock);
4688 }
4689 else
4690 {
4691 StorageGroup sgroup("LiveTV", gCoreContext->GetHostName());
4692 prog->SetPathname(sgroup.FindNextDirMostFree());
4693 }
4694
4696 prog->SetRecordingGroup("LiveTV");
4697
4698 StartedRecording(prog);
4699
4700 *Buffer = MythMediaBuffer::Create(prog->GetPathname(), true);
4701 if (!(*Buffer) || !(*Buffer)->IsOpen())
4702 {
4703 LOG(VB_GENERAL, LOG_ERR, LOC + QString("RingBuffer '%1' not open...")
4704 .arg(prog->GetPathname()));
4705
4706 delete *Buffer;
4707 delete prog;
4708
4709 return false;
4710 }
4711
4712 *pginfo = prog;
4713 return true;
4714}
4715
4716bool TVRec::CreateLiveTVRingBuffer(const QString & channum)
4717{
4718 LOG(VB_RECORD, LOG_INFO, LOC + QString("CreateLiveTVRingBuffer(%1)")
4719 .arg(channum));
4720
4721 RecordingInfo *pginfo = nullptr;
4722 MythMediaBuffer *buffer = nullptr;
4723
4724 if (!m_channel ||
4725 !m_channel->CheckChannel(channum))
4726 {
4728 return false;
4729 }
4730
4731 if (!GetProgramRingBufferForLiveTV(&pginfo, &buffer, channum))
4732 {
4733 ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
4735 LOG(VB_GENERAL, LOG_ERR, LOC +
4736 QString("CreateLiveTVRingBuffer(%1) failed").arg(channum));
4737 return false;
4738 }
4739
4740 SetRingBuffer(buffer);
4741
4745
4746 bool discont = (m_tvChain->TotalSize() > 0);
4748 m_channel->GetInputName(), discont);
4749
4750 if (m_curRecording)
4751 {
4753 delete m_curRecording;
4754 }
4755
4756 m_curRecording = pginfo;
4758
4759 return true;
4760}
4761
4762bool TVRec::SwitchLiveTVRingBuffer(const QString & channum,
4763 bool discont, bool set_rec)
4764{
4765 QString msg;
4766 if (m_curRecording)
4767 {
4768 msg = QString(" curRec(%1) curRec.size(%2)")
4770 .arg(m_curRecording->GetFilesize());
4771 }
4772 LOG(VB_RECORD, LOG_INFO, LOC +
4773 QString("SwitchLiveTVRingBuffer(discont %1, set_next_rec %2)")
4774 .arg(discont).arg(set_rec) + msg);
4775
4776 RecordingInfo *pginfo = nullptr;
4777 MythMediaBuffer *buffer = nullptr;
4778
4779 if (!m_channel ||
4780 !m_channel->CheckChannel(channum))
4781 {
4783 return false;
4784 }
4785
4786 if (!GetProgramRingBufferForLiveTV(&pginfo, &buffer, channum))
4787 {
4789 return false;
4790 }
4791
4792 QString oldinputtype = m_tvChain->GetInputType(-1);
4793
4794 pginfo->MarkAsInUse(true, kRecorderInUseID);
4799 m_channel->GetInputName(), discont);
4800
4801 if (set_rec && m_recorder)
4802 {
4803 m_recorder->SetNextRecording(pginfo, buffer);
4804 if (discont)
4806 delete pginfo;
4807 SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4808 }
4809 else if (!set_rec)
4810 {
4811 // dummy recordings are finished before this
4812 // is called and other recordings must be finished..
4813 if (m_curRecording && oldinputtype != "DUMMY")
4814 {
4817 delete m_curRecording;
4818 }
4819 m_curRecording = pginfo;
4820 SetRingBuffer(buffer);
4821 }
4822 else
4823 {
4824 delete buffer;
4825 }
4826
4827 return true;
4828}
4829
4831{
4832 LOG(VB_RECORD, LOG_INFO, LOC + "SwitchRecordingRingBuffer()");
4833
4835 {
4836 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4837 "already switching.");
4838 return nullptr;
4839 }
4840
4841 if (!m_recorder)
4842 {
4843 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4844 "invalid recorder.");
4845 return nullptr;
4846 }
4847
4848 if (!m_curRecording)
4849 {
4850 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4851 "invalid recording.");
4852 return nullptr;
4853 }
4854
4855 if (rcinfo.GetChanID() != m_curRecording->GetChanID())
4856 {
4857 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4858 "Not the same channel.");
4859 return nullptr;
4860 }
4861
4862 auto *ri = new RecordingInfo(rcinfo);
4864
4865 QString pn = LoadProfile(nullptr, ri, profile);
4866
4867 if (pn != m_recProfileName)
4868 {
4869 LOG(VB_RECORD, LOG_ERR, LOC +
4870 QString("SwitchRecordingRingBuffer() -> "
4871 "cannot switch profile '%1' to '%2'")
4872 .arg(m_recProfileName, pn));
4873 return nullptr;
4874 }
4875
4877
4878 ri->MarkAsInUse(true, kRecorderInUseID);
4879 StartedRecording(ri);
4880
4881 bool write = m_genOpt.m_inputType != "IMPORT";
4882 MythMediaBuffer *buffer = MythMediaBuffer::Create(ri->GetPathname(), write);
4883 if (!buffer || !buffer->IsOpen())
4884 {
4885 delete buffer;
4886 ri->SetRecordingStatus(RecStatus::Failed);
4887 FinishedRecording(ri, nullptr);
4888 ri->MarkAsInUse(false, kRecorderInUseID);
4889 delete ri;
4890 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer() -> "
4891 "Failed to create new RB.");
4892 return nullptr;
4893 }
4894
4895 m_recorder->SetNextRecording(ri, buffer);
4896 SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4898 m_switchingBuffer = true;
4899 ri->SetRecordingStatus(RecStatus::Recording);
4900 LOG(VB_RECORD, LOG_INFO, LOC + "SwitchRecordingRingBuffer -> done");
4901 return ri;
4902}
4903
4905{
4906 QMap<uint,TVRec*>::const_iterator it = s_inputs.constFind(inputid);
4907 if (it == s_inputs.constEnd())
4908 return nullptr;
4909 return *it;
4910}
4911
4913{
4914 LOG(VB_RECORD, LOG_INFO, LOC + QString("enable:%1").arg(enable));
4915
4916 if (m_scanner != nullptr)
4917 {
4918 if (enable)
4919 {
4921 && m_eitScanStartTime > MythDate::current().addYears(9))
4922 {
4924 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
4925 }
4926 }
4927 else
4928 {
4929 m_eitScanStartTime = MythDate::current().addYears(10);
4931 {
4933 ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
4934 }
4935 }
4936 }
4937}
4938
4939QString TuningRequest::toString(void) const
4940{
4941 return QString("Program(%1) channel(%2) input(%3) flags(%4)")
4942 .arg(m_program == nullptr ? "NULL" : m_program->toString(),
4943 m_channel.isEmpty() ? "<empty>" : m_channel,
4944 m_input.isEmpty() ? "<empty>" : m_input,
4946}
4947
4948#if CONFIG_DVB
4949#include "recorders/dvbchannel.h"
4951{
4952 // Some DVB devices munge the PMT and/or PAT so the CRC check fails.
4953 // We need to tell the stream data class to not check the CRC on
4954 // these devices. This can cause segfaults.
4955 auto * dvb = dynamic_cast<DVBChannel*>(c);
4956 if (dvb != nullptr)
4957 s->SetIgnoreCRC(dvb->HasCRCBug());
4958}
4959#else
4961#endif // CONFIG_DVB
4962
4963/* 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:1698
static QString GetStartChannel(uint inputid)
Definition: cardutil.cpp:1792
static bool IsEITCapable(const QString &rawtype)
Definition: cardutil.h:170
static QString GetInputName(uint inputid)
Definition: cardutil.cpp:1773
static bool IsV4L(const QString &rawtype)
Definition: cardutil.h:145
static uint GetSourceID(uint inputid)
Definition: cardutil.cpp:1948
static bool IsEncoder(const QString &rawtype)
Definition: cardutil.h:135
static std::vector< uint > GetConflictingInputs(uint inputid)
Definition: cardutil.cpp:2241
static bool IsChannelReusable(const QString &rawtype)
Definition: cardutil.h:222
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:197
static QString GetVideoFilters(uint sourceid, const QString &channum)
Definition: channelutil.h:199
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:86
std::chrono::milliseconds m_dvbTuningDelay
Definition: tv_rec.h:87
bool m_dvbEitScan
Definition: tv_rec.h:88
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:98
int m_connection
Definition: tv_rec.h:97
bool m_skipBtAudio
Definition: tv_rec.h:75
QString m_inputType
Definition: tv_rec.h:73
int m_audioSampleRate
Definition: tv_rec.h:74
bool m_waitForSeqstart
Definition: tv_rec.h:78
QString m_vbiDev
Definition: tv_rec.h:71
uint m_channelTimeout
Definition: tv_rec.h:77
QString m_videoDev
Definition: tv_rec.h:70
QString m_audioDev
Definition: tv_rec.h:72
uint m_signalTimeout
Definition: tv_rec.h:76
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:486
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:508
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:837
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:618
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
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:283
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
This table tells the decoder on which PIDs to find other tables, and their sizes and each table's cur...
Definition: atsctables.h:83
uint TableType(uint i) const
Definition: atsctables.h:127
uint TableCount() const
Definition: atsctables.h:123
uint TablePID(uint i) const
Definition: atsctables.h:135
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:226
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:138
ProgramInfo * m_info
Definition: tv_rec.h:132
bool m_hasLaterShowing
Definition: tv_rec.h:134
QDateTime m_recordingStart
Definition: tv_rec.h:133
bool m_doNotAsk
Definition: tv_rec.h:137
bool m_ask
Definition: tv_rec.h:136
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:70
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:375
uint GetRecordingRuleID(void) const
Definition: programinfo.h:455
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:422
void SaveAutoExpire(AutoExpireType autoExpire, bool updateDelete=false)
Set "autoexpire" field in "recorded" table to "autoExpire".
void SetRecordingRuleType(RecordingType type)
Definition: programinfo.h:588
uint GetRecordingID(void) const
Definition: programinfo.h:452
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:400
uint QueryMplexID(void) const
Queries multiplex any recording would be made on, zero if unknown.
void SetRecordingStatus(RecStatus::Type status)
Definition: programinfo.h:587
void SetRecordingGroup(const QString &group)
Definition: programinfo.h:534
uint GetSourceID(void) const
Definition: programinfo.h:468
void SetScheduledEndTime(const QDateTime &dt)
Definition: programinfo.h:531
void SetRecordingStartTime(const QDateTime &dt)
Definition: programinfo.h:532
void SaveVideoProperties(uint mask, uint video_property_flags)
QString GetTitle(void) const
Definition: programinfo.h:364
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:407
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:393
bool IsLocal(void) const
Definition: programinfo.h:354
void SetChanID(uint _chanid)
Definition: programinfo.h:529
bool IsCommercialFree(void) const
Definition: programinfo.h:484
MarkTypes QueryAverageAspectRatio(void) const
void SetRecordingRuleID(uint id)
Definition: programinfo.h:545
QString MakeUniqueKey(void) const
Creates a unique string that can be used to identify an existing recording.
Definition: programinfo.h:342
QString GetPathname(void) const
Definition: programinfo.h:346
uint GetInputID(void) const
Definition: programinfo.h:469
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:533
void SetStorageGroup(const QString &group)
Definition: programinfo.h:537
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:453
QDateTime GetRecordingEndTime(void) const
Approximate time the recording should have ended, did end, or is intended to end.
Definition: programinfo.h:415
void SetInputID(uint id)
Definition: programinfo.h:547
void SaveCommFlagged(CommFlagStatus flag)
Set "commflagged" field in "recorded" table to "flag".
QString GetSubtitle(void) const
Definition: programinfo.h:366
QString GetCategory(void) const
Definition: programinfo.h:372
virtual void SetRecordingID(uint _recordedid)
Definition: programinfo.h:585
void SetPathname(const QString &pn)
RecordingType GetRecordingRuleType(void) const
Definition: programinfo.h:457
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:143
bool SwitchLiveTVRingBuffer(const QString &channum, bool discont, bool set_rec)
Definition: tv_rec.cpp:4762
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
DTVChannel * GetDTVChannel(void)
Definition: tv_rec.cpp:1259
uint GetFlags(void) const
Definition: tv_rec.h:245
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:3716
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:4497
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:4385
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:146
QWaitCondition m_triggerEventSleepWait
Definition: tv_rec.h:404
uint m_inputId
Definition: tv_rec.h:372
void SetNextLiveTVDir(QString dir)
Definition: tv_rec.cpp:4609
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:238
QDateTime m_eitScanStartTime
Definition: tv_rec.h:398
RecordingInfo * SwitchRecordingRingBuffer(const RecordingInfo &rcinfo)
Definition: tv_rec.cpp:4830
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:3627
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:3460
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:3420
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:4904
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:3437
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:4596
static const uint kFlagSignalMonitorRunning
Definition: tv_rec.h:476
static QString FlagToString(uint f)
Definition: tv_rec.cpp:4515
TVState GetState(void) const
Returns the TVState of the recorder.
Definition: tv_rec.cpp:263
bool CreateLiveTVRingBuffer(const QString &channum)
Definition: tv_rec.cpp:4716
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:4432
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:234
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:3551
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:4912
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:4506
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:3499
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:3959
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:4617
bool TuningNewRecorderReal(MPEGStreamData *streamData, RecordingInfo **rec, RecordingProfile &profile, bool had_dummyrec)
Helper function for TVRec::TuningNewRecorder.
Definition: tv_rec.cpp:4229
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:4183
int m_progNum
Definition: tv_rec.h:122
bool IsOnSameMultiplex(void) const
Definition: tv_rec.h:113
uint m_minorChan
Definition: tv_rec.h:121
uint m_flags
Definition: tv_rec.h:116
RecordingInfo * m_program
Definition: tv_rec.h:117
uint m_majorChan
Definition: tv_rec.h:120
QString toString(void) const
Definition: tv_rec.cpp:4939
QString m_channel
Definition: tv_rec.h:118
QString m_input
Definition: tv_rec.h:119
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:68
#define minor(X)
Definition: compat.h:65
static guint32 * tmp
Definition: goom_core.cpp:26
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:307
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:4960
static int init_jobs(const RecordingInfo *rec, RecordingProfile &profile, bool on_host, bool transcode_bfr_comm, bool on_line_comm)
Definition: tv_rec.cpp:4125
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