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 {
754 SetFlags(kFlagKillRec, __FILE__, __LINE__);
755 }
756 else if (m_curRecording)
757 {
758 QDateTime now = MythDate::current(true);
759 if (now < m_curRecording->GetDesiredEndTime())
761 }
763 // wait for state change to take effect
766
768 }
769}
770
777{
778 return (state == kState_RecordingOnly ||
779 state == kState_WatchingLiveTV);
780}
781
787{
788 return (state == kState_WatchingPreRecorded);
789}
790
797{
798 if (StateIsRecording(state))
799 return kState_None;
800
801 LOG(VB_GENERAL, LOG_ERR, LOC +
802 QString("Unknown state in RemoveRecording: %1")
803 .arg(StateToString(state)));
804 return kState_Error;
805}
806
813{
814 if (StateIsPlaying(state))
815 {
816 if (state == kState_WatchingPreRecorded)
817 return kState_None;
819 }
820
821 QString msg = "Unknown state in RemovePlaying: %1";
822 LOG(VB_GENERAL, LOG_ERR, LOC + msg.arg(StateToString(state)));
823
824 return kState_Error;
825}
826
833{
834 if (!curRec)
835 return;
836
838 LOG(VB_RECORD, LOG_INFO, LOC + QString("StartedRecording(%1) fn(%2)")
839 .arg(curRec->MakeUniqueKey(), curRec->GetPathname()));
840
841 if (curRec->IsCommercialFree())
843
844 AutoRunInitType t = (curRec->GetRecordingGroup() == "LiveTV") ?
846 InitAutoRunJobs(curRec, t, nullptr, __LINE__);
847
848 SendMythSystemRecEvent("REC_STARTED", curRec);
849}
850
859{
860 if (!curRec)
861 return;
862
863 // Make sure the recording group is up to date
864 const QString recgrp = curRec->QueryRecordingGroup();
865 curRec->SetRecordingGroup(recgrp);
866
867 bool is_good = true;
868 if (recq)
869 {
870 LOG((recq->IsDamaged()) ? VB_GENERAL : VB_RECORD, LOG_INFO,
871 LOC + QString("FinishedRecording(%1) %2 recq:\n%3")
872 .arg(curRec->MakeUniqueKey(),
873 (recq->IsDamaged()) ? "damaged" : "good",
874 recq->toStringXML()));
875 is_good = !recq->IsDamaged();
876 delete recq;
877 recq = nullptr;
878 }
879
880 RecStatus::Type ors = curRec->GetRecordingStatus();
881 // Set the final recording status
884 else if (curRec->GetRecordingStatus() != RecStatus::Recorded)
887 is_good &= (curRec->GetRecordingStatus() == RecStatus::Recorded);
888
889 // Figure out if this was already done for this recording
890 bool was_finished = false;
891 static QMutex s_finRecLock;
892 static QHash<QString,QDateTime> s_finRecMap;
893 {
894 QMutexLocker locker(&s_finRecLock);
895 QDateTime now = MythDate::current();
896 QDateTime expired = now.addSecs(-5LL * 60);
897 QHash<QString,QDateTime>::iterator it = s_finRecMap.begin();
898 while (it != s_finRecMap.end())
899 {
900 if ((*it) < expired)
901 it = s_finRecMap.erase(it);
902 else
903 ++it;
904 }
905 QString key = curRec->MakeUniqueKey();
906 it = s_finRecMap.find(key);
907 if (it != s_finRecMap.end())
908 was_finished = true;
909 else
910 s_finRecMap[key] = now;
911 }
912
913 // Print something informative to the log
914 LOG(VB_RECORD, LOG_INFO, LOC +
915 QString("FinishedRecording(%1) %2 quality"
916 "\n\t\t\ttitle: %3\n\t\t\t"
917 "in recgroup: %4 status: %5:%6 %7 %8")
918 .arg(curRec->MakeUniqueKey(),
919 is_good ? "Good" : "Bad",
920 curRec->GetTitle(),
921 recgrp,
924 HasFlags(kFlagDummyRecorderRunning)?"is_dummy":"not_dummy",
925 was_finished?"already_finished":"finished_now"));
926
927 // This has already been called on this recording..
928 if (was_finished)
929 return;
930
931 // Notify the frontend watching live tv that this file is final
932 if (m_tvChain)
934
935 // if this is a dummy recorder, do no more..
937 {
938 curRec->FinishedRecording(true); // so end time is updated
939 SendMythSystemRecEvent("REC_FINISHED", curRec);
940 return;
941 }
942
943 // Get the width and set the videoprops
944 MarkTypes aspectRatio = curRec->QueryAverageAspectRatio();
945 uint avg_height = curRec->QueryAverageHeight();
946 bool progressive = curRec->QueryAverageScanProgressive();
947
948 uint16_t flags {VID_UNKNOWN};
949 if (avg_height > 2000)
950 flags |= VID_4K;
951 else if (avg_height > 1000)
952 flags |= VID_1080;
953 else if (avg_height > 700)
954 flags |= VID_720;
955 if (progressive)
956 flags |= VID_PROGRESSIVE;
957 if (!is_good)
958 flags |= VID_DAMAGED;
959 if ((aspectRatio == MARK_ASPECT_16_9) ||
960 (aspectRatio == MARK_ASPECT_2_21_1))
961 flags |= VID_WIDESCREEN;
962
963 curRec->SaveVideoProperties
964 (VID_4K | VID_1080 | VID_720 | VID_DAMAGED |
965 VID_WIDESCREEN | VID_PROGRESSIVE, flags);
966
967 // Make sure really short recordings have positive run time.
968 if (curRec->GetRecordingEndTime() <= curRec->GetRecordingStartTime())
969 {
970 curRec->SetRecordingEndTime(
971 curRec->GetRecordingStartTime().addSecs(60));
972 }
973
974 // HACK Temporary hack, ensure we've loaded the recording file info, do it now
975 // so that it contains the final filesize information
976 if (!curRec->GetRecordingFile())
977 curRec->LoadRecordingFile();
978
979 // Generate a preview
980 uint64_t fsize = curRec->GetFilesize();
981 if (curRec->IsLocal() && (fsize >= 1000) &&
983 {
985 }
986
987 // store recording in recorded table
988 curRec->FinishedRecording(!is_good || (recgrp == "LiveTV"));
989
990 // send out UPDATE_RECORDING_STATUS message
991 LOG(VB_RECORD, LOG_INFO, LOC +
992 QString("FinishedRecording -- UPDATE_RECORDING_STATUS: %1")
993 .arg(RecStatus::toString(is_good ? curRec->GetRecordingStatus()
995 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
996 .arg(curRec->GetInputID())
997 .arg(curRec->GetChanID())
999 .arg(is_good ? curRec->GetRecordingStatus() : RecStatus::Failed)
1000 .arg(curRec->GetRecordingEndTime(MythDate::ISODate)));
1002
1003 // send out REC_FINISHED message
1004 SendMythSystemRecEvent("REC_FINISHED", curRec);
1005
1006 // send out DONE_RECORDING message
1007 auto secsSince = MythDate::secsInPast(curRec->GetRecordingStartTime());
1008 QString message = QString("DONE_RECORDING %1 %2 %3")
1009 .arg(m_inputId).arg(secsSince.count()).arg(GetFramesWritten());
1010 MythEvent me2(message);
1011 gCoreContext->dispatch(me2);
1012
1013 // Handle JobQueue
1014 QHash<QString,int>::iterator autoJob =
1015 m_autoRunJobs.find(curRec->MakeUniqueKey());
1016 if (autoJob == m_autoRunJobs.end())
1017 {
1018 LOG(VB_GENERAL, LOG_INFO,
1019 "autoRunJobs not initialized until FinishedRecording()");
1021 (recgrp == "LiveTV") ? kAutoRunNone : kAutoRunProfile;
1022 InitAutoRunJobs(curRec, t, nullptr, __LINE__);
1023 autoJob = m_autoRunJobs.find(curRec->MakeUniqueKey());
1024 }
1025 LOG(VB_JOBQUEUE, LOG_INFO, QString("AutoRunJobs 0x%1").arg(*autoJob,0,16));
1026 if ((recgrp == "LiveTV") || (fsize < 1000) ||
1027 (curRec->GetRecordingStatus() != RecStatus::Recorded) ||
1028 (curRec->GetRecordingStartTime().secsTo(
1029 MythDate::current()) < 120))
1030 {
1033 }
1034 if (*autoJob != JOB_NONE)
1035 JobQueue::QueueRecordingJobs(*curRec, *autoJob);
1036 m_autoRunJobs.erase(autoJob);
1037}
1038
1039// NOLINTBEGIN(cppcoreguidelines-macro-usage)
1040#define TRANSITION(ASTATE,BSTATE) \
1041 ((m_internalState == (ASTATE)) && (m_desiredNextState == (BSTATE)))
1042#define SET_NEXT() do { nextState = m_desiredNextState; changed = true; } while(false)
1043#define SET_LAST() do { nextState = m_internalState; changed = true; } while(false)
1044// NOLINTEND(cppcoreguidelines-macro-usage)
1045
1054{
1055 TVState nextState = m_internalState;
1056
1057 bool changed = false;
1058
1059 QString transMsg = QString(" %1 to %2")
1061
1063 {
1064 LOG(VB_GENERAL, LOG_ERR, LOC +
1065 "HandleStateChange(): Null transition" + transMsg);
1066 m_changeState = false;
1067 return;
1068 }
1069
1070 // Stop EIT scanning on this input before any tuning,
1071 // to avoid race condition with it's tuning requests.
1073 {
1074 LOG(VB_EIT, LOG_INFO, LOC + QString("Stop EIT scan on input %1").arg(GetInputId()));
1075
1077 ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1079 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
1080 }
1081
1082 // Stop EIT scanning on all conflicting inputs so that
1083 // the tuner card is available for a new tuning request.
1084 // Conflicting inputs are inputs that have independent video sources
1085 // but that share a tuner card, such as a DVB-S/S2 tuner card that
1086 // connects to multiple satellites with a DiSEqC switch.
1087 if (m_scanner && !m_eitInputs.empty())
1088 {
1089 s_inputsLock.lockForRead();
1090 s_eitLock.lock();
1091 for (auto input : m_eitInputs)
1092 {
1093 auto *tv_rec = s_inputs.value(input);
1094 if (tv_rec && tv_rec->m_scanner && tv_rec->HasFlags(kFlagEITScannerRunning))
1095 {
1096 LOG(VB_EIT, LOG_INFO, LOC +
1097 QString("Stop EIT scan active on conflicting input %1")
1098 .arg(input));
1099 tv_rec->m_scanner->StopActiveScan();
1100 tv_rec->ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1101 tv_rec->TuningShutdowns(TuningRequest(kFlagNoRec));
1103 tv_rec->m_eitScanStartTime = MythDate::current().addSecs(secs.count());
1104 }
1105 }
1106 s_eitLock.unlock();
1107 s_inputsLock.unlock();
1108 }
1109
1110 // Handle different state transitions
1112 {
1114 SET_NEXT();
1115 }
1117 {
1119 SET_NEXT();
1120 }
1122 {
1123 SetPseudoLiveTVRecording(nullptr);
1124
1125 SET_NEXT();
1126 }
1128 {
1129 SetPseudoLiveTVRecording(nullptr);
1131 SET_NEXT();
1132 }
1134 {
1137 (GetFlags()&kFlagKillRec)));
1138 SET_NEXT();
1139 }
1140
1141 QString msg = changed ? "Changing from" : "Unknown state transition:";
1142 LOG(VB_GENERAL, LOG_INFO, LOC + msg + transMsg);
1143
1144 // update internal state variable
1145 m_internalState = nextState;
1146 m_changeState = false;
1147
1149 {
1151 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
1152 }
1153 else
1154 {
1155 m_eitScanStartTime = MythDate::current().addYears(1);
1156 }
1157}
1158#undef TRANSITION
1159#undef SET_NEXT
1160#undef SET_LAST
1161
1166{
1167 QMutexLocker lock(&m_stateChangeLock);
1168 m_desiredNextState = nextState;
1169 m_changeState = true;
1170 WakeEventLoop();
1171}
1172
1188{
1189 LOG(VB_RECORD, LOG_INFO, LOC + QString("TeardownRecorder(%1)")
1190 .arg((request_flags & kFlagKillRec) ? "kFlagKillRec" : ""));
1191
1192 m_pauseNotify = false;
1193 m_isPip = false;
1194
1196 {
1199 delete m_recorderThread;
1200 m_recorderThread = nullptr;
1201 }
1203 __FILE__, __LINE__);
1204
1205 RecordingQuality *recq = nullptr;
1206 if (m_recorder)
1207 {
1208 if (GetV4LChannel())
1209 m_channel->SetFd(-1);
1210
1212
1213 QMutexLocker locker(&m_stateChangeLock);
1214 delete m_recorder;
1215 m_recorder = nullptr;
1216 }
1217
1218 if (m_buffer)
1219 {
1220 LOG(VB_FILE, LOG_INFO, LOC + "calling StopReads()");
1222 }
1223
1224 if (m_curRecording)
1225 {
1226 if (!!(request_flags & kFlagKillRec))
1228
1230
1232 delete m_curRecording;
1233 m_curRecording = nullptr;
1234 }
1235
1236 m_pauseNotify = true;
1237
1238 if (GetDTVChannel())
1240}
1241
1243{
1244 return dynamic_cast<DTVRecorder*>(m_recorder);
1245}
1246
1248{
1249 if (m_channel &&
1250 ((m_genOpt.m_inputType == "DVB" && m_dvbOpt.m_dvbOnDemand) ||
1251 m_genOpt.m_inputType == "FREEBOX" ||
1252 m_genOpt.m_inputType == "VBOX" ||
1253 m_genOpt.m_inputType == "HDHOMERUN" ||
1254 m_genOpt.m_inputType == "EXTERNAL" ||
1256 {
1257 m_channel->Close();
1258 }
1259}
1260
1262{
1263 return dynamic_cast<DTVChannel*>(m_channel);
1264}
1265
1266// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
1268{
1269#if CONFIG_V4L2
1270 return dynamic_cast<V4LChannel*>(m_channel);
1271#else
1272 return nullptr;
1273#endif // CONFIG_V4L2
1274}
1275
1276// Check if EIT is enabled for the video source connected to this input
1277static bool get_use_eit(uint inputid)
1278{
1280 query.prepare(
1281 "SELECT SUM(useeit) "
1282 "FROM videosource, capturecard "
1283 "WHERE videosource.sourceid = capturecard.sourceid AND"
1284 " capturecard.cardid = :INPUTID");
1285 query.bindValue(":INPUTID", inputid);
1286
1287 if (!query.exec() || !query.isActive())
1288 {
1289 MythDB::DBError("get_use_eit", query);
1290 return false;
1291 }
1292 if (query.next())
1293 return query.value(0).toBool();
1294 return false;
1295}
1296
1297static bool is_dishnet_eit(uint inputid)
1298{
1300 query.prepare(
1301 "SELECT SUM(dishnet_eit) "
1302 "FROM videosource, capturecard "
1303 "WHERE videosource.sourceid = capturecard.sourceid AND"
1304 " capturecard.cardid = :INPUTID");
1305 query.bindValue(":INPUTID", inputid);
1306
1307 if (!query.exec() || !query.isActive())
1308 {
1309 MythDB::DBError("is_dishnet_eit", query);
1310 return false;
1311 }
1312 if (query.next())
1313 return query.value(0).toBool();
1314 return false;
1315}
1316
1317// Highest capturecard instance number including multirec instances
1318static int get_highest_input(void)
1319{
1321 query.prepare(
1322 "SELECT MAX(cardid) "
1323 "FROM capturecard ");
1324
1325 if (!query.exec() || !query.isActive())
1326 {
1327 MythDB::DBError("highest_input", query);
1328 return -1;
1329 }
1330 if (query.next())
1331 return query.value(0).toInt();
1332 return -1;
1333}
1334
1335static std::chrono::seconds eit_start_rand(uint inputId, std::chrono::seconds eitTransportTimeout)
1336{
1337 // Randomize start time a bit
1338 auto timeout = std::chrono::seconds(MythRandom(0, eitTransportTimeout.count() / 3));
1339
1340 // Use the highest input number and the current input number
1341 // to distribute the scan start evenly over eitTransportTimeout
1342 int highest_input = get_highest_input();
1343 if (highest_input > 0)
1344 timeout += eitTransportTimeout * inputId / highest_input;
1345
1346 return timeout;
1347}
1348
1350void TVRec::run(void)
1351{
1352 QMutexLocker lock(&m_stateChangeLock);
1353 SetFlags(kFlagRunMainLoop, __FILE__, __LINE__);
1354 ClearFlags(kFlagExitPlayer | kFlagFinishRecording, __FILE__, __LINE__);
1355
1356 // Check whether we should use the EITScanner in this TVRec instance
1357 if (CardUtil::IsEITCapable(m_genOpt.m_inputType) && // Card type capable of receiving EIT?
1358 (!GetDTVChannel() || GetDTVChannel()->IsMaster()) && // Card is master and not a multirec instance
1359 (m_dvbOpt.m_dvbEitScan || get_use_eit(m_inputId))) // EIT is selected for card OR EIT is selected for video source
1360 {
1363 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
1364 }
1365 else
1366 {
1367 m_eitScanStartTime = MythDate::current().addYears(10);
1368 }
1369
1370 while (HasFlags(kFlagRunMainLoop))
1371 {
1372 // If there is a state change queued up, do it...
1373 if (m_changeState)
1374 {
1377 __FILE__, __LINE__);
1378 }
1379
1380 // Quick exit on fatal errors.
1381 if (IsErrored())
1382 {
1383 LOG(VB_GENERAL, LOG_ERR, LOC +
1384 "RunTV encountered fatal error, exiting event thread.");
1385 ClearFlags(kFlagRunMainLoop, __FILE__, __LINE__);
1386 TeardownAll();
1387 return;
1388 }
1389
1390 // Handle any tuning events.. Blindly grabbing the lock here
1391 // can sometimes cause a deadlock with Init() while it waits
1392 // to make sure this thread starts. Until a better solution
1393 // is found, don't run HandleTuning unless we can safely get
1394 // the lock.
1395 if (s_inputsLock.tryLockForRead())
1396 {
1397 HandleTuning();
1398 s_inputsLock.unlock();
1399 }
1400
1401 // Tell frontends about pending recordings
1403
1404 // If we are recording a program, check if the recording is
1405 // over or someone has asked us to finish the recording.
1406 // Add an extra 60 seconds to the recording end time if we
1407 // might want a back to back recording.
1408 QDateTime recEnd = (!m_pendingRecordings.empty()) ?
1409 m_recordEndTime.addSecs(60) : m_recordEndTime;
1410 if ((GetState() == kState_RecordingOnly) &&
1411 (MythDate::current() > recEnd ||
1413 {
1415 ClearFlags(kFlagFinishRecording, __FILE__, __LINE__);
1416 }
1417
1418 if (m_curRecording)
1419 {
1421
1422 if (m_recorder)
1423 {
1425
1426 // Check for recorder errors
1427 if (m_recorder->IsErrored())
1428 {
1430
1432 {
1433 QString message = QString("QUIT_LIVETV %1").arg(m_inputId);
1434 MythEvent me(message);
1436 }
1437 else
1438 {
1440 }
1441 }
1442 }
1443 }
1444
1445 // Check for the end of the current program..
1447 {
1448 QDateTime now = MythDate::current();
1449 bool has_finish = HasFlags(kFlagFinishRecording);
1450 bool has_rec = m_pseudoLiveTVRecording;
1451 bool enable_ui = true;
1452
1453 m_pendingRecLock.lock();
1454 bool rec_soon = m_pendingRecordings.contains(m_inputId);
1455 m_pendingRecLock.unlock();
1456
1457 if (has_rec && (has_finish || (now > m_recordEndTime)))
1458 {
1459 SetPseudoLiveTVRecording(nullptr);
1460 }
1461 else if (!has_rec && !rec_soon && m_curRecording &&
1463 {
1464 if (!m_switchingBuffer)
1465 {
1466 LOG(VB_RECORD, LOG_INFO, LOC +
1467 "Switching Buffer (" +
1468 QString("!has_rec(%1) && ").arg(has_rec) +
1469 QString("!rec_soon(%1) && (").arg(rec_soon) +
1470 MythDate::toString(now, MythDate::ISODate) + " >= " +
1472 QString("(%1) ))")
1473 .arg(now >= m_curRecording->GetScheduledEndTime()));
1474
1475 m_switchingBuffer = true;
1476
1478 false, true);
1479 }
1480 else
1481 {
1482 LOG(VB_RECORD, LOG_INFO, "Waiting for ringbuffer switch");
1483 }
1484 }
1485 else
1486 {
1487 enable_ui = false;
1488 }
1489
1490 if (enable_ui)
1491 {
1492 LOG(VB_RECORD, LOG_INFO, LOC + "Enabling Full LiveTV UI.");
1493 QString message = QString("LIVETV_WATCH %1 0").arg(m_inputId);
1494 MythEvent me(message);
1496 }
1497 }
1498
1499 // Check for ExitPlayer flag, and if set change to a non-watching
1500 // state (either kState_RecordingOnly or kState_None).
1502 {
1507 ClearFlags(kFlagExitPlayer, __FILE__, __LINE__);
1508 }
1509
1510 // Start active EIT scan
1511 bool conflicting_input = false;
1512 if (m_scanner && m_channel &&
1514 {
1516 {
1517 LOG(VB_EIT, LOG_INFO, LOC +
1518 QString("EIT scanning disabled for input %1")
1519 .arg(GetInputId()));
1520 m_eitScanStartTime = MythDate::current().addYears(10);
1521 }
1522 else if (!get_use_eit(GetInputId()))
1523 {
1524 LOG(VB_EIT, LOG_INFO, LOC +
1525 QString("EIT scanning disabled for video source %1")
1526 .arg(GetSourceID()));
1527 m_eitScanStartTime = MythDate::current().addYears(10);
1528 }
1529 else
1530 {
1531 LOG(VB_EIT, LOG_INFO, LOC +
1532 QString("EIT scanning enabled for input %1 connected to video source %2 '%3'")
1534
1535 // Check if another card in the same input group is busy recording.
1536 // This could be either a virtual DVB-device or a second tuner on a single card.
1537 s_inputsLock.lockForRead();
1538 s_eitLock.lock();
1539 bool allow_eit = true;
1540 std::vector<uint> inputids = CardUtil::GetConflictingInputs(m_inputId);
1541 InputInfo busy_input;
1542 for (uint i = 0; i < inputids.size() && allow_eit; ++i)
1543 allow_eit = !RemoteIsBusy(inputids[i], busy_input);
1544
1545 // Check if another card in the same input group is busy with an EIT scan.
1546 // We cannot start an EIT scan on this input if there is already an EIT scan
1547 // running on a conflicting real input.
1548 // Note that EIT scans never run on virtual inputs.
1549 if (allow_eit)
1550 {
1551 for (auto input : inputids)
1552 {
1553 auto *tv_rec = s_inputs.value(input);
1554 if (tv_rec && tv_rec->m_scanner)
1555 {
1556 conflicting_input = true;
1557 if (tv_rec->HasFlags(kFlagEITScannerRunning))
1558 {
1559 LOG(VB_EIT, LOG_INFO, LOC +
1560 QString("EIT scan on conflicting input %1").arg(input));
1561 allow_eit = false;
1562 busy_input.m_inputId = tv_rec->m_inputId;
1563 break;
1564 }
1565 }
1566 }
1567 }
1568
1569 if (allow_eit)
1570 {
1571 LOG(VB_EIT, LOG_INFO, LOC +
1572 QString("Start EIT active scan on input %1")
1573 .arg(m_inputId));
1575 SetFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1576 m_eitScanStartTime = MythDate::current().addYears(1);
1577 if (conflicting_input)
1579 else
1580 m_eitScanStopTime = MythDate::current().addYears(1);
1581 }
1582 else
1583 {
1584 const int seconds_postpone = 300;
1585 LOG(VB_EIT, LOG_INFO, LOC +
1586 QString("Postponing EIT scan on input %1 for %2 seconds because input %3 is busy")
1587 .arg(m_inputId).arg(seconds_postpone).arg(busy_input.m_inputId));
1588 m_eitScanStartTime = m_eitScanStartTime.addSecs(seconds_postpone);
1589 }
1590 s_eitLock.unlock();
1591 s_inputsLock.unlock();
1592 }
1593 }
1594
1595
1596 // Stop active EIT scan and allow start of the EIT scan on one of the conflicting real inputs.
1598 {
1599 LOG(VB_EIT, LOG_INFO, LOC +
1600 QString("Stop EIT scan on input %1 to allow scan on a conflicting input")
1601 .arg(GetInputId()));
1602
1604 ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1606
1608 secs += m_eitScanPeriod;
1609 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
1610 }
1611
1612 // We should be no more than a few thousand milliseconds,
1613 // as the end recording code does not have a trigger...
1614 // NOTE: If you change anything here, make sure that
1615 // WaitforEventThreadSleep() will still work...
1616 if (m_tuningRequests.empty() && !m_changeState)
1617 {
1618 lock.unlock(); // stateChangeLock
1619
1620 {
1621 QMutexLocker locker(&m_triggerEventSleepLock);
1623 m_triggerEventSleepWait.wakeAll();
1624 }
1625
1626 sched_yield();
1627
1628 {
1629 QMutexLocker locker(&m_triggerEventLoopLock);
1630 // We check triggerEventLoopSignal because it is possible
1631 // that WakeEventLoop() was called since we
1632 // unlocked the stateChangeLock
1634 {
1636 &m_triggerEventLoopLock, 1000 /* ms */);
1637 }
1639 }
1640
1641 lock.relock(); // stateChangeLock
1642 }
1643 }
1644
1645 if (GetState() != kState_None)
1646 {
1649 }
1650
1651 TeardownAll();
1652}
1653
1659bool TVRec::WaitForEventThreadSleep(bool wake, std::chrono::milliseconds time)
1660{
1661 bool ok = false;
1662 MythTimer t;
1663 t.start();
1664
1665 while (!ok && (t.elapsed() < time))
1666 {
1667 MythTimer t2;
1668 t2.start();
1669
1670 if (wake)
1671 WakeEventLoop();
1672
1673 m_stateChangeLock.unlock();
1674
1675 sched_yield();
1676
1677 {
1678 QMutexLocker locker(&m_triggerEventSleepLock);
1682 }
1683
1684 m_stateChangeLock.lock();
1685
1686 // verify that we were triggered.
1687 ok = (m_tuningRequests.empty() && !m_changeState);
1688
1689 std::chrono::milliseconds te = t2.elapsed();
1690 if (!ok && te < 10ms)
1691 std::this_thread::sleep_for(10ms - te);
1692 }
1693 return ok;
1694}
1695
1697{
1698 QMutexLocker pendlock(&m_pendingRecLock);
1699
1700 for (auto it = m_pendingRecordings.begin(); it != m_pendingRecordings.end();)
1701 {
1702 if (MythDate::current() > (*it).m_recordingStart.addSecs(30))
1703 {
1704 LOG(VB_RECORD, LOG_INFO, LOC + "Deleting stale pending recording " +
1705 QString("[%1] '%2'")
1706 .arg((*it).m_info->GetInputID())
1707 .arg((*it).m_info->GetTitle()));
1708
1709 delete (*it).m_info;
1710 it = m_pendingRecordings.erase(it);
1711 }
1712 else
1713 {
1714 it++;
1715 }
1716 }
1717
1718 if (m_pendingRecordings.empty())
1719 return;
1720
1721 // Make sure EIT scan is stopped so it does't interfere
1723 {
1724 LOG(VB_CHANNEL, LOG_INFO,
1725 LOC + "Stopping active EIT scan for pending recording.");
1727 }
1728
1729 // If we have a pending recording and AskAllowRecording
1730 // or DoNotAskAllowRecording is set and the frontend is
1731 // ready send an ASK_RECORDING query to frontend.
1732
1733 bool has_rec = false;
1734 auto it = m_pendingRecordings.begin();
1735 if ((1 == m_pendingRecordings.size()) &&
1736 (*it).m_ask &&
1737 ((*it).m_info->GetInputID() == m_inputId) &&
1739 {
1741 has_rec = m_pseudoLiveTVRecording &&
1743 (*it).m_recordingStart);
1744 }
1745
1746 for (it = m_pendingRecordings.begin(); it != m_pendingRecordings.end(); ++it)
1747 {
1748 if (!(*it).m_ask && !(*it).m_doNotAsk)
1749 continue;
1750
1751 auto timeuntil = ((*it).m_doNotAsk) ?
1752 -1s: MythDate::secsInFuture((*it).m_recordingStart);
1753
1754 if (has_rec)
1755 (*it).m_canceled = true;
1756
1757 QString query = QString("ASK_RECORDING %1 %2 %3 %4")
1758 .arg(m_inputId)
1759 .arg(timeuntil.count())
1760 .arg(has_rec ? 1 : 0)
1761 .arg((*it).m_hasLaterShowing ? 1 : 0);
1762
1763 LOG(VB_GENERAL, LOG_INFO, LOC + query);
1764
1765 QStringList msg;
1766 (*it).m_info->ToStringList(msg);
1767 MythEvent me(query, msg);
1769
1770 (*it).m_ask = (*it).m_doNotAsk = false;
1771 }
1772}
1773
1775 uint &parentid,
1776 GeneralDBOptions &gen_opts,
1777 DVBDBOptions &dvb_opts,
1778 FireWireDBOptions &firewire_opts)
1779{
1780 int testnum = 0;
1781 QString test;
1782
1784 query.prepare(
1785 "SELECT videodevice, vbidevice, audiodevice, "
1786 " audioratelimit, cardtype, "
1787 " skipbtaudio, signal_timeout, channel_timeout, "
1788 " dvb_wait_for_seqstart, "
1789 ""
1790 " dvb_on_demand, dvb_tuning_delay, dvb_eitscan,"
1791 ""
1792 " firewire_speed, firewire_model, firewire_connection, "
1793 " parentid "
1794 ""
1795 "FROM capturecard "
1796 "WHERE cardid = :INPUTID");
1797 query.bindValue(":INPUTID", inputid);
1798
1799 if (!query.exec() || !query.isActive())
1800 {
1801 MythDB::DBError("getdevices", query);
1802 return false;
1803 }
1804
1805 if (!query.next())
1806 return false;
1807
1808 // General options
1809 test = query.value(0).toString();
1810 if (!test.isEmpty())
1811 gen_opts.m_videoDev = test;
1812
1813 test = query.value(1).toString();
1814 if (!test.isEmpty())
1815 gen_opts.m_vbiDev = test;
1816
1817 test = query.value(2).toString();
1818 if (!test.isEmpty())
1819 gen_opts.m_audioDev = test;
1820
1821 gen_opts.m_audioSampleRate = std::max(testnum, query.value(3).toInt());
1822
1823 test = query.value(4).toString();
1824 if (!test.isEmpty())
1825 gen_opts.m_inputType = test;
1826
1827 gen_opts.m_skipBtAudio = query.value(5).toBool();
1828
1829 gen_opts.m_signalTimeout = (uint) std::max(query.value(6).toInt(), 0);
1830 gen_opts.m_channelTimeout = (uint) std::max(query.value(7).toInt(), 0);
1831
1832 // We should have at least 1000 ms to acquire tables...
1833 int table_timeout = ((int)gen_opts.m_channelTimeout -
1834 (int)gen_opts.m_signalTimeout);
1835 if (table_timeout < 1000)
1836 gen_opts.m_channelTimeout = gen_opts.m_signalTimeout + 1000;
1837
1838 gen_opts.m_waitForSeqstart = query.value(8).toBool();
1839
1840 // DVB options
1841 uint dvboff = 9;
1842 dvb_opts.m_dvbOnDemand = query.value(dvboff + 0).toBool();
1843 dvb_opts.m_dvbTuningDelay = std::chrono::milliseconds(query.value(dvboff + 1).toUInt());
1844 dvb_opts.m_dvbEitScan = query.value(dvboff + 2).toBool();
1845
1846 // Firewire options
1847 uint fireoff = dvboff + 3;
1848 firewire_opts.m_speed = query.value(fireoff + 0).toUInt();
1849
1850 test = query.value(fireoff + 1).toString();
1851 if (!test.isEmpty())
1852 firewire_opts.m_model = test;
1853
1854 firewire_opts.m_connection = query.value(fireoff + 2).toUInt();
1855
1856 parentid = query.value(15).toUInt();
1857
1858 return true;
1859}
1860
1861static void GetPidsToCache(DTVSignalMonitor *dtvMon, pid_cache_t &pid_cache)
1862{
1863 if (!dtvMon->GetATSCStreamData())
1864 return;
1865
1866 const MasterGuideTable *mgt = dtvMon->GetATSCStreamData()->GetCachedMGT();
1867 if (!mgt)
1868 return;
1869
1870 for (uint i = 0; i < mgt->TableCount(); ++i)
1871 {
1872 pid_cache_item_t item(mgt->TablePID(i), mgt->TableType(i));
1873 pid_cache.push_back(item);
1874 }
1875 dtvMon->GetATSCStreamData()->ReturnCachedTable(mgt);
1876}
1877
1878static bool ApplyCachedPids(DTVSignalMonitor *dtvMon, const DTVChannel* channel)
1879{
1880 pid_cache_t pid_cache;
1881 channel->GetCachedPids(pid_cache);
1882 bool vctpid_cached = false;
1883 for (const auto& pid : pid_cache)
1884 {
1885 if ((pid.GetTableID() == TableID::TVCT) ||
1886 (pid.GetTableID() == TableID::CVCT))
1887 {
1888 vctpid_cached = true;
1889 if (dtvMon->GetATSCStreamData())
1890 dtvMon->GetATSCStreamData()->AddListeningPID(pid.GetPID());
1891 }
1892 }
1893 return vctpid_cached;
1894}
1895
1912{
1913 LOG(VB_RECORD, LOG_INFO, LOC + "Setting up table monitoring.");
1914
1916 DTVChannel *dtvchan = GetDTVChannel();
1917 if (!sm || !dtvchan)
1918 {
1919 LOG(VB_GENERAL, LOG_ERR, LOC + "Setting up table monitoring.");
1920 return false;
1921 }
1922
1923 MPEGStreamData *sd = nullptr;
1924 if (GetDTVRecorder())
1925 {
1926 sd = GetDTVRecorder()->GetStreamData();
1927 sd->SetCaching(true);
1928 }
1929
1930 QString recording_type = "all";
1934 const StandardSetting *setting = profile.byName("recordingtype");
1935 if (setting)
1936 recording_type = setting->getValue();
1937
1938 const QString tuningmode = dtvchan->GetTuningMode();
1939
1940 // Check if this is an ATSC Channel
1941 int major = dtvchan->GetMajorChannel();
1942 int minor = dtvchan->GetMinorChannel();
1943 if ((minor > 0) && (tuningmode == "atsc"))
1944 {
1945 QString msg = QString("ATSC channel: %1_%2").arg(major).arg(minor);
1946 LOG(VB_RECORD, LOG_INFO, LOC + msg);
1947
1948 auto *asd = dynamic_cast<ATSCStreamData*>(sd);
1949 if (!asd)
1950 {
1951 sd = asd = new ATSCStreamData(major, minor, m_inputId);
1952 sd->SetCaching(true);
1953 if (GetDTVRecorder())
1955 }
1956
1957 asd->Reset();
1958 sm->SetStreamData(sd);
1959 sm->SetChannel(major, minor);
1960 sd->SetRecordingType(recording_type);
1961
1962 // Try to get pid of VCT from cache and
1963 // require MGT if we don't have VCT pid.
1964 if (!ApplyCachedPids(sm, dtvchan))
1966
1967 LOG(VB_RECORD, LOG_INFO, LOC +
1968 "Successfully set up ATSC table monitoring.");
1969 return true;
1970 }
1971
1972 // Check if this is an DVB channel
1973 int progNum = dtvchan->GetProgramNumber();
1974 if ((progNum >= 0) && (tuningmode == "dvb") && CardUtil::IsChannelReusable(m_genOpt.m_inputType))
1975 {
1976 int netid = dtvchan->GetOriginalNetworkID();
1977 int tsid = dtvchan->GetTransportID();
1978
1979 auto *dsd = dynamic_cast<DVBStreamData*>(sd);
1980 if (!dsd)
1981 {
1982 sd = dsd = new DVBStreamData(netid, tsid, progNum, m_inputId);
1983 sd->SetCaching(true);
1984 if (GetDTVRecorder())
1986 }
1987
1988 LOG(VB_RECORD, LOG_INFO, LOC +
1989 QString("DVB service_id %1 on net_id %2 tsid %3")
1990 .arg(progNum).arg(netid).arg(tsid));
1991
1993
1994 dsd->Reset();
1995 sm->SetStreamData(sd);
1996 sm->SetDVBService(netid, tsid, progNum);
1997 sd->SetRecordingType(recording_type);
1998
2002 sm->SetRotorTarget(1.0F);
2003
2004 if (EITscan)
2005 {
2007 sm->IgnoreEncrypted(true);
2008 }
2009
2010 LOG(VB_RECORD, LOG_INFO, LOC +
2011 "Successfully set up DVB table monitoring.");
2012 return true;
2013 }
2014
2015 // Check if this is an MPEG channel
2016 if (progNum >= 0)
2017 {
2018 if (!sd)
2019 {
2020 sd = new MPEGStreamData(progNum, m_inputId, true);
2021 sd->SetCaching(true);
2022 if (GetDTVRecorder())
2024 }
2025
2026 QString msg = QString("MPEG program number: %1").arg(progNum);
2027 LOG(VB_RECORD, LOG_INFO, LOC + msg);
2028
2030
2031 sd->Reset();
2032 sm->SetStreamData(sd);
2033 sm->SetProgramNumber(progNum);
2034 sd->SetRecordingType(recording_type);
2035
2039 sm->SetRotorTarget(1.0F);
2040
2041 if (EITscan)
2042 {
2044 sm->IgnoreEncrypted(true);
2045 }
2046
2047 LOG(VB_RECORD, LOG_INFO, LOC +
2048 "Successfully set up MPEG table monitoring.");
2049 return true;
2050 }
2051
2052 // If this is not an ATSC, DVB or MPEG channel then check to make sure
2053 // that we have permanent pidcache entries.
2054 bool ok = false;
2055 if (GetDTVChannel())
2056 {
2057 pid_cache_t pid_cache;
2058 GetDTVChannel()->GetCachedPids(pid_cache);
2059 for (auto item = pid_cache.cbegin(); !ok && item != pid_cache.cend(); ++item)
2060 ok |= item->IsPermanent();
2061 }
2062
2063 if (!ok)
2064 {
2065 QString msg = "No valid DTV info, ATSC maj(%1) min(%2), MPEG pn(%3)";
2066 LOG(VB_GENERAL, LOG_ERR, LOC + msg.arg(major).arg(minor).arg(progNum));
2067 }
2068 else
2069 {
2070 LOG(VB_RECORD, LOG_INFO, LOC +
2071 "Successfully set up raw pid monitoring.");
2072 }
2073
2074 return ok;
2075}
2076
2091bool TVRec::SetupSignalMonitor(bool tablemon, bool EITscan, bool notify)
2092{
2093 LOG(VB_RECORD, LOG_INFO, LOC + QString("SetupSignalMonitor(%1, %2)")
2094 .arg(tablemon).arg(notify));
2095
2096 // if it already exists, there no need to initialize it
2097 if (m_signalMonitor)
2098 return true;
2099
2100 // if there is no channel object we can't monitor it
2101 if (!m_channel)
2102 return false;
2103
2104 // nothing to monitor here either (DummyChannel)
2105 if (m_genOpt.m_inputType == "IMPORT" || m_genOpt.m_inputType == "DEMO")
2106 return true;
2107
2108 // make sure statics are initialized
2110
2113 m_channel, false);
2114
2115 if (m_signalMonitor)
2116 {
2117 LOG(VB_RECORD, LOG_INFO, LOC + "Signal monitor successfully created");
2118 // If this is a monitor for Digital TV, initialize table monitors
2119 if (GetDTVSignalMonitor() && tablemon &&
2120 !SetupDTVSignalMonitor(EITscan))
2121 {
2122 LOG(VB_GENERAL, LOG_ERR, LOC +
2123 "Failed to setup digital signal monitoring");
2124
2125 return false;
2126 }
2127
2133
2134 // Start the monitoring thread
2136 }
2137
2138 return true;
2139}
2140
2146{
2147 if (!m_signalMonitor)
2148 return;
2149
2150 LOG(VB_RECORD, LOG_INFO, LOC + "TeardownSignalMonitor() -- begin");
2151
2152 // If this is a DTV signal monitor, save any pids we know about.
2154 DTVChannel *dtvChan = GetDTVChannel();
2155 if (dtvMon && dtvChan)
2156 {
2157 pid_cache_t pid_cache;
2158 GetPidsToCache(dtvMon, pid_cache);
2159 if (!pid_cache.empty())
2160 dtvChan->SaveCachedPids(pid_cache);
2161 }
2162
2163 if (m_signalMonitor)
2164 {
2165 delete m_signalMonitor;
2166 m_signalMonitor = nullptr;
2167 }
2168
2169 LOG(VB_RECORD, LOG_INFO, LOC + "TeardownSignalMonitor() -- end");
2170}
2171
2183std::chrono::milliseconds TVRec::SetSignalMonitoringRate(std::chrono::milliseconds rate, int notifyFrontend)
2184{
2185 QString msg = "SetSignalMonitoringRate(%1, %2)";
2186 LOG(VB_RECORD, LOG_INFO, LOC +
2187 msg.arg(rate.count()).arg(notifyFrontend) + "-- start");
2188
2189 QMutexLocker lock(&m_stateChangeLock);
2190
2192 {
2193 LOG(VB_GENERAL, LOG_ERR, LOC +
2194 "Signal Monitoring is notsupported by your hardware.");
2195 return 0ms;
2196 }
2197
2199 {
2200 LOG(VB_GENERAL, LOG_ERR, LOC +
2201 "Signal can only be monitored in LiveTV Mode.");
2202 return 0ms;
2203 }
2204
2205 ClearFlags(kFlagRingBufferReady, __FILE__, __LINE__);
2206
2207 TuningRequest req = (rate > 0ms) ?
2210
2212
2213 // Wait for RingBuffer reset
2216 LOG(VB_RECORD, LOG_INFO, LOC +
2217 msg.arg(rate.count()).arg(notifyFrontend) + " -- end");
2218 return 1ms;
2219}
2220
2222{
2223 return dynamic_cast<DTVSignalMonitor*>(m_signalMonitor);
2224}
2225
2237bool TVRec::ShouldSwitchToAnotherInput(const QString& chanid) const
2238{
2239 QString msg("");
2241
2242 if (!query.isConnected())
2243 return false;
2244
2245 query.prepare("SELECT channel.channum, channel.callsign "
2246 "FROM channel "
2247 "WHERE channel.chanid = :CHANID");
2248 query.bindValue(":CHANID", chanid);
2249 if (!query.exec() || !query.next())
2250 {
2251 MythDB::DBError("ShouldSwitchToAnotherInput", query);
2252 return false;
2253 }
2254
2255 QString channelname = query.value(0).toString();
2256 QString callsign = query.value(1).toString();
2257
2258 query.prepare(
2259 "SELECT channel.channum "
2260 "FROM channel, capturecard "
2261 "WHERE deleted IS NULL AND "
2262 " ( channel.chanid = :CHANID OR "
2263 " ( channel.channum = :CHANNUM AND "
2264 " channel.callsign = :CALLSIGN ) "
2265 " ) AND "
2266 " channel.sourceid = capturecard.sourceid AND "
2267 " capturecard.cardid = :INPUTID");
2268 query.bindValue(":CHANID", chanid);
2269 query.bindValue(":CHANNUM", channelname);
2270 query.bindValue(":CALLSIGN", callsign);
2271 query.bindValue(":INPUTID", m_inputId);
2272
2273 if (!query.exec() || !query.isActive())
2274 {
2275 MythDB::DBError("ShouldSwitchToAnotherInput", query);
2276 }
2277 else if (query.size() > 0)
2278 {
2279 msg = "Found channel (%1) on current input[%2].";
2280 LOG(VB_RECORD, LOG_INFO, LOC + msg.arg(channelname).arg(m_inputId));
2281 return false;
2282 }
2283
2284 // We didn't find it on the current input, so now we check other inputs.
2285 query.prepare(
2286 "SELECT channel.channum, capturecard.cardid "
2287 "FROM channel, capturecard "
2288 "WHERE deleted IS NULL AND "
2289 " ( channel.chanid = :CHANID OR "
2290 " ( channel.channum = :CHANNUM AND "
2291 " channel.callsign = :CALLSIGN ) "
2292 " ) AND "
2293 " channel.sourceid = capturecard.sourceid AND "
2294 " capturecard.cardid != :INPUTID");
2295 query.bindValue(":CHANID", chanid);
2296 query.bindValue(":CHANNUM", channelname);
2297 query.bindValue(":CALLSIGN", callsign);
2298 query.bindValue(":INPUTID", m_inputId);
2299
2300 if (!query.exec() || !query.isActive())
2301 {
2302 MythDB::DBError("ShouldSwitchToAnotherInput", query);
2303 }
2304 else if (query.next())
2305 {
2306 msg = QString("Found channel (%1) on different input(%2).")
2307 .arg(query.value(0).toString(), query.value(1).toString());
2308 LOG(VB_RECORD, LOG_INFO, LOC + msg);
2309 return true;
2310 }
2311
2312 msg = QString("Did not find channel(%1) on any input.").arg(channelname);
2313 LOG(VB_RECORD, LOG_ERR, LOC + msg);
2314 return false;
2315}
2316
2327bool TVRec::CheckChannel(const QString& name) const
2328{
2329 if (!m_channel)
2330 return false;
2331
2332 return m_channel->CheckChannel(name);
2333}
2334
2338static QString add_spacer(const QString &channel, const QString &spacer)
2339{
2340 QString chan = channel;
2341 if ((chan.length() >= 2) && !spacer.isEmpty())
2342 return chan.left(chan.length()-1) + spacer + chan.right(1);
2343 return chan;
2344}
2345
2374 uint &complete_valid_channel_on_rec,
2375 bool &is_extra_char_useful,
2376 QString &needed_spacer) const
2377{
2378#if DEBUG_CHANNEL_PREFIX
2379 LOG(VB_GENERAL, LOG_DEBUG, QString("CheckChannelPrefix(%1)").arg(prefix));
2380#endif
2381
2382 static const std::array<const QString,5> s_spacers = { "", "_", "-", "#", "." };
2383
2385 QString basequery = QString(
2386 "SELECT channel.chanid, channel.channum, capturecard.cardid "
2387 "FROM channel, capturecard "
2388 "WHERE deleted IS NULL AND "
2389 " channel.channum LIKE '%1%' AND "
2390 " channel.sourceid = capturecard.sourceid");
2391
2392 const std::array<const QString,2> inputquery
2393 {
2394 QString(" AND capturecard.cardid = '%1'").arg(m_inputId),
2395 QString(" AND capturecard.cardid != '%1'").arg(m_inputId),
2396 };
2397
2398 std::vector<unsigned int> fchanid;
2399 std::vector<QString> fchannum;
2400 std::vector<unsigned int> finputid;
2401 std::vector<QString> fspacer;
2402
2403 for (const auto & str : inputquery)
2404 {
2405 for (const auto & spacer : s_spacers)
2406 {
2407 QString qprefix = add_spacer(
2408 prefix, (spacer == "_") ? "\\_" : spacer);
2409 query.prepare(basequery.arg(qprefix) + str);
2410
2411 if (!query.exec() || !query.isActive())
2412 {
2413 MythDB::DBError("checkchannel -- locate channum", query);
2414 }
2415 else if (query.size())
2416 {
2417 while (query.next())
2418 {
2419 fchanid.push_back(query.value(0).toUInt());
2420 fchannum.push_back(query.value(1).toString());
2421 finputid.push_back(query.value(2).toUInt());
2422 fspacer.emplace_back(spacer);
2423#if DEBUG_CHANNEL_PREFIX
2424 LOG(VB_GENERAL, LOG_DEBUG,
2425 QString("(%1,%2) Adding %3 rec %4")
2426 .arg(i).arg(j).arg(query.value(1).toString(),6)
2427 .arg(query.value(2).toUInt()));
2428#endif
2429 }
2430 }
2431
2432 if (prefix.length() < 2)
2433 break;
2434 }
2435 }
2436
2437 // Now process the lists for the info we need...
2438 is_extra_char_useful = false;
2439 complete_valid_channel_on_rec = 0;
2440 needed_spacer.clear();
2441
2442 if (fchanid.empty())
2443 return false;
2444
2445 if (fchanid.size() == 1) // Unique channel...
2446 {
2447 needed_spacer = fspacer[0];
2448 bool nc = (fchannum[0] != add_spacer(prefix, fspacer[0]));
2449
2450 complete_valid_channel_on_rec = nc ? 0 : finputid[0];
2451 is_extra_char_useful = nc;
2452 return true;
2453 }
2454
2455 // If we get this far there is more than one channel
2456 // sharing the prefix we were given.
2457
2458 // Is an extra characher useful for disambiguation?
2459 is_extra_char_useful = false;
2460 for (uint i = 0; (i < fchannum.size()) && !is_extra_char_useful; i++)
2461 {
2462 is_extra_char_useful = (fchannum[i] != add_spacer(prefix, fspacer[i]));
2463#if DEBUG_CHANNEL_PREFIX
2464 LOG(VB_GENERAL, LOG_DEBUG, QString("is_extra_char_useful(%1!=%2): %3")
2465 .arg(fchannum[i]).arg(add_spacer(prefix, fspacer[i]))
2466 .arg(is_extra_char_useful));
2467#endif
2468 }
2469
2470 // Are any of the channels complete w/o spacer?
2471 // If so set complete_valid_channel_on_rec,
2472 // with a preference for our inputid.
2473 for (size_t i = 0; i < fchannum.size(); i++)
2474 {
2475 if (fchannum[i] == prefix)
2476 {
2477 complete_valid_channel_on_rec = finputid[i];
2478 if (finputid[i] == m_inputId)
2479 break;
2480 }
2481 }
2482
2483 if (complete_valid_channel_on_rec != 0)
2484 return true;
2485
2486 // Add a spacer, if one is needed to select a valid channel.
2487 bool spacer_needed = true;
2488 for (uint i = 0; (i < fspacer.size() && spacer_needed); i++)
2489 spacer_needed = !fspacer[i].isEmpty();
2490 if (spacer_needed)
2491 needed_spacer = fspacer[0];
2492
2493 // If it isn't useful to wait for more characters,
2494 // then try to commit to any true match immediately.
2495 for (size_t i = 0; i < (is_extra_char_useful ? 0 : fchanid.size()); i++)
2496 {
2497 if (fchannum[i] == add_spacer(prefix, fspacer[i]))
2498 {
2499 needed_spacer = fspacer[i];
2500 complete_valid_channel_on_rec = finputid[i];
2501 return true;
2502 }
2503 }
2504
2505 return true;
2506}
2507
2509 const QString &channum)
2510{
2511 if (!m_recorder)
2512 return false;
2513
2514 QString videoFilters = ChannelUtil::GetVideoFilters(sourceid, channum);
2515 if (!videoFilters.isEmpty())
2516 {
2517 m_recorder->SetVideoFilters(videoFilters);
2518 return true;
2519 }
2520
2521 return false;
2522}
2523
2529{
2530 return ((m_recorder && m_recorder->IsRecording()) ||
2532}
2533
2539bool TVRec::IsBusy(InputInfo *busy_input, std::chrono::seconds time_buffer) const
2540{
2541 InputInfo dummy;
2542 if (!busy_input)
2543 busy_input = &dummy;
2544
2545 busy_input->Clear();
2546
2547 if (!m_channel)
2548 return false;
2549
2550 if (!m_channel->GetInputID())
2551 return false;
2552
2553 uint chanid = 0;
2554
2555 if (GetState() != kState_None)
2556 {
2557 busy_input->m_inputId = m_channel->GetInputID();
2558 chanid = m_channel->GetChanID();
2559 }
2560
2561 PendingInfo pendinfo;
2562 bool has_pending = false;
2563 {
2564 m_pendingRecLock.lock();
2565 PendingMap::const_iterator it = m_pendingRecordings.find(m_inputId);
2566 has_pending = (it != m_pendingRecordings.end());
2567 if (has_pending)
2568 pendinfo = *it;
2569 m_pendingRecLock.unlock();
2570 }
2571
2572 if (!busy_input->m_inputId && has_pending)
2573 {
2574 auto timeLeft = MythDate::secsInFuture(pendinfo.m_recordingStart);
2575
2576 if (timeLeft <= time_buffer)
2577 {
2578 QString channum;
2579 QString input;
2580 if (pendinfo.m_info->QueryTuningInfo(channum, input))
2581 {
2582 busy_input->m_inputId = m_channel->GetInputID();
2583 chanid = pendinfo.m_info->GetChanID();
2584 }
2585 }
2586 }
2587
2588 if (busy_input->m_inputId)
2589 {
2590 CardUtil::GetInputInfo(*busy_input);
2591 busy_input->m_chanId = chanid;
2592 busy_input->m_mplexId = ChannelUtil::GetMplexID(busy_input->m_chanId);
2593 busy_input->m_mplexId =
2594 (32767 == busy_input->m_mplexId) ? 0 : busy_input->m_mplexId;
2595 }
2596
2597 return busy_input->m_inputId != 0U;
2598}
2599
2600
2608{
2609 QMutexLocker lock(&m_stateChangeLock);
2610
2611 if (m_recorder)
2612 return m_recorder->GetFrameRate();
2613 return -1.0F;
2614}
2615
2623{
2624 QMutexLocker lock(&m_stateChangeLock);
2625
2626 if (m_recorder)
2627 return m_recorder->GetFramesWritten();
2628 return -1;
2629}
2630
2638{
2639 QMutexLocker lock(&m_stateChangeLock);
2640
2641 if (m_buffer)
2642 return m_buffer->GetWritePosition();
2643 return -1;
2644}
2645
2653int64_t TVRec::GetKeyframePosition(uint64_t desired) const
2654{
2655 QMutexLocker lock(&m_stateChangeLock);
2656
2657 if (m_recorder)
2658 return m_recorder->GetKeyframePosition(desired);
2659 return -1;
2660}
2661
2671 int64_t start, int64_t end, frm_pos_map_t &map) const
2672{
2673 QMutexLocker lock(&m_stateChangeLock);
2674
2675 if (m_recorder)
2676 return m_recorder->GetKeyframePositions(start, end, map);
2677
2678 return false;
2679}
2680
2682 int64_t start, int64_t end, frm_pos_map_t &map) const
2683{
2684 QMutexLocker lock(&m_stateChangeLock);
2685
2686 if (m_recorder)
2687 return m_recorder->GetKeyframeDurations(start, end, map);
2688
2689 return false;
2690}
2691
2697long long TVRec::GetMaxBitrate(void) const
2698{
2699 long long bitrate = 0;
2700 if (m_genOpt.m_inputType == "MPEG")
2701 { // NOLINT(bugprone-branch-clone)
2702 bitrate = 10080000LL; // use DVD max bit rate
2703 }
2704 else if (m_genOpt.m_inputType == "HDPVR")
2705 {
2706 bitrate = 20200000LL; // Peak bit rate for HD-PVR
2707 }
2709 {
2710 bitrate = 22200000LL; // 1080i
2711 }
2712 else // frame grabber
2713 {
2714 bitrate = 10080000LL; // use DVD max bit rate, probably too big
2715 }
2716
2717 return bitrate;
2718}
2719
2725void TVRec::SpawnLiveTV(LiveTVChain *newchain, bool pip, QString startchan)
2726{
2727 QMutexLocker lock(&m_stateChangeLock);
2728
2729 m_tvChain = newchain;
2730 m_tvChain->IncrRef(); // mark it for TVRec use
2732
2733 QString hostprefix = MythCoreContext::GenMythURL(
2736
2737 m_tvChain->SetHostPrefix(hostprefix);
2739
2740 m_isPip = pip;
2741 m_liveTVStartChannel = std::move(startchan);
2742
2743 // Change to WatchingLiveTV
2745 // Wait for state change to take effect
2747
2748 // Make sure StartRecording can't steal our tuner
2749 SetFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2750}
2751
2756{
2757 if (m_tvChain)
2758 return m_tvChain->GetID();
2759 return "";
2760}
2761
2771{
2772 QMutexLocker lock(&m_stateChangeLock);
2773
2775 return; // already stopped
2776
2777 if (!m_curRecording)
2778 return;
2779
2780 const QString recgrp = m_curRecording->QueryRecordingGroup();
2782
2783 if (recgrp != "LiveTV" && !m_pseudoLiveTVRecording)
2784 {
2785 // User wants this recording to continue
2787 }
2788 else if (recgrp == "LiveTV" && m_pseudoLiveTVRecording)
2789 {
2790 // User wants to abandon scheduled recording
2791 SetPseudoLiveTVRecording(nullptr);
2792 }
2793}
2794
2805{
2806 if (!m_channel)
2807 return;
2808
2809 // Notify scheduler of the recording.
2810 // + set up recording so it can be resumed
2811 rec->SetInputID(m_inputId);
2813
2814 if (rec->GetRecordingRuleType() == kNotRecording)
2815 {
2818 }
2819
2820 // + remove any end offset which would mismatch the live session
2821 rec->GetRecordingRule()->m_endOffset = 0;
2822
2823 // + save RecStatus::Inactive recstatus to so that a reschedule call
2824 // doesn't start recording this on another input before we
2825 // send the SCHEDULER_ADD_RECORDING message to the scheduler.
2827 rec->AddHistory(false);
2828
2829 // + save RecordingRule so that we get a recordid
2830 // (don't allow RescheduleMatch(), avoiding unneeded reschedule)
2831 rec->GetRecordingRule()->Save(false);
2832
2833 // + save recordid to recorded entry
2834 rec->ApplyRecordRecID();
2835
2836 // + set proper recstatus (saved later)
2838
2839 // + pass proginfo to scheduler and reschedule
2840 QStringList prog;
2841 rec->ToStringList(prog);
2842 MythEvent me("SCHEDULER_ADD_RECORDING", prog);
2844
2845 // Allow scheduler to end this recording before post-roll,
2846 // if it has another recording for this recorder.
2847 ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2848}
2849
2851 RecordingProfile *recpro, int line)
2852{
2853 if (kAutoRunProfile == t)
2854 {
2856 if (!recpro)
2857 {
2858 LoadProfile(nullptr, rec, profile);
2859 recpro = &profile;
2860 }
2862 init_jobs(rec, *recpro, m_runJobOnHostOnly,
2864 }
2865 else
2866 {
2868 }
2869 LOG(VB_JOBQUEUE, LOG_INFO,
2870 QString("InitAutoRunJobs for %1, line %2 -> 0x%3")
2871 .arg(rec->MakeUniqueKey()).arg(line)
2872 .arg(m_autoRunJobs[rec->MakeUniqueKey()],0,16));
2873}
2874
2886void TVRec::SetLiveRecording([[maybe_unused]] int recording)
2887{
2888 LOG(VB_GENERAL, LOG_INFO, LOC +
2889 QString("SetLiveRecording(%1)").arg(recording));
2890 QMutexLocker locker(&m_stateChangeLock);
2891
2893 bool was_rec = m_pseudoLiveTVRecording;
2895 if (was_rec && !m_pseudoLiveTVRecording)
2896 {
2897 LOG(VB_GENERAL, LOG_INFO, LOC + "SetLiveRecording() -- cancel");
2898 // cancel -- 'recording' should be 0 or -1
2899 SetFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2901 InitAutoRunJobs(m_curRecording, kAutoRunNone, nullptr, __LINE__);
2902 }
2903 else if (!was_rec && m_pseudoLiveTVRecording)
2904 {
2905 LOG(VB_GENERAL, LOG_INFO, LOC + "SetLiveRecording() -- record");
2906 // record -- 'recording' should be 1 or -1
2907
2908 // If the last recording was flagged for keeping
2909 // in the frontend, then add the recording rule
2910 // so that transcode, commfrag, etc can be run.
2915 InitAutoRunJobs(m_curRecording, kAutoRunProfile, nullptr, __LINE__);
2916 }
2917
2918 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
2919 .arg(m_curRecording->GetInputID())
2920 .arg(m_curRecording->GetChanID())
2922 .arg(recstat)
2924
2926}
2927
2933{
2934 QMutexLocker lock(&m_stateChangeLock);
2935 LOG(VB_RECORD, LOG_INFO, LOC +
2936 QString("StopLiveTV(void) curRec: 0x%1 pseudoRec: 0x%2")
2937 .arg((uint64_t)m_curRecording,0,16)
2938 .arg((uint64_t)m_pseudoLiveTVRecording,0,16));
2939
2941 return;
2942
2943 bool hadPseudoLiveTVRec = m_pseudoLiveTVRecording;
2945
2946 if (!hadPseudoLiveTVRec && m_pseudoLiveTVRecording)
2948
2949 // Figure out next state and if needed recording end time.
2950 TVState next_state = kState_None;
2952 {
2954 next_state = kState_RecordingOnly;
2955 }
2956
2957 // Change to the appropriate state
2958 ChangeState(next_state);
2959
2960 // Wait for state change to take effect...
2962
2963 // We are done with the tvchain...
2964 if (m_tvChain)
2965 {
2966 m_tvChain->DecrRef();
2967 }
2968 m_tvChain = nullptr;
2969}
2970
2980{
2981 QMutexLocker lock(&m_stateChangeLock);
2982
2983 if (!m_recorder)
2984 {
2985 LOG(VB_GENERAL, LOG_ERR, LOC +
2986 "PauseRecorder() called with no recorder");
2987 return;
2988 }
2989
2990 m_recorder->Pause();
2991}
2992
2999{
3000 if (m_pauseNotify)
3001 WakeEventLoop();
3002}
3003
3007void TVRec::ToggleChannelFavorite(const QString& changroupname)
3008{
3009 QMutexLocker lock(&m_stateChangeLock);
3010
3011 if (!m_channel)
3012 return;
3013
3014 // Get current channel id...
3015 uint sourceid = m_channel->GetSourceID();
3016 QString channum = m_channel->GetChannelName();
3017 uint chanid = ChannelUtil::GetChanID(sourceid, channum);
3018
3019 if (!chanid)
3020 {
3021 LOG(VB_GENERAL, LOG_ERR, LOC +
3022 QString("Channel: \'%1\' was not found in the database.\n"
3023 "\t\tMost likely, the 'starting channel' for this "
3024 "Input Connection is invalid.\n"
3025 "\t\tCould not toggle favorite.").arg(channum));
3026 return;
3027 }
3028
3029 int changrpid = ChannelGroup::GetChannelGroupId(changroupname);
3030 if (changrpid <1)
3031 {
3032 LOG(VB_RECORD, LOG_ERR, LOC +
3033 QString("ToggleChannelFavorite: Invalid channel group name %1,")
3034 .arg(changroupname));
3035 }
3036 else
3037 {
3038 bool result = ChannelGroup::ToggleChannel(chanid, changrpid, true);
3039
3040 if (!result)
3041 {
3042 LOG(VB_RECORD, LOG_ERR, LOC + "Unable to toggle channel favorite.");
3043 }
3044 else
3045 {
3046 LOG(VB_RECORD, LOG_INFO, LOC +
3047 QString("Toggled channel favorite.channum %1, chan group %2")
3048 .arg(channum, changroupname));
3049 }
3050 }
3051}
3052
3059{
3060 QMutexLocker lock(&m_stateChangeLock);
3061 if (!m_channel)
3062 return -1;
3063
3064 int ret = m_channel->GetPictureAttribute(attr);
3065
3066 return (ret < 0) ? -1 : ret / 655;
3067}
3068
3077 PictureAttribute attr,
3078 bool direction)
3079{
3080 QMutexLocker lock(&m_stateChangeLock);
3081 if (!m_channel)
3082 return -1;
3083
3084 int ret = m_channel->ChangePictureAttribute(type, attr, direction);
3085
3086 return (ret < 0) ? -1 : ret / 655;
3087}
3088
3092QString TVRec::GetInput(void) const
3093{
3094 if (m_channel)
3095 return m_channel->GetInputName();
3096 return {};
3097}
3098
3103{
3104 if (m_channel)
3105 return m_channel->GetSourceID();
3106 return 0;
3107}
3108
3117QString TVRec::SetInput(QString input)
3118{
3119 QMutexLocker lock(&m_stateChangeLock);
3120 QString origIn = input;
3121 LOG(VB_RECORD, LOG_INFO, LOC + "SetInput(" + input + ") -- begin");
3122
3123 if (!m_channel)
3124 {
3125 LOG(VB_RECORD, LOG_INFO, LOC + "SetInput() -- end no channel class");
3126 return {};
3127 }
3128
3129 LOG(VB_RECORD, LOG_INFO, LOC + "SetInput(" + origIn + ":" + input +
3130 ") -- end nothing to do");
3131 return input;
3132}
3133
3143void TVRec::SetChannel(const QString& name, uint requestType)
3144{
3145 QMutexLocker locker1(&m_setChannelLock);
3146 QMutexLocker locker2(&m_stateChangeLock);
3147
3148 LOG(VB_CHANNEL, LOG_INFO, LOC +
3149 QString("SetChannel(%1) -- begin").arg(name));
3150
3151 // Detect tuning request type if needed
3152 if (requestType & kFlagDetect)
3153 {
3155 requestType = m_lastTuningRequest.m_flags & (kFlagRec | kFlagNoRec);
3156 }
3157
3158 // Clear the RingBuffer reset flag, in case we wait for a reset below
3159 ClearFlags(kFlagRingBufferReady, __FILE__, __LINE__);
3160
3161 // Clear out any EITScan channel change requests
3162 auto it = m_tuningRequests.begin();
3163 while (it != m_tuningRequests.end())
3164 {
3165 if ((*it).m_flags & kFlagEITScan)
3166 it = m_tuningRequests.erase(it);
3167 else
3168 ++it;
3169 }
3170
3171 // Actually add the tuning request to the queue, and
3172 // then wait for it to start tuning
3173 m_tuningRequests.enqueue(TuningRequest(requestType, name));
3175
3176 // If we are using a recorder, wait for a RingBuffer reset
3177 if (requestType & kFlagRec)
3178 {
3181 }
3182 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("SetChannel(%1) -- end").arg(name));
3183}
3184
3192bool TVRec::QueueEITChannelChange(const QString &name)
3193{
3194 LOG(VB_CHANNEL, LOG_INFO, LOC +
3195 QString("QueueEITChannelChange(%1)").arg(name));
3196
3197 bool ok = false;
3198 if (m_setChannelLock.tryLock())
3199 {
3200 if (m_stateChangeLock.tryLock())
3201 {
3202 if (m_tuningRequests.empty())
3203 {
3205 ok = true;
3206 }
3207 m_stateChangeLock.unlock();
3208 }
3209 m_setChannelLock.unlock();
3210 }
3211
3212 LOG(VB_CHANNEL, LOG_DEBUG, LOC +
3213 QString("QueueEITChannelChange(%1) %2")
3214 .arg(name, ok ? "done" : "failed"));
3215
3216 return ok;
3217}
3218
3220 QString &title, QString &subtitle,
3221 QString &desc, QString &category,
3222 QString &starttime, QString &endtime,
3223 QString &callsign, QString &iconpath,
3224 QString &channum, uint &sourceChanid,
3225 QString &seriesid, QString &programid)
3226{
3227 QString compare = "<=";
3228 QString sortorder = "desc";
3229 uint chanid = 0;
3230
3231 if (sourceChanid)
3232 {
3233 chanid = sourceChanid;
3234
3235 if (BROWSE_UP == direction) {
3237 } else if (BROWSE_DOWN == direction) {
3239 } else if (BROWSE_FAVORITE == direction) {
3240 chanid = m_channel->GetNextChannel(
3242 } else if (BROWSE_LEFT == direction) {
3243 compare = "<";
3244 } else if (BROWSE_RIGHT == direction) {
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 chanid = m_channel->GetNextChannel(channum,
3261 } else if (BROWSE_LEFT == direction) {
3263 compare = "<";
3264 } else if (BROWSE_RIGHT == direction) {
3266 compare = ">";
3267 sortorder = "asc";
3268 }
3269 }
3270
3271 QString querystr = QString(
3272 "SELECT title, subtitle, description, category, "
3273 " starttime, endtime, callsign, icon, "
3274 " channum, seriesid, programid "
3275 "FROM program, channel "
3276 "WHERE program.chanid = channel.chanid AND "
3277 " channel.chanid = :CHANID AND "
3278 " starttime %1 :STARTTIME "
3279 "ORDER BY starttime %2 "
3280 "LIMIT 1").arg(compare, sortorder);
3281
3283 query.prepare(querystr);
3284 query.bindValue(":CHANID", chanid);
3285 query.bindValue(":STARTTIME", starttime);
3286
3287 // Clear everything now in case either query fails.
3288 title = subtitle = desc = category = "";
3289 starttime = endtime = callsign = iconpath = "";
3290 channum = seriesid = programid = "";
3291 sourceChanid = 0;
3292
3293 // Try to get the program info
3294 if (!query.exec() && !query.isActive())
3295 {
3296 MythDB::DBError("GetNextProgram -- get program info", query);
3297 }
3298 else if (query.next())
3299 {
3300 title = query.value(0).toString();
3301 subtitle = query.value(1).toString();
3302 desc = query.value(2).toString();
3303 category = query.value(3).toString();
3304 starttime = query.value(4).toString();
3305 endtime = query.value(5).toString();
3306 callsign = query.value(6).toString();
3307 iconpath = query.value(7).toString();
3308 channum = query.value(8).toString();
3309 seriesid = query.value(9).toString();
3310 programid = query.value(10).toString();
3311 sourceChanid = chanid;
3312 return;
3313 }
3314
3315 // Couldn't get program info, so get the channel info instead
3316 query.prepare(
3317 "SELECT channum, callsign, icon "
3318 "FROM channel "
3319 "WHERE chanid = :CHANID");
3320 query.bindValue(":CHANID", chanid);
3321
3322 if (!query.exec() || !query.isActive())
3323 {
3324 MythDB::DBError("GetNextProgram -- get channel info", query);
3325 }
3326 else if (query.next())
3327 {
3328 sourceChanid = chanid;
3329 channum = query.value(0).toString();
3330 callsign = query.value(1).toString();
3331 iconpath = query.value(2).toString();
3332 }
3333}
3334
3335bool TVRec::GetChannelInfo(uint &chanid, uint &sourceid,
3336 QString &callsign, QString &channum,
3337 QString &channame, QString &xmltvid) const
3338{
3339 callsign.clear();
3340 channum.clear();
3341 channame.clear();
3342 xmltvid.clear();
3343
3344 if ((!chanid || !sourceid) && !m_channel)
3345 return false;
3346
3347 if (!chanid)
3348 chanid = (uint) std::max(m_channel->GetChanID(), 0);
3349
3350 if (!sourceid)
3351 sourceid = m_channel->GetSourceID();
3352
3354 query.prepare(
3355 "SELECT callsign, channum, name, xmltvid "
3356 "FROM channel "
3357 "WHERE chanid = :CHANID");
3358 query.bindValue(":CHANID", chanid);
3359 if (!query.exec() || !query.isActive())
3360 {
3361 MythDB::DBError("GetChannelInfo", query);
3362 return false;
3363 }
3364
3365 if (!query.next())
3366 return false;
3367
3368 callsign = query.value(0).toString();
3369 channum = query.value(1).toString();
3370 channame = query.value(2).toString();
3371 xmltvid = query.value(3).toString();
3372
3373 return true;
3374}
3375
3376bool TVRec::SetChannelInfo(uint chanid, uint sourceid,
3377 const QString& oldchannum,
3378 const QString& callsign, const QString& channum,
3379 const QString& channame, const QString& xmltvid)
3380{
3381 if (!chanid || !sourceid || channum.isEmpty())
3382 return false;
3383
3385 query.prepare(
3386 "UPDATE channel "
3387 "SET callsign = :CALLSIGN, "
3388 " channum = :CHANNUM, "
3389 " name = :CHANNAME, "
3390 " xmltvid = :XMLTVID "
3391 "WHERE chanid = :CHANID AND "
3392 " sourceid = :SOURCEID");
3393 query.bindValue(":CALLSIGN", callsign);
3394 query.bindValue(":CHANNUM", channum);
3395 query.bindValue(":CHANNAME", channame);
3396 query.bindValue(":XMLTVID", xmltvid);
3397 query.bindValue(":CHANID", chanid);
3398 query.bindValue(":SOURCEID", sourceid);
3399
3400 if (!query.exec())
3401 {
3402 MythDB::DBError("SetChannelInfo", query);
3403 return false;
3404 }
3405
3406 if (m_channel)
3407 m_channel->Renumber(sourceid, oldchannum, channum);
3408
3409 return true;
3410}
3411
3412void TVRec::SetChannelTimeout(std::chrono::milliseconds timeout)
3413{
3415 LOG(VB_CHANNEL, LOG_INFO, LOC +
3416 QString("Override tune timeout: %1ms")
3418}
3419
3424{
3425 QMutexLocker lock(&m_stateChangeLock);
3426
3427 MythMediaBuffer *oldbuffer = m_buffer;
3428 m_buffer = Buffer;
3429
3430 if (oldbuffer && (oldbuffer != Buffer))
3431 {
3433 ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3434 delete oldbuffer;
3435 }
3436
3437 m_switchingBuffer = false;
3438}
3439
3441{
3442 LOG(VB_GENERAL, LOG_INFO, LOC + "RingBufferChanged()");
3443
3444 QMutexLocker lock(&m_stateChangeLock);
3445
3446 if (pginfo)
3447 {
3448 if (m_curRecording)
3449 {
3452 delete m_curRecording;
3453 }
3455 m_curRecording = new RecordingInfo(*pginfo);
3458 }
3459
3461}
3462
3464 QString &input) const
3465{
3466 QString channum;
3467
3468 if (request.m_program)
3469 {
3470 request.m_program->QueryTuningInfo(channum, input);
3471 return channum;
3472 }
3473
3474 channum = request.m_channel;
3475 input = request.m_input;
3476
3477 // If this is Live TV startup, we need a channel...
3478 if (channum.isEmpty() && (request.m_flags & kFlagLiveTV))
3479 {
3480 if (!m_liveTVStartChannel.isEmpty())
3481 {
3482 channum = m_liveTVStartChannel;
3483 }
3484 else
3485 {
3488 }
3489 }
3490 if (request.m_flags & kFlagLiveTV)
3491 m_channel->Init(channum, false);
3492
3493 if (m_channel && !channum.isEmpty() && (channum.indexOf("NextChannel") >= 0))
3494 {
3495 // FIXME This is just horrible
3496 int dir = channum.right(channum.length() - 12).toInt();
3497 uint chanid = m_channel->GetNextChannel(0, static_cast<ChannelChangeDirection>(dir));
3498 channum = ChannelUtil::GetChanNum(chanid);
3499 }
3500
3501 return channum;
3502}
3503
3505{
3506 if ((request.m_flags & kFlagAntennaAdjust) || request.m_input.isEmpty() ||
3508 {
3509 return false;
3510 }
3511
3512 uint sourceid = m_channel->GetSourceID();
3513 QString oldchannum = m_channel->GetChannelName();
3514 QString newchannum = request.m_channel;
3515
3516 if (ChannelUtil::IsOnSameMultiplex(sourceid, newchannum, oldchannum))
3517 {
3519 auto *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
3520
3521 if (atsc)
3522 {
3523 uint major = 0;
3524 uint minor = 0;
3525 ChannelUtil::GetATSCChannel(sourceid, newchannum, major, minor);
3526
3527 if (minor && atsc->HasChannel(major, minor))
3528 {
3529 request.m_majorChan = major;
3530 request.m_minorChan = minor;
3531 return true;
3532 }
3533 }
3534
3535 if (mpeg)
3536 {
3537 uint progNum = ChannelUtil::GetProgramNumber(sourceid, newchannum);
3538 if (mpeg->HasProgram(progNum))
3539 {
3540 request.m_progNum = progNum;
3541 return true;
3542 }
3543 }
3544 }
3545
3546 return false;
3547}
3548
3557{
3558 if (!m_tuningRequests.empty())
3559 {
3560 TuningRequest request = m_tuningRequests.front();
3561 LOG(VB_RECORD, LOG_INFO, LOC +
3562 "HandleTuning Request: " + request.toString());
3563
3564 QString input;
3565 request.m_channel = TuningGetChanNum(request, input);
3566 request.m_input = input;
3567
3568 if (TuningOnSameMultiplex(request))
3569 LOG(VB_CHANNEL, LOG_INFO, LOC + "On same multiplex");
3570
3571 TuningShutdowns(request);
3572
3573 // The dequeue isn't safe to do until now because we
3574 // release the stateChangeLock to teardown a recorder
3576
3577 // Now we start new stuff
3578 if (request.m_flags & (kFlagRecording|kFlagLiveTV|
3580 {
3581 if (!m_recorder)
3582 {
3583 LOG(VB_RECORD, LOG_INFO, LOC +
3584 "No recorder yet, calling TuningFrequency");
3585 TuningFrequency(request);
3586 }
3587 else
3588 {
3589 LOG(VB_RECORD, LOG_INFO, LOC + "Waiting for recorder pause..");
3590 SetFlags(kFlagWaitingForRecPause, __FILE__, __LINE__);
3591 }
3592 }
3593 m_lastTuningRequest = request;
3594 }
3595
3597 {
3598 if (!m_recorder || !m_recorder->IsPaused())
3599 return;
3600
3601 ClearFlags(kFlagWaitingForRecPause, __FILE__, __LINE__);
3602 LOG(VB_RECORD, LOG_INFO, LOC +
3603 "Recorder paused, calling TuningFrequency");
3605 }
3606
3607 MPEGStreamData *streamData = nullptr;
3609 {
3610 streamData = TuningSignalCheck();
3611 if (streamData == nullptr)
3612 return;
3613 }
3614
3616 {
3617 if (m_recorder)
3619 else
3620 TuningNewRecorder(streamData);
3621
3622 // If we got this far it is safe to set a new starting channel...
3623 if (m_channel)
3625 }
3626}
3627
3633{
3634 LOG(VB_RECORD, LOG_INFO, LOC + QString("TuningShutdowns(%1)")
3635 .arg(request.toString()));
3636
3637 if (m_scanner && !(request.m_flags & kFlagEITScan) &&
3639 {
3641 ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
3643 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
3644 }
3645
3646 if (m_scanner && !request.IsOnSameMultiplex())
3648
3650 {
3651 MPEGStreamData *sd = nullptr;
3652 if (GetDTVSignalMonitor())
3655 ClearFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3656
3657 // Delete StreamData if it is not in use by the recorder.
3658 MPEGStreamData *rec_sd = nullptr;
3659 if (GetDTVRecorder())
3660 rec_sd = GetDTVRecorder()->GetStreamData();
3661 if (sd && (sd != rec_sd))
3662 delete sd;
3663 }
3665 ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3666
3667 // At this point any waits are canceled.
3668
3669 if (request.m_flags & kFlagNoRec)
3670 {
3672 {
3674 ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3676 }
3677
3679 (m_curRecording &&
3682 {
3683 m_stateChangeLock.unlock();
3684 TeardownRecorder(request.m_flags);
3685 m_stateChangeLock.lock();
3686 }
3687 // At this point the recorders are shut down
3688
3689 CloseChannel();
3690 // At this point the channel is shut down
3691 }
3692
3693 if (m_buffer && (request.m_flags & kFlagKillRingBuffer))
3694 {
3695 LOG(VB_RECORD, LOG_INFO, LOC + "Tearing down RingBuffer");
3696 SetRingBuffer(nullptr);
3697 // At this point the ringbuffer is shut down
3698 }
3699
3700 // Clear pending actions from last request
3701 ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
3702}
3703
3722{
3723 LOG(VB_GENERAL, LOG_INFO, LOC + QString("TuningFrequency(%1)")
3724 .arg(request.toString()));
3725
3726 DTVChannel *dtvchan = GetDTVChannel();
3727 if (dtvchan)
3728 {
3729 MPEGStreamData *mpeg = nullptr;
3730
3731 if (GetDTVRecorder())
3733
3734 // Tune with SI table standard (dvb, atsc, mpeg) from database, see issue #452
3736
3737 const QString tuningmode = (HasFlags(kFlagEITScannerRunning)) ?
3738 dtvchan->GetSIStandard() :
3739 dtvchan->GetSuggestedTuningMode(
3741
3742 dtvchan->SetTuningMode(tuningmode);
3743
3744 if (request.m_minorChan && (tuningmode == "atsc"))
3745 {
3746 auto *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
3747 if (atsc)
3748 atsc->SetDesiredChannel(request.m_majorChan, request.m_minorChan);
3749 }
3750 else if (request.m_progNum >= 0)
3751 {
3752 if (mpeg)
3753 mpeg->SetDesiredProgram(request.m_progNum);
3754 }
3755 }
3756
3757 if (request.IsOnSameMultiplex())
3758 {
3759 // Update the channel number for SwitchLiveTVRingBuffer (called from
3760 // TuningRestartRecorder). This ensures that the livetvchain will be
3761 // updated with the new channel number
3762 if (m_channel)
3763 {
3765 m_channel->GetChannelName(), request.m_channel );
3766 }
3767
3768 QStringList slist;
3769 slist<<"message"<<QObject::tr("On known multiplex...");
3770 MythEvent me(QString("SIGNAL %1").arg(m_inputId), slist);
3772
3773 SetFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3774 return;
3775 }
3776
3777 QString channum = request.m_channel;
3778
3779 bool ok1 = true;
3780 if (m_channel)
3781 {
3782 m_channel->Open();
3783 if (!channum.isEmpty())
3784 ok1 = m_channel->SetChannelByString(channum);
3785 else
3786 ok1 = false;
3787 }
3788
3789 if (!ok1)
3790 {
3791 if (!(request.m_flags & kFlagLiveTV) || !(request.m_flags & kFlagEITScan))
3792 {
3793 if (m_curRecording)
3795
3796 LOG(VB_GENERAL, LOG_ERR, LOC +
3797 QString("Failed to set channel to %1. Reverting to kState_None")
3798 .arg(channum));
3801 else
3803 return;
3804 }
3805
3806 LOG(VB_GENERAL, LOG_ERR, LOC +
3807 QString("Failed to set channel to %1.").arg(channum));
3808 }
3809
3810 bool mpts_only = GetDTVChannel() &&
3811 GetDTVChannel()->GetFormat().compare("MPTS") == 0;
3812 if (mpts_only)
3813 {
3814 // Not using a signal monitor, so just set the status to recording
3816 if (m_curRecording)
3817 {
3819 }
3820 }
3821
3822
3823 bool livetv = (request.m_flags & kFlagLiveTV) != 0U;
3824 bool antadj = (request.m_flags & kFlagAntennaAdjust) != 0U;
3825 bool use_sm = !mpts_only && SignalMonitor::IsRequired(m_genOpt.m_inputType);
3826 bool use_dr = use_sm && (livetv || antadj);
3827 bool has_dummy = false;
3828
3829 if (use_dr)
3830 {
3831 // We need there to be a ringbuffer for these modes
3832 bool ok2 = false;
3834 m_pseudoLiveTVRecording = nullptr;
3835
3836 m_tvChain->SetInputType("DUMMY");
3837
3838 if (!m_buffer)
3839 ok2 = CreateLiveTVRingBuffer(channum);
3840 else
3841 ok2 = SwitchLiveTVRingBuffer(channum, true, false);
3843
3845
3846 if (!ok2)
3847 {
3848 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RingBuffer 1");
3849 return;
3850 }
3851
3852 has_dummy = true;
3853 }
3854
3855 // Start signal monitoring for devices capable of monitoring
3856 if (use_sm)
3857 {
3858 LOG(VB_RECORD, LOG_INFO, LOC + "Starting Signal Monitor");
3859 bool error = false;
3860 if (!SetupSignalMonitor(
3861 !antadj, (request.m_flags & kFlagEITScan) != 0U, livetv || antadj))
3862 {
3863 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to setup signal monitor");
3864 if (m_signalMonitor)
3865 {
3866 delete m_signalMonitor;
3867 m_signalMonitor = nullptr;
3868 }
3869
3870 // pretend the signal monitor is running to prevent segfault
3871 SetFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3872 ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3873 error = true;
3874 }
3875
3876 if (m_signalMonitor)
3877 {
3878 if (request.m_flags & kFlagEITScan)
3879 {
3881 SetVideoStreamsRequired(0);
3883 }
3884
3885 SetFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3886 ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3887 if (!antadj)
3888 {
3889 QDateTime expire = MythDate::current();
3890
3891 SetFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3892 if (m_curRecording)
3893 {
3895 // If startRecordingDeadline is passed, this
3896 // recording is marked as failed, so the scheduler
3897 // can try another showing.
3899 expire.addMSecs(m_genOpt.m_channelTimeout);
3901 expire.addMSecs(m_genOpt.m_channelTimeout * 2 / 3);
3902 // Keep trying to record this showing (even if it
3903 // has been marked as failed) until the scheduled
3904 // end time.
3906 m_curRecording->GetRecordingEndTime().addSecs(-10);
3907
3908 LOG(VB_CHANNEL, LOG_DEBUG, LOC +
3909 QString("Pre-fail start deadline: %1 "
3910 "Start recording deadline: %2 "
3911 "Good signal deadline: %3")
3912 .arg(m_preFailDeadline.toLocalTime()
3913 .toString("hh:mm:ss.zzz"),
3914 m_startRecordingDeadline.toLocalTime()
3915 .toString("hh:mm:ss.zzz"),
3916 m_signalMonitorDeadline.toLocalTime()
3917 .toString("hh:mm:ss.zzz")));
3918 }
3919 else
3920 {
3922 expire.addMSecs(m_genOpt.m_channelTimeout);
3923 }
3925
3926 //System Event TUNING_TIMEOUT deadline
3928 m_signalEventCmdSent = false;
3929 }
3930 }
3931
3932 if (has_dummy && m_buffer)
3933 {
3934 // Make sure recorder doesn't point to bogus ringbuffer before
3935 // it is potentially restarted without a new ringbuffer, if
3936 // the next channel won't tune and the user exits LiveTV.
3937 if (m_recorder)
3938 m_recorder->SetRingBuffer(nullptr);
3939
3940 SetFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3941 LOG(VB_RECORD, LOG_INFO, "DummyDTVRecorder -- started");
3942 SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
3943 }
3944
3945 // if we had problems starting the signal monitor,
3946 // we don't want to start the recorder...
3947 if (error)
3948 return;
3949 }
3950
3951 // Request a recorder, if the command is a recording command
3952 ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3953 if (request.m_flags & kFlagRec && !antadj)
3954 SetFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3955}
3956
3965{
3966 RecStatus::Type newRecStatus = RecStatus::Unknown;
3967 bool keep_trying = false;
3968 QDateTime current_time = MythDate::current();
3969
3970 if ((m_signalMonitor->IsErrored() || current_time > m_signalEventCmdTimeout) &&
3972 {
3973 gCoreContext->SendSystemEvent(QString("TUNING_SIGNAL_TIMEOUT CARDID %1")
3974 .arg(m_inputId));
3975 m_signalEventCmdSent = true;
3976 }
3977
3979 {
3980 LOG(VB_RECORD, LOG_INFO, LOC + "TuningSignalCheck: Good signal");
3981 if (m_curRecording && (current_time > m_startRecordingDeadline))
3982 {
3983 newRecStatus = RecStatus::Failing;
3984 m_curRecording->SaveVideoProperties(VID_DAMAGED, VID_DAMAGED);
3985
3986 QString desc = tr("Good signal seen after %1 ms")
3988 m_startRecordingDeadline.msecsTo(current_time));
3989 QString title = m_curRecording->GetTitle();
3990 if (!m_curRecording->GetSubtitle().isEmpty())
3991 title += " - " + m_curRecording->GetSubtitle();
3992
3994 "Recording", title,
3995 tr("See 'Tuning timeout' in mythtv-setup "
3996 "for this input."));
3998
3999 LOG(VB_GENERAL, LOG_WARNING, LOC +
4000 QString("It took longer than %1 ms to get a signal lock. "
4001 "Keeping status of '%2'")
4003 .arg(RecStatus::toString(newRecStatus, kSingleRecord)));
4004 LOG(VB_GENERAL, LOG_WARNING, LOC +
4005 "See 'Tuning timeout' in mythtv-setup for this input");
4006 }
4007 else
4008 {
4009 newRecStatus = RecStatus::Recording;
4010 }
4011 }
4012 else if (m_signalMonitor->IsErrored() || current_time > m_signalMonitorDeadline)
4013 {
4014 LOG(VB_GENERAL, LOG_ERR, LOC + "TuningSignalCheck: SignalMonitor " +
4015 (m_signalMonitor->IsErrored() ? "failed" : "timed out"));
4016
4017 ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4018 newRecStatus = RecStatus::Failed;
4019
4021 {
4023 }
4024 }
4025 else if (m_curRecording && !m_reachedPreFail && current_time > m_preFailDeadline)
4026 {
4027 LOG(VB_GENERAL, LOG_ERR, LOC +
4028 "TuningSignalCheck: Hit pre-fail timeout");
4029 SendMythSystemRecEvent("REC_PREFAIL", m_curRecording);
4030 m_reachedPreFail = true;
4031 return nullptr;
4032 }
4034 current_time > m_startRecordingDeadline)
4035 {
4036 newRecStatus = RecStatus::Failing;
4038 keep_trying = true;
4039
4040 SendMythSystemRecEvent("REC_FAILING", m_curRecording);
4041
4042 QString desc = tr("Taking more than %1 ms to get a lock.")
4044 QString title = m_curRecording->GetTitle();
4045 if (!m_curRecording->GetSubtitle().isEmpty())
4046 title += " - " + m_curRecording->GetSubtitle();
4047
4049 "Recording", title,
4050 tr("See 'Tuning timeout' in mythtv-setup "
4051 "for this input."));
4052 mn.SetDuration(30s);
4054
4055 LOG(VB_GENERAL, LOG_WARNING, LOC +
4056 QString("TuningSignalCheck: taking more than %1 ms to get a lock. "
4057 "marking this recording as '%2'.")
4059 .arg(RecStatus::toString(newRecStatus, kSingleRecord)));
4060 LOG(VB_GENERAL, LOG_WARNING, LOC +
4061 "See 'Tuning timeout' in mythtv-setup for this input");
4062 }
4063 else
4064 {
4065 if (m_signalMonitorCheckCnt) // Don't flood log file
4066 {
4068 }
4069 else
4070 {
4071 LOG(VB_RECORD, LOG_INFO, LOC +
4072 QString("TuningSignalCheck: Still waiting. Will timeout @ %1")
4073 .arg(m_signalMonitorDeadline.toLocalTime()
4074 .toString("hh:mm:ss.zzz")));
4076 }
4077 return nullptr;
4078 }
4079
4080 SetRecordingStatus(newRecStatus, __LINE__);
4081
4082 if (m_curRecording)
4083 {
4084 m_curRecording->SetRecordingStatus(newRecStatus);
4085 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
4086 .arg(m_curRecording->GetInputID())
4087 .arg(m_curRecording->GetChanID())
4089 .arg(newRecStatus)
4092 }
4093
4094 if (keep_trying)
4095 return nullptr;
4096
4097 // grab useful data from DTV signal monitor before we kill it...
4098 MPEGStreamData *streamData = nullptr;
4099 if (GetDTVSignalMonitor())
4100 streamData = GetDTVSignalMonitor()->GetStreamData();
4101
4103 {
4104 // shut down signal monitoring
4106 ClearFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
4107 }
4108 ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
4109
4110 if (streamData)
4111 {
4112 auto *dsd = dynamic_cast<DVBStreamData*>(streamData);
4113 if (dsd)
4115 if (m_scanner)
4116 {
4117 if (get_use_eit(GetInputId()))
4118 {
4120 }
4121 else
4122 {
4123 LOG(VB_EIT, LOG_INFO, LOC +
4124 QString("EIT scanning disabled for video source %1")
4125 .arg(GetSourceID())); }
4126 }
4127 }
4128
4129 return streamData;
4130}
4131
4133 bool on_host, bool transcode_bfr_comm, bool on_line_comm)
4134{
4135 if (!rec)
4136 return 0; // no jobs for Live TV recordings..
4137
4138 int jobs = 0; // start with no jobs
4139
4140 // grab standard jobs flags from program info
4142
4143 // disable commercial flagging on PBS, BBC, etc.
4144 if (rec->IsCommercialFree())
4146
4147 // disable transcoding if the profile does not allow auto transcoding
4148 const StandardSetting *autoTrans = profile.byName("autotranscode");
4149 if ((!autoTrans) || (autoTrans->getValue().toInt() == 0))
4151
4152 bool ml = JobQueue::JobIsInMask(JOB_METADATA, jobs);
4153 if (ml)
4154 {
4155 // When allowed, metadata lookup should occur at the
4156 // start of a recording to make the additional info
4157 // available immediately (and for use in future jobs).
4158 QString host = on_host ? gCoreContext->GetHostName() : "";
4160 rec->GetChanID(),
4161 rec->GetRecordingStartTime(), "", "",
4162 host, JOB_LIVE_REC);
4163
4164 // don't do regular metadata lookup, we won't need it.
4166 }
4167
4168 // is commercial flagging enabled, and is on-line comm flagging enabled?
4169 bool rt = JobQueue::JobIsInMask(JOB_COMMFLAG, jobs) && on_line_comm;
4170 // also, we either need transcoding to be disabled or
4171 // we need to be allowed to commercial flag before transcoding?
4173 !transcode_bfr_comm;
4174 if (rt)
4175 {
4176 // queue up real-time (i.e. on-line) commercial flagging.
4177 QString host = on_host ? gCoreContext->GetHostName() : "";
4179 rec->GetChanID(),
4180 rec->GetRecordingStartTime(), "", "",
4181 host, JOB_LIVE_REC);
4182
4183 // don't do regular comm flagging, we won't need it.
4185 }
4186
4187 return jobs;
4188}
4189
4190QString TVRec::LoadProfile(void *tvchain, RecordingInfo *rec,
4192{
4193 // Determine the correct recording profile.
4194 // In LiveTV mode use "Live TV" profile, otherwise use the
4195 // recording's specified profile. If the desired profile can't
4196 // be found, fall back to the "Default" profile for input type.
4197 QString profileName = "Live TV";
4198 if (!tvchain && rec)
4199 profileName = rec->GetRecordingRule()->m_recProfile;
4200
4201 QString profileRequested = profileName;
4202
4203 if (profile.loadByType(profileName, m_genOpt.m_inputType,
4205 {
4206 LOG(VB_RECORD, LOG_INFO, LOC +
4207 QString("Using profile '%1' to record")
4208 .arg(profileName));
4209 }
4210 else
4211 {
4212 profileName = "Default";
4213 if (profile.loadByType(profileName, m_genOpt.m_inputType, m_genOpt.m_videoDev))
4214 {
4215 LOG(VB_RECORD, LOG_INFO, LOC +
4216 QString("Profile '%1' not found, using "
4217 "fallback profile '%2' to record")
4218 .arg(profileRequested, profileName));
4219 }
4220 else
4221 {
4222 LOG(VB_RECORD, LOG_ERR, LOC +
4223 QString("Profile '%1' not found, and unable "
4224 "to load fallback profile '%2'. Results "
4225 "may be unpredicable")
4226 .arg(profileRequested, profileName));
4227 }
4228 }
4229
4230 return profileName;
4231}
4232
4237 RecordingInfo **rec,
4239 bool had_dummyrec)
4240{
4241 if (m_tvChain)
4242 {
4243 bool ok = false;
4244 if (!m_buffer)
4245 {
4247 SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4248 }
4249 else
4250 {
4252 true, !had_dummyrec && m_recorder);
4253 }
4254 if (!ok)
4255 {
4256 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RingBuffer 2");
4257 return false;
4258 }
4259 *rec = m_curRecording; // new'd in Create/SwitchLiveTVRingBuffer()
4260 }
4261
4263 {
4264 bool write = m_genOpt.m_inputType != "IMPORT";
4265 QString pathname = (*rec)->GetPathname();
4266 LOG(VB_GENERAL, LOG_INFO, LOC + QString("rec->GetPathname(): '%1'")
4267 .arg(pathname));
4269 if (!m_buffer->IsOpen() && write)
4270 {
4271 LOG(VB_GENERAL, LOG_ERR, LOC +
4272 QString("RingBuffer '%1' not open...")
4273 .arg(pathname));
4274 SetRingBuffer(nullptr);
4275 ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
4276 return false;
4277 }
4278 }
4279
4280 if (!m_buffer)
4281 {
4282 LOG(VB_GENERAL, LOG_ERR, LOC +
4283 QString("Failed to start recorder! ringBuffer is NULL\n"
4284 "\t\t\t\t Tuning request was %1\n")
4286
4287 if (HasFlags(kFlagLiveTV))
4288 {
4289 QString message = QString("QUIT_LIVETV %1").arg(m_inputId);
4290 MythEvent me(message);
4292 }
4293 return false;
4294 }
4295
4296 if (m_channel && m_genOpt.m_inputType == "MJPEG")
4297 m_channel->Close(); // Needed because of NVR::MJPEGInit()
4298
4299 LOG(VB_GENERAL, LOG_INFO, LOC + "TuningNewRecorder - CreateRecorder()");
4301
4302 if (m_recorder)
4303 {
4306 if (m_recorder->IsErrored())
4307 {
4308 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialize recorder!");
4309 delete m_recorder;
4310 m_recorder = nullptr;
4311 }
4312 }
4313
4314 if (!m_recorder)
4315 {
4316 LOG(VB_GENERAL, LOG_ERR, LOC +
4317 QString("Failed to start recorder!\n"
4318 "\t\t\t\t Tuning request was %1\n")
4320
4321 if (HasFlags(kFlagLiveTV))
4322 {
4323 QString message = QString("QUIT_LIVETV %1").arg(m_inputId);
4324 MythEvent me(message);
4326 }
4328 if (m_tvChain)
4329 (*rec) = nullptr;
4330 return false;
4331 }
4332
4333 if (*rec)
4334 m_recorder->SetRecording(*rec);
4335
4336 if (GetDTVRecorder() && streamData)
4337 {
4338 const StandardSetting *setting = profile.byName("recordingtype");
4339 if (setting)
4340 streamData->SetRecordingType(setting->getValue());
4341 GetDTVRecorder()->SetStreamData(streamData);
4342 }
4343
4344 if (m_channel && m_genOpt.m_inputType == "MJPEG")
4345 m_channel->Open(); // Needed because of NVR::MJPEGInit()
4346
4347 // Setup for framebuffer capture devices..
4348 if (m_channel)
4349 {
4352 }
4353
4354 if (GetV4LChannel())
4355 {
4357 CloseChannel();
4358 }
4359
4360 m_recorderThread = new MThread("RecThread", m_recorder);
4362
4363 // Wait for recorder to start.
4364 m_stateChangeLock.unlock();
4365 while (!m_recorder->IsRecording() && !m_recorder->IsErrored())
4366 std::this_thread::sleep_for(5us);
4367 m_stateChangeLock.lock();
4368
4369 if (GetV4LChannel())
4371
4372 SetFlags(kFlagRecorderRunning | kFlagRingBufferReady, __FILE__, __LINE__);
4373
4374 ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4375
4376 //workaround for failed import recordings, no signal monitor means we never
4377 //go to recording state and the status here seems to override the status
4378 //set in the importrecorder and backend via setrecordingstatus
4379 if (m_genOpt.m_inputType == "IMPORT")
4380 {
4382 if (m_curRecording)
4384 }
4385
4386 return true;
4387}
4388
4393{
4394 LOG(VB_RECORD, LOG_INFO, LOC + "Starting Recorder");
4395
4396 bool had_dummyrec = false;
4398 {
4400 ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
4402 had_dummyrec = true;
4403 }
4404
4406
4409
4410 if (TuningNewRecorderReal(streamData, &rec, profile, had_dummyrec))
4411 return;
4412
4413 SetRecordingStatus(RecStatus::Failed, __LINE__, true);
4415
4416 if (rec)
4417 {
4418 // Make sure the scheduler knows...
4420 LOG(VB_RECORD, LOG_INFO, LOC +
4421 QString("TuningNewRecorder -- UPDATE_RECORDING_STATUS: %1")
4423 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
4424 .arg(rec->GetInputID())
4425 .arg(rec->GetChanID())
4427 .arg(RecStatus::Failed)
4430 }
4431
4432 if (m_tvChain)
4433 delete rec;
4434}
4435
4440{
4441 LOG(VB_RECORD, LOG_INFO, LOC + "Restarting Recorder");
4442
4443 bool had_dummyrec = false;
4444
4445 if (m_curRecording)
4446 {
4449 }
4450
4452 {
4453 ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
4454 had_dummyrec = true;
4455 }
4456
4457 SwitchLiveTVRingBuffer(m_channel->GetChannelName(), true, !had_dummyrec);
4458
4459 if (had_dummyrec)
4460 {
4462 ProgramInfo *progInfo = m_tvChain->GetProgramAt(-1);
4463 RecordingInfo recinfo(*progInfo);
4464 delete progInfo;
4465 recinfo.SetInputID(m_inputId);
4466 m_recorder->SetRecording(&recinfo);
4467 }
4468 m_recorder->Reset();
4469
4470 // Set file descriptor of channel from recorder for V4L
4471 if (GetV4LChannel())
4473
4474 // Some recorders unpause on Reset, others do not...
4476
4478 {
4480 QString msg1 = QString("Recording: %1 %2 %3 %4")
4481 .arg(rcinfo1->GetTitle(), QString::number(rcinfo1->GetChanID()),
4484 ProgramInfo *rcinfo2 = m_tvChain->GetProgramAt(-1);
4485 QString msg2 = QString("Recording: %1 %2 %3 %4")
4486 .arg(rcinfo2->GetTitle(), QString::number(rcinfo2->GetChanID()),
4489 delete rcinfo2;
4490 LOG(VB_RECORD, LOG_INFO, LOC + "Pseudo LiveTV recording starting." +
4491 "\n\t\t\t" + msg1 + "\n\t\t\t" + msg2);
4492
4495
4497
4498 InitAutoRunJobs(m_curRecording, kAutoRunProfile, nullptr, __LINE__);
4499 }
4500
4501 ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4502}
4503
4504void TVRec::SetFlags(uint f, const QString & file, int line)
4505{
4506 QMutexLocker lock(&m_stateChangeLock);
4507 m_stateFlags |= f;
4508 LOG(VB_RECORD, LOG_INFO, LOC + QString("SetFlags(%1) -> %2 @ %3:%4")
4509 .arg(FlagToString(f), FlagToString(m_stateFlags), file, QString::number(line)));
4510 WakeEventLoop();
4511}
4512
4513void TVRec::ClearFlags(uint f, const QString & file, int line)
4514{
4515 QMutexLocker lock(&m_stateChangeLock);
4516 m_stateFlags &= ~f;
4517 LOG(VB_RECORD, LOG_INFO, LOC + QString("ClearFlags(%1) -> %2 @ %3:%4")
4518 .arg(FlagToString(f), FlagToString(m_stateFlags), file, QString::number(line)));
4519 WakeEventLoop();
4520}
4521
4523{
4524 QString msg("");
4525
4526 // General flags
4527 if (kFlagFrontendReady & f)
4528 msg += "FrontendReady,";
4529 if (kFlagRunMainLoop & f)
4530 msg += "RunMainLoop,";
4531 if (kFlagExitPlayer & f)
4532 msg += "ExitPlayer,";
4533 if (kFlagFinishRecording & f)
4534 msg += "FinishRecording,";
4535 if (kFlagErrored & f)
4536 msg += "Errored,";
4538 msg += "CancelNextRecording,";
4539
4540 // Tuning flags
4541 if ((kFlagRec & f) == kFlagRec)
4542 {
4543 msg += "REC,";
4544 }
4545 else
4546 {
4547 if (kFlagLiveTV & f)
4548 msg += "LiveTV,";
4549 if (kFlagRecording & f)
4550 msg += "Recording,";
4551 }
4552 if ((kFlagNoRec & f) == kFlagNoRec)
4553 {
4554 msg += "NOREC,";
4555 }
4556 else
4557 {
4558 if (kFlagEITScan & f)
4559 msg += "EITScan,";
4560 if (kFlagCloseRec & f)
4561 msg += "CloseRec,";
4562 if (kFlagKillRec & f)
4563 msg += "KillRec,";
4564 if (kFlagAntennaAdjust & f)
4565 msg += "AntennaAdjust,";
4566 }
4568 {
4569 msg += "PENDINGACTIONS,";
4570 }
4571 else
4572 {
4574 msg += "WaitingForRecPause,";
4575 if (kFlagWaitingForSignal & f)
4576 msg += "WaitingForSignal,";
4578 msg += "NeedToStartRecorder,";
4579 if (kFlagKillRingBuffer & f)
4580 msg += "KillRingBuffer,";
4581 }
4582 if ((kFlagAnyRunning & f) == kFlagAnyRunning)
4583 {
4584 msg += "ANYRUNNING,";
4585 }
4586 else
4587 {
4589 msg += "SignalMonitorRunning,";
4590 if (kFlagEITScannerRunning & f)
4591 msg += "EITScannerRunning,";
4593 {
4594 msg += "ANYRECRUNNING,";
4595 }
4596 else
4597 {
4599 msg += "DummyRecorderRunning,";
4600 if (kFlagRecorderRunning & f)
4601 msg += "RecorderRunning,";
4602 }
4603 }
4604 if (kFlagRingBufferReady & f)
4605 msg += "RingBufferReady,";
4606
4607 if (msg.isEmpty())
4608 msg = QString("0x%1").arg(f,0,16);
4609
4610 return msg;
4611}
4612
4614{
4615 QMutexLocker lock(&m_nextLiveTVDirLock);
4616
4617 bool found = !m_nextLiveTVDir.isEmpty();
4618 if (!found && m_triggerLiveTVDir.wait(&m_nextLiveTVDirLock, 500))
4619 {
4620 found = !m_nextLiveTVDir.isEmpty();
4621 }
4622
4623 return found;
4624}
4625
4627{
4628 QMutexLocker lock(&m_nextLiveTVDirLock);
4629
4630 m_nextLiveTVDir = std::move(dir);
4631 m_triggerLiveTVDir.wakeAll();
4632}
4633
4636 const QString & channum)
4637{
4638 LOG(VB_RECORD, LOG_INFO, LOC + "GetProgramRingBufferForLiveTV()");
4639 if (!m_channel || !m_tvChain || !pginfo || !Buffer)
4640 return false;
4641
4642 m_nextLiveTVDirLock.lock();
4643 m_nextLiveTVDir.clear();
4644 m_nextLiveTVDirLock.unlock();
4645
4646 // Dispatch this early, the response can take a while.
4647 MythEvent me(QString("QUERY_NEXT_LIVETV_DIR %1").arg(m_inputId));
4649
4650 uint sourceid = m_channel->GetSourceID();
4651 int chanid = ChannelUtil::GetChanID(sourceid, channum);
4652
4653 if (chanid < 0)
4654 {
4655 // Test setups might have zero channels
4656 if (m_genOpt.m_inputType == "IMPORT" || m_genOpt.m_inputType == "DEMO")
4657 {
4658 chanid = 9999;
4659 }
4660 else
4661 {
4662 LOG(VB_GENERAL, LOG_ERR, LOC +
4663 QString("Channel: \'%1\' was not found in the database.\n"
4664 "\t\tMost likely, the 'starting channel' for this "
4665 "Input Connection is invalid.\n"
4666 "\t\tCould not start livetv.").arg(channum));
4667 return false;
4668 }
4669 }
4670
4671 auto hoursMax =
4672 gCoreContext->GetDurSetting<std::chrono::hours>("MaxHoursPerLiveTVRecording", 8h);
4673 if (hoursMax <= 0h)
4674 hoursMax = 8h;
4675
4676 RecordingInfo *prog = nullptr;
4678 {
4680 }
4681 else
4682 {
4683 prog = new RecordingInfo(
4684 chanid, MythDate::current(true), true, hoursMax);
4685 }
4686
4687 prog->SetInputID(m_inputId);
4688
4689 if (prog->GetRecordingStartTime() == prog->GetRecordingEndTime())
4690 {
4691 LOG(VB_GENERAL, LOG_ERR, LOC + "GetProgramRingBufferForLiveTV()"
4692 "\n\t\t\tProgramInfo is invalid."
4693 "\n" + prog->toString());
4694 prog->SetScheduledEndTime(prog->GetRecordingStartTime().addSecs(3600));
4696
4697 prog->SetChanID(chanid);
4698 }
4699
4702
4703 prog->SetStorageGroup("LiveTV");
4704
4706 {
4707 QMutexLocker lock(&m_nextLiveTVDirLock);
4709 }
4710 else
4711 {
4712 StorageGroup sgroup("LiveTV", gCoreContext->GetHostName());
4713 prog->SetPathname(sgroup.FindNextDirMostFree());
4714 }
4715
4717 prog->SetRecordingGroup("LiveTV");
4718
4719 StartedRecording(prog);
4720
4721 *Buffer = MythMediaBuffer::Create(prog->GetPathname(), true);
4722 if (!(*Buffer) || !(*Buffer)->IsOpen())
4723 {
4724 LOG(VB_GENERAL, LOG_ERR, LOC + QString("RingBuffer '%1' not open...")
4725 .arg(prog->GetPathname()));
4726
4727 delete *Buffer;
4728 delete prog;
4729
4730 return false;
4731 }
4732
4733 *pginfo = prog;
4734 return true;
4735}
4736
4737bool TVRec::CreateLiveTVRingBuffer(const QString & channum)
4738{
4739 LOG(VB_RECORD, LOG_INFO, LOC + QString("CreateLiveTVRingBuffer(%1)")
4740 .arg(channum));
4741
4742 RecordingInfo *pginfo = nullptr;
4743 MythMediaBuffer *buffer = nullptr;
4744
4745 if (!m_channel ||
4746 !m_channel->CheckChannel(channum))
4747 {
4749 return false;
4750 }
4751
4752 if (!GetProgramRingBufferForLiveTV(&pginfo, &buffer, channum))
4753 {
4754 ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
4756 LOG(VB_GENERAL, LOG_ERR, LOC +
4757 QString("CreateLiveTVRingBuffer(%1) failed").arg(channum));
4758 return false;
4759 }
4760
4761 SetRingBuffer(buffer);
4762
4766
4767 bool discont = (m_tvChain->TotalSize() > 0);
4769 m_channel->GetInputName(), discont);
4770
4771 if (m_curRecording)
4772 {
4774 delete m_curRecording;
4775 }
4776
4777 m_curRecording = pginfo;
4779
4780 return true;
4781}
4782
4783bool TVRec::SwitchLiveTVRingBuffer(const QString & channum,
4784 bool discont, bool set_rec)
4785{
4786 QString msg;
4787 if (m_curRecording)
4788 {
4789 msg = QString(" curRec(%1) curRec.size(%2)")
4791 .arg(m_curRecording->GetFilesize());
4792 }
4793 LOG(VB_RECORD, LOG_INFO, LOC +
4794 QString("SwitchLiveTVRingBuffer(discont %1, set_next_rec %2)")
4795 .arg(discont).arg(set_rec) + msg);
4796
4797 RecordingInfo *pginfo = nullptr;
4798 MythMediaBuffer *buffer = nullptr;
4799
4800 if (!m_channel ||
4801 !m_channel->CheckChannel(channum))
4802 {
4804 return false;
4805 }
4806
4807 if (!GetProgramRingBufferForLiveTV(&pginfo, &buffer, channum))
4808 {
4810 return false;
4811 }
4812
4813 QString oldinputtype = m_tvChain->GetInputType(-1);
4814
4815 pginfo->MarkAsInUse(true, kRecorderInUseID);
4820 m_channel->GetInputName(), discont);
4821
4822 if (set_rec && m_recorder)
4823 {
4824 m_recorder->SetNextRecording(pginfo, buffer);
4825 if (discont)
4827 delete pginfo;
4828 SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4829 }
4830 else if (!set_rec)
4831 {
4832 // dummy recordings are finished before this
4833 // is called and other recordings must be finished..
4834 if (m_curRecording && oldinputtype != "DUMMY")
4835 {
4838 delete m_curRecording;
4839 }
4840 m_curRecording = pginfo;
4841 SetRingBuffer(buffer);
4842 }
4843 else
4844 {
4845 delete buffer;
4846 }
4847
4848 return true;
4849}
4850
4852{
4853 LOG(VB_RECORD, LOG_INFO, LOC + "SwitchRecordingRingBuffer()");
4854
4856 {
4857 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4858 "already switching.");
4859 return nullptr;
4860 }
4861
4862 if (!m_recorder)
4863 {
4864 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4865 "invalid recorder.");
4866 return nullptr;
4867 }
4868
4869 if (!m_curRecording)
4870 {
4871 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4872 "invalid recording.");
4873 return nullptr;
4874 }
4875
4876 if (rcinfo.GetChanID() != m_curRecording->GetChanID())
4877 {
4878 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4879 "Not the same channel.");
4880 return nullptr;
4881 }
4882
4883 auto *ri = new RecordingInfo(rcinfo);
4885
4886 QString pn = LoadProfile(nullptr, ri, profile);
4887
4888 if (pn != m_recProfileName)
4889 {
4890 LOG(VB_RECORD, LOG_ERR, LOC +
4891 QString("SwitchRecordingRingBuffer() -> "
4892 "cannot switch profile '%1' to '%2'")
4893 .arg(m_recProfileName, pn));
4894 return nullptr;
4895 }
4896
4898
4899 ri->MarkAsInUse(true, kRecorderInUseID);
4900 StartedRecording(ri);
4901
4902 bool write = m_genOpt.m_inputType != "IMPORT";
4903 MythMediaBuffer *buffer = MythMediaBuffer::Create(ri->GetPathname(), write);
4904 if (!buffer || !buffer->IsOpen())
4905 {
4906 delete buffer;
4907 ri->SetRecordingStatus(RecStatus::Failed);
4908 FinishedRecording(ri, nullptr);
4909 ri->MarkAsInUse(false, kRecorderInUseID);
4910 delete ri;
4911 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer() -> "
4912 "Failed to create new RB.");
4913 return nullptr;
4914 }
4915
4916 m_recorder->SetNextRecording(ri, buffer);
4917 SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4919 m_switchingBuffer = true;
4920 ri->SetRecordingStatus(RecStatus::Recording);
4921 LOG(VB_RECORD, LOG_INFO, LOC + "SwitchRecordingRingBuffer -> done");
4922 return ri;
4923}
4924
4926{
4927 QMap<uint,TVRec*>::const_iterator it = s_inputs.constFind(inputid);
4928 if (it == s_inputs.constEnd())
4929 return nullptr;
4930 return *it;
4931}
4932
4934{
4935 LOG(VB_RECORD, LOG_INFO, LOC + QString("enable:%1").arg(enable));
4936
4937 if (m_scanner != nullptr)
4938 {
4939 if (enable)
4940 {
4942 && m_eitScanStartTime > MythDate::current().addYears(9))
4943 {
4945 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
4946 }
4947 }
4948 else
4949 {
4950 m_eitScanStartTime = MythDate::current().addYears(10);
4952 {
4954 ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
4955 }
4956 }
4957 }
4958}
4959
4960QString TuningRequest::toString(void) const
4961{
4962 return QString("Program(%1) channel(%2) input(%3) flags(%4)")
4963 .arg(m_program == nullptr ? "NULL" : m_program->toString(),
4964 m_channel.isEmpty() ? "<empty>" : m_channel,
4965 m_input.isEmpty() ? "<empty>" : m_input,
4967}
4968
4969#if CONFIG_DVB
4970#include "recorders/dvbchannel.h"
4972{
4973 // Some DVB devices munge the PMT and/or PAT so the CRC check fails.
4974 // We need to tell the stream data class to not check the CRC on
4975 // these devices. This can cause segfaults.
4976 auto * dvb = dynamic_cast<DVBChannel*>(c);
4977 if (dvb != nullptr)
4978 s->SetIgnoreCRC(dvb->HasCRCBug());
4979}
4980#else
4982#endif // CONFIG_DVB
4983
4984/* 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:1708
static QString GetStartChannel(uint inputid)
Definition: cardutil.cpp:1802
static bool IsEITCapable(const QString &rawtype)
Definition: cardutil.h:172
static QString GetInputName(uint inputid)
Definition: cardutil.cpp:1783
static bool IsV4L(const QString &rawtype)
Definition: cardutil.h:147
static uint GetSourceID(uint inputid)
Definition: cardutil.cpp:1960
static bool IsEncoder(const QString &rawtype)
Definition: cardutil.h:137
static std::vector< uint > GetConflictingInputs(uint inputid)
Definition: cardutil.cpp:2253
static bool IsChannelReusable(const QString &rawtype)
Definition: cardutil.h:224
Abstract class providing a generic interface to tuning hardware.
Definition: channelbase.h:32
virtual QString GetInputName(void) const
Definition: channelbase.h:69
virtual int ChangePictureAttribute(PictureAdjustType, PictureAttribute, bool)
Definition: channelbase.h:95
virtual uint GetSourceID(void) const
Definition: channelbase.h:71
virtual bool Open(void)=0
Opens the channel changing hardware for use.
virtual uint GetNextChannel(uint chanid, ChannelChangeDirection direction) const
virtual void StoreInputChannels(void)
Saves current channel as the default channel for the current input.
virtual void Close(void)=0
Closes the channel changing hardware to use.
virtual bool IsOpen(void) const =0
Reports whether channel is already open.
virtual QString GetChannelName(void) const
Definition: channelbase.h:64
virtual void Renumber(uint sourceid, const QString &oldChanNum, const QString &newChanNum)
Changes a channum if we have it cached anywhere.
bool CheckChannel(const QString &channum) const
virtual int GetPictureAttribute(PictureAttribute) const
Definition: channelbase.h:94
virtual int GetChanID(void) const
virtual void SetFd(int fd)
Sets file descriptor.
Definition: channelbase.h:55
virtual bool Init(QString &startchannel, bool setchan)
Definition: channelbase.cpp:65
virtual int GetInputID(void) const
Definition: channelbase.h:67
static ChannelBase * CreateChannel(TVRec *tvrec, const GeneralDBOptions &genOpt, const DVBDBOptions &dvbOpt, const FireWireDBOptions &fwOpt, const QString &startchannel, bool enter_power_save_mode, QString &rbFileExt, bool setchan)
virtual bool InitPictureAttributes(void)
Definition: channelbase.h:93
virtual bool SetChannelByString(const QString &chan)=0
static bool ToggleChannel(uint chanid, int changrpid, bool delete_chan)
static int GetChannelGroupId(const QString &changroupname)
static uint GetMplexID(uint sourceid, const QString &channum)
static int GetChanID(int db_mplexid, int service_transport_id, int major_channel, int minor_channel, int program_number)
static int GetProgramNumber(uint sourceid, const QString &channum)
Definition: channelutil.h:198
static QString GetVideoFilters(uint sourceid, const QString &channum)
Definition: channelutil.h:200
static bool IsOnSameMultiplex(uint srcid, const QString &new_channum, const QString &old_channum)
static QString GetChanNum(int chan_id)
Returns the channel-number string of the given channel.
static bool GetATSCChannel(uint sourceid, const QString &channum, uint &major, uint &minor)
Class providing a generic interface to digital tuning hardware.
Definition: dtvchannel.h:34
int GetProgramNumber(void) const
Returns program number in PAT, -1 if unknown.
Definition: dtvchannel.h:89
void SaveCachedPids(const pid_cache_t &pid_cache) const
Saves MPEG PIDs to cache to database.
Definition: dtvchannel.cpp:107
QString GetTuningMode(void) const
Returns tuning mode last set by SetTuningMode().
Definition: dtvchannel.cpp:73
uint GetTransportID(void) const
Returns DVB transport_stream_id, 0 if unknown.
Definition: dtvchannel.h:105
QString GetSIStandard(void) const
Returns PSIP table standard: MPEG, DVB, ATSC, or OpenCable.
Definition: dtvchannel.cpp:45
void SetTuningMode(const QString &tuning_mode)
Sets tuning mode: "mpeg", "dvb", "atsc", etc.
Definition: dtvchannel.cpp:87
QString GetFormat(void)
Definition: dtvchannel.h:46
uint GetMajorChannel(void) const
Returns major channel, 0 if unknown.
Definition: dtvchannel.h:93
uint GetMinorChannel(void) const
Returns minor channel, 0 if unknown.
Definition: dtvchannel.h:97
void GetCachedPids(pid_cache_t &pid_cache) const
Returns cached MPEG PIDs for last tuned channel.
Definition: dtvchannel.cpp:97
QString GetSuggestedTuningMode(bool is_live_tv) const
Returns suggested tuning mode: "mpeg", "dvb", or "atsc".
Definition: dtvchannel.cpp:57
virtual bool EnterPowerSavingMode(void)
Enters power saving mode if the card supports it.
Definition: dtvchannel.h:65
uint GetOriginalNetworkID(void) const
Returns DVB original_network_id, 0 if unknown.
Definition: dtvchannel.h:101
This is a specialization of RecorderBase used to handle MPEG-2, MPEG-4, MPEG-4 AVC,...
Definition: dtvrecorder.h:35
MPEGStreamData * GetStreamData(void) const
Definition: dtvrecorder.h:58
virtual void SetStreamData(MPEGStreamData *data)
This class is intended to detect the presence of needed tables.
void SetDVBService(uint network_id, uint transport_id, int service_id)
void AddFlags(uint64_t _flags) override
ATSCStreamData * GetATSCStreamData()
Returns the ATSC stream data if it exists.
void SetChannel(int major, int minor)
virtual void SetRotorTarget(float)
Sets rotor target pos from 0.0 to 1.0.
void IgnoreEncrypted(bool ignore)
MPEGStreamData * GetStreamData()
Returns the MPEG stream data if it exists.
void SetProgramNumber(int progNum)
virtual void SetStreamData(MPEGStreamData *data)
Sets the MPEG stream data for DTVSignalMonitor to use, and connects the table signals to the monitor.
Provides interface to the tuning hardware when using DVB drivers.
Definition: dvbchannel.h:31
bool m_dvbOnDemand
Definition: tv_rec.h:85
std::chrono::milliseconds m_dvbTuningDelay
Definition: tv_rec.h:86
bool m_dvbEitScan
Definition: tv_rec.h:87
void SetDishNetEIT(bool use_dishnet_eit)
Acts as glue between ChannelBase, EITSource and EITHelper.
Definition: eitscanner.h:29
void StopEITEventProcessing(void)
Stops inserting Event Information Tables into DB.
Definition: eitscanner.cpp:221
void StartEITEventProcessing(ChannelBase *channel, EITSource *eitSource)
Start inserting Event Information Tables from the multiplex we happen to be tuned to into the databas...
Definition: eitscanner.cpp:188
void StartActiveScan(TVRec *rec, std::chrono::seconds max_seconds_per_multiplex)
Start active EIT scan.
Definition: eitscanner.cpp:253
void StopActiveScan(void)
Stop active EIT scan.
Definition: eitscanner.cpp:333
QString m_model
Definition: tv_rec.h:97
int m_connection
Definition: tv_rec.h:96
bool m_skipBtAudio
Definition: tv_rec.h:74
QString m_inputType
Definition: tv_rec.h:72
int m_audioSampleRate
Definition: tv_rec.h:73
bool m_waitForSeqstart
Definition: tv_rec.h:77
QString m_vbiDev
Definition: tv_rec.h:70
uint m_channelTimeout
Definition: tv_rec.h:76
QString m_videoDev
Definition: tv_rec.h:69
QString m_audioDev
Definition: tv_rec.h:71
uint m_signalTimeout
Definition: tv_rec.h:75
uint m_chanId
chanid restriction if applicable
Definition: inputinfo.h:51
uint m_inputId
unique key in DB for this input
Definition: inputinfo.h:49
uint m_sourceId
associated channel listings source
Definition: inputinfo.h:48
uint m_mplexId
mplexid restriction if applicable
Definition: inputinfo.h:50
virtual void Clear(void)
Definition: inputinfo.cpp:6
static bool QueueRecordingJobs(const RecordingInfo &recinfo, int jobTypes=JOB_NONE)
Definition: jobqueue.cpp:498
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:520
static bool JobIsNotInMask(int job, int mask)
Definition: jobqueue.h:199
Keeps track of recordings in a current LiveTV instance.
Definition: livetvchain.h:33
int TotalSize(void) const
QString GetID(void) const
Definition: livetvchain.h:54
void FinishedRecording(ProgramInfo *pginfo)
void ReloadAll(const QStringList &data=QStringList())
void SetInputType(const QString &type)
Definition: livetvchain.cpp:52
ProgramInfo * GetProgramAt(int at) const
Returns program at the desired location.
void SetHostPrefix(const QString &prefix)
Definition: livetvchain.cpp:47
QString GetInputType(int pos=-1) const
void AppendNewProgram(ProgramInfo *pginfo, const QString &channum, const QString &inputname, bool discont)
Definition: livetvchain.cpp:63
Encapsulates data about MPEG stream and emits events for each table.
void SetRecordingType(const QString &recording_type)
virtual void ReturnCachedTable(const PSIPTable *psip) const
void SetIgnoreCRC(bool haveCRCbug)
virtual void Reset(void)
void SetVideoStreamsRequired(uint num)
void SetCaching(bool cacheTables)
virtual void AddListeningPID(uint pid, PIDPriority priority=kPIDPriorityNormal)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838
QVariant value(int i) const
Definition: mythdbcon.h:204
int size(void) const
Definition: mythdbcon.h:214
bool isActive(void) const
Definition: mythdbcon.h:215
bool isConnected(void) const
Only updated once during object creation.
Definition: mythdbcon.h:137
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:49
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:267
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:284
This table tells the decoder on which PIDs to find other tables, and their sizes and each table's cur...
Definition: atsctables.h:81
uint TableType(uint i) const
Definition: atsctables.h:125
uint TableCount() const
Definition: atsctables.h:121
uint TablePID(uint i) const
Definition: atsctables.h:133
QString GetHostName(void)
QString GetSetting(const QString &key, const QString &defaultval="")
void SendSystemEvent(const QString &msg)
int GetBackendServerPort(void)
Returns the locally defined backend control port.
T GetDurSetting(const QString &key, T defaultval=T::zero())
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)
bool GetBoolSetting(const QString &key, bool defaultval=false)
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
T dequeue()
Removes item from front of list and returns a copy. O(1).
Definition: mythdeque.h:31
void enqueue(const T &d)
Adds item to the back of the list. O(1).
Definition: mythdeque.h:41
This class is used as a container for messages.
Definition: mythevent.h:17
long long GetWritePosition(void) const
Returns how far into a ThreadedFileWriter file we have written.
virtual bool IsOpen(void) const =0
static MythMediaBuffer * Create(const QString &Filename, bool Write, bool UseReadAhead=true, std::chrono::milliseconds Timeout=kDefaultOpenTimeout, bool StreamOnly=false)
Creates a RingBuffer instance.
static const Type kCheck
static const Type kError
void SetDuration(std::chrono::seconds Duration)
Contains a duration during which the notification will be displayed for. The duration is informative ...
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:14
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:91
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
std::vector< uint > m_possibleConflicts
Definition: tv_rec.h:137
ProgramInfo * m_info
Definition: tv_rec.h:131
bool m_hasLaterShowing
Definition: tv_rec.h:133
QDateTime m_recordingStart
Definition: tv_rec.h:132
bool m_doNotAsk
Definition: tv_rec.h:136
bool m_ask
Definition: tv_rec.h:135
static void GetPreviewImage(const ProgramInfo &pginfo, const QString &token)
Submit a request for the generation of a preview image.
Holds information on recordings and videos.
Definition: programinfo.h:74
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:380
uint GetRecordingRuleID(void) const
Definition: programinfo.h:460
bool IsSameProgramWeakCheck(const ProgramInfo &other) const
Checks for duplicate using only title, chanid and startts.
QString toString(Verbosity v=kLongDescription, const QString &sep=":", const QString &grp="\"") const
bool QueryTuningInfo(QString &channum, QString &input) const
Returns the channel and input needed to record the program.
void UpdateInUseMark(bool force=false)
QString GetRecordingGroup(void) const
Definition: programinfo.h:427
void SaveAutoExpire(AutoExpireType autoExpire, bool updateDelete=false)
Set "autoexpire" field in "recorded" table to "autoExpire".
void SetRecordingRuleType(RecordingType type)
Definition: programinfo.h:593
uint GetRecordingID(void) const
Definition: programinfo.h:457
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:405
uint QueryMplexID(void) const
Queries multiplex any recording would be made on, zero if unknown.
void SetRecordingStatus(RecStatus::Type status)
Definition: programinfo.h:592
void SetRecordingGroup(const QString &group)
Definition: programinfo.h:539
uint GetSourceID(void) const
Definition: programinfo.h:473
void SetScheduledEndTime(const QDateTime &dt)
Definition: programinfo.h:536
void SetRecordingStartTime(const QDateTime &dt)
Definition: programinfo.h:537
void SaveVideoProperties(uint mask, uint video_property_flags)
QString GetTitle(void) const
Definition: programinfo.h:368
uint QueryAverageHeight(void) const
If present in recording this loads average height of the main video stream from database's stream mar...
void MarkAsInUse(bool inuse, const QString &usedFor="")
Tracks a recording's in use status, to prevent deletion and to allow the storage scheduler to perform...
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:412
bool QueryAverageScanProgressive(void) const
If present in recording this loads average video scan type of the main video stream from database's s...
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:398
bool IsLocal(void) const
Definition: programinfo.h:358
void SetChanID(uint _chanid)
Definition: programinfo.h:534
bool IsCommercialFree(void) const
Definition: programinfo.h:489
MarkTypes QueryAverageAspectRatio(void) const
void SetRecordingRuleID(uint id)
Definition: programinfo.h:550
QString MakeUniqueKey(void) const
Creates a unique string that can be used to identify an existing recording.
Definition: programinfo.h:346
QString GetPathname(void) const
Definition: programinfo.h:350
uint GetInputID(void) const
Definition: programinfo.h:474
void ToStringList(QStringList &list) const
Serializes ProgramInfo into a QStringList which can be passed over a socket.
void SetRecordingEndTime(const QDateTime &dt)
Definition: programinfo.h:538
void SetStorageGroup(const QString &group)
Definition: programinfo.h:542
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:458
QDateTime GetRecordingEndTime(void) const
Approximate time the recording should have ended, did end, or is intended to end.
Definition: programinfo.h:420
void SetInputID(uint id)
Definition: programinfo.h:552
void SaveCommFlagged(CommFlagStatus flag)
Set "commflagged" field in "recorded" table to "flag".
QString GetSubtitle(void) const
Definition: programinfo.h:370
QString GetCategory(void) const
Definition: programinfo.h:377
virtual void SetRecordingID(uint _recordedid)
Definition: programinfo.h:590
void SetPathname(const QString &pn)
RecordingType GetRecordingRuleType(void) const
Definition: programinfo.h:462
QString QueryRecordingGroup(void) const
Query recgroup from recorded.
static QString toString(RecStatus::Type recstatus, uint id)
Converts "recstatus" into a short (unreadable) string.
virtual void Reset(void)=0
Reset the recorder to the startup state.
virtual bool IsRecording(void)
Tells whether the StartRecorder() loop is running.
virtual void Pause(bool clear=true)
Pause tells recorder to pause, it should not block.
virtual void SetVideoFilters(QString &filters)=0
Tells recorder which filters to use.
virtual int GetVideoFd(void)=0
Returns file descriptor of recorder device.
virtual void Unpause(void)
Unpause tells recorder to unpause.
virtual bool CheckForRingBufferSwitch(void)
If requested, switch to new RingBuffer/ProgramInfo objects.
virtual void Initialize(void)=0
This is called between SetOptionsFromProfile() and run() to initialize any devices,...
void SetRecording(const RecordingInfo *pginfo)
Changes the Recording from the one set initially with SetOptionsFromProfile().
double GetFrameRate(void) const
Returns the latest frame rate.
Definition: recorderbase.h:210
bool GetKeyframePositions(long long start, long long end, frm_pos_map_t &map) const
void SetNextRecording(const RecordingInfo *ri, MythMediaBuffer *Buffer)
Sets next recording info, to be applied as soon as practical.
virtual void StopRecording(void)
StopRecording() signals to the recorder that it should stop recording and exit cleanly.
virtual RecordingQuality * GetRecordingQuality(const RecordingInfo *ri) const
Returns a report about the current recordings quality.
bool GetKeyframeDurations(long long start, long long end, frm_pos_map_t &map) const
virtual long long GetFramesWritten(void)=0
Returns number of frames written to disk.
long long GetKeyframePosition(long long desired) const
Returns closest keyframe position before the desired frame.
virtual bool IsPaused(bool holding_lock=false) const
Returns true iff recorder is paused.
void SavePositionMap(bool force=false, bool finished=false)
Save the seektable to the DB.
static RecorderBase * CreateRecorder(TVRec *tvrec, ChannelBase *channel, RecordingProfile &profile, const GeneralDBOptions &genOpt)
virtual bool IsErrored(void)=0
Tells us whether an unrecoverable error has been encountered.
void SetRingBuffer(MythMediaBuffer *Buffer)
Tells recorder to use an externally created ringbuffer.
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:36
void FinishedRecording(bool allowReRecord)
If not a premature stop, adds program to history of recorded programs.
void SetDesiredStartTime(const QDateTime &dt)
RecordingRule * GetRecordingRule(void)
Returns the "record" field, creating it if necessary.
void ApplyRecordRecGroupChange(const QString &newrecgroup)
Sets the recording group, both in this RecordingInfo and in the database.
int GetAutoRunJobs(void) const
Returns a bitmap of which jobs are attached to this RecordingInfo.
void AddHistory(bool resched=true, bool forcedup=false, bool future=false)
Adds recording history, creating "record" it if necessary.
void ApplyRecordRecID(void)
Sets recordid to match RecordingRule recordid.
void UpdateRecordingEnd(void)
Update information in the recorded table when the end-time of a recording is changed.
uint64_t GetFilesize(void) const override
void LoadRecordingFile()
void StartedRecording(const QString &ext)
Inserts this RecordingInfo into the database as an existing recording.
void SetDesiredEndTime(const QDateTime &dt)
RecordingFile * GetRecordingFile() const
bool IsDamaged(void) const
QString toStringXML(void) const
RecordingType m_type
AutoExpireType GetAutoExpire(void) const
Definition: recordingrule.h:62
QString m_recProfile
bool Save(bool sendSig=true)
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
virtual int IncrRef(void)
Increments reference count.
static void Init()
Initializes the some static constants needed by SignalMonitorValue.
void AddListener(SignalMonitorListener *listener)
static SignalMonitor * Init(const QString &cardtype, int db_cardnum, ChannelBase *channel, bool release_stream)
static const uint64_t kDVBSigMon_WaitForPos
Wait for rotor to complete turning the antenna.
static const uint64_t kDTVSigMon_WaitForSDT
bool IsErrored(void) const
Definition: signalmonitor.h:82
virtual bool HasExtraSlowTuning(void) const
Definition: signalmonitor.h:60
virtual bool IsAllGood(void) const
Definition: signalmonitor.h:81
static bool IsSupported(const QString &cardtype)
static const uint64_t kDTVSigMon_WaitForPMT
void SetUpdateRate(std::chrono::milliseconds msec)
Sets the number of milliseconds between signal monitoring attempts in the signal monitoring thread.
static const uint64_t kDTVSigMon_WaitForPAT
void SetNotifyFrontend(bool notify)
Enables or disables frontend notification of the current signal value.
Definition: signalmonitor.h:92
static const uint64_t kDTVSigMon_WaitForMGT
static bool IsRequired(const QString &cardtype)
Returns true iff the card type supports signal monitoring.
virtual void Start()
Start signal monitoring thread.
static QString GetSourceName(uint sourceid)
Definition: sourceutil.cpp:50
virtual QString getValue(void) const
QString FindNextDirMostFree(void)
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:142
bool SwitchLiveTVRingBuffer(const QString &channum, bool discont, bool set_rec)
Definition: tv_rec.cpp:4783
V4LChannel * GetV4LChannel(void)
Definition: tv_rec.cpp:1267
bool ShouldSwitchToAnotherInput(const QString &chanid) const
Checks if named channel exists on current tuner, or another tuner.
Definition: tv_rec.cpp:2237
bool GetChannelInfo(uint &chanid, uint &sourceid, QString &callsign, QString &channum, QString &channame, QString &xmltvid) const
Definition: tv_rec.cpp:3335
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:1911
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:2670
static bool GetDevices(uint inputid, uint &parentid, GeneralDBOptions &gen_opts, DVBDBOptions &dvb_opts, FireWireDBOptions &firewire_opts)
Definition: tv_rec.cpp:1774
void NotifySchedulerOfRecording(RecordingInfo *rec)
Tell scheduler about the recording.
Definition: tv_rec.cpp:2804
void PauseRecorder(void)
Tells "recorder" to pause, used for channel and input changes.
Definition: tv_rec.cpp:2979
void SetChannelTimeout(std::chrono::milliseconds timeout)
Definition: tv_rec.cpp:3412
DTVChannel * GetDTVChannel(void)
Definition: tv_rec.cpp:1261
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:858
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:796
bool SetVideoFiltersForChannel(uint sourceid, const QString &channum)
Definition: tv_rec.cpp:2508
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:3376
QString GetChainID(void)
Get the chainid of the livetv instance.
Definition: tv_rec.cpp:2755
void TuningFrequency(const TuningRequest &request)
Performs initial tuning required for any tuning event.
Definition: tv_rec.cpp:3721
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:4504
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:776
AutoRunInitType
Definition: tv_rec.h:327
@ kAutoRunProfile
Definition: tv_rec.h:327
@ kAutoRunNone
Definition: tv_rec.h:327
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:3143
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:4392
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:3192
QString m_nextLiveTVDir
Definition: tv_rec.h:421
DVBDBOptions m_dvbOpt
Definition: tv_rec.h:382
std::chrono::seconds m_eitTransportTimeout
Definition: tv_rec.h:364
QString m_liveTVStartChannel
Definition: tv_rec.h:424
QString m_overRecordCategory
Definition: tv_rec.h:369
ProgramInfo * GetRecording(void)
Allocates and returns a ProgramInfo for the current recording.
Definition: tv_rec.cpp:277
friend class TuningRequest
Definition: tv_rec.h:145
QWaitCondition m_triggerEventSleepWait
Definition: tv_rec.h:404
uint m_inputId
Definition: tv_rec.h:372
void SetNextLiveTVDir(QString dir)
Definition: tv_rec.cpp:4626
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:2697
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:1659
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:3007
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:1053
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:2998
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:3076
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:4851
long long GetFramesWritten(void)
Returns number of frames written to disk by recorder.
Definition: tv_rec.cpp:2622
void StartedRecording(RecordingInfo *curRec)
Inserts a "curRec" into the database.
Definition: tv_rec.cpp:832
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:3632
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:2373
void StopLiveTV(void)
Tells TVRec to stop a "Live TV" recorder.
Definition: tv_rec.cpp:2932
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:3219
RecorderBase * m_recorder
Definition: tv_rec.h:338
long long GetFilePosition(void)
Returns total number of bytes written by RingBuffer.
Definition: tv_rec.cpp:2637
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:1187
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:2539
void CheckForRecGroupChange(void)
Check if frontend changed the recording group.
Definition: tv_rec.cpp:2770
bool m_isPip
Definition: tv_rec.h:374
QString TuningGetChanNum(const TuningRequest &request, QString &input) const
Definition: tv_rec.cpp:3463
FireWireDBOptions m_fwOpt
Definition: tv_rec.h:383
void run(void) override
Event handling method, contains event loop.
Definition: tv_rec.cpp:1350
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:3423
QString GetInput(void) const
Returns current input.
Definition: tv_rec.cpp:3092
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:4925
void SetLiveRecording(int recording)
Tells the Scheduler about changes to the recording status of the LiveTV recording.
Definition: tv_rec.cpp:2886
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:812
void RingBufferChanged(MythMediaBuffer *Buffer, RecordingInfo *pginfo, RecordingQuality *recq)
Definition: tv_rec.cpp:3440
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:4613
static const uint kFlagSignalMonitorRunning
Definition: tv_rec.h:476
static QString FlagToString(uint f)
Definition: tv_rec.cpp:4522
TVState GetState(void) const
Returns the TVState of the recorder.
Definition: tv_rec.cpp:263
bool CreateLiveTVRingBuffer(const QString &channum)
Definition: tv_rec.cpp:4737
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:4439
DTVRecorder * GetDTVRecorder(void)
Definition: tv_rec.cpp:1242
bool m_reachedPreFail
Definition: tv_rec.h:351
int GetPictureAttribute(PictureAttribute attr)
Definition: tv_rec.cpp:3058
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:2091
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:2850
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:1696
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:2725
void HandleTuning(void)
Handles all tuning events.
Definition: tv_rec.cpp:3556
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:4933
std::chrono::milliseconds SetSignalMonitoringRate(std::chrono::milliseconds rate, int notifyFrontend=1)
Sets the signal monitoring rate.
Definition: tv_rec.cpp:2183
void ClearFlags(uint f, const QString &file, int line)
Definition: tv_rec.cpp:4513
bool IsReallyRecording(void)
Returns true if frontend can consider the recorder started.
Definition: tv_rec.cpp:2528
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:2653
void CloseChannel(void)
Definition: tv_rec.cpp:1247
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:3504
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:2327
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:2145
MPEGStreamData * TuningSignalCheck(void)
This checks if we have a channel lock.
Definition: tv_rec.cpp:3964
bool GetKeyframeDurations(int64_t start, int64_t end, frm_pos_map_t &map) const
Definition: tv_rec.cpp:2681
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:1165
bool GetProgramRingBufferForLiveTV(RecordingInfo **pginfo, MythMediaBuffer **Buffer, const QString &channum)
Definition: tv_rec.cpp:4634
bool TuningNewRecorderReal(MPEGStreamData *streamData, RecordingInfo **rec, RecordingProfile &profile, bool had_dummyrec)
Helper function for TVRec::TuningNewRecorder.
Definition: tv_rec.cpp:4236
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:2607
DTVSignalMonitor * GetDTVSignalMonitor(void)
Definition: tv_rec.cpp:2221
uint GetSourceID(void) const
Returns current source id.
Definition: tv_rec.cpp:3102
static const uint kFlagWaitingForRecPause
Definition: tv_rec.h:470
QString SetInput(QString input)
Changes to the specified input.
Definition: tv_rec.cpp:3117
static bool StateIsPlaying(TVState state)
Returns true if we are in any state associated with a player.
Definition: tv_rec.cpp:786
QString LoadProfile(void *tvchain, RecordingInfo *rec, RecordingProfile &profile) const
Definition: tv_rec.cpp:4190
int m_progNum
Definition: tv_rec.h:121
bool IsOnSameMultiplex(void) const
Definition: tv_rec.h:112
uint m_minorChan
Definition: tv_rec.h:120
uint m_flags
Definition: tv_rec.h:115
RecordingInfo * m_program
Definition: tv_rec.h:116
uint m_majorChan
Definition: tv_rec.h:119
QString toString(void) const
Definition: tv_rec.cpp:4960
QString m_channel
Definition: tv_rec.h:117
QString m_input
Definition: tv_rec.h:118
Implements tuning for TV cards using the V4L driver API, both versions 1 and 2.
Definition: v4lchannel.h:33
unsigned int uint
Definition: compat.h:60
#define minor(X)
Definition: compat.h:58
unsigned short uint16_t
Definition: iso6937tables.h:3
@ JOB_METADATA
Definition: jobqueue.h:80
@ JOB_NONE
Definition: jobqueue.h:75
@ JOB_COMMFLAG
Definition: jobqueue.h:79
@ JOB_TRANSCODE
Definition: jobqueue.h:78
@ JOB_LIVE_REC
Definition: jobqueue.h:61
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
Convenience inline random number generator functions.
void SendMythSystemRecEvent(const QString &msg, const RecordingInfo *pginfo)
std::chrono::seconds secsInPast(const QDateTime &past)
Definition: mythdate.cpp:212
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
@ ISODate
Default UTC.
Definition: mythdate.h:17
std::chrono::seconds secsInFuture(const QDateTime &future)
Definition: mythdate.cpp:217
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
uint32_t MythRandom()
generate 32 random bits
Definition: mythrandom.h:20
def error(message)
Definition: smolt.py:409
def write(text, progress=True)
Definition: mythburn.py:306
const QString kRecorderInUseID
MarkTypes
Definition: programtypes.h:46
@ MARK_ASPECT_2_21_1
Definition: programtypes.h:67
@ MARK_ASPECT_16_9
Definition: programtypes.h:66
@ kLiveTVAutoExpire
Definition: programtypes.h:196
@ COMM_FLAG_COMMFREE
Definition: programtypes.h:123
QMap< long long, long long > frm_pos_map_t
Frame # -> File offset map.
Definition: programtypes.h:44
@ kNotRecording
@ kSingleRecord
QString StateToString(TVState state)
Returns a human readable QString representing a TVState.
Definition: tv.cpp:11
BrowseDirection
Used to request ProgramInfo for channel browsing.
Definition: tv.h:41
@ BROWSE_SAME
Fetch browse information on current channel and time.
Definition: tv.h:43
@ BROWSE_RIGHT
Fetch information on current channel in the future.
Definition: tv.h:47
@ BROWSE_LEFT
Fetch information on current channel in the past.
Definition: tv.h:46
@ BROWSE_UP
Fetch information on previous channel.
Definition: tv.h:44
@ BROWSE_FAVORITE
Fetch information on the next favorite channel.
Definition: tv.h:48
@ BROWSE_DOWN
Fetch information on next channel.
Definition: tv.h:45
PictureAdjustType
Definition: tv.h:124
ChannelChangeDirection
ChannelChangeDirection is an enumeration of possible channel changing directions.
Definition: tv.h:32
@ CHANNEL_DIRECTION_SAME
Definition: tv.h:36
@ CHANNEL_DIRECTION_DOWN
Definition: tv.h:34
@ CHANNEL_DIRECTION_FAVORITE
Definition: tv.h:35
@ CHANNEL_DIRECTION_UP
Definition: tv.h:33
TVState
TVState is an enumeration of the states used by TV and TVRec.
Definition: tv.h:54
@ kState_None
None State, this is the initial state in both TV and TVRec, it indicates that we are ready to change ...
Definition: tv.h:61
@ kState_RecordingOnly
Recording Only is a TVRec only state for when we are recording a program, but there is no one current...
Definition: tv.h:87
@ kState_WatchingLiveTV
Watching LiveTV is the state for when we are watching a recording and the user has control over the c...
Definition: tv.h:66
@ kState_Error
Error State, if we ever try to enter this state errored is set.
Definition: tv.h:57
@ kState_WatchingPreRecorded
Watching Pre-recorded is a TV only state for when we are watching a pre-existing recording.
Definition: tv.h:70
@ kState_ChangingState
This is a placeholder state which we never actually enter, but is returned by GetState() when we are ...
Definition: tv.h:92
static std::chrono::seconds eit_start_rand(uint inputId, std::chrono::seconds eitTransportTimeout)
Definition: tv_rec.cpp:1335
#define LOC
Definition: tv_rec.cpp:48
static void GetPidsToCache(DTVSignalMonitor *dtvMon, pid_cache_t &pid_cache)
Definition: tv_rec.cpp:1861
static int get_highest_input(void)
Definition: tv_rec.cpp:1318
static void apply_broken_dvb_driver_crc_hack(ChannelBase *, MPEGStreamData *)
Definition: tv_rec.cpp:4981
static int init_jobs(const RecordingInfo *rec, RecordingProfile &profile, bool on_host, bool transcode_bfr_comm, bool on_line_comm)
Definition: tv_rec.cpp:4132
static bool get_use_eit(uint inputid)
Definition: tv_rec.cpp:1277
#define SET_NEXT()
Definition: tv_rec.cpp:1042
static QString add_spacer(const QString &channel, const QString &spacer)
Adds the spacer before the last character in chan.
Definition: tv_rec.cpp:2338
static bool ApplyCachedPids(DTVSignalMonitor *dtvMon, const DTVChannel *channel)
Definition: tv_rec.cpp:1878
static bool is_dishnet_eit(uint inputid)
Definition: tv_rec.cpp:1297
#define TRANSITION(ASTATE, BSTATE)
Definition: tv_rec.cpp:1040
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