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