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 inputids2.reserve(inputids.size());
525 states.reserve(inputids.size());
526
527 // Stop remote recordings if needed
528 for (uint inputid : inputids)
529 {
530 InputInfo busy_input;
531 bool is_busy = RemoteIsBusy(inputid, busy_input);
532
533 if (is_busy && !sourceid)
534 {
535 mplexid = pendinfo.m_info->QueryMplexID();
536 chanid = pendinfo.m_info->GetChanID();
537 sourceid = pendinfo.m_info->GetSourceID();
538 }
539
540 if (is_busy &&
541 ((sourceid != busy_input.m_sourceId) ||
542 (mplexid != busy_input.m_mplexId) ||
543 ((mplexid == 0 || mplexid == 32767) &&
544 chanid != busy_input.m_chanId)))
545 {
546 states.push_back((TVState) RemoteGetState(inputid));
547 inputids2.push_back(inputid);
548 }
549 }
550
551 bool ok = true;
552 for (uint i = 0; (i < inputids2.size()) && ok; i++)
553 {
554 LOG(VB_RECORD, LOG_INFO, LOC +
555 QString("Attempting to stop input [%1] in state %2")
556 .arg(inputids2[i]).arg(StateToString(states[i])));
557
558 bool success = RemoteStopRecording(inputids2[i]);
559 if (success)
560 {
561 uint state = RemoteGetState(inputids2[i]);
562 LOG(VB_GENERAL, LOG_INFO, LOC + QString("a [%1]: %2")
563 .arg(inputids2[i]).arg(StateToString((TVState)state)));
564 success = (kState_None == state);
565 }
566
567 // If we managed to stop LiveTV recording, restart playback..
568 if (success && states[i] == kState_WatchingLiveTV)
569 {
570 QString message = QString("QUIT_LIVETV %1").arg(inputids2[i]);
571 MythEvent me(message);
573 }
574
575 LOG(VB_RECORD, LOG_INFO, LOC +
576 QString("Stopping recording on [%1], %2") .arg(inputids2[i])
577 .arg(success ? "succeeded" : "failed"));
578
579 ok &= success;
580 }
581
582 // If we failed to stop the remote recordings, don't record
583 if (!ok)
584 {
586 cancelNext = true;
587 }
588
589 inputids.clear();
590
591 LOG(VB_RECORD, LOG_INFO, LOC + "Checking input group recorders - done");
592 }
593
594 bool did_switch = false;
595 if (!cancelNext && (GetState() == kState_RecordingOnly))
596 {
598 did_switch = (nullptr != ri2);
599 if (did_switch)
600 {
601 // Make sure scheduler is allowed to end this recording
602 ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
603
605 }
606 else
607 {
608 // If in post-roll, end recording
609 m_stateChangeLock.unlock();
611 m_stateChangeLock.lock();
612 }
613 }
614
615 if (!cancelNext && (GetState() == kState_None))
616 {
617 if (m_tvChain)
618 {
619 QString message = QString("LIVETV_EXITED");
620 MythEvent me(message, m_tvChain->GetID());
623 m_tvChain = nullptr;
624 }
625
627
628 // Tell event loop to begin recording.
629 m_curRecording = new RecordingInfo(*rcinfo);
634
635 // Make sure scheduler is allowed to end this recording
636 ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
637
640 else
641 LOG(VB_RECORD, LOG_WARNING, LOC + "Still failing.");
643 }
644 else if (!cancelNext && (GetState() == kState_WatchingLiveTV))
645 {
649
650 // We want the frontend to change channel for recording
651 // and disable the UI for channel change, PiP, etc.
652
653 QString message = QString("LIVETV_WATCH %1 1").arg(m_inputId);
654 QStringList prog;
655 rcinfo->ToStringList(prog);
656 MythEvent me(message, prog);
658 }
659 else if (!did_switch)
660 {
661 QString msg = QString("Wanted to record: %1 %2 %3 %4\n\t\t\t")
662 .arg(rcinfo->GetTitle(),
663 QString::number(rcinfo->GetChanID()),
666
667 if (cancelNext)
668 {
669 msg += "But a user has canceled this recording";
671 }
672 else
673 {
674 msg += QString("But the current state is: %1")
677 }
678
680 {
681 msg += QString("\n\t\t\tCurrently recording: %1 %2 %3 %4")
682 .arg(m_curRecording->GetTitle(),
683 QString::number(m_curRecording->GetChanID()),
686 }
687
688 LOG(VB_GENERAL, LOG_INFO, LOC + msg);
689 }
690
691 for (const auto & pend : std::as_const(m_pendingRecordings))
692 delete pend.m_info;
693 m_pendingRecordings.clear();
694
695 if (!did_switch)
696 {
698
699 QMutexLocker locker(&m_pendingRecLock);
700 if ((m_curRecording) &&
705 {
706 SetRecordingStatus(RecStatus::Failed, __LINE__, true);
707 }
708 return m_recStatus;
709 }
710
711 return GetRecordingStatus();
712}
713
715{
716 QMutexLocker pendlock(&m_pendingRecLock);
717 return m_recStatus;
718}
719
721 RecStatus::Type new_status, int line, bool have_lock)
722{
723 RecStatus::Type old_status { RecStatus::Unknown };
724 if (have_lock)
725 {
726 old_status = m_recStatus;
727 m_recStatus = new_status;
728 }
729 else
730 {
731 m_pendingRecLock.lock();
732 old_status = m_recStatus;
733 m_recStatus = new_status;
734 m_pendingRecLock.unlock();
735 }
736
737 LOG(VB_RECORD, LOG_INFO, LOC +
738 QString("SetRecordingStatus(%1->%2) on line %3")
739 .arg(RecStatus::toString(old_status, kSingleRecord),
741 QString::number(line)));
742}
743
750void TVRec::StopRecording(bool killFile)
751{
753 {
754 QMutexLocker lock(&m_stateChangeLock);
755 if (killFile)
756 {
757 SetFlags(kFlagKillRec, __FILE__, __LINE__);
758 }
759 else if (m_curRecording)
760 {
761 QDateTime now = MythDate::current(true);
762 if (now < m_curRecording->GetDesiredEndTime())
764 }
766 // wait for state change to take effect
769
771 }
772}
773
780{
781 return (state == kState_RecordingOnly ||
782 state == kState_WatchingLiveTV);
783}
784
790{
791 return (state == kState_WatchingPreRecorded);
792}
793
800{
801 if (StateIsRecording(state))
802 return kState_None;
803
804 LOG(VB_GENERAL, LOG_ERR, LOC +
805 QString("Unknown state in RemoveRecording: %1")
806 .arg(StateToString(state)));
807 return kState_Error;
808}
809
816{
817 if (StateIsPlaying(state))
818 {
819 if (state == kState_WatchingPreRecorded)
820 return kState_None;
822 }
823
824 QString msg = "Unknown state in RemovePlaying: %1";
825 LOG(VB_GENERAL, LOG_ERR, LOC + msg.arg(StateToString(state)));
826
827 return kState_Error;
828}
829
836{
837 if (!curRec)
838 return;
839
841 LOG(VB_RECORD, LOG_INFO, LOC + QString("StartedRecording(%1) fn(%2)")
842 .arg(curRec->MakeUniqueKey(), curRec->GetPathname()));
843
844 if (curRec->IsCommercialFree())
846
847 AutoRunInitType t = (curRec->GetRecordingGroup() == "LiveTV") ?
849 InitAutoRunJobs(curRec, t, nullptr, __LINE__);
850
851 SendMythSystemRecEvent("REC_STARTED", curRec);
852}
853
862{
863 if (!curRec)
864 return;
865
866 // Make sure the recording group is up to date
867 const QString recgrp = curRec->QueryRecordingGroup();
868 curRec->SetRecordingGroup(recgrp);
869
870 bool is_good = true;
871 if (recq)
872 {
873 LOG((recq->IsDamaged()) ? VB_GENERAL : VB_RECORD, LOG_INFO,
874 LOC + QString("FinishedRecording(%1) %2 recq:\n%3")
875 .arg(curRec->MakeUniqueKey(),
876 (recq->IsDamaged()) ? "damaged" : "good",
877 recq->toStringXML()));
878 is_good = !recq->IsDamaged();
879 delete recq;
880 recq = nullptr;
881 }
882
883 RecStatus::Type ors = curRec->GetRecordingStatus();
884 // Set the final recording status
887 else if (curRec->GetRecordingStatus() != RecStatus::Recorded)
890 is_good &= (curRec->GetRecordingStatus() == RecStatus::Recorded);
891
892 // Figure out if this was already done for this recording
893 bool was_finished = false;
894 static QMutex s_finRecLock;
895 static QHash<QString,QDateTime> s_finRecMap;
896 {
897 QMutexLocker locker(&s_finRecLock);
898 QDateTime now = MythDate::current();
899 QDateTime expired = now.addSecs(-5LL * 60);
900 QHash<QString,QDateTime>::iterator it = s_finRecMap.begin();
901 while (it != s_finRecMap.end())
902 {
903 if ((*it) < expired)
904 it = s_finRecMap.erase(it);
905 else
906 ++it;
907 }
908 QString key = curRec->MakeUniqueKey();
909 it = s_finRecMap.find(key);
910 if (it != s_finRecMap.end())
911 was_finished = true;
912 else
913 s_finRecMap[key] = now;
914 }
915
916 // Print something informative to the log
917 LOG(VB_RECORD, LOG_INFO, LOC +
918 QString("FinishedRecording(%1) %2 quality"
919 "\n\t\t\ttitle: %3\n\t\t\t"
920 "in recgroup: %4 status: %5:%6 %7 %8")
921 .arg(curRec->MakeUniqueKey(),
922 is_good ? "Good" : "Bad",
923 curRec->GetTitle(),
924 recgrp,
927 HasFlags(kFlagDummyRecorderRunning)?"is_dummy":"not_dummy",
928 was_finished?"already_finished":"finished_now"));
929
930 // This has already been called on this recording..
931 if (was_finished)
932 return;
933
934 // Notify the frontend watching live tv that this file is final
935 if (m_tvChain)
937
938 // if this is a dummy recorder, do no more..
940 {
941 curRec->FinishedRecording(true); // so end time is updated
942 SendMythSystemRecEvent("REC_FINISHED", curRec);
943 return;
944 }
945
946 // Get the width and set the videoprops
947 MarkTypes aspectRatio = curRec->QueryAverageAspectRatio();
948 uint avg_height = curRec->QueryAverageHeight();
949 bool progressive = curRec->QueryAverageScanProgressive();
950
951 uint16_t flags {VID_UNKNOWN};
952 if (avg_height > 2000)
953 flags |= VID_4K;
954 else if (avg_height > 1000)
955 flags |= VID_1080;
956 else if (avg_height > 700)
957 flags |= VID_720;
958 if (progressive)
959 flags |= VID_PROGRESSIVE;
960 if (!is_good)
961 flags |= VID_DAMAGED;
962 if ((aspectRatio == MARK_ASPECT_16_9) ||
963 (aspectRatio == MARK_ASPECT_2_21_1))
964 flags |= VID_WIDESCREEN;
965
966 curRec->SaveVideoProperties
967 (VID_4K | VID_1080 | VID_720 | VID_DAMAGED |
968 VID_WIDESCREEN | VID_PROGRESSIVE, flags);
969
970 // Make sure really short recordings have positive run time.
971 if (curRec->GetRecordingEndTime() <= curRec->GetRecordingStartTime())
972 {
973 curRec->SetRecordingEndTime(
974 curRec->GetRecordingStartTime().addSecs(60));
975 }
976
977 // HACK Temporary hack, ensure we've loaded the recording file info, do it now
978 // so that it contains the final filesize information
979 if (!curRec->GetRecordingFile())
980 curRec->LoadRecordingFile();
981
982 // Generate a preview
983 uint64_t fsize = curRec->GetFilesize();
984 if (curRec->IsLocal() && (fsize >= 1000) &&
986 {
988 }
989
990 // store recording in recorded table
991 curRec->FinishedRecording(!is_good || (recgrp == "LiveTV"));
992
993 // send out UPDATE_RECORDING_STATUS message
994 LOG(VB_RECORD, LOG_INFO, LOC +
995 QString("FinishedRecording -- UPDATE_RECORDING_STATUS: %1")
996 .arg(RecStatus::toString(is_good ? curRec->GetRecordingStatus()
998 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
999 .arg(curRec->GetInputID())
1000 .arg(curRec->GetChanID())
1002 .arg(is_good ? curRec->GetRecordingStatus() : RecStatus::Failed)
1003 .arg(curRec->GetRecordingEndTime(MythDate::ISODate)));
1005
1006 // send out REC_FINISHED message
1007 SendMythSystemRecEvent("REC_FINISHED", curRec);
1008
1009 // send out DONE_RECORDING message
1010 auto secsSince = MythDate::secsInPast(curRec->GetRecordingStartTime());
1011 QString message = QString("DONE_RECORDING %1 %2 %3")
1012 .arg(m_inputId).arg(secsSince.count()).arg(GetFramesWritten());
1013 MythEvent me2(message);
1014 gCoreContext->dispatch(me2);
1015
1016 // Handle JobQueue
1017 QHash<QString,int>::iterator autoJob =
1018 m_autoRunJobs.find(curRec->MakeUniqueKey());
1019 if (autoJob == m_autoRunJobs.end())
1020 {
1021 LOG(VB_GENERAL, LOG_INFO,
1022 "autoRunJobs not initialized until FinishedRecording()");
1024 (recgrp == "LiveTV") ? kAutoRunNone : kAutoRunProfile;
1025 InitAutoRunJobs(curRec, t, nullptr, __LINE__);
1026 autoJob = m_autoRunJobs.find(curRec->MakeUniqueKey());
1027 }
1028 LOG(VB_JOBQUEUE, LOG_INFO, QString("AutoRunJobs 0x%1").arg(*autoJob,0,16));
1029 if ((recgrp == "LiveTV") || (fsize < 1000) ||
1030 (curRec->GetRecordingStatus() != RecStatus::Recorded) ||
1031 (curRec->GetRecordingStartTime().secsTo(
1032 MythDate::current()) < 120))
1033 {
1036 }
1037 if (*autoJob != JOB_NONE)
1038 JobQueue::QueueRecordingJobs(*curRec, *autoJob);
1039 m_autoRunJobs.erase(autoJob);
1040}
1041
1042// NOLINTBEGIN(cppcoreguidelines-macro-usage)
1043#define TRANSITION(ASTATE,BSTATE) \
1044 ((m_internalState == (ASTATE)) && (m_desiredNextState == (BSTATE)))
1045#define SET_NEXT() do { nextState = m_desiredNextState; changed = true; } while(false)
1046#define SET_LAST() do { nextState = m_internalState; changed = true; } while(false)
1047// NOLINTEND(cppcoreguidelines-macro-usage)
1048
1057{
1058 TVState nextState = m_internalState;
1059
1060 bool changed = false;
1061
1062 QString transMsg = QString(" %1 to %2")
1064
1066 {
1067 LOG(VB_GENERAL, LOG_ERR, LOC +
1068 "HandleStateChange(): Null transition" + transMsg);
1069 m_changeState = false;
1070 return;
1071 }
1072
1073 // Stop EIT scanning on this input before any tuning,
1074 // to avoid race condition with it's tuning requests.
1076 {
1077 LOG(VB_EIT, LOG_INFO, LOC + QString("Stop EIT scan on input %1").arg(GetInputId()));
1078
1080 ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1082 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
1083 }
1084
1085 // Stop EIT scanning on all conflicting inputs so that
1086 // the tuner card is available for a new tuning request.
1087 // Conflicting inputs are inputs that have independent video sources
1088 // but that share a tuner card, such as a DVB-S/S2 tuner card that
1089 // connects to multiple satellites with a DiSEqC switch.
1090 if (m_scanner && !m_eitInputs.empty())
1091 {
1092 s_inputsLock.lockForRead();
1093 s_eitLock.lock();
1094 for (auto input : m_eitInputs)
1095 {
1096 auto *tv_rec = s_inputs.value(input);
1097 if (tv_rec && tv_rec->m_scanner && tv_rec->HasFlags(kFlagEITScannerRunning))
1098 {
1099 LOG(VB_EIT, LOG_INFO, LOC +
1100 QString("Stop EIT scan active on conflicting input %1")
1101 .arg(input));
1102 tv_rec->m_scanner->StopActiveScan();
1103 tv_rec->ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1104 tv_rec->TuningShutdowns(TuningRequest(kFlagNoRec));
1106 tv_rec->m_eitScanStartTime = MythDate::current().addSecs(secs.count());
1107 }
1108 }
1109 s_eitLock.unlock();
1110 s_inputsLock.unlock();
1111 }
1112
1113 // Handle different state transitions
1115 {
1117 SET_NEXT();
1118 }
1120 {
1122 SET_NEXT();
1123 }
1125 {
1126 SetPseudoLiveTVRecording(nullptr);
1127
1128 SET_NEXT();
1129 }
1131 {
1132 SetPseudoLiveTVRecording(nullptr);
1134 SET_NEXT();
1135 }
1137 {
1140 (GetFlags()&kFlagKillRec)));
1141 SET_NEXT();
1142 }
1143
1144 QString msg = changed ? "Changing from" : "Unknown state transition:";
1145 LOG(VB_GENERAL, LOG_INFO, LOC + msg + transMsg);
1146
1147 // update internal state variable
1148 m_internalState = nextState;
1149 m_changeState = false;
1150
1152 {
1154 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
1155 }
1156 else
1157 {
1158 m_eitScanStartTime = MythDate::current().addYears(1);
1159 }
1160}
1161#undef TRANSITION
1162#undef SET_NEXT
1163#undef SET_LAST
1164
1169{
1170 QMutexLocker lock(&m_stateChangeLock);
1171 m_desiredNextState = nextState;
1172 m_changeState = true;
1173 WakeEventLoop();
1174}
1175
1191{
1192 LOG(VB_RECORD, LOG_INFO, LOC + QString("TeardownRecorder(%1)")
1193 .arg((request_flags & kFlagKillRec) ? "kFlagKillRec" : ""));
1194
1195 m_pauseNotify = false;
1196 m_isPip = false;
1197
1199 {
1202 delete m_recorderThread;
1203 m_recorderThread = nullptr;
1204 }
1206 __FILE__, __LINE__);
1207
1208 RecordingQuality *recq = nullptr;
1209 if (m_recorder)
1210 {
1211 if (GetV4LChannel())
1212 m_channel->SetFd(-1);
1213
1215
1216 QMutexLocker locker(&m_stateChangeLock);
1217 delete m_recorder;
1218 m_recorder = nullptr;
1219 }
1220
1221 if (m_buffer)
1222 {
1223 LOG(VB_FILE, LOG_INFO, LOC + "calling StopReads()");
1225 }
1226
1227 if (m_curRecording)
1228 {
1229 if (!!(request_flags & kFlagKillRec))
1231
1233
1235 delete m_curRecording;
1236 m_curRecording = nullptr;
1237 }
1238
1239 m_pauseNotify = true;
1240
1241 if (GetDTVChannel())
1243}
1244
1246{
1247 return dynamic_cast<DTVRecorder*>(m_recorder);
1248}
1249
1251{
1252 if (m_channel &&
1253 ((m_genOpt.m_inputType == "DVB" && m_dvbOpt.m_dvbOnDemand) ||
1254 m_genOpt.m_inputType == "FREEBOX" ||
1255 m_genOpt.m_inputType == "VBOX" ||
1256 m_genOpt.m_inputType == "HDHOMERUN" ||
1257 m_genOpt.m_inputType == "EXTERNAL" ||
1259 {
1260 m_channel->Close();
1261 }
1262}
1263
1265{
1266 return dynamic_cast<DTVChannel*>(m_channel);
1267}
1268
1269// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
1271{
1272#if CONFIG_V4L2
1273 return dynamic_cast<V4LChannel*>(m_channel);
1274#else
1275 return nullptr;
1276#endif // CONFIG_V4L2
1277}
1278
1279// Check if EIT is enabled for the video source connected to this input
1280static bool get_use_eit(uint inputid)
1281{
1283 query.prepare(
1284 "SELECT SUM(useeit) "
1285 "FROM videosource, capturecard "
1286 "WHERE videosource.sourceid = capturecard.sourceid AND"
1287 " capturecard.cardid = :INPUTID");
1288 query.bindValue(":INPUTID", inputid);
1289
1290 if (!query.exec() || !query.isActive())
1291 {
1292 MythDB::DBError("get_use_eit", query);
1293 return false;
1294 }
1295 if (query.next())
1296 return query.value(0).toBool();
1297 return false;
1298}
1299
1300static bool is_dishnet_eit(uint inputid)
1301{
1303 query.prepare(
1304 "SELECT SUM(dishnet_eit) "
1305 "FROM videosource, capturecard "
1306 "WHERE videosource.sourceid = capturecard.sourceid AND"
1307 " capturecard.cardid = :INPUTID");
1308 query.bindValue(":INPUTID", inputid);
1309
1310 if (!query.exec() || !query.isActive())
1311 {
1312 MythDB::DBError("is_dishnet_eit", query);
1313 return false;
1314 }
1315 if (query.next())
1316 return query.value(0).toBool();
1317 return false;
1318}
1319
1320// Highest capturecard instance number including multirec instances
1321static int get_highest_input(void)
1322{
1324 query.prepare(
1325 "SELECT MAX(cardid) "
1326 "FROM capturecard ");
1327
1328 if (!query.exec() || !query.isActive())
1329 {
1330 MythDB::DBError("highest_input", query);
1331 return -1;
1332 }
1333 if (query.next())
1334 return query.value(0).toInt();
1335 return -1;
1336}
1337
1338static std::chrono::seconds eit_start_rand(uint inputId, std::chrono::seconds eitTransportTimeout)
1339{
1340 // Randomize start time a bit
1341 auto timeout = std::chrono::seconds(MythRandom(0, eitTransportTimeout.count() / 3));
1342
1343 // Use the highest input number and the current input number
1344 // to distribute the scan start evenly over eitTransportTimeout
1345 int highest_input = get_highest_input();
1346 if (highest_input > 0)
1347 timeout += eitTransportTimeout * inputId / highest_input;
1348
1349 return timeout;
1350}
1351
1353void TVRec::run(void)
1354{
1355 QMutexLocker lock(&m_stateChangeLock);
1356 SetFlags(kFlagRunMainLoop, __FILE__, __LINE__);
1357 ClearFlags(kFlagExitPlayer | kFlagFinishRecording, __FILE__, __LINE__);
1358
1359 // Check whether we should use the EITScanner in this TVRec instance
1360 if (CardUtil::IsEITCapable(m_genOpt.m_inputType) && // Card type capable of receiving EIT?
1361 (!GetDTVChannel() || GetDTVChannel()->IsMaster()) && // Card is master and not a multirec instance
1362 (m_dvbOpt.m_dvbEitScan || get_use_eit(m_inputId))) // EIT is selected for card OR EIT is selected for video source
1363 {
1366 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
1367 }
1368 else
1369 {
1370 m_eitScanStartTime = MythDate::current().addYears(10);
1371 }
1372
1373 while (HasFlags(kFlagRunMainLoop))
1374 {
1375 // If there is a state change queued up, do it...
1376 if (m_changeState)
1377 {
1380 __FILE__, __LINE__);
1381 }
1382
1383 // Quick exit on fatal errors.
1384 if (IsErrored())
1385 {
1386 LOG(VB_GENERAL, LOG_ERR, LOC +
1387 "RunTV encountered fatal error, exiting event thread.");
1388 ClearFlags(kFlagRunMainLoop, __FILE__, __LINE__);
1389 TeardownAll();
1390 return;
1391 }
1392
1393 // Handle any tuning events.. Blindly grabbing the lock here
1394 // can sometimes cause a deadlock with Init() while it waits
1395 // to make sure this thread starts. Until a better solution
1396 // is found, don't run HandleTuning unless we can safely get
1397 // the lock.
1398 if (s_inputsLock.tryLockForRead())
1399 {
1400 HandleTuning();
1401 s_inputsLock.unlock();
1402 }
1403
1404 // Tell frontends about pending recordings
1406
1407 // If we are recording a program, check if the recording is
1408 // over or someone has asked us to finish the recording.
1409 // Add an extra 60 seconds to the recording end time if we
1410 // might want a back to back recording.
1411 QDateTime recEnd = (!m_pendingRecordings.empty()) ?
1412 m_recordEndTime.addSecs(60) : m_recordEndTime;
1413 if ((GetState() == kState_RecordingOnly) &&
1414 (MythDate::current() > recEnd ||
1416 {
1418 ClearFlags(kFlagFinishRecording, __FILE__, __LINE__);
1419 }
1420
1421 if (m_curRecording)
1422 {
1424
1425 if (m_recorder)
1426 {
1428
1429 // Check for recorder errors
1430 if (m_recorder->IsErrored())
1431 {
1433
1435 {
1436 QString message = QString("QUIT_LIVETV %1").arg(m_inputId);
1437 MythEvent me(message);
1439 }
1440 else
1441 {
1443 }
1444 }
1445 }
1446 }
1447
1448 // Check for the end of the current program..
1450 {
1451 QDateTime now = MythDate::current();
1452 bool has_finish = HasFlags(kFlagFinishRecording);
1453 bool has_rec = m_pseudoLiveTVRecording;
1454 bool enable_ui = true;
1455
1456 m_pendingRecLock.lock();
1457 bool rec_soon = m_pendingRecordings.contains(m_inputId);
1458 m_pendingRecLock.unlock();
1459
1460 if (has_rec && (has_finish || (now > m_recordEndTime)))
1461 {
1462 SetPseudoLiveTVRecording(nullptr);
1463 }
1464 else if (!has_rec && !rec_soon && m_curRecording &&
1466 {
1467 if (!m_switchingBuffer)
1468 {
1469 LOG(VB_RECORD, LOG_INFO, LOC +
1470 "Switching Buffer (" +
1471 QString("!has_rec(%1) && ").arg(has_rec) +
1472 QString("!rec_soon(%1) && (").arg(rec_soon) +
1473 MythDate::toString(now, MythDate::ISODate) + " >= " +
1475 QString("(%1) ))")
1476 .arg(now >= m_curRecording->GetScheduledEndTime()));
1477
1478 m_switchingBuffer = true;
1479
1481 false, true);
1482 }
1483 else
1484 {
1485 LOG(VB_RECORD, LOG_INFO, "Waiting for ringbuffer switch");
1486 }
1487 }
1488 else
1489 {
1490 enable_ui = false;
1491 }
1492
1493 if (enable_ui)
1494 {
1495 LOG(VB_RECORD, LOG_INFO, LOC + "Enabling Full LiveTV UI.");
1496 QString message = QString("LIVETV_WATCH %1 0").arg(m_inputId);
1497 MythEvent me(message);
1499 }
1500 }
1501
1502 // Check for ExitPlayer flag, and if set change to a non-watching
1503 // state (either kState_RecordingOnly or kState_None).
1505 {
1510 ClearFlags(kFlagExitPlayer, __FILE__, __LINE__);
1511 }
1512
1513 // Start active EIT scan
1514 bool conflicting_input = false;
1515 if (m_scanner && m_channel &&
1517 {
1519 {
1520 LOG(VB_EIT, LOG_INFO, LOC +
1521 QString("EIT scanning disabled for input %1")
1522 .arg(GetInputId()));
1523 m_eitScanStartTime = MythDate::current().addYears(10);
1524 }
1525 else if (!get_use_eit(GetInputId()))
1526 {
1527 LOG(VB_EIT, LOG_INFO, LOC +
1528 QString("EIT scanning disabled for video source %1")
1529 .arg(GetSourceID()));
1530 m_eitScanStartTime = MythDate::current().addYears(10);
1531 }
1532 else
1533 {
1534 LOG(VB_EIT, LOG_INFO, LOC +
1535 QString("EIT scanning enabled for input %1 connected to video source %2 '%3'")
1537
1538 // Check if another card in the same input group is busy recording.
1539 // This could be either a virtual DVB-device or a second tuner on a single card.
1540 s_inputsLock.lockForRead();
1541 s_eitLock.lock();
1542 bool allow_eit = true;
1543 std::vector<uint> inputids = CardUtil::GetConflictingInputs(m_inputId);
1544 InputInfo busy_input;
1545 for (uint i = 0; i < inputids.size() && allow_eit; ++i)
1546 allow_eit = !RemoteIsBusy(inputids[i], busy_input);
1547
1548 // Check if another card in the same input group is busy with an EIT scan.
1549 // We cannot start an EIT scan on this input if there is already an EIT scan
1550 // running on a conflicting real input.
1551 // Note that EIT scans never run on virtual inputs.
1552 if (allow_eit)
1553 {
1554 for (auto input : inputids)
1555 {
1556 auto *tv_rec = s_inputs.value(input);
1557 if (tv_rec && tv_rec->m_scanner)
1558 {
1559 conflicting_input = true;
1560 if (tv_rec->HasFlags(kFlagEITScannerRunning))
1561 {
1562 LOG(VB_EIT, LOG_INFO, LOC +
1563 QString("EIT scan on conflicting input %1").arg(input));
1564 allow_eit = false;
1565 busy_input.m_inputId = tv_rec->m_inputId;
1566 break;
1567 }
1568 }
1569 }
1570 }
1571
1572 if (allow_eit)
1573 {
1574 LOG(VB_EIT, LOG_INFO, LOC +
1575 QString("Start EIT active scan on input %1")
1576 .arg(m_inputId));
1578 SetFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1579 m_eitScanStartTime = MythDate::current().addYears(1);
1580 if (conflicting_input)
1582 else
1583 m_eitScanStopTime = MythDate::current().addYears(1);
1584 }
1585 else
1586 {
1587 const int seconds_postpone = 300;
1588 LOG(VB_EIT, LOG_INFO, LOC +
1589 QString("Postponing EIT scan on input %1 for %2 seconds because input %3 is busy")
1590 .arg(m_inputId).arg(seconds_postpone).arg(busy_input.m_inputId));
1591 m_eitScanStartTime = m_eitScanStartTime.addSecs(seconds_postpone);
1592 }
1593 s_eitLock.unlock();
1594 s_inputsLock.unlock();
1595 }
1596 }
1597
1598
1599 // Stop active EIT scan and allow start of the EIT scan on one of the conflicting real inputs.
1601 {
1602 LOG(VB_EIT, LOG_INFO, LOC +
1603 QString("Stop EIT scan on input %1 to allow scan on a conflicting input")
1604 .arg(GetInputId()));
1605
1607 ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1609
1611 secs += m_eitScanPeriod;
1612 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
1613 }
1614
1615 // We should be no more than a few thousand milliseconds,
1616 // as the end recording code does not have a trigger...
1617 // NOTE: If you change anything here, make sure that
1618 // WaitforEventThreadSleep() will still work...
1619 if (m_tuningRequests.empty() && !m_changeState)
1620 {
1621 lock.unlock(); // stateChangeLock
1622
1623 {
1624 QMutexLocker locker(&m_triggerEventSleepLock);
1626 m_triggerEventSleepWait.wakeAll();
1627 }
1628
1629 sched_yield();
1630
1631 {
1632 QMutexLocker locker(&m_triggerEventLoopLock);
1633 // We check triggerEventLoopSignal because it is possible
1634 // that WakeEventLoop() was called since we
1635 // unlocked the stateChangeLock
1637 {
1639 &m_triggerEventLoopLock, 1000 /* ms */);
1640 }
1642 }
1643
1644 lock.relock(); // stateChangeLock
1645 }
1646 }
1647
1648 if (GetState() != kState_None)
1649 {
1652 }
1653
1654 TeardownAll();
1655}
1656
1662bool TVRec::WaitForEventThreadSleep(bool wake, std::chrono::milliseconds time)
1663{
1664 bool ok = false;
1665 MythTimer t;
1666 t.start();
1667
1668 while (!ok && (t.elapsed() < time))
1669 {
1670 MythTimer t2;
1671 t2.start();
1672
1673 if (wake)
1674 WakeEventLoop();
1675
1676 m_stateChangeLock.unlock();
1677
1678 sched_yield();
1679
1680 {
1681 QMutexLocker locker(&m_triggerEventSleepLock);
1685 }
1686
1687 m_stateChangeLock.lock();
1688
1689 // verify that we were triggered.
1690 ok = (m_tuningRequests.empty() && !m_changeState);
1691
1692 std::chrono::milliseconds te = t2.elapsed();
1693 if (!ok && te < 10ms)
1694 std::this_thread::sleep_for(10ms - te);
1695 }
1696 return ok;
1697}
1698
1700{
1701 QMutexLocker pendlock(&m_pendingRecLock);
1702
1703 for (auto it = m_pendingRecordings.begin(); it != m_pendingRecordings.end();)
1704 {
1705 if (MythDate::current() > (*it).m_recordingStart.addSecs(30))
1706 {
1707 LOG(VB_RECORD, LOG_INFO, LOC + "Deleting stale pending recording " +
1708 QString("[%1] '%2'")
1709 .arg((*it).m_info->GetInputID())
1710 .arg((*it).m_info->GetTitle()));
1711
1712 delete (*it).m_info;
1713 it = m_pendingRecordings.erase(it);
1714 }
1715 else
1716 {
1717 it++;
1718 }
1719 }
1720
1721 if (m_pendingRecordings.empty())
1722 return;
1723
1724 // Make sure EIT scan is stopped so it does't interfere
1726 {
1727 LOG(VB_CHANNEL, LOG_INFO,
1728 LOC + "Stopping active EIT scan for pending recording.");
1730 }
1731
1732 // If we have a pending recording and AskAllowRecording
1733 // or DoNotAskAllowRecording is set and the frontend is
1734 // ready send an ASK_RECORDING query to frontend.
1735
1736 bool has_rec = false;
1737 auto it = m_pendingRecordings.begin();
1738 if ((1 == m_pendingRecordings.size()) &&
1739 (*it).m_ask &&
1740 ((*it).m_info->GetInputID() == m_inputId) &&
1742 {
1744 has_rec = m_pseudoLiveTVRecording &&
1746 (*it).m_recordingStart);
1747 }
1748
1749 for (it = m_pendingRecordings.begin(); it != m_pendingRecordings.end(); ++it)
1750 {
1751 if (!(*it).m_ask && !(*it).m_doNotAsk)
1752 continue;
1753
1754 auto timeuntil = ((*it).m_doNotAsk) ?
1755 -1s: MythDate::secsInFuture((*it).m_recordingStart);
1756
1757 if (has_rec)
1758 (*it).m_canceled = true;
1759
1760 QString query = QString("ASK_RECORDING %1 %2 %3 %4")
1761 .arg(m_inputId)
1762 .arg(timeuntil.count())
1763 .arg(has_rec ? 1 : 0)
1764 .arg((*it).m_hasLaterShowing ? 1 : 0);
1765
1766 LOG(VB_GENERAL, LOG_INFO, LOC + query);
1767
1768 QStringList msg;
1769 (*it).m_info->ToStringList(msg);
1770 MythEvent me(query, msg);
1772
1773 (*it).m_ask = (*it).m_doNotAsk = false;
1774 }
1775}
1776
1778 uint &parentid,
1779 GeneralDBOptions &gen_opts,
1780 DVBDBOptions &dvb_opts,
1781 FireWireDBOptions &firewire_opts)
1782{
1783 int testnum = 0;
1784 QString test;
1785
1787 query.prepare(
1788 "SELECT videodevice, vbidevice, audiodevice, "
1789 " audioratelimit, cardtype, "
1790 " skipbtaudio, signal_timeout, channel_timeout, "
1791 " dvb_wait_for_seqstart, "
1792 ""
1793 " dvb_on_demand, dvb_tuning_delay, dvb_eitscan,"
1794 ""
1795 " firewire_speed, firewire_model, firewire_connection, "
1796 " parentid "
1797 ""
1798 "FROM capturecard "
1799 "WHERE cardid = :INPUTID");
1800 query.bindValue(":INPUTID", inputid);
1801
1802 if (!query.exec() || !query.isActive())
1803 {
1804 MythDB::DBError("getdevices", query);
1805 return false;
1806 }
1807
1808 if (!query.next())
1809 return false;
1810
1811 // General options
1812 test = query.value(0).toString();
1813 if (!test.isEmpty())
1814 gen_opts.m_videoDev = test;
1815
1816 test = query.value(1).toString();
1817 if (!test.isEmpty())
1818 gen_opts.m_vbiDev = test;
1819
1820 test = query.value(2).toString();
1821 if (!test.isEmpty())
1822 gen_opts.m_audioDev = test;
1823
1824 gen_opts.m_audioSampleRate = std::max(testnum, query.value(3).toInt());
1825
1826 test = query.value(4).toString();
1827 if (!test.isEmpty())
1828 gen_opts.m_inputType = test;
1829
1830 gen_opts.m_skipBtAudio = query.value(5).toBool();
1831
1832 gen_opts.m_signalTimeout = (uint) std::max(query.value(6).toInt(), 0);
1833 gen_opts.m_channelTimeout = (uint) std::max(query.value(7).toInt(), 0);
1834
1835 // We should have at least 1000 ms to acquire tables...
1836 int table_timeout = ((int)gen_opts.m_channelTimeout -
1837 (int)gen_opts.m_signalTimeout);
1838 if (table_timeout < 1000)
1839 gen_opts.m_channelTimeout = gen_opts.m_signalTimeout + 1000;
1840
1841 gen_opts.m_waitForSeqstart = query.value(8).toBool();
1842
1843 // DVB options
1844 uint dvboff = 9;
1845 dvb_opts.m_dvbOnDemand = query.value(dvboff + 0).toBool();
1846 dvb_opts.m_dvbTuningDelay = std::chrono::milliseconds(query.value(dvboff + 1).toUInt());
1847 dvb_opts.m_dvbEitScan = query.value(dvboff + 2).toBool();
1848
1849 // Firewire options
1850 uint fireoff = dvboff + 3;
1851 firewire_opts.m_speed = query.value(fireoff + 0).toUInt();
1852
1853 test = query.value(fireoff + 1).toString();
1854 if (!test.isEmpty())
1855 firewire_opts.m_model = test;
1856
1857 firewire_opts.m_connection = query.value(fireoff + 2).toUInt();
1858
1859 parentid = query.value(15).toUInt();
1860
1861 return true;
1862}
1863
1864static void GetPidsToCache(DTVSignalMonitor *dtvMon, pid_cache_t &pid_cache)
1865{
1866 if (!dtvMon->GetATSCStreamData())
1867 return;
1868
1869 const MasterGuideTable *mgt = dtvMon->GetATSCStreamData()->GetCachedMGT();
1870 if (!mgt)
1871 return;
1872
1873 for (uint i = 0; i < mgt->TableCount(); ++i)
1874 {
1875 pid_cache_item_t item(mgt->TablePID(i), mgt->TableType(i));
1876 pid_cache.push_back(item);
1877 }
1878 dtvMon->GetATSCStreamData()->ReturnCachedTable(mgt);
1879}
1880
1881static bool ApplyCachedPids(DTVSignalMonitor *dtvMon, const DTVChannel* channel)
1882{
1883 pid_cache_t pid_cache;
1884 channel->GetCachedPids(pid_cache);
1885 bool vctpid_cached = false;
1886 for (const auto& pid : pid_cache)
1887 {
1888 if ((pid.GetTableID() == TableID::TVCT) ||
1889 (pid.GetTableID() == TableID::CVCT))
1890 {
1891 vctpid_cached = true;
1892 if (dtvMon->GetATSCStreamData())
1893 dtvMon->GetATSCStreamData()->AddListeningPID(pid.GetPID());
1894 }
1895 }
1896 return vctpid_cached;
1897}
1898
1915{
1916 LOG(VB_RECORD, LOG_INFO, LOC + "Setting up table monitoring.");
1917
1919 DTVChannel *dtvchan = GetDTVChannel();
1920 if (!sm || !dtvchan)
1921 {
1922 LOG(VB_GENERAL, LOG_ERR, LOC + "Setting up table monitoring.");
1923 return false;
1924 }
1925
1926 MPEGStreamData *sd = nullptr;
1927 if (GetDTVRecorder())
1928 {
1929 sd = GetDTVRecorder()->GetStreamData();
1930 sd->SetCaching(true);
1931 }
1932
1933 QString recording_type = "all";
1937 const StandardSetting *setting = profile.byName("recordingtype");
1938 if (setting)
1939 recording_type = setting->getValue();
1940
1941 const QString tuningmode = dtvchan->GetTuningMode();
1942
1943 // Check if this is an ATSC Channel
1944 int major = dtvchan->GetMajorChannel();
1945 int minor = dtvchan->GetMinorChannel();
1946 if ((minor > 0) && (tuningmode == "atsc"))
1947 {
1948 QString msg = QString("ATSC channel: %1_%2").arg(major).arg(minor);
1949 LOG(VB_RECORD, LOG_INFO, LOC + msg);
1950
1951 auto *asd = dynamic_cast<ATSCStreamData*>(sd);
1952 if (!asd)
1953 {
1954 sd = asd = new ATSCStreamData(major, minor, m_inputId);
1955 sd->SetCaching(true);
1956 if (GetDTVRecorder())
1958 }
1959
1960 asd->Reset();
1961 sm->SetStreamData(sd);
1962 sm->SetChannel(major, minor);
1963 sd->SetRecordingType(recording_type);
1964
1965 // Try to get pid of VCT from cache and
1966 // require MGT if we don't have VCT pid.
1967 if (!ApplyCachedPids(sm, dtvchan))
1969
1970 LOG(VB_RECORD, LOG_INFO, LOC +
1971 "Successfully set up ATSC table monitoring.");
1972 return true;
1973 }
1974
1975 // Check if this is an DVB channel
1976 int progNum = dtvchan->GetProgramNumber();
1977 if ((progNum >= 0) && (tuningmode == "dvb") && CardUtil::IsChannelReusable(m_genOpt.m_inputType))
1978 {
1979 int netid = dtvchan->GetOriginalNetworkID();
1980 int tsid = dtvchan->GetTransportID();
1981
1982 auto *dsd = dynamic_cast<DVBStreamData*>(sd);
1983 if (!dsd)
1984 {
1985 sd = dsd = new DVBStreamData(netid, tsid, progNum, m_inputId);
1986 sd->SetCaching(true);
1987 if (GetDTVRecorder())
1989 }
1990
1991 LOG(VB_RECORD, LOG_INFO, LOC +
1992 QString("DVB service_id %1 on net_id %2 tsid %3")
1993 .arg(progNum).arg(netid).arg(tsid));
1994
1996
1997 dsd->Reset();
1998 sm->SetStreamData(sd);
1999 sm->SetDVBService(netid, tsid, progNum);
2000 sd->SetRecordingType(recording_type);
2001
2005 sm->SetRotorTarget(1.0F);
2006
2007 if (EITscan)
2008 {
2010 sm->IgnoreEncrypted(true);
2011 }
2012
2013 LOG(VB_RECORD, LOG_INFO, LOC +
2014 "Successfully set up DVB table monitoring.");
2015 return true;
2016 }
2017
2018 // Check if this is an MPEG channel
2019 if (progNum >= 0)
2020 {
2021 if (!sd)
2022 {
2023 sd = new MPEGStreamData(progNum, m_inputId, true);
2024 sd->SetCaching(true);
2025 if (GetDTVRecorder())
2027 }
2028
2029 QString msg = QString("MPEG program number: %1").arg(progNum);
2030 LOG(VB_RECORD, LOG_INFO, LOC + msg);
2031
2033
2034 sd->Reset();
2035 sm->SetStreamData(sd);
2036 sm->SetProgramNumber(progNum);
2037 sd->SetRecordingType(recording_type);
2038
2042 sm->SetRotorTarget(1.0F);
2043
2044 if (EITscan)
2045 {
2047 sm->IgnoreEncrypted(true);
2048 }
2049
2050 LOG(VB_RECORD, LOG_INFO, LOC +
2051 "Successfully set up MPEG table monitoring.");
2052 return true;
2053 }
2054
2055 // If this is not an ATSC, DVB or MPEG channel then check to make sure
2056 // that we have permanent pidcache entries.
2057 bool ok = false;
2058 if (GetDTVChannel())
2059 {
2060 pid_cache_t pid_cache;
2061 GetDTVChannel()->GetCachedPids(pid_cache);
2062 for (auto item = pid_cache.cbegin(); !ok && item != pid_cache.cend(); ++item)
2063 ok |= item->IsPermanent();
2064 }
2065
2066 if (!ok)
2067 {
2068 QString msg = "No valid DTV info, ATSC maj(%1) min(%2), MPEG pn(%3)";
2069 LOG(VB_GENERAL, LOG_ERR, LOC + msg.arg(major).arg(minor).arg(progNum));
2070 }
2071 else
2072 {
2073 LOG(VB_RECORD, LOG_INFO, LOC +
2074 "Successfully set up raw pid monitoring.");
2075 }
2076
2077 return ok;
2078}
2079
2094bool TVRec::SetupSignalMonitor(bool tablemon, bool EITscan, bool notify)
2095{
2096 LOG(VB_RECORD, LOG_INFO, LOC + QString("SetupSignalMonitor(%1, %2)")
2097 .arg(tablemon).arg(notify));
2098
2099 // if it already exists, there no need to initialize it
2100 if (m_signalMonitor)
2101 return true;
2102
2103 // if there is no channel object we can't monitor it
2104 if (!m_channel)
2105 return false;
2106
2107 // nothing to monitor here either (DummyChannel)
2108 if (m_genOpt.m_inputType == "IMPORT" || m_genOpt.m_inputType == "DEMO")
2109 return true;
2110
2111 // make sure statics are initialized
2113
2116 m_channel, false);
2117
2118 if (m_signalMonitor)
2119 {
2120 LOG(VB_RECORD, LOG_INFO, LOC + "Signal monitor successfully created");
2121 // If this is a monitor for Digital TV, initialize table monitors
2122 if (GetDTVSignalMonitor() && tablemon &&
2123 !SetupDTVSignalMonitor(EITscan))
2124 {
2125 LOG(VB_GENERAL, LOG_ERR, LOC +
2126 "Failed to setup digital signal monitoring");
2127
2128 return false;
2129 }
2130
2136
2137 // Start the monitoring thread
2139 }
2140
2141 return true;
2142}
2143
2149{
2150 if (!m_signalMonitor)
2151 return;
2152
2153 LOG(VB_RECORD, LOG_INFO, LOC + "TeardownSignalMonitor() -- begin");
2154
2155 // If this is a DTV signal monitor, save any pids we know about.
2157 DTVChannel *dtvChan = GetDTVChannel();
2158 if (dtvMon && dtvChan)
2159 {
2160 pid_cache_t pid_cache;
2161 GetPidsToCache(dtvMon, pid_cache);
2162 if (!pid_cache.empty())
2163 dtvChan->SaveCachedPids(pid_cache);
2164 }
2165
2166 if (m_signalMonitor)
2167 {
2168 delete m_signalMonitor;
2169 m_signalMonitor = nullptr;
2170 }
2171
2172 LOG(VB_RECORD, LOG_INFO, LOC + "TeardownSignalMonitor() -- end");
2173}
2174
2186std::chrono::milliseconds TVRec::SetSignalMonitoringRate(std::chrono::milliseconds rate, int notifyFrontend)
2187{
2188 QString msg = "SetSignalMonitoringRate(%1, %2)";
2189 LOG(VB_RECORD, LOG_INFO, LOC +
2190 msg.arg(rate.count()).arg(notifyFrontend) + "-- start");
2191
2192 QMutexLocker lock(&m_stateChangeLock);
2193
2195 {
2196 LOG(VB_GENERAL, LOG_ERR, LOC +
2197 "Signal Monitoring is notsupported by your hardware.");
2198 return 0ms;
2199 }
2200
2202 {
2203 LOG(VB_GENERAL, LOG_ERR, LOC +
2204 "Signal can only be monitored in LiveTV Mode.");
2205 return 0ms;
2206 }
2207
2208 ClearFlags(kFlagRingBufferReady, __FILE__, __LINE__);
2209
2210 TuningRequest req = (rate > 0ms) ?
2213
2215
2216 // Wait for RingBuffer reset
2219 LOG(VB_RECORD, LOG_INFO, LOC +
2220 msg.arg(rate.count()).arg(notifyFrontend) + " -- end");
2221 return 1ms;
2222}
2223
2225{
2226 return dynamic_cast<DTVSignalMonitor*>(m_signalMonitor);
2227}
2228
2240bool TVRec::ShouldSwitchToAnotherInput(const QString& chanid) const
2241{
2242 QString msg("");
2244
2245 if (!query.isConnected())
2246 return false;
2247
2248 query.prepare("SELECT channel.channum, channel.callsign "
2249 "FROM channel "
2250 "WHERE channel.chanid = :CHANID");
2251 query.bindValue(":CHANID", chanid);
2252 if (!query.exec() || !query.next())
2253 {
2254 MythDB::DBError("ShouldSwitchToAnotherInput", query);
2255 return false;
2256 }
2257
2258 QString channelname = query.value(0).toString();
2259 QString callsign = query.value(1).toString();
2260
2261 query.prepare(
2262 "SELECT channel.channum "
2263 "FROM channel, capturecard "
2264 "WHERE deleted IS NULL AND "
2265 " ( channel.chanid = :CHANID OR "
2266 " ( channel.channum = :CHANNUM AND "
2267 " channel.callsign = :CALLSIGN ) "
2268 " ) AND "
2269 " channel.sourceid = capturecard.sourceid AND "
2270 " capturecard.cardid = :INPUTID");
2271 query.bindValue(":CHANID", chanid);
2272 query.bindValue(":CHANNUM", channelname);
2273 query.bindValue(":CALLSIGN", callsign);
2274 query.bindValue(":INPUTID", m_inputId);
2275
2276 if (!query.exec() || !query.isActive())
2277 {
2278 MythDB::DBError("ShouldSwitchToAnotherInput", query);
2279 }
2280 else if (query.size() > 0)
2281 {
2282 msg = "Found channel (%1) on current input[%2].";
2283 LOG(VB_RECORD, LOG_INFO, LOC + msg.arg(channelname).arg(m_inputId));
2284 return false;
2285 }
2286
2287 // We didn't find it on the current input, so now we check other inputs.
2288 query.prepare(
2289 "SELECT channel.channum, capturecard.cardid "
2290 "FROM channel, capturecard "
2291 "WHERE deleted IS NULL AND "
2292 " ( channel.chanid = :CHANID OR "
2293 " ( channel.channum = :CHANNUM AND "
2294 " channel.callsign = :CALLSIGN ) "
2295 " ) AND "
2296 " channel.sourceid = capturecard.sourceid AND "
2297 " capturecard.cardid != :INPUTID");
2298 query.bindValue(":CHANID", chanid);
2299 query.bindValue(":CHANNUM", channelname);
2300 query.bindValue(":CALLSIGN", callsign);
2301 query.bindValue(":INPUTID", m_inputId);
2302
2303 if (!query.exec() || !query.isActive())
2304 {
2305 MythDB::DBError("ShouldSwitchToAnotherInput", query);
2306 }
2307 else if (query.next())
2308 {
2309 msg = QString("Found channel (%1) on different input(%2).")
2310 .arg(query.value(0).toString(), query.value(1).toString());
2311 LOG(VB_RECORD, LOG_INFO, LOC + msg);
2312 return true;
2313 }
2314
2315 msg = QString("Did not find channel(%1) on any input.").arg(channelname);
2316 LOG(VB_RECORD, LOG_ERR, LOC + msg);
2317 return false;
2318}
2319
2330bool TVRec::CheckChannel(const QString& name) const
2331{
2332 if (!m_channel)
2333 return false;
2334
2335 return m_channel->CheckChannel(name);
2336}
2337
2341static QString add_spacer(const QString &channel, const QString &spacer)
2342{
2343 QString chan = channel;
2344 if ((chan.length() >= 2) && !spacer.isEmpty())
2345 return chan.left(chan.length()-1) + spacer + chan.right(1);
2346 return chan;
2347}
2348
2377 uint &complete_valid_channel_on_rec,
2378 bool &is_extra_char_useful,
2379 QString &needed_spacer) const
2380{
2381#if DEBUG_CHANNEL_PREFIX
2382 LOG(VB_GENERAL, LOG_DEBUG, QString("CheckChannelPrefix(%1)").arg(prefix));
2383#endif
2384
2385 static const std::array<const QString,5> s_spacers = { "", "_", "-", "#", "." };
2386
2388 QString basequery = QString(
2389 "SELECT channel.chanid, channel.channum, capturecard.cardid "
2390 "FROM channel, capturecard "
2391 "WHERE deleted IS NULL AND "
2392 " channel.channum LIKE '%1%' AND "
2393 " channel.sourceid = capturecard.sourceid");
2394
2395 const std::array<const QString,2> inputquery
2396 {
2397 QString(" AND capturecard.cardid = '%1'").arg(m_inputId),
2398 QString(" AND capturecard.cardid != '%1'").arg(m_inputId),
2399 };
2400
2401 std::vector<unsigned int> fchanid;
2402 std::vector<QString> fchannum;
2403 std::vector<unsigned int> finputid;
2404 std::vector<QString> fspacer;
2405
2406 for (const auto & str : inputquery)
2407 {
2408 for (const auto & spacer : s_spacers)
2409 {
2410 QString qprefix = add_spacer(
2411 prefix, (spacer == "_") ? "\\_" : spacer);
2412 query.prepare(basequery.arg(qprefix) + str);
2413
2414 if (!query.exec() || !query.isActive())
2415 {
2416 MythDB::DBError("checkchannel -- locate channum", query);
2417 }
2418 else if (query.size())
2419 {
2420 while (query.next())
2421 {
2422 fchanid.push_back(query.value(0).toUInt());
2423 fchannum.push_back(query.value(1).toString());
2424 finputid.push_back(query.value(2).toUInt());
2425 fspacer.emplace_back(spacer);
2426#if DEBUG_CHANNEL_PREFIX
2427 LOG(VB_GENERAL, LOG_DEBUG,
2428 QString("(%1,%2) Adding %3 rec %4")
2429 .arg(i).arg(j).arg(query.value(1).toString(),6)
2430 .arg(query.value(2).toUInt()));
2431#endif
2432 }
2433 }
2434
2435 if (prefix.length() < 2)
2436 break;
2437 }
2438 }
2439
2440 // Now process the lists for the info we need...
2441 is_extra_char_useful = false;
2442 complete_valid_channel_on_rec = 0;
2443 needed_spacer.clear();
2444
2445 if (fchanid.empty())
2446 return false;
2447
2448 if (fchanid.size() == 1) // Unique channel...
2449 {
2450 needed_spacer = fspacer[0];
2451 bool nc = (fchannum[0] != add_spacer(prefix, fspacer[0]));
2452
2453 complete_valid_channel_on_rec = nc ? 0 : finputid[0];
2454 is_extra_char_useful = nc;
2455 return true;
2456 }
2457
2458 // If we get this far there is more than one channel
2459 // sharing the prefix we were given.
2460
2461 // Is an extra characher useful for disambiguation?
2462 is_extra_char_useful = false;
2463 for (uint i = 0; (i < fchannum.size()) && !is_extra_char_useful; i++)
2464 {
2465 is_extra_char_useful = (fchannum[i] != add_spacer(prefix, fspacer[i]));
2466#if DEBUG_CHANNEL_PREFIX
2467 LOG(VB_GENERAL, LOG_DEBUG, QString("is_extra_char_useful(%1!=%2): %3")
2468 .arg(fchannum[i]).arg(add_spacer(prefix, fspacer[i]))
2469 .arg(is_extra_char_useful));
2470#endif
2471 }
2472
2473 // Are any of the channels complete w/o spacer?
2474 // If so set complete_valid_channel_on_rec,
2475 // with a preference for our inputid.
2476 for (size_t i = 0; i < fchannum.size(); i++)
2477 {
2478 if (fchannum[i] == prefix)
2479 {
2480 complete_valid_channel_on_rec = finputid[i];
2481 if (finputid[i] == m_inputId)
2482 break;
2483 }
2484 }
2485
2486 if (complete_valid_channel_on_rec != 0)
2487 return true;
2488
2489 // Add a spacer, if one is needed to select a valid channel.
2490 bool spacer_needed = true;
2491 for (uint i = 0; (i < fspacer.size() && spacer_needed); i++)
2492 spacer_needed = !fspacer[i].isEmpty();
2493 if (spacer_needed)
2494 needed_spacer = fspacer[0];
2495
2496 // If it isn't useful to wait for more characters,
2497 // then try to commit to any true match immediately.
2498 for (size_t i = 0; i < (is_extra_char_useful ? 0 : fchanid.size()); i++)
2499 {
2500 if (fchannum[i] == add_spacer(prefix, fspacer[i]))
2501 {
2502 needed_spacer = fspacer[i];
2503 complete_valid_channel_on_rec = finputid[i];
2504 return true;
2505 }
2506 }
2507
2508 return true;
2509}
2510
2512 const QString &channum)
2513{
2514 if (!m_recorder)
2515 return false;
2516
2517 QString videoFilters = ChannelUtil::GetVideoFilters(sourceid, channum);
2518 if (!videoFilters.isEmpty())
2519 {
2520 m_recorder->SetVideoFilters(videoFilters);
2521 return true;
2522 }
2523
2524 return false;
2525}
2526
2532{
2533 return ((m_recorder && m_recorder->IsRecording()) ||
2535}
2536
2542bool TVRec::IsBusy(InputInfo *busy_input, std::chrono::seconds time_buffer) const
2543{
2544 InputInfo dummy;
2545 if (!busy_input)
2546 busy_input = &dummy;
2547
2548 busy_input->Clear();
2549
2550 if (!m_channel)
2551 return false;
2552
2553 if (!m_channel->GetInputID())
2554 return false;
2555
2556 uint chanid = 0;
2557
2558 if (GetState() != kState_None)
2559 {
2560 busy_input->m_inputId = m_channel->GetInputID();
2561 chanid = m_channel->GetChanID();
2562 }
2563
2564 PendingInfo pendinfo;
2565 bool has_pending = false;
2566 {
2567 m_pendingRecLock.lock();
2568 PendingMap::const_iterator it = m_pendingRecordings.find(m_inputId);
2569 has_pending = (it != m_pendingRecordings.end());
2570 if (has_pending)
2571 pendinfo = *it;
2572 m_pendingRecLock.unlock();
2573 }
2574
2575 if (!busy_input->m_inputId && has_pending)
2576 {
2577 auto timeLeft = MythDate::secsInFuture(pendinfo.m_recordingStart);
2578
2579 if (timeLeft <= time_buffer)
2580 {
2581 QString channum;
2582 QString input;
2583 if (pendinfo.m_info->QueryTuningInfo(channum, input))
2584 {
2585 busy_input->m_inputId = m_channel->GetInputID();
2586 chanid = pendinfo.m_info->GetChanID();
2587 }
2588 }
2589 }
2590
2591 if (busy_input->m_inputId)
2592 {
2593 CardUtil::GetInputInfo(*busy_input);
2594 busy_input->m_chanId = chanid;
2595 busy_input->m_mplexId = ChannelUtil::GetMplexID(busy_input->m_chanId);
2596 busy_input->m_mplexId =
2597 (32767 == busy_input->m_mplexId) ? 0 : busy_input->m_mplexId;
2598 }
2599
2600 return busy_input->m_inputId != 0U;
2601}
2602
2603
2611{
2612 QMutexLocker lock(&m_stateChangeLock);
2613
2614 if (m_recorder)
2615 return m_recorder->GetFrameRate();
2616 return -1.0F;
2617}
2618
2626{
2627 QMutexLocker lock(&m_stateChangeLock);
2628
2629 if (m_recorder)
2630 return m_recorder->GetFramesWritten();
2631 return -1;
2632}
2633
2641{
2642 QMutexLocker lock(&m_stateChangeLock);
2643
2644 if (m_buffer)
2645 return m_buffer->GetWritePosition();
2646 return -1;
2647}
2648
2656int64_t TVRec::GetKeyframePosition(uint64_t desired) const
2657{
2658 QMutexLocker lock(&m_stateChangeLock);
2659
2660 if (m_recorder)
2661 return m_recorder->GetKeyframePosition(desired);
2662 return -1;
2663}
2664
2674 int64_t start, int64_t end, frm_pos_map_t &map) const
2675{
2676 QMutexLocker lock(&m_stateChangeLock);
2677
2678 if (m_recorder)
2679 return m_recorder->GetKeyframePositions(start, end, map);
2680
2681 return false;
2682}
2683
2685 int64_t start, int64_t end, frm_pos_map_t &map) const
2686{
2687 QMutexLocker lock(&m_stateChangeLock);
2688
2689 if (m_recorder)
2690 return m_recorder->GetKeyframeDurations(start, end, map);
2691
2692 return false;
2693}
2694
2700long long TVRec::GetMaxBitrate(void) const
2701{
2702 long long bitrate = 0;
2703 if (m_genOpt.m_inputType == "MPEG")
2704 { // NOLINT(bugprone-branch-clone)
2705 bitrate = 10080000LL; // use DVD max bit rate
2706 }
2707 else if (m_genOpt.m_inputType == "HDPVR")
2708 {
2709 bitrate = 20200000LL; // Peak bit rate for HD-PVR
2710 }
2712 {
2713 bitrate = 22200000LL; // 1080i
2714 }
2715 else // frame grabber
2716 {
2717 bitrate = 10080000LL; // use DVD max bit rate, probably too big
2718 }
2719
2720 return bitrate;
2721}
2722
2728void TVRec::SpawnLiveTV(LiveTVChain *newchain, bool pip, QString startchan)
2729{
2730 QMutexLocker lock(&m_stateChangeLock);
2731
2732 m_tvChain = newchain;
2733 m_tvChain->IncrRef(); // mark it for TVRec use
2735
2736 QString hostprefix = MythCoreContext::GenMythURL(
2739
2740 m_tvChain->SetHostPrefix(hostprefix);
2742
2743 m_isPip = pip;
2744 m_liveTVStartChannel = std::move(startchan);
2745
2746 // Change to WatchingLiveTV
2748 // Wait for state change to take effect
2750
2751 // Make sure StartRecording can't steal our tuner
2752 SetFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2753}
2754
2759{
2760 if (m_tvChain)
2761 return m_tvChain->GetID();
2762 return "";
2763}
2764
2774{
2775 QMutexLocker lock(&m_stateChangeLock);
2776
2778 return; // already stopped
2779
2780 if (!m_curRecording)
2781 return;
2782
2783 const QString recgrp = m_curRecording->QueryRecordingGroup();
2785
2786 if (recgrp != "LiveTV" && !m_pseudoLiveTVRecording)
2787 {
2788 // User wants this recording to continue
2790 }
2791 else if (recgrp == "LiveTV" && m_pseudoLiveTVRecording)
2792 {
2793 // User wants to abandon scheduled recording
2794 SetPseudoLiveTVRecording(nullptr);
2795 }
2796}
2797
2808{
2809 if (!m_channel)
2810 return;
2811
2812 // Notify scheduler of the recording.
2813 // + set up recording so it can be resumed
2814 rec->SetInputID(m_inputId);
2816
2817 if (rec->GetRecordingRuleType() == kNotRecording)
2818 {
2821 }
2822
2823 // + remove any end offset which would mismatch the live session
2824 rec->GetRecordingRule()->m_endOffset = 0;
2825
2826 // + save RecStatus::Inactive recstatus to so that a reschedule call
2827 // doesn't start recording this on another input before we
2828 // send the SCHEDULER_ADD_RECORDING message to the scheduler.
2830 rec->AddHistory(false);
2831
2832 // + save RecordingRule so that we get a recordid
2833 // (don't allow RescheduleMatch(), avoiding unneeded reschedule)
2834 rec->GetRecordingRule()->Save(false);
2835
2836 // + save recordid to recorded entry
2837 rec->ApplyRecordRecID();
2838
2839 // + set proper recstatus (saved later)
2841
2842 // + pass proginfo to scheduler and reschedule
2843 QStringList prog;
2844 rec->ToStringList(prog);
2845 MythEvent me("SCHEDULER_ADD_RECORDING", prog);
2847
2848 // Allow scheduler to end this recording before post-roll,
2849 // if it has another recording for this recorder.
2850 ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2851}
2852
2854 RecordingProfile *recpro, int line)
2855{
2856 if (kAutoRunProfile == t)
2857 {
2859 if (!recpro)
2860 {
2861 LoadProfile(nullptr, rec, profile);
2862 recpro = &profile;
2863 }
2865 init_jobs(rec, *recpro, m_runJobOnHostOnly,
2867 }
2868 else
2869 {
2871 }
2872 LOG(VB_JOBQUEUE, LOG_INFO,
2873 QString("InitAutoRunJobs for %1, line %2 -> 0x%3")
2874 .arg(rec->MakeUniqueKey()).arg(line)
2875 .arg(m_autoRunJobs[rec->MakeUniqueKey()],0,16));
2876}
2877
2889void TVRec::SetLiveRecording([[maybe_unused]] int recording)
2890{
2891 LOG(VB_GENERAL, LOG_INFO, LOC +
2892 QString("SetLiveRecording(%1)").arg(recording));
2893 QMutexLocker locker(&m_stateChangeLock);
2894
2896 bool was_rec = m_pseudoLiveTVRecording;
2898 if (was_rec && !m_pseudoLiveTVRecording)
2899 {
2900 LOG(VB_GENERAL, LOG_INFO, LOC + "SetLiveRecording() -- cancel");
2901 // cancel -- 'recording' should be 0 or -1
2902 SetFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2904 InitAutoRunJobs(m_curRecording, kAutoRunNone, nullptr, __LINE__);
2905 }
2906 else if (!was_rec && m_pseudoLiveTVRecording)
2907 {
2908 LOG(VB_GENERAL, LOG_INFO, LOC + "SetLiveRecording() -- record");
2909 // record -- 'recording' should be 1 or -1
2910
2911 // If the last recording was flagged for keeping
2912 // in the frontend, then add the recording rule
2913 // so that transcode, commfrag, etc can be run.
2918 InitAutoRunJobs(m_curRecording, kAutoRunProfile, nullptr, __LINE__);
2919 }
2920
2921 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
2922 .arg(m_curRecording->GetInputID())
2923 .arg(m_curRecording->GetChanID())
2925 .arg(recstat)
2927
2929}
2930
2936{
2937 QMutexLocker lock(&m_stateChangeLock);
2938 LOG(VB_RECORD, LOG_INFO, LOC +
2939 QString("StopLiveTV(void) curRec: 0x%1 pseudoRec: 0x%2")
2940 .arg((uint64_t)m_curRecording,0,16)
2941 .arg((uint64_t)m_pseudoLiveTVRecording,0,16));
2942
2944 return;
2945
2946 bool hadPseudoLiveTVRec = m_pseudoLiveTVRecording;
2948
2949 if (!hadPseudoLiveTVRec && m_pseudoLiveTVRecording)
2951
2952 // Figure out next state and if needed recording end time.
2953 TVState next_state = kState_None;
2955 {
2957 next_state = kState_RecordingOnly;
2958 }
2959
2960 // Change to the appropriate state
2961 ChangeState(next_state);
2962
2963 // Wait for state change to take effect...
2965
2966 // We are done with the tvchain...
2967 if (m_tvChain)
2968 {
2969 m_tvChain->DecrRef();
2970 }
2971 m_tvChain = nullptr;
2972}
2973
2983{
2984 QMutexLocker lock(&m_stateChangeLock);
2985
2986 if (!m_recorder)
2987 {
2988 LOG(VB_GENERAL, LOG_ERR, LOC +
2989 "PauseRecorder() called with no recorder");
2990 return;
2991 }
2992
2993 m_recorder->Pause();
2994}
2995
3002{
3003 if (m_pauseNotify)
3004 WakeEventLoop();
3005}
3006
3010void TVRec::ToggleChannelFavorite(const QString& changroupname)
3011{
3012 QMutexLocker lock(&m_stateChangeLock);
3013
3014 if (!m_channel)
3015 return;
3016
3017 // Get current channel id...
3018 uint sourceid = m_channel->GetSourceID();
3019 QString channum = m_channel->GetChannelName();
3020 uint chanid = ChannelUtil::GetChanID(sourceid, channum);
3021
3022 if (!chanid)
3023 {
3024 LOG(VB_GENERAL, LOG_ERR, LOC +
3025 QString("Channel: \'%1\' was not found in the database.\n"
3026 "\t\tMost likely, the 'starting channel' for this "
3027 "Input Connection is invalid.\n"
3028 "\t\tCould not toggle favorite.").arg(channum));
3029 return;
3030 }
3031
3032 int changrpid = ChannelGroup::GetChannelGroupId(changroupname);
3033 if (changrpid <1)
3034 {
3035 LOG(VB_RECORD, LOG_ERR, LOC +
3036 QString("ToggleChannelFavorite: Invalid channel group name %1,")
3037 .arg(changroupname));
3038 }
3039 else
3040 {
3041 bool result = ChannelGroup::ToggleChannel(chanid, changrpid, true);
3042
3043 if (!result)
3044 {
3045 LOG(VB_RECORD, LOG_ERR, LOC + "Unable to toggle channel favorite.");
3046 }
3047 else
3048 {
3049 LOG(VB_RECORD, LOG_INFO, LOC +
3050 QString("Toggled channel favorite.channum %1, chan group %2")
3051 .arg(channum, changroupname));
3052 }
3053 }
3054}
3055
3062{
3063 QMutexLocker lock(&m_stateChangeLock);
3064 if (!m_channel)
3065 return -1;
3066
3067 int ret = m_channel->GetPictureAttribute(attr);
3068
3069 return (ret < 0) ? -1 : ret / 655;
3070}
3071
3080 PictureAttribute attr,
3081 bool direction)
3082{
3083 QMutexLocker lock(&m_stateChangeLock);
3084 if (!m_channel)
3085 return -1;
3086
3087 int ret = m_channel->ChangePictureAttribute(type, attr, direction);
3088
3089 return (ret < 0) ? -1 : ret / 655;
3090}
3091
3095QString TVRec::GetInput(void) const
3096{
3097 if (m_channel)
3098 return m_channel->GetInputName();
3099 return {};
3100}
3101
3106{
3107 if (m_channel)
3108 return m_channel->GetSourceID();
3109 return 0;
3110}
3111
3120QString TVRec::SetInput(QString input)
3121{
3122 QMutexLocker lock(&m_stateChangeLock);
3123 QString origIn = input;
3124 LOG(VB_RECORD, LOG_INFO, LOC + "SetInput(" + input + ") -- begin");
3125
3126 if (!m_channel)
3127 {
3128 LOG(VB_RECORD, LOG_INFO, LOC + "SetInput() -- end no channel class");
3129 return {};
3130 }
3131
3132 LOG(VB_RECORD, LOG_INFO, LOC + "SetInput(" + origIn + ":" + input +
3133 ") -- end nothing to do");
3134 return input;
3135}
3136
3146void TVRec::SetChannel(const QString& name, uint requestType)
3147{
3148 QMutexLocker locker1(&m_setChannelLock);
3149 QMutexLocker locker2(&m_stateChangeLock);
3150
3151 LOG(VB_CHANNEL, LOG_INFO, LOC +
3152 QString("SetChannel(%1) -- begin").arg(name));
3153
3154 // Detect tuning request type if needed
3155 if (requestType & kFlagDetect)
3156 {
3158 requestType = m_lastTuningRequest.m_flags & (kFlagRec | kFlagNoRec);
3159 }
3160
3161 // Clear the RingBuffer reset flag, in case we wait for a reset below
3162 ClearFlags(kFlagRingBufferReady, __FILE__, __LINE__);
3163
3164 // Clear out any EITScan channel change requests
3165 auto it = m_tuningRequests.begin();
3166 while (it != m_tuningRequests.end())
3167 {
3168 if ((*it).m_flags & kFlagEITScan)
3169 it = m_tuningRequests.erase(it);
3170 else
3171 ++it;
3172 }
3173
3174 // Actually add the tuning request to the queue, and
3175 // then wait for it to start tuning
3176 m_tuningRequests.enqueue(TuningRequest(requestType, name));
3178
3179 // If we are using a recorder, wait for a RingBuffer reset
3180 if (requestType & kFlagRec)
3181 {
3184 }
3185 LOG(VB_CHANNEL, LOG_INFO, LOC + QString("SetChannel(%1) -- end").arg(name));
3186}
3187
3195bool TVRec::QueueEITChannelChange(const QString &name)
3196{
3197 LOG(VB_CHANNEL, LOG_INFO, LOC +
3198 QString("QueueEITChannelChange(%1)").arg(name));
3199
3200 bool ok = false;
3201 if (m_setChannelLock.tryLock())
3202 {
3203 if (m_stateChangeLock.tryLock())
3204 {
3205 if (m_tuningRequests.empty())
3206 {
3208 ok = true;
3209 }
3210 m_stateChangeLock.unlock();
3211 }
3212 m_setChannelLock.unlock();
3213 }
3214
3215 LOG(VB_CHANNEL, LOG_DEBUG, LOC +
3216 QString("QueueEITChannelChange(%1) %2")
3217 .arg(name, ok ? "done" : "failed"));
3218
3219 return ok;
3220}
3221
3223 QString &title, QString &subtitle,
3224 QString &desc, QString &category,
3225 QString &starttime, QString &endtime,
3226 QString &callsign, QString &iconpath,
3227 QString &channum, uint &sourceChanid,
3228 QString &seriesid, QString &programid)
3229{
3230 QString compare = "<=";
3231 QString sortorder = "desc";
3232 uint chanid = 0;
3233
3234 if (sourceChanid)
3235 {
3236 chanid = sourceChanid;
3237
3238 if (BROWSE_UP == direction) {
3240 } else if (BROWSE_DOWN == direction) {
3242 } else if (BROWSE_FAVORITE == direction) {
3243 chanid = m_channel->GetNextChannel(
3245 } else if (BROWSE_LEFT == direction) {
3246 compare = "<";
3247 } else if (BROWSE_RIGHT == direction) {
3248 compare = ">";
3249 sortorder = "asc";
3250 }
3251 }
3252
3253 if (!chanid)
3254 {
3255 if (BROWSE_SAME == direction) {
3257 } else if (BROWSE_UP == direction) {
3258 chanid = m_channel->GetNextChannel(channum, CHANNEL_DIRECTION_UP);
3259 } else if (BROWSE_DOWN == direction) {
3261 } else if (BROWSE_FAVORITE == direction) {
3262 chanid = m_channel->GetNextChannel(channum,
3264 } else if (BROWSE_LEFT == direction) {
3266 compare = "<";
3267 } else if (BROWSE_RIGHT == direction) {
3269 compare = ">";
3270 sortorder = "asc";
3271 }
3272 }
3273
3274 QString querystr = QString(
3275 "SELECT title, subtitle, description, category, "
3276 " starttime, endtime, callsign, icon, "
3277 " channum, seriesid, programid "
3278 "FROM program, channel "
3279 "WHERE program.chanid = channel.chanid AND "
3280 " channel.chanid = :CHANID AND "
3281 " starttime %1 :STARTTIME "
3282 "ORDER BY starttime %2 "
3283 "LIMIT 1").arg(compare, sortorder);
3284
3286 query.prepare(querystr);
3287 query.bindValue(":CHANID", chanid);
3288 query.bindValue(":STARTTIME", starttime);
3289
3290 // Clear everything now in case either query fails.
3291 title = subtitle = desc = category = "";
3292 starttime = endtime = callsign = iconpath = "";
3293 channum = seriesid = programid = "";
3294 sourceChanid = 0;
3295
3296 // Try to get the program info
3297 if (!query.exec() && !query.isActive())
3298 {
3299 MythDB::DBError("GetNextProgram -- get program info", query);
3300 }
3301 else if (query.next())
3302 {
3303 title = query.value(0).toString();
3304 subtitle = query.value(1).toString();
3305 desc = query.value(2).toString();
3306 category = query.value(3).toString();
3307 starttime = query.value(4).toString();
3308 endtime = query.value(5).toString();
3309 callsign = query.value(6).toString();
3310 iconpath = query.value(7).toString();
3311 channum = query.value(8).toString();
3312 seriesid = query.value(9).toString();
3313 programid = query.value(10).toString();
3314 sourceChanid = chanid;
3315 return;
3316 }
3317
3318 // Couldn't get program info, so get the channel info instead
3319 query.prepare(
3320 "SELECT channum, callsign, icon "
3321 "FROM channel "
3322 "WHERE chanid = :CHANID");
3323 query.bindValue(":CHANID", chanid);
3324
3325 if (!query.exec() || !query.isActive())
3326 {
3327 MythDB::DBError("GetNextProgram -- get channel info", query);
3328 }
3329 else if (query.next())
3330 {
3331 sourceChanid = chanid;
3332 channum = query.value(0).toString();
3333 callsign = query.value(1).toString();
3334 iconpath = query.value(2).toString();
3335 }
3336}
3337
3338bool TVRec::GetChannelInfo(uint &chanid, uint &sourceid,
3339 QString &callsign, QString &channum,
3340 QString &channame, QString &xmltvid) const
3341{
3342 callsign.clear();
3343 channum.clear();
3344 channame.clear();
3345 xmltvid.clear();
3346
3347 if ((!chanid || !sourceid) && !m_channel)
3348 return false;
3349
3350 if (!chanid)
3351 chanid = (uint) std::max(m_channel->GetChanID(), 0);
3352
3353 if (!sourceid)
3354 sourceid = m_channel->GetSourceID();
3355
3357 query.prepare(
3358 "SELECT callsign, channum, name, xmltvid "
3359 "FROM channel "
3360 "WHERE chanid = :CHANID");
3361 query.bindValue(":CHANID", chanid);
3362 if (!query.exec() || !query.isActive())
3363 {
3364 MythDB::DBError("GetChannelInfo", query);
3365 return false;
3366 }
3367
3368 if (!query.next())
3369 return false;
3370
3371 callsign = query.value(0).toString();
3372 channum = query.value(1).toString();
3373 channame = query.value(2).toString();
3374 xmltvid = query.value(3).toString();
3375
3376 return true;
3377}
3378
3379bool TVRec::SetChannelInfo(uint chanid, uint sourceid,
3380 const QString& oldchannum,
3381 const QString& callsign, const QString& channum,
3382 const QString& channame, const QString& xmltvid)
3383{
3384 if (!chanid || !sourceid || channum.isEmpty())
3385 return false;
3386
3388 query.prepare(
3389 "UPDATE channel "
3390 "SET callsign = :CALLSIGN, "
3391 " channum = :CHANNUM, "
3392 " name = :CHANNAME, "
3393 " xmltvid = :XMLTVID "
3394 "WHERE chanid = :CHANID AND "
3395 " sourceid = :SOURCEID");
3396 query.bindValue(":CALLSIGN", callsign);
3397 query.bindValue(":CHANNUM", channum);
3398 query.bindValue(":CHANNAME", channame);
3399 query.bindValue(":XMLTVID", xmltvid);
3400 query.bindValue(":CHANID", chanid);
3401 query.bindValue(":SOURCEID", sourceid);
3402
3403 if (!query.exec())
3404 {
3405 MythDB::DBError("SetChannelInfo", query);
3406 return false;
3407 }
3408
3409 if (m_channel)
3410 m_channel->Renumber(sourceid, oldchannum, channum);
3411
3412 return true;
3413}
3414
3415void TVRec::SetChannelTimeout(std::chrono::milliseconds timeout)
3416{
3418 LOG(VB_CHANNEL, LOG_INFO, LOC +
3419 QString("Override tune timeout: %1ms")
3421}
3422
3427{
3428 QMutexLocker lock(&m_stateChangeLock);
3429
3430 MythMediaBuffer *oldbuffer = m_buffer;
3431 m_buffer = Buffer;
3432
3433 if (oldbuffer && (oldbuffer != Buffer))
3434 {
3436 ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3437 delete oldbuffer;
3438 }
3439
3440 m_switchingBuffer = false;
3441}
3442
3444{
3445 LOG(VB_GENERAL, LOG_INFO, LOC + "RingBufferChanged()");
3446
3447 QMutexLocker lock(&m_stateChangeLock);
3448
3449 if (pginfo)
3450 {
3451 if (m_curRecording)
3452 {
3455 delete m_curRecording;
3456 }
3458 m_curRecording = new RecordingInfo(*pginfo);
3461 }
3462
3464}
3465
3467 QString &input) const
3468{
3469 QString channum;
3470
3471 if (request.m_program)
3472 {
3473 request.m_program->QueryTuningInfo(channum, input);
3474 return channum;
3475 }
3476
3477 channum = request.m_channel;
3478 input = request.m_input;
3479
3480 // If this is Live TV startup, we need a channel...
3481 if (channum.isEmpty() && (request.m_flags & kFlagLiveTV))
3482 {
3483 if (!m_liveTVStartChannel.isEmpty())
3484 {
3485 channum = m_liveTVStartChannel;
3486 }
3487 else
3488 {
3491 }
3492 }
3493 if (request.m_flags & kFlagLiveTV)
3494 m_channel->Init(channum, false);
3495
3496 if (m_channel && !channum.isEmpty() && (channum.indexOf("NextChannel") >= 0))
3497 {
3498 // FIXME This is just horrible
3499#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
3500 int dir = channum.rightRef(channum.length() - 12).toInt();
3501#else
3502 int dir = QStringView(channum).right(channum.length() - 12).toInt();
3503#endif
3504 uint chanid = m_channel->GetNextChannel(0, static_cast<ChannelChangeDirection>(dir));
3505 channum = ChannelUtil::GetChanNum(chanid);
3506 }
3507
3508 return channum;
3509}
3510
3512{
3513 if ((request.m_flags & kFlagAntennaAdjust) || request.m_input.isEmpty() ||
3515 {
3516 return false;
3517 }
3518
3519 uint sourceid = m_channel->GetSourceID();
3520 QString oldchannum = m_channel->GetChannelName();
3521 QString newchannum = request.m_channel;
3522
3523 if (ChannelUtil::IsOnSameMultiplex(sourceid, newchannum, oldchannum))
3524 {
3526 auto *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
3527
3528 if (atsc)
3529 {
3530 uint major = 0;
3531 uint minor = 0;
3532 ChannelUtil::GetATSCChannel(sourceid, newchannum, major, minor);
3533
3534 if (minor && atsc->HasChannel(major, minor))
3535 {
3536 request.m_majorChan = major;
3537 request.m_minorChan = minor;
3538 return true;
3539 }
3540 }
3541
3542 if (mpeg)
3543 {
3544 uint progNum = ChannelUtil::GetProgramNumber(sourceid, newchannum);
3545 if (mpeg->HasProgram(progNum))
3546 {
3547 request.m_progNum = progNum;
3548 return true;
3549 }
3550 }
3551 }
3552
3553 return false;
3554}
3555
3564{
3565 if (!m_tuningRequests.empty())
3566 {
3567 TuningRequest request = m_tuningRequests.front();
3568 LOG(VB_RECORD, LOG_INFO, LOC +
3569 "HandleTuning Request: " + request.toString());
3570
3571 QString input;
3572 request.m_channel = TuningGetChanNum(request, input);
3573 request.m_input = input;
3574
3575 if (TuningOnSameMultiplex(request))
3576 LOG(VB_CHANNEL, LOG_INFO, LOC + "On same multiplex");
3577
3578 TuningShutdowns(request);
3579
3580 // The dequeue isn't safe to do until now because we
3581 // release the stateChangeLock to teardown a recorder
3583
3584 // Now we start new stuff
3585 if (request.m_flags & (kFlagRecording|kFlagLiveTV|
3587 {
3588 if (!m_recorder)
3589 {
3590 LOG(VB_RECORD, LOG_INFO, LOC +
3591 "No recorder yet, calling TuningFrequency");
3592 TuningFrequency(request);
3593 }
3594 else
3595 {
3596 LOG(VB_RECORD, LOG_INFO, LOC + "Waiting for recorder pause..");
3597 SetFlags(kFlagWaitingForRecPause, __FILE__, __LINE__);
3598 }
3599 }
3600 m_lastTuningRequest = request;
3601 }
3602
3604 {
3605 if (!m_recorder || !m_recorder->IsPaused())
3606 return;
3607
3608 ClearFlags(kFlagWaitingForRecPause, __FILE__, __LINE__);
3609 LOG(VB_RECORD, LOG_INFO, LOC +
3610 "Recorder paused, calling TuningFrequency");
3612 }
3613
3614 MPEGStreamData *streamData = nullptr;
3616 {
3617 streamData = TuningSignalCheck();
3618 if (streamData == nullptr)
3619 return;
3620 }
3621
3623 {
3624 if (m_recorder)
3626 else
3627 TuningNewRecorder(streamData);
3628
3629 // If we got this far it is safe to set a new starting channel...
3630 if (m_channel)
3632 }
3633}
3634
3640{
3641 LOG(VB_RECORD, LOG_INFO, LOC + QString("TuningShutdowns(%1)")
3642 .arg(request.toString()));
3643
3644 if (m_scanner && !(request.m_flags & kFlagEITScan) &&
3646 {
3648 ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
3650 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
3651 }
3652
3653 if (m_scanner && !request.IsOnSameMultiplex())
3655
3657 {
3658 MPEGStreamData *sd = nullptr;
3659 if (GetDTVSignalMonitor())
3662 ClearFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3663
3664 // Delete StreamData if it is not in use by the recorder.
3665 MPEGStreamData *rec_sd = nullptr;
3666 if (GetDTVRecorder())
3667 rec_sd = GetDTVRecorder()->GetStreamData();
3668 if (sd && (sd != rec_sd))
3669 delete sd;
3670 }
3672 ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3673
3674 // At this point any waits are canceled.
3675
3676 if (request.m_flags & kFlagNoRec)
3677 {
3679 {
3681 ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3683 }
3684
3686 (m_curRecording &&
3689 {
3690 m_stateChangeLock.unlock();
3691 TeardownRecorder(request.m_flags);
3692 m_stateChangeLock.lock();
3693 }
3694 // At this point the recorders are shut down
3695
3696 CloseChannel();
3697 // At this point the channel is shut down
3698 }
3699
3700 if (m_buffer && (request.m_flags & kFlagKillRingBuffer))
3701 {
3702 LOG(VB_RECORD, LOG_INFO, LOC + "Tearing down RingBuffer");
3703 SetRingBuffer(nullptr);
3704 // At this point the ringbuffer is shut down
3705 }
3706
3707 // Clear pending actions from last request
3708 ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
3709}
3710
3729{
3730 LOG(VB_GENERAL, LOG_INFO, LOC + QString("TuningFrequency(%1)")
3731 .arg(request.toString()));
3732
3733 DTVChannel *dtvchan = GetDTVChannel();
3734 if (dtvchan)
3735 {
3736 MPEGStreamData *mpeg = nullptr;
3737
3738 if (GetDTVRecorder())
3740
3741 // Tune with SI table standard (dvb, atsc, mpeg) from database, see issue #452
3743
3744 const QString tuningmode = (HasFlags(kFlagEITScannerRunning)) ?
3745 dtvchan->GetSIStandard() :
3746 dtvchan->GetSuggestedTuningMode(
3748
3749 dtvchan->SetTuningMode(tuningmode);
3750
3751 if (request.m_minorChan && (tuningmode == "atsc"))
3752 {
3753 auto *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
3754 if (atsc)
3755 atsc->SetDesiredChannel(request.m_majorChan, request.m_minorChan);
3756 }
3757 else if (request.m_progNum >= 0)
3758 {
3759 if (mpeg)
3760 mpeg->SetDesiredProgram(request.m_progNum);
3761 }
3762 }
3763
3764 if (request.IsOnSameMultiplex())
3765 {
3766 // Update the channel number for SwitchLiveTVRingBuffer (called from
3767 // TuningRestartRecorder). This ensures that the livetvchain will be
3768 // updated with the new channel number
3769 if (m_channel)
3770 {
3772 m_channel->GetChannelName(), request.m_channel );
3773 }
3774
3775 QStringList slist;
3776 slist<<"message"<<QObject::tr("On known multiplex...");
3777 MythEvent me(QString("SIGNAL %1").arg(m_inputId), slist);
3779
3780 SetFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3781 return;
3782 }
3783
3784 QString channum = request.m_channel;
3785
3786 bool ok1 = true;
3787 if (!dtvchan && m_channel)
3788 {
3789 m_channel->Open();
3790 if (!channum.isEmpty())
3791 ok1 = m_channel->SetChannelByString(channum);
3792 else
3793 ok1 = false;
3794 }
3795
3796 if (!ok1)
3797 {
3798 if (!(request.m_flags & kFlagLiveTV) || !(request.m_flags & kFlagEITScan))
3799 {
3800 if (m_curRecording)
3802
3803 LOG(VB_GENERAL, LOG_ERR, LOC +
3804 QString("Failed to set channel to %1. Reverting to kState_None")
3805 .arg(channum));
3808 else
3810 return;
3811 }
3812
3813 LOG(VB_GENERAL, LOG_ERR, LOC +
3814 QString("Failed to set channel to %1.").arg(channum));
3815 }
3816
3817 bool mpts_only = GetDTVChannel() &&
3818 GetDTVChannel()->GetFormat().compare("MPTS") == 0;
3819 if (mpts_only)
3820 {
3821 // Not using a signal monitor, so just set the status to recording
3823 if (m_curRecording)
3824 {
3826 }
3827 }
3828
3829
3830 bool livetv = (request.m_flags & kFlagLiveTV) != 0U;
3831 bool antadj = (request.m_flags & kFlagAntennaAdjust) != 0U;
3832 bool use_sm = !mpts_only && SignalMonitor::IsRequired(m_genOpt.m_inputType);
3833 bool use_dr = use_sm && (livetv || antadj);
3834 bool has_dummy = false;
3835
3836 if (use_dr)
3837 {
3838 // We need there to be a ringbuffer for these modes
3839 bool ok2 = false;
3841 m_pseudoLiveTVRecording = nullptr;
3842
3843 m_tvChain->SetInputType("DUMMY");
3844
3845 if (!m_buffer)
3846 ok2 = CreateLiveTVRingBuffer(channum);
3847 else
3848 ok2 = SwitchLiveTVRingBuffer(channum, true, false);
3850
3852
3853 if (!ok2)
3854 {
3855 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RingBuffer 1");
3856 return;
3857 }
3858
3859 has_dummy = true;
3860 }
3861
3862 // Start signal monitoring for devices capable of monitoring
3863 if (use_sm)
3864 {
3865 LOG(VB_RECORD, LOG_INFO, LOC + "Starting Signal Monitor");
3866 bool error = false;
3867 if (!SetupSignalMonitor(
3868 !antadj, (request.m_flags & kFlagEITScan) != 0U, livetv || antadj))
3869 {
3870 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to setup signal monitor");
3871 if (m_signalMonitor)
3872 {
3873 delete m_signalMonitor;
3874 m_signalMonitor = nullptr;
3875 }
3876
3877 // pretend the signal monitor is running to prevent segfault
3878 SetFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3879 ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3880 error = true;
3881 }
3882
3883 if (m_signalMonitor)
3884 {
3885 if (request.m_flags & kFlagEITScan)
3886 {
3888 SetVideoStreamsRequired(0);
3890 }
3891
3892 SetFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3893 ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3894 if (!antadj)
3895 {
3896 QDateTime expire = MythDate::current();
3897
3898 SetFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3899 if (m_curRecording)
3900 {
3902 // If startRecordingDeadline is passed, this
3903 // recording is marked as failed, so the scheduler
3904 // can try another showing.
3906 expire.addMSecs(m_genOpt.m_channelTimeout);
3908 expire.addMSecs(m_genOpt.m_channelTimeout * 2 / 3);
3909 // Keep trying to record this showing (even if it
3910 // has been marked as failed) until the scheduled
3911 // end time.
3913 m_curRecording->GetRecordingEndTime().addSecs(-10);
3914
3915 LOG(VB_CHANNEL, LOG_DEBUG, LOC +
3916 QString("Pre-fail start deadline: %1 "
3917 "Start recording deadline: %2 "
3918 "Good signal deadline: %3")
3919 .arg(m_preFailDeadline.toLocalTime()
3920 .toString("hh:mm:ss.zzz"),
3921 m_startRecordingDeadline.toLocalTime()
3922 .toString("hh:mm:ss.zzz"),
3923 m_signalMonitorDeadline.toLocalTime()
3924 .toString("hh:mm:ss.zzz")));
3925 }
3926 else
3927 {
3929 expire.addMSecs(m_genOpt.m_channelTimeout);
3930 }
3932
3933 //System Event TUNING_TIMEOUT deadline
3935 m_signalEventCmdSent = false;
3936 }
3937 }
3938
3939 if (has_dummy && m_buffer)
3940 {
3941 // Make sure recorder doesn't point to bogus ringbuffer before
3942 // it is potentially restarted without a new ringbuffer, if
3943 // the next channel won't tune and the user exits LiveTV.
3944 if (m_recorder)
3945 m_recorder->SetRingBuffer(nullptr);
3946
3947 SetFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3948 LOG(VB_RECORD, LOG_INFO, "DummyDTVRecorder -- started");
3949 SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
3950 }
3951
3952 // if we had problems starting the signal monitor,
3953 // we don't want to start the recorder...
3954 if (error)
3955 return;
3956 }
3957
3958 // Request a recorder, if the command is a recording command
3959 ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3960 if (request.m_flags & kFlagRec && !antadj)
3961 SetFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3962}
3963
3972{
3973 RecStatus::Type newRecStatus = RecStatus::Unknown;
3974 bool keep_trying = false;
3975 QDateTime current_time = MythDate::current();
3976
3977 if ((m_signalMonitor->IsErrored() || current_time > m_signalEventCmdTimeout) &&
3979 {
3980 gCoreContext->SendSystemEvent(QString("TUNING_SIGNAL_TIMEOUT CARDID %1")
3981 .arg(m_inputId));
3982 m_signalEventCmdSent = true;
3983 }
3984
3986 {
3987 LOG(VB_RECORD, LOG_INFO, LOC + "TuningSignalCheck: Good signal");
3988 if (m_curRecording && (current_time > m_startRecordingDeadline))
3989 {
3990 newRecStatus = RecStatus::Failing;
3991 m_curRecording->SaveVideoProperties(VID_DAMAGED, VID_DAMAGED);
3992
3993 QString desc = tr("Good signal seen after %1 ms")
3995 m_startRecordingDeadline.msecsTo(current_time));
3996 QString title = m_curRecording->GetTitle();
3997 if (!m_curRecording->GetSubtitle().isEmpty())
3998 title += " - " + m_curRecording->GetSubtitle();
3999
4001 "Recording", title,
4002 tr("See 'Tuning timeout' in mythtv-setup "
4003 "for this input."));
4005
4006 LOG(VB_GENERAL, LOG_WARNING, LOC +
4007 QString("It took longer than %1 ms to get a signal lock. "
4008 "Keeping status of '%2'")
4010 .arg(RecStatus::toString(newRecStatus, kSingleRecord)));
4011 LOG(VB_GENERAL, LOG_WARNING, LOC +
4012 "See 'Tuning timeout' in mythtv-setup for this input");
4013 }
4014 else
4015 {
4016 newRecStatus = RecStatus::Recording;
4017 }
4018 }
4019 else if (m_signalMonitor->IsErrored() || current_time > m_signalMonitorDeadline)
4020 {
4021 LOG(VB_GENERAL, LOG_ERR, LOC + "TuningSignalCheck: SignalMonitor " +
4022 (m_signalMonitor->IsErrored() ? "failed" : "timed out"));
4023
4024 ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4025 newRecStatus = RecStatus::Failed;
4026
4028 {
4030 }
4031 }
4032 else if (m_curRecording && !m_reachedPreFail && current_time > m_preFailDeadline)
4033 {
4034 LOG(VB_GENERAL, LOG_ERR, LOC +
4035 "TuningSignalCheck: Hit pre-fail timeout");
4036 SendMythSystemRecEvent("REC_PREFAIL", m_curRecording);
4037 m_reachedPreFail = true;
4038 return nullptr;
4039 }
4041 current_time > m_startRecordingDeadline)
4042 {
4043 newRecStatus = RecStatus::Failing;
4045 keep_trying = true;
4046
4047 SendMythSystemRecEvent("REC_FAILING", m_curRecording);
4048
4049 QString desc = tr("Taking more than %1 ms to get a lock.")
4051 QString title = m_curRecording->GetTitle();
4052 if (!m_curRecording->GetSubtitle().isEmpty())
4053 title += " - " + m_curRecording->GetSubtitle();
4054
4056 "Recording", title,
4057 tr("See 'Tuning timeout' in mythtv-setup "
4058 "for this input."));
4059 mn.SetDuration(30s);
4061
4062 LOG(VB_GENERAL, LOG_WARNING, LOC +
4063 QString("TuningSignalCheck: taking more than %1 ms to get a lock. "
4064 "marking this recording as '%2'.")
4066 .arg(RecStatus::toString(newRecStatus, kSingleRecord)));
4067 LOG(VB_GENERAL, LOG_WARNING, LOC +
4068 "See 'Tuning timeout' in mythtv-setup for this input");
4069 }
4070 else
4071 {
4072 if (m_signalMonitorCheckCnt) // Don't flood log file
4073 {
4075 }
4076 else
4077 {
4078 LOG(VB_RECORD, LOG_INFO, LOC +
4079 QString("TuningSignalCheck: Still waiting. Will timeout @ %1")
4080 .arg(m_signalMonitorDeadline.toLocalTime()
4081 .toString("hh:mm:ss.zzz")));
4083 }
4084 return nullptr;
4085 }
4086
4087 SetRecordingStatus(newRecStatus, __LINE__);
4088
4089 if (m_curRecording)
4090 {
4091 m_curRecording->SetRecordingStatus(newRecStatus);
4092 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
4093 .arg(m_curRecording->GetInputID())
4094 .arg(m_curRecording->GetChanID())
4096 .arg(newRecStatus)
4099 }
4100
4101 if (keep_trying)
4102 return nullptr;
4103
4104 // grab useful data from DTV signal monitor before we kill it...
4105 MPEGStreamData *streamData = nullptr;
4106 if (GetDTVSignalMonitor())
4107 streamData = GetDTVSignalMonitor()->GetStreamData();
4108
4110 {
4111 // shut down signal monitoring
4113 ClearFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
4114 }
4115 ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
4116
4117 if (streamData)
4118 {
4119 auto *dsd = dynamic_cast<DVBStreamData*>(streamData);
4120 if (dsd)
4122 if (m_scanner)
4123 {
4124 if (get_use_eit(GetInputId()))
4125 {
4127 }
4128 else
4129 {
4130 LOG(VB_EIT, LOG_INFO, LOC +
4131 QString("EIT scanning disabled for video source %1")
4132 .arg(GetSourceID())); }
4133 }
4134 }
4135
4136 return streamData;
4137}
4138
4140 bool on_host, bool transcode_bfr_comm, bool on_line_comm)
4141{
4142 if (!rec)
4143 return 0; // no jobs for Live TV recordings..
4144
4145 int jobs = 0; // start with no jobs
4146
4147 // grab standard jobs flags from program info
4149
4150 // disable commercial flagging on PBS, BBC, etc.
4151 if (rec->IsCommercialFree())
4153
4154 // disable transcoding if the profile does not allow auto transcoding
4155 const StandardSetting *autoTrans = profile.byName("autotranscode");
4156 if ((!autoTrans) || (autoTrans->getValue().toInt() == 0))
4158
4159 bool ml = JobQueue::JobIsInMask(JOB_METADATA, jobs);
4160 if (ml)
4161 {
4162 // When allowed, metadata lookup should occur at the
4163 // start of a recording to make the additional info
4164 // available immediately (and for use in future jobs).
4165 QString host = on_host ? gCoreContext->GetHostName() : "";
4167 rec->GetChanID(),
4168 rec->GetRecordingStartTime(), "", "",
4169 host, JOB_LIVE_REC);
4170
4171 // don't do regular metadata lookup, we won't need it.
4173 }
4174
4175 // is commercial flagging enabled, and is on-line comm flagging enabled?
4176 bool rt = JobQueue::JobIsInMask(JOB_COMMFLAG, jobs) && on_line_comm;
4177 // also, we either need transcoding to be disabled or
4178 // we need to be allowed to commercial flag before transcoding?
4180 !transcode_bfr_comm;
4181 if (rt)
4182 {
4183 // queue up real-time (i.e. on-line) commercial flagging.
4184 QString host = on_host ? gCoreContext->GetHostName() : "";
4186 rec->GetChanID(),
4187 rec->GetRecordingStartTime(), "", "",
4188 host, JOB_LIVE_REC);
4189
4190 // don't do regular comm flagging, we won't need it.
4192 }
4193
4194 return jobs;
4195}
4196
4197QString TVRec::LoadProfile(void *tvchain, RecordingInfo *rec,
4199{
4200 // Determine the correct recording profile.
4201 // In LiveTV mode use "Live TV" profile, otherwise use the
4202 // recording's specified profile. If the desired profile can't
4203 // be found, fall back to the "Default" profile for input type.
4204 QString profileName = "Live TV";
4205 if (!tvchain && rec)
4206 profileName = rec->GetRecordingRule()->m_recProfile;
4207
4208 QString profileRequested = profileName;
4209
4210 if (profile.loadByType(profileName, m_genOpt.m_inputType,
4212 {
4213 LOG(VB_RECORD, LOG_INFO, LOC +
4214 QString("Using profile '%1' to record")
4215 .arg(profileName));
4216 }
4217 else
4218 {
4219 profileName = "Default";
4220 if (profile.loadByType(profileName, m_genOpt.m_inputType, m_genOpt.m_videoDev))
4221 {
4222 LOG(VB_RECORD, LOG_INFO, LOC +
4223 QString("Profile '%1' not found, using "
4224 "fallback profile '%2' to record")
4225 .arg(profileRequested, profileName));
4226 }
4227 else
4228 {
4229 LOG(VB_RECORD, LOG_ERR, LOC +
4230 QString("Profile '%1' not found, and unable "
4231 "to load fallback profile '%2'. Results "
4232 "may be unpredicable")
4233 .arg(profileRequested, profileName));
4234 }
4235 }
4236
4237 return profileName;
4238}
4239
4244 RecordingInfo **rec,
4246 bool had_dummyrec)
4247{
4248 if (m_tvChain)
4249 {
4250 bool ok = false;
4251 if (!m_buffer)
4252 {
4254 SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4255 }
4256 else
4257 {
4259 true, !had_dummyrec && m_recorder);
4260 }
4261 if (!ok)
4262 {
4263 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RingBuffer 2");
4264 return false;
4265 }
4266 *rec = m_curRecording; // new'd in Create/SwitchLiveTVRingBuffer()
4267 }
4268
4270 {
4271 bool write = m_genOpt.m_inputType != "IMPORT";
4272 QString pathname = (*rec)->GetPathname();
4273 LOG(VB_GENERAL, LOG_INFO, LOC + QString("rec->GetPathname(): '%1'")
4274 .arg(pathname));
4276 if (!m_buffer->IsOpen() && write)
4277 {
4278 LOG(VB_GENERAL, LOG_ERR, LOC +
4279 QString("RingBuffer '%1' not open...")
4280 .arg(pathname));
4281 SetRingBuffer(nullptr);
4282 ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
4283 return false;
4284 }
4285 }
4286
4287 if (!m_buffer)
4288 {
4289 LOG(VB_GENERAL, LOG_ERR, LOC +
4290 QString("Failed to start recorder! ringBuffer is NULL\n"
4291 "\t\t\t\t Tuning request was %1\n")
4293
4294 if (HasFlags(kFlagLiveTV))
4295 {
4296 QString message = QString("QUIT_LIVETV %1").arg(m_inputId);
4297 MythEvent me(message);
4299 }
4300 return false;
4301 }
4302
4303 if (m_channel && m_genOpt.m_inputType == "MJPEG")
4304 m_channel->Close(); // Needed because of NVR::MJPEGInit()
4305
4306 LOG(VB_GENERAL, LOG_INFO, LOC + "TuningNewRecorder - CreateRecorder()");
4308
4309 if (m_recorder)
4310 {
4313 if (m_recorder->IsErrored())
4314 {
4315 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialize recorder!");
4316 delete m_recorder;
4317 m_recorder = nullptr;
4318 }
4319 }
4320
4321 if (!m_recorder)
4322 {
4323 LOG(VB_GENERAL, LOG_ERR, LOC +
4324 QString("Failed to start recorder!\n"
4325 "\t\t\t\t Tuning request was %1\n")
4327
4328 if (HasFlags(kFlagLiveTV))
4329 {
4330 QString message = QString("QUIT_LIVETV %1").arg(m_inputId);
4331 MythEvent me(message);
4333 }
4335 if (m_tvChain)
4336 (*rec) = nullptr;
4337 return false;
4338 }
4339
4340 if (*rec)
4341 m_recorder->SetRecording(*rec);
4342
4343 if (GetDTVRecorder() && streamData)
4344 {
4345 const StandardSetting *setting = profile.byName("recordingtype");
4346 if (setting)
4347 streamData->SetRecordingType(setting->getValue());
4348 GetDTVRecorder()->SetStreamData(streamData);
4349 }
4350
4351 if (m_channel && m_genOpt.m_inputType == "MJPEG")
4352 m_channel->Open(); // Needed because of NVR::MJPEGInit()
4353
4354 // Setup for framebuffer capture devices..
4355 if (m_channel)
4356 {
4359 }
4360
4361 if (GetV4LChannel())
4362 {
4364 CloseChannel();
4365 }
4366
4367 m_recorderThread = new MThread("RecThread", m_recorder);
4369
4370 // Wait for recorder to start.
4371 m_stateChangeLock.unlock();
4372 while (!m_recorder->IsRecording() && !m_recorder->IsErrored())
4373 std::this_thread::sleep_for(5us);
4374 m_stateChangeLock.lock();
4375
4376 if (GetV4LChannel())
4378
4379 SetFlags(kFlagRecorderRunning | kFlagRingBufferReady, __FILE__, __LINE__);
4380
4381 ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4382
4383 //workaround for failed import recordings, no signal monitor means we never
4384 //go to recording state and the status here seems to override the status
4385 //set in the importrecorder and backend via setrecordingstatus
4386 if (m_genOpt.m_inputType == "IMPORT")
4387 {
4389 if (m_curRecording)
4391 }
4392
4393 return true;
4394}
4395
4400{
4401 LOG(VB_RECORD, LOG_INFO, LOC + "Starting Recorder");
4402
4403 bool had_dummyrec = false;
4405 {
4407 ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
4409 had_dummyrec = true;
4410 }
4411
4413
4416
4417 if (TuningNewRecorderReal(streamData, &rec, profile, had_dummyrec))
4418 return;
4419
4420 SetRecordingStatus(RecStatus::Failed, __LINE__, true);
4422
4423 if (rec)
4424 {
4425 // Make sure the scheduler knows...
4427 LOG(VB_RECORD, LOG_INFO, LOC +
4428 QString("TuningNewRecorder -- UPDATE_RECORDING_STATUS: %1")
4430 MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
4431 .arg(rec->GetInputID())
4432 .arg(rec->GetChanID())
4434 .arg(RecStatus::Failed)
4437 }
4438
4439 if (m_tvChain)
4440 delete rec;
4441}
4442
4447{
4448 LOG(VB_RECORD, LOG_INFO, LOC + "Restarting Recorder");
4449
4450 bool had_dummyrec = false;
4451
4452 if (m_curRecording)
4453 {
4456 }
4457
4459 {
4460 ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
4461 had_dummyrec = true;
4462 }
4463
4464 SwitchLiveTVRingBuffer(m_channel->GetChannelName(), true, !had_dummyrec);
4465
4466 if (had_dummyrec)
4467 {
4469 ProgramInfo *progInfo = m_tvChain->GetProgramAt(-1);
4470 RecordingInfo recinfo(*progInfo);
4471 delete progInfo;
4472 recinfo.SetInputID(m_inputId);
4473 m_recorder->SetRecording(&recinfo);
4474 }
4475 m_recorder->Reset();
4476
4477 // Set file descriptor of channel from recorder for V4L
4478 if (GetV4LChannel())
4480
4481 // Some recorders unpause on Reset, others do not...
4483
4485 {
4487 QString msg1 = QString("Recording: %1 %2 %3 %4")
4488 .arg(rcinfo1->GetTitle(), QString::number(rcinfo1->GetChanID()),
4491 ProgramInfo *rcinfo2 = m_tvChain->GetProgramAt(-1);
4492 QString msg2 = QString("Recording: %1 %2 %3 %4")
4493 .arg(rcinfo2->GetTitle(), QString::number(rcinfo2->GetChanID()),
4496 delete rcinfo2;
4497 LOG(VB_RECORD, LOG_INFO, LOC + "Pseudo LiveTV recording starting." +
4498 "\n\t\t\t" + msg1 + "\n\t\t\t" + msg2);
4499
4502
4504
4505 InitAutoRunJobs(m_curRecording, kAutoRunProfile, nullptr, __LINE__);
4506 }
4507
4508 ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4509}
4510
4511void TVRec::SetFlags(uint f, const QString & file, int line)
4512{
4513 QMutexLocker lock(&m_stateChangeLock);
4514 m_stateFlags |= f;
4515 LOG(VB_RECORD, LOG_INFO, LOC + QString("SetFlags(%1) -> %2 @ %3:%4")
4516 .arg(FlagToString(f), FlagToString(m_stateFlags), file, QString::number(line)));
4517 WakeEventLoop();
4518}
4519
4520void TVRec::ClearFlags(uint f, const QString & file, int line)
4521{
4522 QMutexLocker lock(&m_stateChangeLock);
4523 m_stateFlags &= ~f;
4524 LOG(VB_RECORD, LOG_INFO, LOC + QString("ClearFlags(%1) -> %2 @ %3:%4")
4525 .arg(FlagToString(f), FlagToString(m_stateFlags), file, QString::number(line)));
4526 WakeEventLoop();
4527}
4528
4530{
4531 QString msg("");
4532
4533 // General flags
4534 if (kFlagFrontendReady & f)
4535 msg += "FrontendReady,";
4536 if (kFlagRunMainLoop & f)
4537 msg += "RunMainLoop,";
4538 if (kFlagExitPlayer & f)
4539 msg += "ExitPlayer,";
4540 if (kFlagFinishRecording & f)
4541 msg += "FinishRecording,";
4542 if (kFlagErrored & f)
4543 msg += "Errored,";
4545 msg += "CancelNextRecording,";
4546
4547 // Tuning flags
4548 if ((kFlagRec & f) == kFlagRec)
4549 {
4550 msg += "REC,";
4551 }
4552 else
4553 {
4554 if (kFlagLiveTV & f)
4555 msg += "LiveTV,";
4556 if (kFlagRecording & f)
4557 msg += "Recording,";
4558 }
4559 if ((kFlagNoRec & f) == kFlagNoRec)
4560 {
4561 msg += "NOREC,";
4562 }
4563 else
4564 {
4565 if (kFlagEITScan & f)
4566 msg += "EITScan,";
4567 if (kFlagCloseRec & f)
4568 msg += "CloseRec,";
4569 if (kFlagKillRec & f)
4570 msg += "KillRec,";
4571 if (kFlagAntennaAdjust & f)
4572 msg += "AntennaAdjust,";
4573 }
4575 {
4576 msg += "PENDINGACTIONS,";
4577 }
4578 else
4579 {
4581 msg += "WaitingForRecPause,";
4582 if (kFlagWaitingForSignal & f)
4583 msg += "WaitingForSignal,";
4585 msg += "NeedToStartRecorder,";
4586 if (kFlagKillRingBuffer & f)
4587 msg += "KillRingBuffer,";
4588 }
4589 if ((kFlagAnyRunning & f) == kFlagAnyRunning)
4590 {
4591 msg += "ANYRUNNING,";
4592 }
4593 else
4594 {
4596 msg += "SignalMonitorRunning,";
4597 if (kFlagEITScannerRunning & f)
4598 msg += "EITScannerRunning,";
4600 {
4601 msg += "ANYRECRUNNING,";
4602 }
4603 else
4604 {
4606 msg += "DummyRecorderRunning,";
4607 if (kFlagRecorderRunning & f)
4608 msg += "RecorderRunning,";
4609 }
4610 }
4611 if (kFlagRingBufferReady & f)
4612 msg += "RingBufferReady,";
4613
4614 if (msg.isEmpty())
4615 msg = QString("0x%1").arg(f,0,16);
4616
4617 return msg;
4618}
4619
4621{
4622 QMutexLocker lock(&m_nextLiveTVDirLock);
4623
4624 bool found = !m_nextLiveTVDir.isEmpty();
4625 if (!found && m_triggerLiveTVDir.wait(&m_nextLiveTVDirLock, 500))
4626 {
4627 found = !m_nextLiveTVDir.isEmpty();
4628 }
4629
4630 return found;
4631}
4632
4634{
4635 QMutexLocker lock(&m_nextLiveTVDirLock);
4636
4637 m_nextLiveTVDir = std::move(dir);
4638 m_triggerLiveTVDir.wakeAll();
4639}
4640
4643 const QString & channum)
4644{
4645 LOG(VB_RECORD, LOG_INFO, LOC + "GetProgramRingBufferForLiveTV()");
4646 if (!m_channel || !m_tvChain || !pginfo || !Buffer)
4647 return false;
4648
4649 m_nextLiveTVDirLock.lock();
4650 m_nextLiveTVDir.clear();
4651 m_nextLiveTVDirLock.unlock();
4652
4653 // Dispatch this early, the response can take a while.
4654 MythEvent me(QString("QUERY_NEXT_LIVETV_DIR %1").arg(m_inputId));
4656
4657 uint sourceid = m_channel->GetSourceID();
4658 int chanid = ChannelUtil::GetChanID(sourceid, channum);
4659
4660 if (chanid < 0)
4661 {
4662 // Test setups might have zero channels
4663 if (m_genOpt.m_inputType == "IMPORT" || m_genOpt.m_inputType == "DEMO")
4664 {
4665 chanid = 9999;
4666 }
4667 else
4668 {
4669 LOG(VB_GENERAL, LOG_ERR, LOC +
4670 QString("Channel: \'%1\' was not found in the database.\n"
4671 "\t\tMost likely, the 'starting channel' for this "
4672 "Input Connection is invalid.\n"
4673 "\t\tCould not start livetv.").arg(channum));
4674 return false;
4675 }
4676 }
4677
4678 auto hoursMax =
4679 gCoreContext->GetDurSetting<std::chrono::hours>("MaxHoursPerLiveTVRecording", 8h);
4680 if (hoursMax <= 0h)
4681 hoursMax = 8h;
4682
4683 RecordingInfo *prog = nullptr;
4685 {
4687 }
4688 else
4689 {
4690 prog = new RecordingInfo(
4691 chanid, MythDate::current(true), true, hoursMax);
4692 }
4693
4694 prog->SetInputID(m_inputId);
4695
4696 if (prog->GetRecordingStartTime() == prog->GetRecordingEndTime())
4697 {
4698 LOG(VB_GENERAL, LOG_ERR, LOC + "GetProgramRingBufferForLiveTV()"
4699 "\n\t\t\tProgramInfo is invalid."
4700 "\n" + prog->toString());
4701 prog->SetScheduledEndTime(prog->GetRecordingStartTime().addSecs(3600));
4703
4704 prog->SetChanID(chanid);
4705 }
4706
4709
4710 prog->SetStorageGroup("LiveTV");
4711
4713 {
4714 QMutexLocker lock(&m_nextLiveTVDirLock);
4716 }
4717 else
4718 {
4719 StorageGroup sgroup("LiveTV", gCoreContext->GetHostName());
4720 prog->SetPathname(sgroup.FindNextDirMostFree());
4721 }
4722
4724 prog->SetRecordingGroup("LiveTV");
4725
4726 StartedRecording(prog);
4727
4728 *Buffer = MythMediaBuffer::Create(prog->GetPathname(), true);
4729 if (!(*Buffer) || !(*Buffer)->IsOpen())
4730 {
4731 LOG(VB_GENERAL, LOG_ERR, LOC + QString("RingBuffer '%1' not open...")
4732 .arg(prog->GetPathname()));
4733
4734 delete *Buffer;
4735 delete prog;
4736
4737 return false;
4738 }
4739
4740 *pginfo = prog;
4741 return true;
4742}
4743
4744bool TVRec::CreateLiveTVRingBuffer(const QString & channum)
4745{
4746 LOG(VB_RECORD, LOG_INFO, LOC + QString("CreateLiveTVRingBuffer(%1)")
4747 .arg(channum));
4748
4749 RecordingInfo *pginfo = nullptr;
4750 MythMediaBuffer *buffer = nullptr;
4751
4752 if (!m_channel ||
4753 !m_channel->CheckChannel(channum))
4754 {
4756 return false;
4757 }
4758
4759 if (!GetProgramRingBufferForLiveTV(&pginfo, &buffer, channum))
4760 {
4761 ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
4763 LOG(VB_GENERAL, LOG_ERR, LOC +
4764 QString("CreateLiveTVRingBuffer(%1) failed").arg(channum));
4765 return false;
4766 }
4767
4768 SetRingBuffer(buffer);
4769
4773
4774 bool discont = (m_tvChain->TotalSize() > 0);
4776 m_channel->GetInputName(), discont);
4777
4778 if (m_curRecording)
4779 {
4781 delete m_curRecording;
4782 }
4783
4784 m_curRecording = pginfo;
4786
4787 return true;
4788}
4789
4790bool TVRec::SwitchLiveTVRingBuffer(const QString & channum,
4791 bool discont, bool set_rec)
4792{
4793 QString msg;
4794 if (m_curRecording)
4795 {
4796 msg = QString(" curRec(%1) curRec.size(%2)")
4798 .arg(m_curRecording->GetFilesize());
4799 }
4800 LOG(VB_RECORD, LOG_INFO, LOC +
4801 QString("SwitchLiveTVRingBuffer(discont %1, set_next_rec %2)")
4802 .arg(discont).arg(set_rec) + msg);
4803
4804 RecordingInfo *pginfo = nullptr;
4805 MythMediaBuffer *buffer = nullptr;
4806
4807 if (!m_channel ||
4808 !m_channel->CheckChannel(channum))
4809 {
4811 return false;
4812 }
4813
4814 if (!GetProgramRingBufferForLiveTV(&pginfo, &buffer, channum))
4815 {
4817 return false;
4818 }
4819
4820 QString oldinputtype = m_tvChain->GetInputType(-1);
4821
4822 pginfo->MarkAsInUse(true, kRecorderInUseID);
4827 m_channel->GetInputName(), discont);
4828
4829 if (set_rec && m_recorder)
4830 {
4831 m_recorder->SetNextRecording(pginfo, buffer);
4832 if (discont)
4834 delete pginfo;
4835 SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4836 }
4837 else if (!set_rec)
4838 {
4839 // dummy recordings are finished before this
4840 // is called and other recordings must be finished..
4841 if (m_curRecording && oldinputtype != "DUMMY")
4842 {
4845 delete m_curRecording;
4846 }
4847 m_curRecording = pginfo;
4848 SetRingBuffer(buffer);
4849 }
4850 else
4851 {
4852 delete buffer;
4853 }
4854
4855 return true;
4856}
4857
4859{
4860 LOG(VB_RECORD, LOG_INFO, LOC + "SwitchRecordingRingBuffer()");
4861
4863 {
4864 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4865 "already switching.");
4866 return nullptr;
4867 }
4868
4869 if (!m_recorder)
4870 {
4871 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4872 "invalid recorder.");
4873 return nullptr;
4874 }
4875
4876 if (!m_curRecording)
4877 {
4878 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4879 "invalid recording.");
4880 return nullptr;
4881 }
4882
4883 if (rcinfo.GetChanID() != m_curRecording->GetChanID())
4884 {
4885 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4886 "Not the same channel.");
4887 return nullptr;
4888 }
4889
4890 auto *ri = new RecordingInfo(rcinfo);
4892
4893 QString pn = LoadProfile(nullptr, ri, profile);
4894
4895 if (pn != m_recProfileName)
4896 {
4897 LOG(VB_RECORD, LOG_ERR, LOC +
4898 QString("SwitchRecordingRingBuffer() -> "
4899 "cannot switch profile '%1' to '%2'")
4900 .arg(m_recProfileName, pn));
4901 return nullptr;
4902 }
4903
4905
4906 ri->MarkAsInUse(true, kRecorderInUseID);
4907 StartedRecording(ri);
4908
4909 bool write = m_genOpt.m_inputType != "IMPORT";
4910 MythMediaBuffer *buffer = MythMediaBuffer::Create(ri->GetPathname(), write);
4911 if (!buffer || !buffer->IsOpen())
4912 {
4913 delete buffer;
4914 ri->SetRecordingStatus(RecStatus::Failed);
4915 FinishedRecording(ri, nullptr);
4916 ri->MarkAsInUse(false, kRecorderInUseID);
4917 delete ri;
4918 LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer() -> "
4919 "Failed to create new RB.");
4920 return nullptr;
4921 }
4922
4923 m_recorder->SetNextRecording(ri, buffer);
4924 SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4926 m_switchingBuffer = true;
4927 ri->SetRecordingStatus(RecStatus::Recording);
4928 LOG(VB_RECORD, LOG_INFO, LOC + "SwitchRecordingRingBuffer -> done");
4929 return ri;
4930}
4931
4933{
4934 QMap<uint,TVRec*>::const_iterator it = s_inputs.constFind(inputid);
4935 if (it == s_inputs.constEnd())
4936 return nullptr;
4937 return *it;
4938}
4939
4941{
4942 LOG(VB_RECORD, LOG_INFO, LOC + QString("enable:%1").arg(enable));
4943
4944 if (m_scanner != nullptr)
4945 {
4946 if (enable)
4947 {
4949 && m_eitScanStartTime > MythDate::current().addYears(9))
4950 {
4952 m_eitScanStartTime = MythDate::current().addSecs(secs.count());
4953 }
4954 }
4955 else
4956 {
4957 m_eitScanStartTime = MythDate::current().addYears(10);
4959 {
4961 ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
4962 }
4963 }
4964 }
4965}
4966
4967QString TuningRequest::toString(void) const
4968{
4969 return QString("Program(%1) channel(%2) input(%3) flags(%4)")
4970 .arg(m_program == nullptr ? "NULL" : m_program->toString(),
4971 m_channel.isEmpty() ? "<empty>" : m_channel,
4972 m_input.isEmpty() ? "<empty>" : m_input,
4974}
4975
4976#if CONFIG_DVB
4977#include "recorders/dvbchannel.h"
4979{
4980 // Some DVB devices munge the PMT and/or PAT so the CRC check fails.
4981 // We need to tell the stream data class to not check the CRC on
4982 // these devices. This can cause segfaults.
4983 auto * dvb = dynamic_cast<DVBChannel*>(c);
4984 if (dvb != nullptr)
4985 s->SetIgnoreCRC(dvb->HasCRCBug());
4986}
4987#else
4989#endif // CONFIG_DVB
4990
4991/* 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:1709
static QString GetStartChannel(uint inputid)
Definition: cardutil.cpp:1803
static bool IsEITCapable(const QString &rawtype)
Definition: cardutil.h:172
static QString GetInputName(uint inputid)
Definition: cardutil.cpp:1784
static bool IsV4L(const QString &rawtype)
Definition: cardutil.h:147
static uint GetSourceID(uint inputid)
Definition: cardutil.cpp:1961
static bool IsEncoder(const QString &rawtype)
Definition: cardutil.h:137
static std::vector< uint > GetConflictingInputs(uint inputid)
Definition: cardutil.cpp:2254
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:35
int GetProgramNumber(void) const
Returns program number in PAT, -1 if unknown.
Definition: dtvchannel.h:90
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:106
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:47
uint GetMajorChannel(void) const
Returns major channel, 0 if unknown.
Definition: dtvchannel.h:94
uint GetMinorChannel(void) const
Returns minor channel, 0 if unknown.
Definition: dtvchannel.h:98
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:66
uint GetOriginalNetworkID(void) const
Returns DVB original_network_id, 0 if unknown.
Definition: dtvchannel.h:102
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:501
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:523
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:129
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:839
QVariant value(int i) const
Definition: mythdbcon.h:205
int size(void) const
Definition: mythdbcon.h:215
bool isActive(void) const
Definition: mythdbcon.h:216
bool isConnected(void) const
Only updated once during object creation.
Definition: mythdbcon.h:138
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:620
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:890
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:814
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:552
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:75
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:381
uint GetRecordingRuleID(void) const
Definition: programinfo.h:461
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:428
void SaveAutoExpire(AutoExpireType autoExpire, bool updateDelete=false)
Set "autoexpire" field in "recorded" table to "autoExpire".
void SetRecordingRuleType(RecordingType type)
Definition: programinfo.h:594
uint GetRecordingID(void) const
Definition: programinfo.h:458
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:406
uint QueryMplexID(void) const
Queries multiplex any recording would be made on, zero if unknown.
void SetRecordingStatus(RecStatus::Type status)
Definition: programinfo.h:593
void SetRecordingGroup(const QString &group)
Definition: programinfo.h:540
uint GetSourceID(void) const
Definition: programinfo.h:474
void SetScheduledEndTime(const QDateTime &dt)
Definition: programinfo.h:537
void SetRecordingStartTime(const QDateTime &dt)
Definition: programinfo.h:538
void SaveVideoProperties(uint mask, uint video_property_flags)
QString GetTitle(void) const
Definition: programinfo.h:369
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:413
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:399
bool IsLocal(void) const
Definition: programinfo.h:359
void SetChanID(uint _chanid)
Definition: programinfo.h:535
bool IsCommercialFree(void) const
Definition: programinfo.h:490
MarkTypes QueryAverageAspectRatio(void) const
void SetRecordingRuleID(uint id)
Definition: programinfo.h:551
QString MakeUniqueKey(void) const
Creates a unique string that can be used to identify an existing recording.
Definition: programinfo.h:347
QString GetPathname(void) const
Definition: programinfo.h:351
uint GetInputID(void) const
Definition: programinfo.h:475
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:539
void SetStorageGroup(const QString &group)
Definition: programinfo.h:543
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:459
QDateTime GetRecordingEndTime(void) const
Approximate time the recording should have ended, did end, or is intended to end.
Definition: programinfo.h:421
void SetInputID(uint id)
Definition: programinfo.h:553
void SaveCommFlagged(CommFlagStatus flag)
Set "commflagged" field in "recorded" table to "flag".
QString GetSubtitle(void) const
Definition: programinfo.h:371
QString GetCategory(void) const
Definition: programinfo.h:378
virtual void SetRecordingID(uint _recordedid)
Definition: programinfo.h:591
void SetPathname(const QString &pn)
RecordingType GetRecordingRuleType(void) const
Definition: programinfo.h:463
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:63
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:4790
V4LChannel * GetV4LChannel(void)
Definition: tv_rec.cpp:1270
bool ShouldSwitchToAnotherInput(const QString &chanid) const
Checks if named channel exists on current tuner, or another tuner.
Definition: tv_rec.cpp:2240
bool GetChannelInfo(uint &chanid, uint &sourceid, QString &callsign, QString &channum, QString &channame, QString &xmltvid) const
Definition: tv_rec.cpp:3338
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:1914
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:2673
static bool GetDevices(uint inputid, uint &parentid, GeneralDBOptions &gen_opts, DVBDBOptions &dvb_opts, FireWireDBOptions &firewire_opts)
Definition: tv_rec.cpp:1777
void NotifySchedulerOfRecording(RecordingInfo *rec)
Tell scheduler about the recording.
Definition: tv_rec.cpp:2807
void PauseRecorder(void)
Tells "recorder" to pause, used for channel and input changes.
Definition: tv_rec.cpp:2982
void SetChannelTimeout(std::chrono::milliseconds timeout)
Definition: tv_rec.cpp:3415
DTVChannel * GetDTVChannel(void)
Definition: tv_rec.cpp:1264
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:861
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:799
bool SetVideoFiltersForChannel(uint sourceid, const QString &channum)
Definition: tv_rec.cpp:2511
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:3379
QString GetChainID(void)
Get the chainid of the livetv instance.
Definition: tv_rec.cpp:2758
void TuningFrequency(const TuningRequest &request)
Performs initial tuning required for any tuning event.
Definition: tv_rec.cpp:3728
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:4511
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:779
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:3146
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:4399
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:3195
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:4633
QString m_recProfileName
Definition: tv_rec.h:385
RecStatus::Type GetRecordingStatus(void) const
Definition: tv_rec.cpp:714
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:2700
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:1662
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:3010
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:1056
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:3001
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:3079
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:4858
long long GetFramesWritten(void)
Returns number of frames written to disk by recorder.
Definition: tv_rec.cpp:2625
void StartedRecording(RecordingInfo *curRec)
Inserts a "curRec" into the database.
Definition: tv_rec.cpp:835
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:3639
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:2376
void StopLiveTV(void)
Tells TVRec to stop a "Live TV" recorder.
Definition: tv_rec.cpp:2935
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:3222
RecorderBase * m_recorder
Definition: tv_rec.h:338
long long GetFilePosition(void)
Returns total number of bytes written by RingBuffer.
Definition: tv_rec.cpp:2640
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:1190
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:2542
void CheckForRecGroupChange(void)
Check if frontend changed the recording group.
Definition: tv_rec.cpp:2773
bool m_isPip
Definition: tv_rec.h:374
QString TuningGetChanNum(const TuningRequest &request, QString &input) const
Definition: tv_rec.cpp:3466
FireWireDBOptions m_fwOpt
Definition: tv_rec.h:383
void run(void) override
Event handling method, contains event loop.
Definition: tv_rec.cpp:1353
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:720
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:3426
QString GetInput(void) const
Returns current input.
Definition: tv_rec.cpp:3095
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:4932
void SetLiveRecording(int recording)
Tells the Scheduler about changes to the recording status of the LiveTV recording.
Definition: tv_rec.cpp:2889
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:815
void RingBufferChanged(MythMediaBuffer *Buffer, RecordingInfo *pginfo, RecordingQuality *recq)
Definition: tv_rec.cpp:3443
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:4620
static const uint kFlagSignalMonitorRunning
Definition: tv_rec.h:476
static QString FlagToString(uint f)
Definition: tv_rec.cpp:4529
TVState GetState(void) const
Returns the TVState of the recorder.
Definition: tv_rec.cpp:263
bool CreateLiveTVRingBuffer(const QString &channum)
Definition: tv_rec.cpp:4744
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:4446
DTVRecorder * GetDTVRecorder(void)
Definition: tv_rec.cpp:1245
bool m_reachedPreFail
Definition: tv_rec.h:351
int GetPictureAttribute(PictureAttribute attr)
Definition: tv_rec.cpp:3061
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:2094
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:2853
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:1699
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:2728
void HandleTuning(void)
Handles all tuning events.
Definition: tv_rec.cpp:3563
void StopRecording(bool killFile=false)
Changes from a recording state to kState_None.
Definition: tv_rec.cpp:750
void EnableActiveScan(bool enable)
Definition: tv_rec.cpp:4940
std::chrono::milliseconds SetSignalMonitoringRate(std::chrono::milliseconds rate, int notifyFrontend=1)
Sets the signal monitoring rate.
Definition: tv_rec.cpp:2186
void ClearFlags(uint f, const QString &file, int line)
Definition: tv_rec.cpp:4520
bool IsReallyRecording(void)
Returns true if frontend can consider the recorder started.
Definition: tv_rec.cpp:2531
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:2656
void CloseChannel(void)
Definition: tv_rec.cpp:1250
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:3511
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:2330
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:2148
MPEGStreamData * TuningSignalCheck(void)
This checks if we have a channel lock.
Definition: tv_rec.cpp:3971
bool GetKeyframeDurations(int64_t start, int64_t end, frm_pos_map_t &map) const
Definition: tv_rec.cpp:2684
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:1168
bool GetProgramRingBufferForLiveTV(RecordingInfo **pginfo, MythMediaBuffer **Buffer, const QString &channum)
Definition: tv_rec.cpp:4641
bool TuningNewRecorderReal(MPEGStreamData *streamData, RecordingInfo **rec, RecordingProfile &profile, bool had_dummyrec)
Helper function for TVRec::TuningNewRecorder.
Definition: tv_rec.cpp:4243
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:2610
DTVSignalMonitor * GetDTVSignalMonitor(void)
Definition: tv_rec.cpp:2224
uint GetSourceID(void) const
Returns current source id.
Definition: tv_rec.cpp:3105
static const uint kFlagWaitingForRecPause
Definition: tv_rec.h:470
QString SetInput(QString input)
Changes to the specified input.
Definition: tv_rec.cpp:3120
static bool StateIsPlaying(TVState state)
Returns true if we are in any state associated with a player.
Definition: tv_rec.cpp:789
QString LoadProfile(void *tvchain, RecordingInfo *rec, RecordingProfile &profile) const
Definition: tv_rec.cpp:4197
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:4967
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:18
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:1338
#define LOC
Definition: tv_rec.cpp:48
static void GetPidsToCache(DTVSignalMonitor *dtvMon, pid_cache_t &pid_cache)
Definition: tv_rec.cpp:1864
static int get_highest_input(void)
Definition: tv_rec.cpp:1321
static void apply_broken_dvb_driver_crc_hack(ChannelBase *, MPEGStreamData *)
Definition: tv_rec.cpp:4988
static int init_jobs(const RecordingInfo *rec, RecordingProfile &profile, bool on_host, bool transcode_bfr_comm, bool on_line_comm)
Definition: tv_rec.cpp:4139
static bool get_use_eit(uint inputid)
Definition: tv_rec.cpp:1280
#define SET_NEXT()
Definition: tv_rec.cpp:1045
static QString add_spacer(const QString &channel, const QString &spacer)
Adds the spacer before the last character in chan.
Definition: tv_rec.cpp:2341
static bool ApplyCachedPids(DTVSignalMonitor *dtvMon, const DTVChannel *channel)
Definition: tv_rec.cpp:1881
static bool is_dishnet_eit(uint inputid)
Definition: tv_rec.cpp:1300
#define TRANSITION(ASTATE, BSTATE)
Definition: tv_rec.cpp:1043
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