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 "compat.h"
13 #include "previewgeneratorqueue.h"
14 #include "dtvsignalmonitor.h"
15 #include "recordingprofile.h"
16 #include "mythcorecontext.h"
17 #include "mythsystemevent.h"
18 #include "atscstreamdata.h"
19 #include "dvbstreamdata.h"
20 #include "recordingrule.h"
21 #include "channelgroup.h"
22 #include "storagegroup.h"
23 #include "tvremoteutil.h"
24 #include "dtvrecorder.h"
25 #include "livetvchain.h"
26 #include "programinfo.h"
27 #include "mythlogging.h"
28 #include "channelbase.h"
29 #include "atsctables.h"
30 #include "dtvchannel.h"
31 #include "eitscanner.h"
32 #include "mythconfig.h"
33 #include "remoteutil.h"
34 #include "io/mythmediabuffer.h"
35 #include "v4lchannel.h"
36 #include "cardutil.h"
37 #include "jobqueue.h"
38 #include "mythdb.h"
39 #include "tv_rec.h"
40 #include "mythdate.h"
41 #include "osd.h"
42 #include "../vboxutils.h"
43 
44 #define DEBUG_CHANNEL_PREFIX 0
46 #define LOC QString("TVRec[%1]: ").arg(m_inputId)
47 #define LOC2 QString("TVRec[%1]: ").arg(inputid) // for static functions
48 
50 const uint TVRec::kSignalMonitoringRate = 50; /* msec */
51 
52 QReadWriteLock TVRec::s_inputsLock;
53 QMap<uint,TVRec*> TVRec::s_inputs;
54 
55 static bool is_dishnet_eit(uint inputid);
56 static int init_jobs(const RecordingInfo *rec, RecordingProfile &profile,
57  bool on_host, bool transcode_bfr_comm, bool on_line_comm);
59 static int eit_start_rand(int eitTransportTimeout);
60 
85 TVRec::TVRec(int inputid)
86  // Various threads
87  : m_eventThread(new MThread("TVRecEvent", this)),
88  // Configuration variables from setup routines
89  m_inputId(inputid)
90 {
91  s_inputs[m_inputId] = this;
92 }
93 
94 bool TVRec::CreateChannel(const QString &startchannel,
95  bool enter_power_save_mode)
96 {
97  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("CreateChannel(%1)")
98  .arg(startchannel));
99  // If this recorder is a child and its parent is not in error, we
100  // do not need nor want to set the channel.
101  bool setchan = true;
102  if (m_parentId)
103  {
104  TVRec *parentTV = GetTVRec(m_parentId);
105  if (parentTV && parentTV->GetState() == kState_Error)
106  setchan = false;
107  }
109  this, m_genOpt, m_dvbOpt, m_fwOpt,
110  startchannel, enter_power_save_mode, m_rbFileExt, setchan);
111 
112  if (m_genOpt.m_inputType == "VBOX")
113  {
115  {
116  // VBOX presence failed, recorder is marked errored
117  LOG(VB_GENERAL, LOG_ERR, LOC +
118  QString("CreateChannel(%1) failed due to VBOX not responding "
119  "to network check on inputid [%2]")
120  .arg(startchannel).arg(m_inputId));
121  m_channel = nullptr;
122  }
123  }
124 
125  if (!m_channel)
126  {
127  SetFlags(kFlagErrored, __FILE__, __LINE__);
128  return false;
129  }
130 
131  return true;
132 }
133 
139 bool TVRec::Init(void)
140 {
141  QMutexLocker lock(&m_stateChangeLock);
142 
144  return false;
145 
147 
148  // configure the Channel instance
149  QString startchannel = GetStartChannel(m_inputId);
150  if (!CreateChannel(startchannel, true))
151  return false;
152 
154  gCoreContext->GetBoolSetting("AutoTranscodeBeforeAutoCommflag", false);
155  m_earlyCommFlag = gCoreContext->GetBoolSetting("AutoCommflagWhileRecording", false);
156  m_runJobOnHostOnly = gCoreContext->GetBoolSetting("JobsRunOnRecordHost", false);
158  max(gCoreContext->GetNumSetting("EITTransportTimeout", 5) * 60, 6);
159  m_eitCrawlIdleStart = gCoreContext->GetNumSetting("EITCrawIdleStart", 60);
160  m_audioSampleRateDB = gCoreContext->GetNumSetting("AudioSampleRate");
161  m_overRecordSecNrml = gCoreContext->GetNumSetting("RecordOverTime");
162  m_overRecordSecCat = gCoreContext->GetNumSetting("CategoryOverTime") * 60;
163  m_overRecordCategory= gCoreContext->GetSetting("OverTimeCategory");
164 
165  m_eventThread->start();
166 
168 
169  return true;
170 }
171 
177 {
178  s_inputs.remove(m_inputId);
179 
181  {
182  ClearFlags(kFlagRunMainLoop, __FILE__, __LINE__);
183  m_eventThread->wait();
184  delete m_eventThread;
185  m_eventThread = nullptr;
186  }
187 
188  if (m_channel)
189  {
190  delete m_channel;
191  m_channel = nullptr;
192  }
193 }
194 
196 {
197  LOG(VB_RECORD, LOG_INFO, LOC + "TeardownAll");
198 
200 
201  if (m_scanner)
202  {
203  delete m_scanner;
204  m_scanner = nullptr;
205  }
206 
208 
209  SetRingBuffer(nullptr);
210 }
211 
213 {
214  QMutexLocker locker(&m_triggerEventLoopLock);
216  m_triggerEventLoopWait.wakeAll();
217 }
218 
226 {
227  if (m_changeState)
228  return kState_ChangingState;
229  return m_internalState;
230 }
231 
240 {
241  QMutexLocker lock(&m_stateChangeLock);
242 
243  ProgramInfo *tmppginfo = nullptr;
244 
246  {
247  tmppginfo = new ProgramInfo(*m_curRecording);
249  }
250  else
251  tmppginfo = new ProgramInfo();
252  tmppginfo->SetInputID(m_inputId);
253 
254  return tmppginfo;
255 }
256 
271 void TVRec::RecordPending(const ProgramInfo *rcinfo, int secsleft,
272  bool hasLater)
273 {
274  QMutexLocker statelock(&m_stateChangeLock);
275  QMutexLocker pendlock(&m_pendingRecLock);
276 
277  if (secsleft < 0)
278  {
279  LOG(VB_RECORD, LOG_INFO, LOC + "Pending recording revoked on " +
280  QString("inputid [%1]").arg(rcinfo->GetInputID()));
281 
282  PendingMap::iterator it = m_pendingRecordings.find(rcinfo->GetInputID());
283  if (it != m_pendingRecordings.end())
284  {
285  (*it).m_ask = false;
286  (*it).m_doNotAsk = (*it).m_canceled = true;
287  }
288  return;
289  }
290 
291  LOG(VB_RECORD, LOG_INFO, LOC +
292  QString("RecordPending on inputid [%1]").arg(rcinfo->GetInputID()));
293 
294  PendingInfo pending;
295  pending.m_info = new ProgramInfo(*rcinfo);
296  pending.m_recordingStart = MythDate::current().addSecs(secsleft);
297  pending.m_hasLaterShowing = hasLater;
298  pending.m_ask = true;
299  pending.m_doNotAsk = false;
300 
301  m_pendingRecordings[rcinfo->GetInputID()] = pending;
302 
303  // If this isn't a recording for this instance to make, we are done
304  if (rcinfo->GetInputID() != m_inputId)
305  return;
306 
307  // We also need to check our input groups
308  vector<uint> inputids = CardUtil::GetConflictingInputs(
309  rcinfo->GetInputID());
310 
311  m_pendingRecordings[rcinfo->GetInputID()].m_possibleConflicts = inputids;
312 
313  pendlock.unlock();
314  statelock.unlock();
315  for (uint inputid : inputids)
316  RemoteRecordPending(inputid, rcinfo, secsleft, hasLater);
317  statelock.relock();
318  pendlock.relock();
319 }
320 
325 {
328  delete old_rec;
329 }
330 
334 QDateTime TVRec::GetRecordEndTime(const ProgramInfo *pi) const
335 {
336  bool spcat = (!m_overRecordCategory.isEmpty() &&
338  int secs = (spcat) ? m_overRecordSecCat : m_overRecordSecNrml;
339  return pi->GetRecordingEndTime().addSecs(secs);
340 }
341 
347 void TVRec::CancelNextRecording(bool cancel)
348 {
349  QMutexLocker pendlock(&m_pendingRecLock);
350  LOG(VB_RECORD, LOG_INFO, LOC +
351  QString("CancelNextRecording(%1) -- begin").arg(cancel));
352 
353  PendingMap::iterator it = m_pendingRecordings.find(m_inputId);
354  if (it == m_pendingRecordings.end())
355  {
356  LOG(VB_RECORD, LOG_INFO, LOC + QString("CancelNextRecording(%1) -- "
357  "error, unknown recording").arg(cancel));
358  return;
359  }
360 
361  if (cancel)
362  {
363  vector<uint> &inputids = (*it).m_possibleConflicts;
364  for (uint inputid : inputids)
365  {
366  LOG(VB_RECORD, LOG_INFO, LOC +
367  QString("CancelNextRecording -- inputid 0x%1")
368  .arg((uint64_t)inputid,0,16));
369 
370  pendlock.unlock();
371  RemoteRecordPending(inputid, (*it).m_info, -1, false);
372  pendlock.relock();
373  }
374 
375  LOG(VB_RECORD, LOG_INFO, LOC +
376  QString("CancelNextRecording -- inputid [%1]")
377  .arg(m_inputId));
378 
379  RecordPending((*it).m_info, -1, false);
380  }
381  else
382  {
383  (*it).m_canceled = false;
384  }
385 
386  LOG(VB_RECORD, LOG_INFO, LOC +
387  QString("CancelNextRecording(%1) -- end").arg(cancel));
388 }
389 
398 {
399  RecordingInfo ri1(*pginfo);
402  RecordingInfo *rcinfo = &ri1;
403 
404  LOG(VB_RECORD, LOG_INFO, LOC + QString("StartRecording(%1)")
406 
407  QMutexLocker lock(&m_stateChangeLock);
408  QString msg("");
409 
412 
413  // Flush out any pending state changes
415 
416  // We need to do this check early so we don't cancel an overrecord
417  // that we're trying to extend.
421  {
422  int post_roll_seconds = m_curRecording->GetRecordingEndTime()
423  .secsTo(m_recordEndTime);
424 
429 
431  .addSecs(post_roll_seconds);
432 
433  msg = QString("updating recording: %1 %2 %3 %4")
437  LOG(VB_RECORD, LOG_INFO, LOC + msg);
438 
439  ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
440 
442  return RecStatus::Recording;
443  }
444 
445  bool cancelNext = false;
446  PendingInfo pendinfo;
447  PendingMap::iterator it;
448 
449  m_pendingRecLock.lock();
450  if ((it = m_pendingRecordings.find(m_inputId)) != m_pendingRecordings.end())
451  {
452  (*it).m_ask = (*it).m_doNotAsk = false;
453  cancelNext = (*it).m_canceled;
454  }
455  m_pendingRecLock.unlock();
456 
457  // Flush out events...
459 
460  // Rescan pending recordings since the event loop may have deleted
461  // a stale entry. If this happens the info pointer will not be valid
462  // since the HandlePendingRecordings loop will have deleted it.
463  m_pendingRecLock.lock();
464  it = m_pendingRecordings.find(m_inputId);
465  bool has_pending = (it != m_pendingRecordings.end());
466  if (has_pending)
467  pendinfo = *it;
468  m_pendingRecLock.unlock();
469 
470  // If the needed input is in a shared input group, and we are
471  // not canceling the recording anyway, check other recorders
472  if (!cancelNext && has_pending && !pendinfo.m_possibleConflicts.empty())
473  {
474  LOG(VB_RECORD, LOG_INFO, LOC +
475  "Checking input group recorders - begin");
476  vector<uint> &inputids = pendinfo.m_possibleConflicts;
477 
478  uint mplexid = 0;
479  uint chanid = 0;
480  uint sourceid = 0;
481  vector<uint> inputids2;
482  vector<TVState> states;
483 
484  // Stop remote recordings if needed
485  for (uint inputid : inputids)
486  {
487  InputInfo busy_input;
488  bool is_busy = RemoteIsBusy(inputid, busy_input);
489 
490  if (is_busy && !sourceid)
491  {
492  mplexid = pendinfo.m_info->QueryMplexID();
493  chanid = pendinfo.m_info->GetChanID();
494  sourceid = pendinfo.m_info->GetSourceID();
495  }
496 
497  if (is_busy &&
498  ((sourceid != busy_input.m_sourceId) ||
499  (mplexid != busy_input.m_mplexId) ||
500  ((mplexid == 0 || mplexid == 32767) &&
501  chanid != busy_input.m_chanId)))
502  {
503  states.push_back((TVState) RemoteGetState(inputid));
504  inputids2.push_back(inputid);
505  }
506  }
507 
508  bool ok = true;
509  for (uint i = 0; (i < inputids2.size()) && ok; i++)
510  {
511  LOG(VB_RECORD, LOG_INFO, LOC +
512  QString("Attempting to stop input [%1] in state %2")
513  .arg(inputids2[i]).arg(StateToString(states[i])));
514 
515  bool success = RemoteStopRecording(inputids2[i]);
516  if (success)
517  {
518  uint state = RemoteGetState(inputids2[i]);
519  LOG(VB_GENERAL, LOG_INFO, LOC + QString("a [%1]: %2")
520  .arg(inputids2[i]).arg(StateToString((TVState)state)));
521  success = (kState_None == state);
522  }
523 
524  // If we managed to stop LiveTV recording, restart playback..
525  if (success && states[i] == kState_WatchingLiveTV)
526  {
527  QString message = QString("QUIT_LIVETV %1").arg(inputids2[i]);
528  MythEvent me(message);
529  gCoreContext->dispatch(me);
530  }
531 
532  LOG(VB_RECORD, LOG_INFO, LOC +
533  QString("Stopping recording on [%1], %2") .arg(inputids2[i])
534  .arg(success ? "succeeded" : "failed"));
535 
536  ok &= success;
537  }
538 
539  // If we failed to stop the remote recordings, don't record
540  if (!ok)
541  {
542  CancelNextRecording(true);
543  cancelNext = true;
544  }
545 
546  inputids.clear();
547 
548  LOG(VB_RECORD, LOG_INFO, LOC + "Checking input group recorders - done");
549  }
550 
551  bool did_switch = false;
552  if (!cancelNext && (GetState() == kState_RecordingOnly))
553  {
555  did_switch = (nullptr != ri2);
556  if (did_switch)
557  {
558  // Make sure scheduler is allowed to end this recording
559  ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
560 
562  }
563  else
564  {
565  // If in post-roll, end recording
566  m_stateChangeLock.unlock();
567  StopRecording();
568  m_stateChangeLock.lock();
569  }
570  }
571 
572  if (!cancelNext && (GetState() == kState_None))
573  {
574  if (m_tvChain)
575  {
576  QString message = QString("LIVETV_EXITED");
577  MythEvent me(message, m_tvChain->GetID());
578  gCoreContext->dispatch(me);
579  m_tvChain->DecrRef();
580  m_tvChain = nullptr;
581  }
582 
584 
585  // Tell event loop to begin recording.
586  m_curRecording = new RecordingInfo(*rcinfo);
591 
592  // Make sure scheduler is allowed to end this recording
593  ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
594 
597  else
598  LOG(VB_RECORD, LOG_WARNING, LOC + "Still failing.");
600  }
601  else if (!cancelNext && (GetState() == kState_WatchingLiveTV))
602  {
606 
607  // We want the frontend to change channel for recording
608  // and disable the UI for channel change, PiP, etc.
609 
610  QString message = QString("LIVETV_WATCH %1 1").arg(m_inputId);
611  QStringList prog;
612  rcinfo->ToStringList(prog);
613  MythEvent me(message, prog);
614  gCoreContext->dispatch(me);
615  }
616  else if (!did_switch)
617  {
618  msg = QString("Wanted to record: %1 %2 %3 %4\n\t\t\t")
619  .arg(rcinfo->GetTitle()).arg(rcinfo->GetChanID())
621  .arg(rcinfo->GetRecordingEndTime(MythDate::ISODate));
622 
623  if (cancelNext)
624  {
625  msg += "But a user has canceled this recording";
627  }
628  else
629  {
630  msg += QString("But the current state is: %1")
633  }
634 
636  {
637  msg += QString("\n\t\t\tCurrently recording: %1 %2 %3 %4")
641  }
642 
643  LOG(VB_GENERAL, LOG_INFO, LOC + msg);
644  }
645 
646  for (const auto & pend : qAsConst(m_pendingRecordings))
647  delete pend.m_info;
648  m_pendingRecordings.clear();
649 
650  if (!did_switch)
651  {
653 
654  QMutexLocker locker(&m_pendingRecLock);
655  if ((m_curRecording) &&
660  {
661  SetRecordingStatus(RecStatus::Failed, __LINE__, true);
662  }
663  return m_recStatus;
664  }
665 
666  return GetRecordingStatus();
667 }
668 
670 {
671  QMutexLocker pendlock(&m_pendingRecLock);
672  return m_recStatus;
673 }
674 
676  RecStatus::Type new_status, int line, bool have_lock)
677 {
678  RecStatus::Type old_status = RecStatus::Unknown;
679  if (have_lock)
680  {
681  old_status = m_recStatus;
682  m_recStatus = new_status;
683  }
684  else
685  {
686  m_pendingRecLock.lock();
687  old_status = m_recStatus;
688  m_recStatus = new_status;
689  m_pendingRecLock.unlock();
690  }
691 
692  LOG(VB_RECORD, LOG_INFO, LOC +
693  QString("SetRecordingStatus(%1->%2) on line %3")
694  .arg(RecStatus::toString(old_status, kSingleRecord))
695  .arg(RecStatus::toString(new_status, kSingleRecord))
696  .arg(line));
697 }
698 
705 void TVRec::StopRecording(bool killFile)
706 {
707  if (StateIsRecording(GetState()))
708  {
709  QMutexLocker lock(&m_stateChangeLock);
710  if (killFile)
711  SetFlags(kFlagKillRec, __FILE__, __LINE__);
712  else if (m_curRecording)
713  {
714  QDateTime now = MythDate::current(true);
715  if (now < m_curRecording->GetDesiredEndTime())
717  }
719  // wait for state change to take effect
721  ClearFlags(kFlagCancelNextRecording|kFlagKillRec, __FILE__, __LINE__);
722 
724  }
725 }
726 
733 {
734  return (state == kState_RecordingOnly ||
735  state == kState_WatchingLiveTV);
736 }
737 
743 {
744  return (state == kState_WatchingPreRecorded);
745 }
746 
753 {
754  if (StateIsRecording(state))
755  return kState_None;
756 
757  LOG(VB_GENERAL, LOG_ERR, LOC +
758  QString("Unknown state in RemoveRecording: %1")
759  .arg(StateToString(state)));
760  return kState_Error;
761 }
762 
769 {
770  if (StateIsPlaying(state))
771  {
772  if (state == kState_WatchingPreRecorded)
773  return kState_None;
774  return kState_RecordingOnly;
775  }
776 
777  QString msg = "Unknown state in RemovePlaying: %1";
778  LOG(VB_GENERAL, LOG_ERR, LOC + msg.arg(StateToString(state)));
779 
780  return kState_Error;
781 }
782 
789 {
790  if (!curRec)
791  return;
792 
793  curRec->StartedRecording(m_rbFileExt);
794  LOG(VB_RECORD, LOG_INFO, LOC + QString("StartedRecording(%1) fn(%2)")
795  .arg(curRec->MakeUniqueKey()).arg(curRec->GetPathname()));
796 
797  if (curRec->IsCommercialFree())
799 
800  AutoRunInitType t = (curRec->GetRecordingGroup() == "LiveTV") ?
802  InitAutoRunJobs(curRec, t, nullptr, __LINE__);
803 
804  SendMythSystemRecEvent("REC_STARTED", curRec);
805 }
806 
815 {
816  if (!curRec)
817  return;
818 
819  // Make sure the recording group is up to date
820  const QString recgrp = curRec->QueryRecordingGroup();
821  curRec->SetRecordingGroup(recgrp);
822 
823  bool is_good = true;
824  if (recq)
825  {
826  LOG((recq->IsDamaged()) ? VB_GENERAL : VB_RECORD, LOG_INFO,
827  LOC + QString("FinishedRecording(%1) %2 recq:%3\n")
828  .arg(curRec->MakeUniqueKey())
829  .arg((recq->IsDamaged()) ? "damaged" : "good")
830  .arg(recq->toStringXML()));
831  is_good = !recq->IsDamaged();
832  delete recq;
833  recq = nullptr;
834  }
835 
836  RecStatus::Type ors = curRec->GetRecordingStatus();
837  // Set the final recording status
838  if (curRec->GetRecordingStatus() == RecStatus::Recording)
840  else if (curRec->GetRecordingStatus() != RecStatus::Recorded)
842  curRec->SetRecordingEndTime(MythDate::current(true));
843  is_good &= (curRec->GetRecordingStatus() == RecStatus::Recorded);
844 
845  // Figure out if this was already done for this recording
846  bool was_finished = false;
847  static QMutex s_finRecLock;
848  static QHash<QString,QDateTime> s_finRecMap;
849  {
850  QMutexLocker locker(&s_finRecLock);
851  QDateTime now = MythDate::current();
852  QDateTime expired = now.addSecs(-60*5);
853  QHash<QString,QDateTime>::iterator it = s_finRecMap.begin();
854  while (it != s_finRecMap.end())
855  {
856  if ((*it) < expired)
857  it = s_finRecMap.erase(it);
858  else
859  ++it;
860  }
861  QString key = curRec->MakeUniqueKey();
862  it = s_finRecMap.find(key);
863  if (it != s_finRecMap.end())
864  was_finished = true;
865  else
866  s_finRecMap[key] = now;
867  }
868 
869  // Print something informative to the log
870  LOG(VB_RECORD, LOG_INFO, LOC +
871  QString("FinishedRecording(%1) %2 quality"
872  "\n\t\t\ttitle: %3\n\t\t\t"
873  "in recgroup: %4 status: %5:%6 %7 %8")
874  .arg(curRec->MakeUniqueKey())
875  .arg(is_good ? "Good" : "Bad")
876  .arg(curRec->GetTitle())
877  .arg(recgrp)
880  .arg(HasFlags(kFlagDummyRecorderRunning)?"is_dummy":"not_dummy")
881  .arg(was_finished?"already_finished":"finished_now"));
882 
883  // This has already been called on this recording..
884  if (was_finished)
885  return;
886 
887  // Notify the frontend watching live tv that this file is final
888  if (m_tvChain)
889  m_tvChain->FinishedRecording(curRec);
890 
891  // if this is a dummy recorder, do no more..
893  {
894  curRec->FinishedRecording(true); // so end time is updated
895  SendMythSystemRecEvent("REC_FINISHED", curRec);
896  return;
897  }
898 
899  // Get the width and set the videoprops
900  MarkTypes aspectRatio = curRec->QueryAverageAspectRatio();
901  uint avg_height = curRec->QueryAverageHeight();
902  bool progressive = curRec->QueryAverageScanProgressive();
903  curRec->SaveVideoProperties
906  ((avg_height > 2000) ? VID_4K :
907  ((avg_height > 1000) ? VID_1080 :
908  ((avg_height > 700) ? VID_720 : 0))) |
909  (progressive ? VID_PROGRESSIVE : 0) |
910  ((is_good) ? 0 : VID_DAMAGED) |
911  (((aspectRatio == MARK_ASPECT_16_9) ||
912  (aspectRatio == MARK_ASPECT_2_21_1)) ? VID_WIDESCREEN : 0));
913 
914  // Make sure really short recordings have positive run time.
915  if (curRec->GetRecordingEndTime() <= curRec->GetRecordingStartTime())
916  {
917  curRec->SetRecordingEndTime(
918  curRec->GetRecordingStartTime().addSecs(60));
919  }
920 
921  // HACK Temporary hack, ensure we've loaded the recording file info, do it now
922  // so that it contains the final filesize information
923  if (!curRec->GetRecordingFile())
924  curRec->LoadRecordingFile();
925 
926  // Generate a preview
927  uint64_t fsize = curRec->GetFilesize();
928  if (curRec->IsLocal() && (fsize >= 1000) &&
930  {
932  }
933 
934  // store recording in recorded table
935  curRec->FinishedRecording(!is_good || (recgrp == "LiveTV"));
936 
937  // send out UPDATE_RECORDING_STATUS message
938  if (recgrp != "LiveTV")
939  {
940  LOG(VB_RECORD, LOG_INFO, LOC +
941  QString("FinishedRecording -- UPDATE_RECORDING_STATUS: %1")
942  .arg(RecStatus::toString(is_good ? curRec->GetRecordingStatus()
944  MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
945  .arg(curRec->GetInputID())
946  .arg(curRec->GetChanID())
948  .arg(is_good ? curRec->GetRecordingStatus() : RecStatus::Failed)
949  .arg(curRec->GetRecordingEndTime(MythDate::ISODate)));
950  gCoreContext->dispatch(me);
951  }
952 
953  // send out REC_FINISHED message
954  SendMythSystemRecEvent("REC_FINISHED", curRec);
955 
956  // send out DONE_RECORDING message
957  int secsSince = curRec->GetRecordingStartTime()
958  .secsTo(MythDate::current());
959  QString message = QString("DONE_RECORDING %1 %2 %3")
960  .arg(m_inputId).arg(secsSince).arg(GetFramesWritten());
961  MythEvent me(message);
962  gCoreContext->dispatch(me);
963 
964  // Handle JobQueue
965  QHash<QString,int>::iterator autoJob =
966  m_autoRunJobs.find(curRec->MakeUniqueKey());
967  if (autoJob == m_autoRunJobs.end())
968  {
969  LOG(VB_GENERAL, LOG_INFO,
970  "autoRunJobs not initialized until FinishedRecording()");
972  (recgrp == "LiveTV") ? kAutoRunNone : kAutoRunProfile;
973  InitAutoRunJobs(curRec, t, nullptr, __LINE__);
974  autoJob = m_autoRunJobs.find(curRec->MakeUniqueKey());
975  }
976  LOG(VB_JOBQUEUE, LOG_INFO, QString("AutoRunJobs 0x%1").arg(*autoJob,0,16));
977  if ((recgrp == "LiveTV") || (fsize < 1000) ||
978  (curRec->GetRecordingStatus() != RecStatus::Recorded) ||
979  (curRec->GetRecordingStartTime().secsTo(
980  MythDate::current()) < 120))
981  {
984  }
985  if (*autoJob != JOB_NONE)
986  JobQueue::QueueRecordingJobs(*curRec, *autoJob);
987  m_autoRunJobs.erase(autoJob);
988 }
989 
990 #define TRANSITION(ASTATE,BSTATE) \
991  ((m_internalState == (ASTATE)) && (m_desiredNextState == (BSTATE)))
992 #define SET_NEXT() do { nextState = m_desiredNextState; changed = true; } while(false)
993 #define SET_LAST() do { nextState = m_internalState; changed = true; } while(false)
994 
1003 {
1004  TVState nextState = m_internalState;
1005 
1006  bool changed = false;
1007 
1008  QString transMsg = QString(" %1 to %2")
1009  .arg(StateToString(nextState))
1011 
1013  {
1014  LOG(VB_GENERAL, LOG_ERR, LOC +
1015  "HandleStateChange(): Null transition" + transMsg);
1016  m_changeState = false;
1017  return;
1018  }
1019 
1020  // Make sure EIT scan is stopped before any tuning,
1021  // to avoid race condition with it's tuning requests.
1023  {
1025  ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1028  }
1029 
1030  // Handle different state transitions
1032  {
1034  SET_NEXT();
1035  }
1037  {
1039  SET_NEXT();
1040  }
1042  {
1043  SetPseudoLiveTVRecording(nullptr);
1044 
1045  SET_NEXT();
1046  }
1048  {
1049  SetPseudoLiveTVRecording(nullptr);
1051  SET_NEXT();
1052  }
1054  {
1057  (GetFlags()&kFlagKillRec)));
1058  SET_NEXT();
1059  }
1060 
1061  QString msg = (changed) ? "Changing from" : "Unknown state transition:";
1062  LOG(VB_GENERAL, LOG_INFO, LOC + msg + transMsg);
1063 
1064  // update internal state variable
1065  m_internalState = nextState;
1066  m_changeState = false;
1067 
1070  {
1073  }
1074  else
1075  {
1076  m_eitScanStartTime = m_eitScanStartTime.addYears(1);
1077  }
1078 }
1079 #undef TRANSITION
1080 #undef SET_NEXT
1081 #undef SET_LAST
1082 
1087 {
1088  QMutexLocker lock(&m_stateChangeLock);
1089  m_desiredNextState = nextState;
1090  m_changeState = true;
1091  WakeEventLoop();
1092 }
1093 
1108 void TVRec::TeardownRecorder(uint request_flags)
1109 {
1110  LOG(VB_RECORD, LOG_INFO, LOC + QString("TeardownRecorder(%1)")
1111  .arg((request_flags & kFlagKillRec) ? "kFlagKillRec" : ""));
1112 
1113  m_pauseNotify = false;
1114  m_isPip = false;
1115 
1117  {
1120  delete m_recorderThread;
1121  m_recorderThread = nullptr;
1122  }
1124  __FILE__, __LINE__);
1125 
1126  RecordingQuality *recq = nullptr;
1127  if (m_recorder)
1128  {
1129  if (GetV4LChannel())
1130  m_channel->SetFd(-1);
1131 
1133 
1134  QMutexLocker locker(&m_stateChangeLock);
1135  delete m_recorder;
1136  m_recorder = nullptr;
1137  }
1138 
1139  if (m_buffer)
1140  {
1141  LOG(VB_FILE, LOG_INFO, LOC + "calling StopReads()");
1142  m_buffer->StopReads();
1143  }
1144 
1145  if (m_curRecording)
1146  {
1147  if (!!(request_flags & kFlagKillRec))
1149 
1151 
1153  delete m_curRecording;
1154  m_curRecording = nullptr;
1155  }
1156 
1157  m_pauseNotify = true;
1158 
1159  if (GetDTVChannel())
1161 }
1162 
1164 {
1165  return dynamic_cast<DTVRecorder*>(m_recorder);
1166 }
1167 
1169 {
1170  if (m_channel &&
1171  ((m_genOpt.m_inputType == "DVB" && m_dvbOpt.m_dvbOnDemand) ||
1172  m_genOpt.m_inputType == "FREEBOX" ||
1173  m_genOpt.m_inputType == "VBOX" ||
1174  m_genOpt.m_inputType == "HDHOMERUN" ||
1176  {
1177  m_channel->Close();
1178  }
1179 }
1180 
1182 {
1183  return dynamic_cast<DTVChannel*>(m_channel);
1184 }
1185 
1187 {
1188 #ifdef USING_V4L2
1189  return dynamic_cast<V4LChannel*>(m_channel);
1190 #else
1191  return nullptr;
1192 #endif // USING_V4L2
1193 }
1194 
1195 static bool get_use_eit(uint inputid)
1196 {
1198  query.prepare(
1199  "SELECT SUM(useeit) "
1200  "FROM videosource, capturecard "
1201  "WHERE videosource.sourceid = capturecard.sourceid AND"
1202  " capturecard.cardid = :INPUTID");
1203  query.bindValue(":INPUTID", inputid);
1204 
1205  if (!query.exec() || !query.isActive())
1206  {
1207  MythDB::DBError("get_use_eit", query);
1208  return false;
1209  }
1210  if (query.next())
1211  return query.value(0).toBool();
1212  return false;
1213 }
1214 
1215 static bool is_dishnet_eit(uint inputid)
1216 {
1218  query.prepare(
1219  "SELECT SUM(dishnet_eit) "
1220  "FROM videosource, capturecard "
1221  "WHERE videosource.sourceid = capturecard.sourceid AND"
1222  " capturecard.cardid = :INPUTID");
1223  query.bindValue(":INPUTID", inputid);
1224 
1225  if (!query.exec() || !query.isActive())
1226  {
1227  MythDB::DBError("is_dishnet_eit", query);
1228  return false;
1229  }
1230  if (query.next())
1231  return query.value(0).toBool();
1232  return false;
1233 }
1234 
1235 static int num_inputs(void)
1236 {
1238 
1239  QString str =
1240  "SELECT COUNT(cardid) "
1241  "FROM capturecard ";
1242 
1243  query.prepare(str);
1244 
1245  if (!query.exec() || !query.isActive())
1246  {
1247  MythDB::DBError("num_inputs", query);
1248  return -1;
1249  }
1250  if (query.next())
1251  return query.value(0).toInt();
1252  return -1;
1253 }
1254 
1255 static int eit_start_rand(int eitTransportTimeout)
1256 {
1257  // randomize start time a bit
1258  int timeout = random() % (eitTransportTimeout / 3);
1259  // get the number of inputs and the position of the current input
1260  // to distribute the the scan start evenly over eitTransportTimeout
1261  int no_inputs = num_inputs();
1262  if (no_inputs > 0)
1263  timeout += eitTransportTimeout / no_inputs;
1264  return timeout;
1265 }
1266 
1268 void TVRec::run(void)
1269 {
1270  QMutexLocker lock(&m_stateChangeLock);
1271  SetFlags(kFlagRunMainLoop, __FILE__, __LINE__);
1272  ClearFlags(kFlagExitPlayer | kFlagFinishRecording, __FILE__, __LINE__);
1273 
1275  // check whether we should use the EITScanner in this TVRec instance
1277  (!GetDTVChannel() || GetDTVChannel()->IsMaster()) &&
1279  {
1283  }
1284  else
1285  {
1286  m_eitScanStartTime = m_eitScanStartTime.addYears(1);
1287  }
1288 
1289  while (HasFlags(kFlagRunMainLoop))
1290  {
1291  // If there is a state change queued up, do it...
1292  if (m_changeState)
1293  {
1296  __FILE__, __LINE__);
1297  }
1298 
1299  // Quick exit on fatal errors.
1300  if (IsErrored())
1301  {
1302  LOG(VB_GENERAL, LOG_ERR, LOC +
1303  "RunTV encountered fatal error, exiting event thread.");
1304  ClearFlags(kFlagRunMainLoop, __FILE__, __LINE__);
1305  TeardownAll();
1306  return;
1307  }
1308 
1309  // Handle any tuning events.. Blindly grabbing the lock here
1310  // can sometimes cause a deadlock with Init() while it waits
1311  // to make sure this thread starts. Until a better solution
1312  // is found, don't run HandleTuning unless we can safely get
1313  // the lock.
1314  if (s_inputsLock.tryLockForRead())
1315  {
1316  HandleTuning();
1317  s_inputsLock.unlock();
1318  }
1319 
1320  // Tell frontends about pending recordings
1322 
1323  // If we are recording a program, check if the recording is
1324  // over or someone has asked us to finish the recording.
1325  // Add an extra 60 seconds to the recording end time if we
1326  // might want a back to back recording.
1327  QDateTime recEnd = (!m_pendingRecordings.empty()) ?
1328  m_recordEndTime.addSecs(60) : m_recordEndTime;
1329  if ((GetState() == kState_RecordingOnly) &&
1330  (MythDate::current() > recEnd ||
1332  {
1334  ClearFlags(kFlagFinishRecording, __FILE__, __LINE__);
1335  }
1336 
1337  if (m_curRecording)
1338  {
1340 
1341  if (m_recorder)
1342  {
1344 
1345  // Check for recorder errors
1346  if (m_recorder->IsErrored())
1347  {
1349 
1351  {
1352  QString message = QString("QUIT_LIVETV %1").arg(m_inputId);
1353  MythEvent me(message);
1354  gCoreContext->dispatch(me);
1355  }
1356  else
1358  }
1359  }
1360  }
1361 
1362  // Check for the end of the current program..
1364  {
1365  QDateTime now = MythDate::current();
1366  bool has_finish = HasFlags(kFlagFinishRecording);
1367  bool has_rec = m_pseudoLiveTVRecording;
1368  bool enable_ui = true;
1369 
1370  m_pendingRecLock.lock();
1371  bool rec_soon =
1373  m_pendingRecLock.unlock();
1374 
1375  if (has_rec && (has_finish || (now > m_recordEndTime)))
1376  {
1377  SetPseudoLiveTVRecording(nullptr);
1378  }
1379  else if (!has_rec && !rec_soon && m_curRecording &&
1380  (now >= m_curRecording->GetScheduledEndTime()))
1381  {
1382  if (!m_switchingBuffer)
1383  {
1384  LOG(VB_RECORD, LOG_INFO, LOC +
1385  "Switching Buffer (" +
1386  QString("!has_rec(%1) && ").arg(has_rec) +
1387  QString("!rec_soon(%1) && (").arg(rec_soon) +
1388  MythDate::toString(now, MythDate::ISODate) + " >= " +
1390  QString("(%1) ))")
1391  .arg(now >= m_curRecording->GetScheduledEndTime()));
1392 
1393  m_switchingBuffer = true;
1394 
1396  false, true);
1397  }
1398  else
1399  {
1400  LOG(VB_RECORD, LOG_INFO, "Waiting for ringbuffer switch");
1401  }
1402  }
1403  else
1404  enable_ui = false;
1405 
1406  if (enable_ui)
1407  {
1408  LOG(VB_RECORD, LOG_INFO, LOC + "Enabling Full LiveTV UI.");
1409  QString message = QString("LIVETV_WATCH %1 0").arg(m_inputId);
1410  MythEvent me(message);
1411  gCoreContext->dispatch(me);
1412  }
1413  }
1414 
1415  // Check for ExitPlayer flag, and if set change to a non-watching
1416  // state (either kState_RecordingOnly or kState_None).
1418  {
1421  else if (StateIsPlaying(m_internalState))
1423  ClearFlags(kFlagExitPlayer, __FILE__, __LINE__);
1424  }
1425 
1426  if (m_scanner && m_channel &&
1428  {
1429  if (!m_dvbOpt.m_dvbEitScan)
1430  {
1431  LOG(VB_EIT, LOG_INFO, LOC +
1432  "EIT scanning disabled for this input.");
1433  m_eitScanStartTime = m_eitScanStartTime.addYears(1);
1434  }
1435  else if (!get_use_eit(GetInputId()))
1436  {
1437  LOG(VB_EIT, LOG_INFO, LOC +
1438  "EIT scanning disabled for all sources on this input.");
1439  m_eitScanStartTime = m_eitScanStartTime.addYears(1);
1440  }
1441  else
1442  {
1443  // Check if another card in the same input group is
1444  // busy. This could be either virtual DVB-devices or
1445  // a second tuner on a single card
1446  s_inputsLock.lockForRead();
1447  bool allow_eit = true;
1448  vector<uint> inputids =
1450  InputInfo busy_input;
1451  for (uint i = 0; i < inputids.size() && allow_eit; ++i)
1452  allow_eit = !RemoteIsBusy(inputids[i], busy_input);
1453  if (allow_eit)
1454  {
1456  SetFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1458  QDateTime::currentDateTime().addYears(1);
1459  }
1460  else
1461  {
1462  LOG(VB_EIT, LOG_INFO, LOC + QString(
1463  "Postponing EIT scan on input [%1] "
1464  "because input %2 is busy")
1465  .arg(m_inputId).arg(busy_input.m_inputId));
1466  m_eitScanStartTime = m_eitScanStartTime.addSecs(300);
1467  }
1468  s_inputsLock.unlock();
1469  }
1470  }
1471 
1472  // We should be no more than a few thousand milliseconds,
1473  // as the end recording code does not have a trigger...
1474  // NOTE: If you change anything here, make sure that
1475  // WaitforEventThreadSleep() will still work...
1476  if (m_tuningRequests.empty() && !m_changeState)
1477  {
1478  lock.unlock(); // stateChangeLock
1479 
1480  {
1481  QMutexLocker locker(&m_triggerEventSleepLock);
1483  m_triggerEventSleepWait.wakeAll();
1484  }
1485 
1486  sched_yield();
1487 
1488  {
1489  QMutexLocker locker(&m_triggerEventLoopLock);
1490  // We check triggerEventLoopSignal because it is possible
1491  // that WakeEventLoop() was called since we
1492  // unlocked the stateChangeLock
1494  {
1496  &m_triggerEventLoopLock, 1000 /* ms */);
1497  }
1498  m_triggerEventLoopSignal = false;
1499  }
1500 
1501  lock.relock(); // stateChangeLock
1502  }
1503  }
1504 
1505  if (GetState() != kState_None)
1506  {
1509  }
1510 
1511  TeardownAll();
1512 }
1513 
1519 bool TVRec::WaitForEventThreadSleep(bool wake, ulong time)
1520 {
1521  bool ok = false;
1522  MythTimer t;
1523  t.start();
1524 
1525  while (!ok && ((unsigned long) t.elapsed()) < time)
1526  {
1527  MythTimer t2;
1528  t2.start();
1529 
1530  if (wake)
1531  WakeEventLoop();
1532 
1533  m_stateChangeLock.unlock();
1534 
1535  sched_yield();
1536 
1537  {
1538  QMutexLocker locker(&m_triggerEventSleepLock);
1541  m_triggerEventSleepSignal = false;
1542  }
1543 
1544  m_stateChangeLock.lock();
1545 
1546  // verify that we were triggered.
1547  ok = (m_tuningRequests.empty() && !m_changeState);
1548 
1549  int te = t2.elapsed();
1550  if (!ok && te < 10)
1551  std::this_thread::sleep_for(std::chrono::microseconds(10-te));
1552  }
1553  return ok;
1554 }
1555 
1557 {
1558  QMutexLocker pendlock(&m_pendingRecLock);
1559 
1560  for (auto it = m_pendingRecordings.begin(); it != m_pendingRecordings.end();)
1561  {
1562  auto next = it; ++next;
1563  if (MythDate::current() > (*it).m_recordingStart.addSecs(30))
1564  {
1565  LOG(VB_RECORD, LOG_INFO, LOC + "Deleting stale pending recording " +
1566  QString("[%1] '%2'")
1567  .arg((*it).m_info->GetInputID())
1568  .arg((*it).m_info->GetTitle()));
1569 
1570  delete (*it).m_info;
1571  m_pendingRecordings.erase(it);
1572  }
1573  it = next;
1574  }
1575 
1576  if (m_pendingRecordings.empty())
1577  return;
1578 
1579  // Make sure EIT scan is stopped so it does't interfere
1581  {
1582  LOG(VB_CHANNEL, LOG_INFO,
1583  LOC + "Stopping active EIT scan for pending recording.");
1585  }
1586 
1587  // If we have a pending recording and AskAllowRecording
1588  // or DoNotAskAllowRecording is set and the frontend is
1589  // ready send an ASK_RECORDING query to frontend.
1590 
1591  bool has_rec = false;
1592  auto it = m_pendingRecordings.begin();
1593  if ((1 == m_pendingRecordings.size()) &&
1594  (*it).m_ask &&
1595  ((*it).m_info->GetInputID() == m_inputId) &&
1597  {
1599  has_rec = m_pseudoLiveTVRecording &&
1601  (*it).m_recordingStart);
1602  }
1603 
1604  for (it = m_pendingRecordings.begin(); it != m_pendingRecordings.end(); ++it)
1605  {
1606  if (!(*it).m_ask && !(*it).m_doNotAsk)
1607  continue;
1608 
1609  int timeuntil = ((*it).m_doNotAsk) ?
1610  -1: MythDate::current().secsTo((*it).m_recordingStart);
1611 
1612  if (has_rec)
1613  (*it).m_canceled = true;
1614 
1615  QString query = QString("ASK_RECORDING %1 %2 %3 %4")
1616  .arg(m_inputId)
1617  .arg(timeuntil)
1618  .arg(has_rec ? 1 : 0)
1619  .arg((*it).m_hasLaterShowing ? 1 : 0);
1620 
1621  LOG(VB_GENERAL, LOG_INFO, LOC + query);
1622 
1623  QStringList msg;
1624  (*it).m_info->ToStringList(msg);
1625  MythEvent me(query, msg);
1626  gCoreContext->dispatch(me);
1627 
1628  (*it).m_ask = (*it).m_doNotAsk = false;
1629  }
1630 }
1631 
1633  uint &parentid,
1634  GeneralDBOptions &gen_opts,
1635  DVBDBOptions &dvb_opts,
1636  FireWireDBOptions &firewire_opts)
1637 {
1638  int testnum = 0;
1639  QString test;
1640 
1642  query.prepare(
1643  "SELECT videodevice, vbidevice, audiodevice, "
1644  " audioratelimit, cardtype, "
1645  " skipbtaudio, signal_timeout, channel_timeout, "
1646  " dvb_wait_for_seqstart, "
1647  ""
1648  " dvb_on_demand, dvb_tuning_delay, dvb_eitscan,"
1649  ""
1650  " firewire_speed, firewire_model, firewire_connection, "
1651  " parentid "
1652  ""
1653  "FROM capturecard "
1654  "WHERE cardid = :INPUTID");
1655  query.bindValue(":INPUTID", inputid);
1656 
1657  if (!query.exec() || !query.isActive())
1658  {
1659  MythDB::DBError("getdevices", query);
1660  return false;
1661  }
1662 
1663  if (!query.next())
1664  return false;
1665 
1666  // General options
1667  test = query.value(0).toString();
1668  if (!test.isEmpty())
1669  gen_opts.m_videoDev = test;
1670 
1671  test = query.value(1).toString();
1672  if (!test.isEmpty())
1673  gen_opts.m_vbiDev = test;
1674 
1675  test = query.value(2).toString();
1676  if (!test.isEmpty())
1677  gen_opts.m_audioDev = test;
1678 
1679  gen_opts.m_audioSampleRate = max(testnum, query.value(3).toInt());
1680 
1681  test = query.value(4).toString();
1682  if (!test.isEmpty())
1683  gen_opts.m_inputType = test;
1684 
1685  gen_opts.m_skipBtAudio = query.value(5).toBool();
1686 
1687  gen_opts.m_signalTimeout = (uint) max(query.value(6).toInt(), 0);
1688  gen_opts.m_channelTimeout = (uint) max(query.value(7).toInt(), 0);
1689 
1690  // We should have at least 100 ms to acquire tables...
1691  int table_timeout = ((int)gen_opts.m_channelTimeout -
1692  (int)gen_opts.m_signalTimeout);
1693  if (table_timeout < 100)
1694  gen_opts.m_channelTimeout = gen_opts.m_signalTimeout + 2500;
1695 
1696  gen_opts.m_waitForSeqstart = query.value(8).toBool();
1697 
1698  // DVB options
1699  uint dvboff = 9;
1700  dvb_opts.m_dvbOnDemand = query.value(dvboff + 0).toBool();
1701  dvb_opts.m_dvbTuningDelay = query.value(dvboff + 1).toUInt();
1702  dvb_opts.m_dvbEitScan = query.value(dvboff + 2).toBool();
1703 
1704  // Firewire options
1705  uint fireoff = dvboff + 3;
1706  firewire_opts.m_speed = query.value(fireoff + 0).toUInt();
1707 
1708  test = query.value(fireoff + 1).toString();
1709  if (!test.isEmpty())
1710  firewire_opts.m_model = test;
1711 
1712  firewire_opts.m_connection = query.value(fireoff + 2).toUInt();
1713 
1714  parentid = query.value(15).toUInt();
1715 
1716  return true;
1717 }
1718 
1720 {
1721  QString startchan;
1722 
1723  LOG(VB_RECORD, LOG_INFO, LOC2 + QString("GetStartChannel[%1]")
1724  .arg(inputid));
1725 
1726  // Get last tuned channel from database, to use as starting channel
1728  query.prepare(
1729  "SELECT startchan "
1730  "FROM capturecard "
1731  "WHERE capturecard.cardid = :INPUTID");
1732  query.bindValue(":INPUTID", inputid);
1733 
1734  if (!query.exec() || !query.isActive())
1735  {
1736  MythDB::DBError("getstartchan", query);
1737  }
1738  else if (query.next())
1739  {
1740  startchan = query.value(0).toString();
1741  if (!startchan.isEmpty())
1742  {
1743  LOG(VB_CHANNEL, LOG_INFO, LOC2 + QString("Start channel: %1")
1744  .arg(startchan));
1745  return startchan;
1746  }
1747  }
1748 
1749  // If we failed to get the last tuned channel,
1750  // get a valid channel on our current input.
1751  query.prepare(
1752  "SELECT channum "
1753  "FROM capturecard, channel "
1754  "WHERE deleted IS NULL AND "
1755  " channel.sourceid = capturecard.sourceid AND "
1756  " capturecard.cardid = :INPUTID");
1757  query.bindValue(":INPUTID", inputid);
1758 
1759  if (!query.exec() || !query.isActive())
1760  {
1761  MythDB::DBError("getstartchan2", query);
1762  }
1763  while (query.next())
1764  {
1765  startchan = query.value(0).toString();
1766  if (!startchan.isEmpty())
1767  {
1768  LOG(VB_GENERAL, LOG_ERR, LOC2 + QString("Start channel from DB is "
1769  "empty, setting to '%1' instead.").arg(startchan));
1770  return startchan;
1771  }
1772  }
1773 
1774  // If we failed to get a channel on our current input,
1775  // widen search to any input.
1776  query.prepare(
1777  "SELECT channum, inputname "
1778  "FROM capturecard, channel "
1779  "WHERE deleted IS NULL AND "
1780  " channel.sourceid = capturecard.sourceid AND "
1781  " capturecard.cardid = :INPUTID");
1782  query.bindValue(":INPUTID", inputid);
1783 
1784  if (!query.exec() || !query.isActive())
1785  {
1786  MythDB::DBError("getstartchan3", query);
1787  }
1788  while (query.next())
1789  {
1790  startchan = query.value(0).toString();
1791  if (!startchan.isEmpty())
1792  {
1793  LOG(VB_GENERAL, LOG_ERR, LOC2 + QString("Start channel invalid, "
1794  "setting to '%1' on input %2 instead.").arg(startchan)
1795  .arg(query.value(1).toString()));
1796  return startchan;
1797  }
1798  }
1799 
1800  // If there are no valid channels, just use a random channel
1801  startchan = "3";
1802  LOG(VB_GENERAL, LOG_ERR, LOC2 + QString("Problem finding starting channel, "
1803  "setting to default of '%1'.").arg(startchan));
1804  return startchan;
1805 }
1806 
1807 static void GetPidsToCache(DTVSignalMonitor *dtvMon, pid_cache_t &pid_cache)
1808 {
1809  if (!dtvMon->GetATSCStreamData())
1810  return;
1811 
1812  const MasterGuideTable *mgt = dtvMon->GetATSCStreamData()->GetCachedMGT();
1813  if (!mgt)
1814  return;
1815 
1816  for (uint i = 0; i < mgt->TableCount(); ++i)
1817  {
1818  pid_cache_item_t item(mgt->TablePID(i), mgt->TableType(i));
1819  pid_cache.push_back(item);
1820  }
1821  dtvMon->GetATSCStreamData()->ReturnCachedTable(mgt);
1822 }
1823 
1825 {
1826  pid_cache_t pid_cache;
1827  channel->GetCachedPids(pid_cache);
1828  bool vctpid_cached = false;
1829  for (auto pid : pid_cache)
1830  {
1831  if ((pid.GetTableID() == TableID::TVCT) ||
1832  (pid.GetTableID() == TableID::CVCT))
1833  {
1834  vctpid_cached = true;
1835  dtvMon->GetATSCStreamData()->AddListeningPID(pid.GetPID());
1836  }
1837  }
1838  return vctpid_cached;
1839 }
1840 
1857 {
1858  LOG(VB_RECORD, LOG_INFO, LOC + "Setting up table monitoring.");
1859 
1861  DTVChannel *dtvchan = GetDTVChannel();
1862  if (!sm || !dtvchan)
1863  {
1864  LOG(VB_GENERAL, LOG_ERR, LOC + "Setting up table monitoring.");
1865  return false;
1866  }
1867 
1868  MPEGStreamData *sd = nullptr;
1869  if (GetDTVRecorder())
1870  {
1871  sd = GetDTVRecorder()->GetStreamData();
1872  sd->SetCaching(true);
1873  }
1874 
1875  QString recording_type = "all";
1879  const StandardSetting *setting = profile.byName("recordingtype");
1880  if (setting)
1881  recording_type = setting->getValue();
1882 
1883  const QString tuningmode = dtvchan->GetTuningMode();
1884 
1885  // Check if this is an ATSC Channel
1886  int major = dtvchan->GetMajorChannel();
1887  int minor = dtvchan->GetMinorChannel();
1888  if ((minor > 0) && (tuningmode == "atsc"))
1889  {
1890  QString msg = QString("ATSC channel: %1_%2").arg(major).arg(minor);
1891  LOG(VB_RECORD, LOG_INFO, LOC + msg);
1892 
1893  auto *asd = dynamic_cast<ATSCStreamData*>(sd);
1894  if (!asd)
1895  {
1896  sd = asd = new ATSCStreamData(major, minor, m_inputId);
1897  sd->SetCaching(true);
1898  if (GetDTVRecorder())
1899  GetDTVRecorder()->SetStreamData(asd);
1900  }
1901 
1902  asd->Reset();
1903  sm->SetStreamData(sd);
1904  sm->SetChannel(major, minor);
1905  sd->SetRecordingType(recording_type);
1906 
1907  // Try to get pid of VCT from cache and
1908  // require MGT if we don't have VCT pid.
1909  if (!ApplyCachedPids(sm, dtvchan))
1911 
1912  LOG(VB_RECORD, LOG_INFO, LOC +
1913  "Successfully set up ATSC table monitoring.");
1914  return true;
1915  }
1916 
1917  // Check if this is an DVB channel
1918  int progNum = dtvchan->GetProgramNumber();
1919  if ((progNum >= 0) && (tuningmode == "dvb") && (m_genOpt.m_inputType != "VBOX"))
1920  {
1921  int netid = dtvchan->GetOriginalNetworkID();
1922  int tsid = dtvchan->GetTransportID();
1923 
1924  auto *dsd = dynamic_cast<DVBStreamData*>(sd);
1925  if (!dsd)
1926  {
1927  sd = dsd = new DVBStreamData(netid, tsid, progNum, m_inputId);
1928  sd->SetCaching(true);
1929  if (GetDTVRecorder())
1930  GetDTVRecorder()->SetStreamData(dsd);
1931  }
1932 
1933  LOG(VB_RECORD, LOG_INFO, LOC +
1934  QString("DVB service_id %1 on net_id %2 tsid %3")
1935  .arg(progNum).arg(netid).arg(tsid));
1936 
1938 
1939  dsd->Reset();
1940  sm->SetStreamData(sd);
1941  sm->SetDVBService(netid, tsid, progNum);
1942  sd->SetRecordingType(recording_type);
1943 
1947  sm->SetRotorTarget(1.0F);
1948 
1949  if (EITscan)
1950  {
1952  sm->IgnoreEncrypted(true);
1953  }
1954 
1955  LOG(VB_RECORD, LOG_INFO, LOC +
1956  "Successfully set up DVB table monitoring.");
1957  return true;
1958  }
1959 
1960  // Check if this is an MPEG channel
1961  if (progNum >= 0)
1962  {
1963  if (!sd)
1964  {
1965  sd = new MPEGStreamData(progNum, m_inputId, true);
1966  sd->SetCaching(true);
1967  if (GetDTVRecorder())
1969  }
1970 
1971  QString msg = QString("MPEG program number: %1").arg(progNum);
1972  LOG(VB_RECORD, LOG_INFO, LOC + msg);
1973 
1975 
1976  sd->Reset();
1977  sm->SetStreamData(sd);
1978  sm->SetProgramNumber(progNum);
1979  sd->SetRecordingType(recording_type);
1980 
1984  sm->SetRotorTarget(1.0F);
1985 
1986  if (EITscan)
1987  {
1989  sm->IgnoreEncrypted(true);
1990  }
1991 
1992  LOG(VB_RECORD, LOG_INFO, LOC +
1993  "Successfully set up MPEG table monitoring.");
1994  return true;
1995  }
1996 
1997  // If this is not an ATSC, DVB or MPEG channel then check to make sure
1998  // that we have permanent pidcache entries.
1999  bool ok = false;
2000  if (GetDTVChannel())
2001  {
2002  pid_cache_t pid_cache;
2003  GetDTVChannel()->GetCachedPids(pid_cache);
2004  for (auto item = pid_cache.cbegin(); !ok && item != pid_cache.cend(); ++item)
2005  ok |= item->IsPermanent();
2006  }
2007 
2008  if (!ok)
2009  {
2010  QString msg = "No valid DTV info, ATSC maj(%1) min(%2), MPEG pn(%3)";
2011  LOG(VB_GENERAL, LOG_ERR, LOC + msg.arg(major).arg(minor).arg(progNum));
2012  }
2013  else
2014  {
2015  LOG(VB_RECORD, LOG_INFO, LOC +
2016  "Successfully set up raw pid monitoring.");
2017  }
2018 
2019  return ok;
2020 }
2021 
2036 bool TVRec::SetupSignalMonitor(bool tablemon, bool EITscan, bool notify)
2037 {
2038  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetupSignalMonitor(%1, %2)")
2039  .arg(tablemon).arg(notify));
2040 
2041  // if it already exists, there no need to initialize it
2042  if (m_signalMonitor)
2043  return true;
2044 
2045  // if there is no channel object we can't monitor it
2046  if (!m_channel)
2047  return false;
2048 
2049  // nothing to monitor here either (DummyChannel)
2050  if (m_genOpt.m_inputType == "IMPORT" || m_genOpt.m_inputType == "DEMO")
2051  return true;
2052 
2053  // make sure statics are initialized
2055 
2058  m_channel, false);
2059 
2060  if (m_signalMonitor)
2061  {
2062  LOG(VB_RECORD, LOG_INFO, LOC + "Signal monitor successfully created");
2063  // If this is a monitor for Digital TV, initialize table monitors
2064  if (GetDTVSignalMonitor() && tablemon &&
2065  !SetupDTVSignalMonitor(EITscan))
2066  {
2067  LOG(VB_GENERAL, LOG_ERR, LOC +
2068  "Failed to setup digital signal monitoring");
2069 
2070  return false;
2071  }
2072 
2075  kSignalMonitoringRate * 5 :
2078 
2079  // Start the monitoring thread
2081  }
2082 
2083  return true;
2084 }
2085 
2091 {
2092  if (!m_signalMonitor)
2093  return;
2094 
2095  LOG(VB_RECORD, LOG_INFO, LOC + "TeardownSignalMonitor() -- begin");
2096 
2097  // If this is a DTV signal monitor, save any pids we know about.
2099  DTVChannel *dtvChan = GetDTVChannel();
2100  if (dtvMon && dtvChan)
2101  {
2102  pid_cache_t pid_cache;
2103  GetPidsToCache(dtvMon, pid_cache);
2104  if (!pid_cache.empty())
2105  dtvChan->SaveCachedPids(pid_cache);
2106  }
2107 
2108  if (m_signalMonitor)
2109  {
2110  delete m_signalMonitor;
2111  m_signalMonitor = nullptr;
2112  }
2113 
2114  LOG(VB_RECORD, LOG_INFO, LOC + "TeardownSignalMonitor() -- end");
2115 }
2116 
2128 int TVRec::SetSignalMonitoringRate(int rate, int notifyFrontend)
2129 {
2130  QString msg = "SetSignalMonitoringRate(%1, %2)";
2131  LOG(VB_RECORD, LOG_INFO, LOC +
2132  msg.arg(rate).arg(notifyFrontend) + "-- start");
2133 
2134  QMutexLocker lock(&m_stateChangeLock);
2135 
2137  {
2138  LOG(VB_GENERAL, LOG_ERR, LOC +
2139  "Signal Monitoring is notsupported by your hardware.");
2140  return 0;
2141  }
2142 
2144  {
2145  LOG(VB_GENERAL, LOG_ERR, LOC +
2146  "Signal can only be monitored in LiveTV Mode.");
2147  return 0;
2148  }
2149 
2150  ClearFlags(kFlagRingBufferReady, __FILE__, __LINE__);
2151 
2152  TuningRequest req = (rate > 0) ?
2155 
2157 
2158  // Wait for RingBuffer reset
2159  while (!HasFlags(kFlagRingBufferReady))
2161  LOG(VB_RECORD, LOG_INFO, LOC +
2162  msg.arg(rate).arg(notifyFrontend) + " -- end");
2163  return 1;
2164 }
2165 
2167 {
2168  return dynamic_cast<DTVSignalMonitor*>(m_signalMonitor);
2169 }
2170 
2182 bool TVRec::ShouldSwitchToAnotherInput(const QString& chanid) const
2183 {
2184  QString msg("");
2186 
2187  if (!query.isConnected())
2188  return false;
2189 
2190  query.prepare("SELECT channel.channum, channel.callsign "
2191  "FROM channel "
2192  "WHERE channel.chanid = :CHANID");
2193  query.bindValue(":CHANID", chanid);
2194  if (!query.exec() || !query.next())
2195  {
2196  MythDB::DBError("ShouldSwitchToAnotherInput", query);
2197  return false;
2198  }
2199 
2200  QString channelname = query.value(0).toString();
2201  QString callsign = query.value(1).toString();
2202 
2203  query.prepare(
2204  "SELECT channel.channum "
2205  "FROM channel, capturecard "
2206  "WHERE deleted IS NULL AND "
2207  " ( channel.chanid = :CHANID OR "
2208  " ( channel.channum = :CHANNUM AND "
2209  " channel.callsign = :CALLSIGN ) "
2210  " ) AND "
2211  " channel.sourceid = capturecard.sourceid AND "
2212  " capturecard.cardid = :INPUTID");
2213  query.bindValue(":CHANID", chanid);
2214  query.bindValue(":CHANNUM", channelname);
2215  query.bindValue(":CALLSIGN", callsign);
2216  query.bindValue(":INPUTID", m_inputId);
2217 
2218  if (!query.exec() || !query.isActive())
2219  {
2220  MythDB::DBError("ShouldSwitchToAnotherInput", query);
2221  }
2222  else if (query.size() > 0)
2223  {
2224  msg = "Found channel (%1) on current input[%2].";
2225  LOG(VB_RECORD, LOG_INFO, LOC + msg.arg(channelname).arg(m_inputId));
2226  return false;
2227  }
2228 
2229  // We didn't find it on the current input, so now we check other inputs.
2230  query.prepare(
2231  "SELECT channel.channum, capturecard.cardid "
2232  "FROM channel, capturecard "
2233  "WHERE deleted IS NULL AND "
2234  " ( channel.chanid = :CHANID OR "
2235  " ( channel.channum = :CHANNUM AND "
2236  " channel.callsign = :CALLSIGN ) "
2237  " ) AND "
2238  " channel.sourceid = capturecard.sourceid AND "
2239  " capturecard.cardid != :INPUTID");
2240  query.bindValue(":CHANID", chanid);
2241  query.bindValue(":CHANNUM", channelname);
2242  query.bindValue(":CALLSIGN", callsign);
2243  query.bindValue(":INPUTID", m_inputId);
2244 
2245  if (!query.exec() || !query.isActive())
2246  {
2247  MythDB::DBError("ShouldSwitchToAnotherInput", query);
2248  }
2249  else if (query.next())
2250  {
2251  msg = QString("Found channel (%1) on different input(%2).")
2252  .arg(query.value(0).toString()).arg(query.value(1).toString());
2253  LOG(VB_RECORD, LOG_INFO, LOC + msg);
2254  return true;
2255  }
2256 
2257  msg = QString("Did not find channel(%1) on any input.").arg(channelname);
2258  LOG(VB_RECORD, LOG_ERR, LOC + msg);
2259  return false;
2260 }
2261 
2272 bool TVRec::CheckChannel(const QString& name) const
2273 {
2274  if (!m_channel)
2275  return false;
2276 
2277  return m_channel->CheckChannel(name);
2278 }
2279 
2283 static QString add_spacer(const QString &channel, const QString &spacer)
2284 {
2285  QString chan = channel;
2286  if ((chan.length() >= 2) && !spacer.isEmpty())
2287  return chan.left(chan.length()-1) + spacer + chan.right(1);
2288  return chan;
2289 }
2290 
2318 bool TVRec::CheckChannelPrefix(const QString &prefix,
2319  uint &complete_valid_channel_on_rec,
2320  bool &is_extra_char_useful,
2321  QString &needed_spacer) const
2322 {
2323 #if DEBUG_CHANNEL_PREFIX
2324  LOG(VB_GENERAL, LOG_DEBUG, QString("CheckChannelPrefix(%1)").arg(prefix));
2325 #endif
2326 
2327  static const uint kSpacerListSize = 5;
2328  static const char* s_spacers[kSpacerListSize] = { "", "_", "-", "#", "." };
2329 
2331  QString basequery = QString(
2332  "SELECT channel.chanid, channel.channum, capturecard.cardid "
2333  "FROM channel, capturecard "
2334  "WHERE deleted IS NULL AND "
2335  " channel.channum LIKE '%1%' AND "
2336  " channel.sourceid = capturecard.sourceid");
2337 
2338  QString inputquery[2] =
2339  {
2340  QString(" AND capturecard.cardid = '%1'").arg(m_inputId),
2341  QString(" AND capturecard.cardid != '%1'").arg(m_inputId),
2342  };
2343 
2344  vector<uint> fchanid;
2345  vector<QString> fchannum;
2346  vector<uint> finputid;
2347  vector<QString> fspacer;
2348 
2349  for (const auto & str : inputquery)
2350  {
2351  for (auto & spacer : s_spacers)
2352  {
2353  QString qprefix = add_spacer(
2354  prefix, (QString(spacer) == "_") ? "\\_" : spacer);
2355  query.prepare(basequery.arg(qprefix) + str);
2356 
2357  if (!query.exec() || !query.isActive())
2358  {
2359  MythDB::DBError("checkchannel -- locate channum", query);
2360  }
2361  else if (query.size())
2362  {
2363  while (query.next())
2364  {
2365  fchanid.push_back(query.value(0).toUInt());
2366  fchannum.push_back(query.value(1).toString());
2367  finputid.push_back(query.value(2).toUInt());
2368  fspacer.emplace_back(spacer);
2369 #if DEBUG_CHANNEL_PREFIX
2370  LOG(VB_GENERAL, LOG_DEBUG,
2371  QString("(%1,%2) Adding %3 rec %4")
2372  .arg(i).arg(j).arg(query.value(1).toString(),6)
2373  .arg(query.value(2).toUInt()));
2374 #endif
2375  }
2376  }
2377 
2378  if (prefix.length() < 2)
2379  break;
2380  }
2381  }
2382 
2383  // Now process the lists for the info we need...
2384  is_extra_char_useful = false;
2385  complete_valid_channel_on_rec = 0;
2386  needed_spacer.clear();
2387 
2388  if (fchanid.empty())
2389  return false;
2390 
2391  if (fchanid.size() == 1) // Unique channel...
2392  {
2393  needed_spacer = fspacer[0];
2394  bool nc = (fchannum[0] != add_spacer(prefix, fspacer[0]));
2395 
2396  complete_valid_channel_on_rec = (nc) ? 0 : finputid[0];
2397  is_extra_char_useful = nc;
2398  return true;
2399  }
2400 
2401  // If we get this far there is more than one channel
2402  // sharing the prefix we were given.
2403 
2404  // Is an extra characher useful for disambiguation?
2405  is_extra_char_useful = false;
2406  for (uint i = 0; (i < fchannum.size()) && !is_extra_char_useful; i++)
2407  {
2408  is_extra_char_useful = (fchannum[i] != add_spacer(prefix, fspacer[i]));
2409 #if DEBUG_CHANNEL_PREFIX
2410  LOG(VB_GENERAL, LOG_DEBUG, QString("is_extra_char_useful(%1!=%2): %3")
2411  .arg(fchannum[i]).arg(add_spacer(prefix, fspacer[i]))
2412  .arg(is_extra_char_useful));
2413 #endif
2414  }
2415 
2416  // Are any of the channels complete w/o spacer?
2417  // If so set complete_valid_channel_on_rec,
2418  // with a preference for our inputid.
2419  for (size_t i = 0; i < fchannum.size(); i++)
2420  {
2421  if (fchannum[i] == prefix)
2422  {
2423  complete_valid_channel_on_rec = finputid[i];
2424  if (finputid[i] == m_inputId)
2425  break;
2426  }
2427  }
2428 
2429  if (complete_valid_channel_on_rec != 0)
2430  return true;
2431 
2432  // Add a spacer, if one is needed to select a valid channel.
2433  bool spacer_needed = true;
2434  for (uint i = 0; (i < fspacer.size() && spacer_needed); i++)
2435  spacer_needed = !fspacer[i].isEmpty();
2436  if (spacer_needed)
2437  needed_spacer = fspacer[0];
2438 
2439  // If it isn't useful to wait for more characters,
2440  // then try to commit to any true match immediately.
2441  for (size_t i = 0; i < ((is_extra_char_useful) ? 0 : fchanid.size()); i++)
2442  {
2443  if (fchannum[i] == add_spacer(prefix, fspacer[i]))
2444  {
2445  needed_spacer = fspacer[i];
2446  complete_valid_channel_on_rec = finputid[i];
2447  return true;
2448  }
2449  }
2450 
2451  return true;
2452 }
2453 
2455  const QString &channum)
2456 {
2457  if (!m_recorder)
2458  return false;
2459 
2460  QString videoFilters = ChannelUtil::GetVideoFilters(sourceid, channum);
2461  if (!videoFilters.isEmpty())
2462  {
2463  m_recorder->SetVideoFilters(videoFilters);
2464  return true;
2465  }
2466 
2467  return false;
2468 }
2469 
2475 {
2476  return ((m_recorder && m_recorder->IsRecording()) ||
2478 }
2479 
2485 bool TVRec::IsBusy(InputInfo *busy_input, int time_buffer) const
2486 {
2487  InputInfo dummy;
2488  if (!busy_input)
2489  busy_input = &dummy;
2490 
2491  busy_input->Clear();
2492 
2493  if (!m_channel)
2494  return false;
2495 
2496  if (!m_channel->GetInputID())
2497  return false;
2498 
2499  uint chanid = 0;
2500 
2501  if (GetState() != kState_None)
2502  {
2503  busy_input->m_inputId = m_channel->GetInputID();
2504  chanid = m_channel->GetChanID();
2505  }
2506 
2507  PendingInfo pendinfo;
2508  bool has_pending = false;
2509  {
2510  m_pendingRecLock.lock();
2511  PendingMap::const_iterator it = m_pendingRecordings.find(m_inputId);
2512  has_pending = (it != m_pendingRecordings.end());
2513  if (has_pending)
2514  pendinfo = *it;
2515  m_pendingRecLock.unlock();
2516  }
2517 
2518  if (!busy_input->m_inputId && has_pending)
2519  {
2520  int timeLeft = MythDate::current()
2521  .secsTo(pendinfo.m_recordingStart);
2522 
2523  if (timeLeft <= time_buffer)
2524  {
2525  QString channum;
2526  QString input;
2527  if (pendinfo.m_info->QueryTuningInfo(channum, input))
2528  {
2529  busy_input->m_inputId = m_channel->GetInputID();
2530  chanid = pendinfo.m_info->GetChanID();
2531  }
2532  }
2533  }
2534 
2535  if (busy_input->m_inputId)
2536  {
2537  CardUtil::GetInputInfo(*busy_input);
2538  busy_input->m_chanId = chanid;
2539  busy_input->m_mplexId = ChannelUtil::GetMplexID(busy_input->m_chanId);
2540  busy_input->m_mplexId =
2541  (32767 == busy_input->m_mplexId) ? 0 : busy_input->m_mplexId;
2542  }
2543 
2544  return busy_input->m_inputId != 0U;
2545 }
2546 
2547 
2555 {
2556  QMutexLocker lock(&m_stateChangeLock);
2557 
2558  if (m_recorder)
2559  return m_recorder->GetFrameRate();
2560  return -1.0F;
2561 }
2562 
2570 {
2571  QMutexLocker lock(&m_stateChangeLock);
2572 
2573  if (m_recorder)
2574  return m_recorder->GetFramesWritten();
2575  return -1;
2576 }
2577 
2584 long long TVRec::GetFilePosition(void)
2585 {
2586  QMutexLocker lock(&m_stateChangeLock);
2587 
2588  if (m_buffer)
2589  return m_buffer->GetWritePosition();
2590  return -1;
2591 }
2592 
2600 int64_t TVRec::GetKeyframePosition(uint64_t desired) const
2601 {
2602  QMutexLocker lock(&m_stateChangeLock);
2603 
2604  if (m_recorder)
2605  return m_recorder->GetKeyframePosition(desired);
2606  return -1;
2607 }
2608 
2618  int64_t start, int64_t end, frm_pos_map_t &map) const
2619 {
2620  QMutexLocker lock(&m_stateChangeLock);
2621 
2622  if (m_recorder)
2623  return m_recorder->GetKeyframePositions(start, end, map);
2624 
2625  return false;
2626 }
2627 
2629  int64_t start, int64_t end, frm_pos_map_t &map) const
2630 {
2631  QMutexLocker lock(&m_stateChangeLock);
2632 
2633  if (m_recorder)
2634  return m_recorder->GetKeyframeDurations(start, end, map);
2635 
2636  return false;
2637 }
2638 
2644 long long TVRec::GetMaxBitrate(void) const
2645 {
2646  long long bitrate = 0;
2647  if (m_genOpt.m_inputType == "MPEG")
2648  { // NOLINT(bugprone-branch-clone)
2649  bitrate = 10080000LL; // use DVD max bit rate
2650  }
2651  else if (m_genOpt.m_inputType == "HDPVR")
2652  {
2653  bitrate = 20200000LL; // Peek bit rate for HD-PVR
2654  }
2656  {
2657  bitrate = 22200000LL; // 1080i
2658  }
2659  else // frame grabber
2660  {
2661  bitrate = 10080000LL; // use DVD max bit rate, probably too big
2662  }
2663 
2664  return bitrate;
2665 }
2666 
2672 void TVRec::SpawnLiveTV(LiveTVChain *newchain, bool pip, QString startchan)
2673 {
2674  QMutexLocker lock(&m_stateChangeLock);
2675 
2676  m_tvChain = newchain;
2677  m_tvChain->IncrRef(); // mark it for TVRec use
2678  m_tvChain->ReloadAll();
2679 
2680  QString hostprefix = MythCoreContext::GenMythURL(
2683 
2684  m_tvChain->SetHostPrefix(hostprefix);
2686 
2687  m_isPip = pip;
2688  m_liveTVStartChannel = std::move(startchan);
2689 
2690  // Change to WatchingLiveTV
2692  // Wait for state change to take effect
2694 
2695  // Make sure StartRecording can't steal our tuner
2696  SetFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2697 }
2698 
2702 QString TVRec::GetChainID(void)
2703 {
2704  if (m_tvChain)
2705  return m_tvChain->GetID();
2706  return "";
2707 }
2708 
2718 {
2719  QMutexLocker lock(&m_stateChangeLock);
2720 
2722  return; // already stopped
2723 
2724  if (!m_curRecording)
2725  return;
2726 
2727  const QString recgrp = m_curRecording->QueryRecordingGroup();
2729 
2730  if (recgrp != "LiveTV" && !m_pseudoLiveTVRecording)
2731  {
2732  // User wants this recording to continue
2734  }
2735  else if (recgrp == "LiveTV" && m_pseudoLiveTVRecording)
2736  {
2737  // User wants to abandon scheduled recording
2738  SetPseudoLiveTVRecording(nullptr);
2739  }
2740 }
2741 
2752 {
2753  if (!m_channel)
2754  return;
2755 
2756  // Notify scheduler of the recording.
2757  // + set up recording so it can be resumed
2758  rec->SetInputID(m_inputId);
2760 
2761  if (rec->GetRecordingRuleType() == kNotRecording)
2762  {
2765  }
2766 
2767  // + remove any end offset which would mismatch the live session
2768  rec->GetRecordingRule()->m_endOffset = 0;
2769 
2770  // + save RecStatus::Inactive recstatus to so that a reschedule call
2771  // doesn't start recording this on another input before we
2772  // send the SCHEDULER_ADD_RECORDING message to the scheduler.
2774  rec->AddHistory(false);
2775 
2776  // + save RecordingRule so that we get a recordid
2777  // (don't allow RescheduleMatch(), avoiding unneeded reschedule)
2778  rec->GetRecordingRule()->Save(false);
2779 
2780  // + save recordid to recorded entry
2781  rec->ApplyRecordRecID();
2782 
2783  // + set proper recstatus (saved later)
2785 
2786  // + pass proginfo to scheduler and reschedule
2787  QStringList prog;
2788  rec->ToStringList(prog);
2789  MythEvent me("SCHEDULER_ADD_RECORDING", prog);
2790  gCoreContext->dispatch(me);
2791 
2792  // Allow scheduler to end this recording before post-roll,
2793  // if it has another recording for this recorder.
2794  ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2795 }
2796 
2798  RecordingProfile *recpro, int line)
2799 {
2800  if (kAutoRunProfile == t)
2801  {
2803  if (!recpro)
2804  {
2805  LoadProfile(nullptr, rec, profile);
2806  recpro = &profile;
2807  }
2808  m_autoRunJobs[rec->MakeUniqueKey()] =
2809  init_jobs(rec, *recpro, m_runJobOnHostOnly,
2811  }
2812  else
2813  {
2815  }
2816  LOG(VB_JOBQUEUE, LOG_INFO,
2817  QString("InitAutoRunJobs for %1, line %2 -> 0x%3")
2818  .arg(rec->MakeUniqueKey()).arg(line)
2819  .arg(m_autoRunJobs[rec->MakeUniqueKey()],0,16));
2820 }
2821 
2833 void TVRec::SetLiveRecording(int recording)
2834 {
2835  LOG(VB_GENERAL, LOG_INFO, LOC +
2836  QString("SetLiveRecording(%1)").arg(recording));
2837  QMutexLocker locker(&m_stateChangeLock);
2838 
2839  (void) recording;
2840 
2842  bool was_rec = m_pseudoLiveTVRecording;
2844  if (was_rec && !m_pseudoLiveTVRecording)
2845  {
2846  LOG(VB_GENERAL, LOG_INFO, LOC + "SetLiveRecording() -- cancel");
2847  // cancel -- 'recording' should be 0 or -1
2848  SetFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2849  m_curRecording->SetRecordingGroup("LiveTV");
2850  InitAutoRunJobs(m_curRecording, kAutoRunNone, nullptr, __LINE__);
2851  }
2852  else if (!was_rec && m_pseudoLiveTVRecording)
2853  {
2854  LOG(VB_GENERAL, LOG_INFO, LOC + "SetLiveRecording() -- record");
2855  // record -- 'recording' should be 1 or -1
2856 
2857  // If the last recording was flagged for keeping
2858  // in the frontend, then add the recording rule
2859  // so that transcode, commfrag, etc can be run.
2862  recstat = m_curRecording->GetRecordingStatus();
2863  m_curRecording->SetRecordingGroup("Default");
2864  InitAutoRunJobs(m_curRecording, kAutoRunProfile, nullptr, __LINE__);
2865  }
2866 
2867  MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
2869  .arg(m_curRecording->GetChanID())
2871  .arg(recstat)
2873 
2874  gCoreContext->dispatch(me);
2875 }
2876 
2882 {
2883  QMutexLocker lock(&m_stateChangeLock);
2884  LOG(VB_RECORD, LOG_INFO, LOC +
2885  QString("StopLiveTV(void) curRec: 0x%1 pseudoRec: 0x%2")
2886  .arg((uint64_t)m_curRecording,0,16)
2887  .arg((uint64_t)m_pseudoLiveTVRecording,0,16));
2888 
2890  return;
2891 
2892  bool hadPseudoLiveTVRec = m_pseudoLiveTVRecording;
2894 
2895  if (!hadPseudoLiveTVRec && m_pseudoLiveTVRecording)
2897 
2898  // Figure out next state and if needed recording end time.
2899  TVState next_state = kState_None;
2901  {
2903  next_state = kState_RecordingOnly;
2904  }
2905 
2906  // Change to the appropriate state
2907  ChangeState(next_state);
2908 
2909  // Wait for state change to take effect...
2911 
2912  // We are done with the tvchain...
2913  if (m_tvChain)
2914  {
2915  m_tvChain->DecrRef();
2916  }
2917  m_tvChain = nullptr;
2918 }
2919 
2929 {
2930  QMutexLocker lock(&m_stateChangeLock);
2931 
2932  if (!m_recorder)
2933  {
2934  LOG(VB_GENERAL, LOG_ERR, LOC +
2935  "PauseRecorder() called with no recorder");
2936  return;
2937  }
2938 
2939  m_recorder->Pause();
2940 }
2941 
2948 {
2949  if (m_pauseNotify)
2950  WakeEventLoop();
2951 }
2952 
2956 void TVRec::ToggleChannelFavorite(const QString& changroupname)
2957 {
2958  QMutexLocker lock(&m_stateChangeLock);
2959 
2960  if (!m_channel)
2961  return;
2962 
2963  // Get current channel id...
2964  uint sourceid = m_channel->GetSourceID();
2965  QString channum = m_channel->GetChannelName();
2966  uint chanid = ChannelUtil::GetChanID(sourceid, channum);
2967 
2968  if (!chanid)
2969  {
2970  LOG(VB_GENERAL, LOG_ERR, LOC +
2971  QString("Channel: \'%1\' was not found in the database.\n"
2972  "\t\tMost likely, the 'starting channel' for this "
2973  "Input Connection is invalid.\n"
2974  "\t\tCould not toggle favorite.").arg(channum));
2975  return;
2976  }
2977 
2978  int changrpid = ChannelGroup::GetChannelGroupId(changroupname);
2979  if (changrpid <1)
2980  {
2981  LOG(VB_RECORD, LOG_ERR, LOC +
2982  QString("ToggleChannelFavorite: Invalid channel group name %1,")
2983  .arg(changroupname));
2984  }
2985  else
2986  {
2987  bool result = ChannelGroup::ToggleChannel(chanid, changrpid, true);
2988 
2989  if (!result)
2990  LOG(VB_RECORD, LOG_ERR, LOC + "Unable to toggle channel favorite.");
2991  else
2992  {
2993  LOG(VB_RECORD, LOG_INFO, LOC +
2994  QString("Toggled channel favorite.channum %1, chan group %2")
2995  .arg(channum).arg(changroupname));
2996  }
2997  }
2998 }
2999 
3006 {
3007  QMutexLocker lock(&m_stateChangeLock);
3008  if (!m_channel)
3009  return -1;
3010 
3011  int ret = m_channel->GetPictureAttribute(attr);
3012 
3013  return (ret < 0) ? -1 : ret / 655;
3014 }
3015 
3024  PictureAttribute attr,
3025  bool direction)
3026 {
3027  QMutexLocker lock(&m_stateChangeLock);
3028  if (!m_channel)
3029  return -1;
3030 
3031  int ret = m_channel->ChangePictureAttribute(type, attr, direction);
3032 
3033  return (ret < 0) ? -1 : ret / 655;
3034 }
3035 
3039 QString TVRec::GetInput(void) const
3040 {
3041  if (m_channel)
3042  return m_channel->GetInputName();
3043  return QString();
3044 }
3045 
3050 {
3051  if (m_channel)
3052  return m_channel->GetSourceID();
3053  return 0;
3054 }
3055 
3064 QString TVRec::SetInput(QString input)
3065 {
3066  QMutexLocker lock(&m_stateChangeLock);
3067  QString origIn = input;
3068  LOG(VB_RECORD, LOG_INFO, LOC + "SetInput(" + input + ") -- begin");
3069 
3070  if (!m_channel)
3071  {
3072  LOG(VB_RECORD, LOG_INFO, LOC + "SetInput() -- end no channel class");
3073  return QString();
3074  }
3075 
3076  LOG(VB_RECORD, LOG_INFO, LOC + "SetInput(" + origIn + ":" + input +
3077  ") -- end nothing to do");
3078  return input;
3079 }
3080 
3090 void TVRec::SetChannel(const QString& name, uint requestType)
3091 {
3092  QMutexLocker locker1(&m_setChannelLock);
3093  QMutexLocker locker2(&m_stateChangeLock);
3094 
3095  LOG(VB_CHANNEL, LOG_INFO, LOC +
3096  QString("SetChannel(%1) -- begin").arg(name));
3097 
3098  // Detect tuning request type if needed
3099  if (requestType & kFlagDetect)
3100  {
3102  requestType = m_lastTuningRequest.m_flags & (kFlagRec | kFlagNoRec);
3103  }
3104 
3105  // Clear the RingBuffer reset flag, in case we wait for a reset below
3106  ClearFlags(kFlagRingBufferReady, __FILE__, __LINE__);
3107 
3108  // Clear out any EITScan channel change requests
3109  auto it = m_tuningRequests.begin();
3110  while (it != m_tuningRequests.end())
3111  {
3112  if ((*it).m_flags & kFlagEITScan)
3113  it = m_tuningRequests.erase(it);
3114  else
3115  ++it;
3116  }
3117 
3118  // Actually add the tuning request to the queue, and
3119  // then wait for it to start tuning
3120  m_tuningRequests.enqueue(TuningRequest(requestType, name));
3122 
3123  // If we are using a recorder, wait for a RingBuffer reset
3124  if (requestType & kFlagRec)
3125  {
3126  while (!HasFlags(kFlagRingBufferReady))
3128  }
3129  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("SetChannel(%1) -- end").arg(name));
3130 }
3131 
3139 bool TVRec::QueueEITChannelChange(const QString &name)
3140 {
3141  LOG(VB_CHANNEL, LOG_INFO, LOC +
3142  QString("QueueEITChannelChange(%1) -- begin").arg(name));
3143 
3144  bool ok = false;
3145  if (m_setChannelLock.tryLock())
3146  {
3147  if (m_stateChangeLock.tryLock())
3148  {
3149  if (m_tuningRequests.empty())
3150  {
3152  ok = true;
3153  }
3154  m_stateChangeLock.unlock();
3155  }
3156  m_setChannelLock.unlock();
3157  }
3158 
3159  LOG(VB_CHANNEL, LOG_INFO, LOC +
3160  QString("QueueEITChannelChange(%1) -- end --> %2").arg(name).arg(ok));
3161 
3162  return ok;
3163 }
3164 
3166  QString &title, QString &subtitle,
3167  QString &desc, QString &category,
3168  QString &starttime, QString &endtime,
3169  QString &callsign, QString &iconpath,
3170  QString &channum, uint &sourceChanid,
3171  QString &seriesid, QString &programid)
3172 {
3173  QString compare = "<=";
3174  QString sortorder = "desc";
3175  uint chanid = 0;
3176 
3177  if (sourceChanid)
3178  {
3179  chanid = sourceChanid;
3180 
3181  if (BROWSE_UP == direction)
3182  chanid = m_channel->GetNextChannel(chanid, CHANNEL_DIRECTION_UP);
3183  else if (BROWSE_DOWN == direction)
3184  chanid = m_channel->GetNextChannel(chanid, CHANNEL_DIRECTION_DOWN);
3185  else if (BROWSE_FAVORITE == direction)
3186  {
3187  chanid = m_channel->GetNextChannel(
3188  chanid, CHANNEL_DIRECTION_FAVORITE);
3189  }
3190  else if (BROWSE_LEFT == direction)
3191  {
3192  compare = "<";
3193  }
3194  else if (BROWSE_RIGHT == direction)
3195  {
3196  compare = ">";
3197  sortorder = "asc";
3198  }
3199  }
3200 
3201  if (!chanid)
3202  {
3203  if (BROWSE_SAME == direction)
3204  chanid = m_channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
3205  else if (BROWSE_UP == direction)
3206  chanid = m_channel->GetNextChannel(channum, CHANNEL_DIRECTION_UP);
3207  else if (BROWSE_DOWN == direction)
3208  chanid = m_channel->GetNextChannel(channum, CHANNEL_DIRECTION_DOWN);
3209  else if (BROWSE_FAVORITE == direction)
3210  {
3211  chanid = m_channel->GetNextChannel(channum,
3213  }
3214  else if (BROWSE_LEFT == direction)
3215  {
3216  chanid = m_channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
3217  compare = "<";
3218  }
3219  else if (BROWSE_RIGHT == direction)
3220  {
3221  chanid = m_channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
3222  compare = ">";
3223  sortorder = "asc";
3224  }
3225  }
3226 
3227  QString querystr = QString(
3228  "SELECT title, subtitle, description, category, "
3229  " starttime, endtime, callsign, icon, "
3230  " channum, seriesid, programid "
3231  "FROM program, channel "
3232  "WHERE program.chanid = channel.chanid AND "
3233  " channel.chanid = :CHANID AND "
3234  " starttime %1 :STARTTIME "
3235  "ORDER BY starttime %2 "
3236  "LIMIT 1").arg(compare).arg(sortorder);
3237 
3239  query.prepare(querystr);
3240  query.bindValue(":CHANID", chanid);
3241  query.bindValue(":STARTTIME", starttime);
3242 
3243  // Clear everything now in case either query fails.
3244  title = subtitle = desc = category = "";
3245  starttime = endtime = callsign = iconpath = "";
3246  channum = seriesid = programid = "";
3247  sourceChanid = 0;
3248 
3249  // Try to get the program info
3250  if (!query.exec() && !query.isActive())
3251  {
3252  MythDB::DBError("GetNextProgram -- get program info", query);
3253  }
3254  else if (query.next())
3255  {
3256  title = query.value(0).toString();
3257  subtitle = query.value(1).toString();
3258  desc = query.value(2).toString();
3259  category = query.value(3).toString();
3260  starttime = query.value(4).toString();
3261  endtime = query.value(5).toString();
3262  callsign = query.value(6).toString();
3263  iconpath = query.value(7).toString();
3264  channum = query.value(8).toString();
3265  seriesid = query.value(9).toString();
3266  programid = query.value(10).toString();
3267  sourceChanid = chanid;
3268  return;
3269  }
3270 
3271  // Couldn't get program info, so get the channel info instead
3272  query.prepare(
3273  "SELECT channum, callsign, icon "
3274  "FROM channel "
3275  "WHERE chanid = :CHANID");
3276  query.bindValue(":CHANID", chanid);
3277 
3278  if (!query.exec() || !query.isActive())
3279  {
3280  MythDB::DBError("GetNextProgram -- get channel info", query);
3281  }
3282  else if (query.next())
3283  {
3284  sourceChanid = chanid;
3285  channum = query.value(0).toString();
3286  callsign = query.value(1).toString();
3287  iconpath = query.value(2).toString();
3288  }
3289 }
3290 
3291 bool TVRec::GetChannelInfo(uint &chanid, uint &sourceid,
3292  QString &callsign, QString &channum,
3293  QString &channame, QString &xmltvid) const
3294 {
3295  callsign.clear();
3296  channum.clear();
3297  channame.clear();
3298  xmltvid.clear();
3299 
3300  if ((!chanid || !sourceid) && !m_channel)
3301  return false;
3302 
3303  if (!chanid)
3304  chanid = (uint) max(m_channel->GetChanID(), 0);
3305 
3306  if (!sourceid)
3307  sourceid = m_channel->GetSourceID();
3308 
3310  query.prepare(
3311  "SELECT callsign, channum, name, xmltvid "
3312  "FROM channel "
3313  "WHERE chanid = :CHANID");
3314  query.bindValue(":CHANID", chanid);
3315  if (!query.exec() || !query.isActive())
3316  {
3317  MythDB::DBError("GetChannelInfo", query);
3318  return false;
3319  }
3320 
3321  if (!query.next())
3322  return false;
3323 
3324  callsign = query.value(0).toString();
3325  channum = query.value(1).toString();
3326  channame = query.value(2).toString();
3327  xmltvid = query.value(3).toString();
3328 
3329  return true;
3330 }
3331 
3332 bool TVRec::SetChannelInfo(uint chanid, uint sourceid,
3333  const QString& oldchannum,
3334  const QString& callsign, const QString& channum,
3335  const QString& channame, const QString& xmltvid)
3336 {
3337  if (!chanid || !sourceid || channum.isEmpty())
3338  return false;
3339 
3341  query.prepare(
3342  "UPDATE channel "
3343  "SET callsign = :CALLSIGN, "
3344  " channum = :CHANNUM, "
3345  " name = :CHANNAME, "
3346  " xmltvid = :XMLTVID "
3347  "WHERE chanid = :CHANID AND "
3348  " sourceid = :SOURCEID");
3349  query.bindValue(":CALLSIGN", callsign);
3350  query.bindValue(":CHANNUM", channum);
3351  query.bindValue(":CHANNAME", channame);
3352  query.bindValue(":XMLTVID", xmltvid);
3353  query.bindValue(":CHANID", chanid);
3354  query.bindValue(":SOURCEID", sourceid);
3355 
3356  if (!query.exec())
3357  {
3358  MythDB::DBError("SetChannelInfo", query);
3359  return false;
3360  }
3361 
3362  if (m_channel)
3363  m_channel->Renumber(sourceid, oldchannum, channum);
3364 
3365  return true;
3366 }
3367 
3372 {
3373  QMutexLocker lock(&m_stateChangeLock);
3374 
3375  MythMediaBuffer *oldbuffer = m_buffer;
3376  m_buffer = Buffer;
3377 
3378  if (oldbuffer && (oldbuffer != Buffer))
3379  {
3381  ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3382  delete oldbuffer;
3383  }
3384 
3385  m_switchingBuffer = false;
3386 }
3387 
3389 {
3390  LOG(VB_GENERAL, LOG_INFO, LOC + "RingBufferChanged()");
3391 
3392  if (pginfo)
3393  {
3394  if (m_curRecording)
3395  {
3398  delete m_curRecording;
3399  }
3401  m_curRecording = new RecordingInfo(*pginfo);
3404  }
3405 
3407 }
3408 
3410  QString &input) const
3411 {
3412  QString channum;
3413 
3414  if (request.m_program)
3415  {
3416  request.m_program->QueryTuningInfo(channum, input);
3417  return channum;
3418  }
3419 
3420  channum = request.m_channel;
3421  input = request.m_input;
3422 
3423  // If this is Live TV startup, we need a channel...
3424  if (channum.isEmpty() && (request.m_flags & kFlagLiveTV))
3425  {
3426  if (!m_liveTVStartChannel.isEmpty())
3427  channum = m_liveTVStartChannel;
3428  else
3429  {
3431  channum = GetStartChannel(m_inputId);
3432  }
3433  }
3434  if (request.m_flags & kFlagLiveTV)
3435  m_channel->Init(channum, false);
3436 
3437  if (m_channel && !channum.isEmpty() && (channum.indexOf("NextChannel") >= 0))
3438  {
3439  // FIXME This is just horrible
3440  int dir = channum.right(channum.length() - 12).toInt();
3441  uint chanid = m_channel->GetNextChannel(0, static_cast<ChannelChangeDirection>(dir));
3442  channum = ChannelUtil::GetChanNum(chanid);
3443  }
3444 
3445  return channum;
3446 }
3447 
3449 {
3450  if ((request.m_flags & kFlagAntennaAdjust) || request.m_input.isEmpty() ||
3452  {
3453  return false;
3454  }
3455 
3456  uint sourceid = m_channel->GetSourceID();
3457  QString oldchannum = m_channel->GetChannelName();
3458  QString newchannum = request.m_channel;
3459 
3460  if (ChannelUtil::IsOnSameMultiplex(sourceid, newchannum, oldchannum))
3461  {
3463  auto *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
3464 
3465  if (atsc)
3466  {
3467  uint major = 0;
3468  uint minor = 0;
3469  ChannelUtil::GetATSCChannel(sourceid, newchannum, major, minor);
3470 
3471  if (minor && atsc->HasChannel(major, minor))
3472  {
3473  request.m_majorChan = major;
3474  request.m_minorChan = minor;
3475  return true;
3476  }
3477  }
3478 
3479  if (mpeg)
3480  {
3481  uint progNum = ChannelUtil::GetProgramNumber(sourceid, newchannum);
3482  if (mpeg->HasProgram(progNum))
3483  {
3484  request.m_progNum = progNum;
3485  return true;
3486  }
3487  }
3488  }
3489 
3490  return false;
3491 }
3492 
3501 {
3502  if (!m_tuningRequests.empty())
3503  {
3504  TuningRequest request = m_tuningRequests.front();
3505  LOG(VB_RECORD, LOG_INFO, LOC +
3506  "HandleTuning Request: " + request.toString());
3507 
3508  QString input;
3509  request.m_channel = TuningGetChanNum(request, input);
3510  request.m_input = input;
3511 
3512  if (TuningOnSameMultiplex(request))
3513  LOG(VB_CHANNEL, LOG_INFO, LOC + "On same multiplex");
3514 
3515  TuningShutdowns(request);
3516 
3517  // The dequeue isn't safe to do until now because we
3518  // release the stateChangeLock to teardown a recorder
3520 
3521  // Now we start new stuff
3522  if (request.m_flags & (kFlagRecording|kFlagLiveTV|
3524  {
3525  if (!m_recorder)
3526  {
3527  LOG(VB_RECORD, LOG_INFO, LOC +
3528  "No recorder yet, calling TuningFrequency");
3529  TuningFrequency(request);
3530  }
3531  else
3532  {
3533  LOG(VB_RECORD, LOG_INFO, LOC + "Waiting for recorder pause..");
3534  SetFlags(kFlagWaitingForRecPause, __FILE__, __LINE__);
3535  }
3536  }
3537  m_lastTuningRequest = request;
3538  }
3539 
3541  {
3542  if (!m_recorder->IsPaused())
3543  return;
3544 
3545  ClearFlags(kFlagWaitingForRecPause, __FILE__, __LINE__);
3546  LOG(VB_RECORD, LOG_INFO, LOC +
3547  "Recorder paused, calling TuningFrequency");
3549  }
3550 
3551  MPEGStreamData *streamData = nullptr;
3552  if (HasFlags(kFlagWaitingForSignal) && !(streamData = TuningSignalCheck()))
3553  return;
3554 
3556  {
3557  if (m_recorder)
3559  else
3560  TuningNewRecorder(streamData);
3561 
3562  // If we got this far it is safe to set a new starting channel...
3563  if (m_channel)
3565  }
3566 }
3567 
3573 {
3574  LOG(VB_RECORD, LOG_INFO, LOC + QString("TuningShutdowns(%1)")
3575  .arg(request.toString()));
3576 
3577  QString channum;
3578  QString inputname;
3579 
3580  if (m_scanner && !(request.m_flags & kFlagEITScan) &&
3582  {
3584  ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
3587  }
3588 
3589  if (m_scanner && !request.IsOnSameMultiplex())
3591 
3593  {
3594  MPEGStreamData *sd = nullptr;
3595  if (GetDTVSignalMonitor())
3598  ClearFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3599 
3600  // Delete StreamData if it is not in use by the recorder.
3601  MPEGStreamData *rec_sd = nullptr;
3602  if (GetDTVRecorder())
3603  rec_sd = GetDTVRecorder()->GetStreamData();
3604  if (sd && (sd != rec_sd))
3605  delete sd;
3606  }
3608  ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3609 
3610  // At this point any waits are canceled.
3611 
3612  if (request.m_flags & kFlagNoRec)
3613  {
3615  {
3617  ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3619  }
3620 
3622  (m_curRecording &&
3625  {
3626  m_stateChangeLock.unlock();
3627  TeardownRecorder(request.m_flags);
3628  m_stateChangeLock.lock();
3629  }
3630  // At this point the recorders are shut down
3631 
3632  CloseChannel();
3633  // At this point the channel is shut down
3634  }
3635 
3636  if (m_buffer && (request.m_flags & kFlagKillRingBuffer))
3637  {
3638  LOG(VB_RECORD, LOG_INFO, LOC + "Tearing down RingBuffer");
3639  SetRingBuffer(nullptr);
3640  // At this point the ringbuffer is shut down
3641  }
3642 
3643  // Clear pending actions from last request
3644  ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
3645 }
3646 
3665 {
3666  LOG(VB_GENERAL, LOG_INFO, LOC + "TuningFrequency");
3667 
3668  DTVChannel *dtvchan = GetDTVChannel();
3669  if (dtvchan)
3670  {
3671  MPEGStreamData *mpeg = nullptr;
3672 
3673  if (GetDTVRecorder())
3674  mpeg = GetDTVRecorder()->GetStreamData();
3675 
3676  const QString tuningmode = (HasFlags(kFlagEITScannerRunning)) ?
3677  dtvchan->GetSIStandard() :
3678  dtvchan->GetSuggestedTuningMode(
3680 
3681  dtvchan->SetTuningMode(tuningmode);
3682 
3683  if (request.m_minorChan && (tuningmode == "atsc"))
3684  {
3686 
3687  auto *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
3688  if (atsc)
3689  atsc->SetDesiredChannel(request.m_majorChan, request.m_minorChan);
3690  }
3691  else if (request.m_progNum >= 0)
3692  {
3694 
3695  if (mpeg)
3696  mpeg->SetDesiredProgram(request.m_progNum);
3697  }
3698  }
3699 
3700  if (request.IsOnSameMultiplex())
3701  {
3702  // Update the channel number for SwitchLiveTVRingBuffer (called from
3703  // TuningRestartRecorder). This ensures that the livetvchain will be
3704  // updated with the new channel number
3705  if (m_channel)
3706  {
3708  m_channel->GetChannelName(), request.m_channel );
3709  }
3710 
3711  QStringList slist;
3712  slist<<"message"<<QObject::tr("On known multiplex...");
3713  MythEvent me(QString("SIGNAL %1").arg(m_inputId), slist);
3714  gCoreContext->dispatch(me);
3715 
3716  SetFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3717  return;
3718  }
3719 
3720  QString channum = request.m_channel;
3721 
3722  bool ok1 = true;
3723  if (m_channel)
3724  {
3725  m_channel->Open();
3726  if (!channum.isEmpty())
3727  ok1 = m_channel->SetChannelByString(channum);
3728  else
3729  ok1 = false;
3730  }
3731 
3732  if (!ok1)
3733  {
3734  if (!(request.m_flags & kFlagLiveTV) || !(request.m_flags & kFlagEITScan))
3735  {
3736  if (m_curRecording)
3738 
3739  LOG(VB_GENERAL, LOG_ERR, LOC +
3740  QString("Failed to set channel to %1. Reverting to kState_None")
3741  .arg(channum));
3744  else
3746  return;
3747  }
3748 
3749  LOG(VB_GENERAL, LOG_ERR, LOC +
3750  QString("Failed to set channel to %1.").arg(channum));
3751  }
3752 
3753 
3754  bool mpts_only = GetDTVChannel() &&
3755  GetDTVChannel()->GetFormat().compare("MPTS") == 0;
3756  if (mpts_only)
3757  {
3758  // Not using a signal monitor, so just set the status to recording
3760  if (m_curRecording)
3761  {
3763  }
3764  }
3765 
3766 
3767  bool livetv = (request.m_flags & kFlagLiveTV) != 0U;
3768  bool antadj = (request.m_flags & kFlagAntennaAdjust) != 0U;
3769  bool use_sm = !mpts_only && SignalMonitor::IsRequired(m_genOpt.m_inputType);
3770  bool use_dr = use_sm && (livetv || antadj);
3771  bool has_dummy = false;
3772 
3773  if (use_dr)
3774  {
3775  // We need there to be a ringbuffer for these modes
3776  bool ok2 = false;
3778  m_pseudoLiveTVRecording = nullptr;
3779 
3780  m_tvChain->SetInputType("DUMMY");
3781 
3782  if (!m_buffer)
3783  ok2 = CreateLiveTVRingBuffer(channum);
3784  else
3785  ok2 = SwitchLiveTVRingBuffer(channum, true, false);
3787 
3789 
3790  if (!ok2)
3791  {
3792  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RingBuffer 1");
3793  return;
3794  }
3795 
3796  has_dummy = true;
3797  }
3798 
3799  // Start signal monitoring for devices capable of monitoring
3800  if (use_sm)
3801  {
3802  LOG(VB_RECORD, LOG_INFO, LOC + "Starting Signal Monitor");
3803  bool error = false;
3804  if (!SetupSignalMonitor(
3805  !antadj, (request.m_flags & kFlagEITScan) != 0U, livetv || antadj))
3806  {
3807  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to setup signal monitor");
3808  if (m_signalMonitor)
3809  {
3810  delete m_signalMonitor;
3811  m_signalMonitor = nullptr;
3812  }
3813 
3814  // pretend the signal monitor is running to prevent segfault
3815  SetFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3816  ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3817  error = true;
3818  }
3819 
3820  if (m_signalMonitor)
3821  {
3822  if (request.m_flags & kFlagEITScan)
3823  {
3825  SetVideoStreamsRequired(0);
3827  }
3828 
3829  SetFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3830  ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3831  if (!antadj)
3832  {
3833  QDateTime expire = MythDate::current();
3834 
3835  SetFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3836  if (m_curRecording)
3837  {
3839  // If startRecordingDeadline is passed, this
3840  // recording is marked as failed, so the scheduler
3841  // can try another showing.
3843  expire.addMSecs(m_genOpt.m_channelTimeout);
3845  expire.addMSecs(m_genOpt.m_channelTimeout * 2 / 3);
3846  // Keep trying to record this showing (even if it
3847  // has been marked as failed) until the scheduled
3848  // end time.
3850  m_curRecording->GetRecordingEndTime().addSecs(-10);
3851 
3852  LOG(VB_CHANNEL, LOG_DEBUG, LOC +
3853  QString("Pre-fail start deadline: %1 "
3854  "Start recording deadline: %2 "
3855  "Good signal deadline: %3")
3856  .arg(m_preFailDeadline.toLocalTime()
3857  .toString("hh:mm:ss.zzz"))
3858  .arg(m_startRecordingDeadline.toLocalTime()
3859  .toString("hh:mm:ss.zzz"))
3860  .arg(m_signalMonitorDeadline.toLocalTime()
3861  .toString("hh:mm:ss.zzz")));
3862  }
3863  else
3864  {
3866  expire.addMSecs(m_genOpt.m_channelTimeout);
3867  }
3869 
3870  //System Event TUNING_TIMEOUT deadline
3872  m_signalEventCmdSent = false;
3873  }
3874  }
3875 
3876  if (has_dummy && m_buffer)
3877  {
3878  // Make sure recorder doesn't point to bogus ringbuffer before
3879  // it is potentially restarted without a new ringbuffer, if
3880  // the next channel won't tune and the user exits LiveTV.
3881  if (m_recorder)
3882  m_recorder->SetRingBuffer(nullptr);
3883 
3884  SetFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3885  LOG(VB_RECORD, LOG_INFO, "DummyDTVRecorder -- started");
3886  SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
3887  }
3888 
3889  // if we had problems starting the signal monitor,
3890  // we don't want to start the recorder...
3891  if (error)
3892  return;
3893  }
3894 
3895  // Request a recorder, if the command is a recording command
3896  ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3897  if (request.m_flags & kFlagRec && !antadj)
3898  SetFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3899 }
3900 
3909 {
3910  RecStatus::Type newRecStatus = RecStatus::Unknown;
3911  bool keep_trying = false;
3912  QDateTime current_time = MythDate::current();
3913 
3914  if ((m_signalMonitor->IsErrored() || current_time > m_signalEventCmdTimeout) &&
3916  {
3917  gCoreContext->SendSystemEvent(QString("TUNING_SIGNAL_TIMEOUT CARDID %1")
3918  .arg(m_inputId));
3919  m_signalEventCmdSent = true;
3920  }
3921 
3922  if (m_signalMonitor->IsAllGood())
3923  {
3924  LOG(VB_RECORD, LOG_INFO, LOC + "TuningSignalCheck: Good signal");
3925  if (m_curRecording && (current_time > m_startRecordingDeadline))
3926  {
3927  newRecStatus = RecStatus::Failing;
3929 
3930  QString desc = tr("Good signal seen after %1 ms")
3931  .arg(m_genOpt.m_channelTimeout +
3932  m_startRecordingDeadline.msecsTo(current_time));
3933  QString title = m_curRecording->GetTitle();
3934  if (!m_curRecording->GetSubtitle().isEmpty())
3935  title += " - " + m_curRecording->GetSubtitle();
3936 
3938  "Recording", title,
3939  tr("See 'Tuning timeout' in mythtv-setup "
3940  "for this input."));
3942 
3943  LOG(VB_GENERAL, LOG_WARNING, LOC +
3944  QString("It took longer than %1 ms to get a signal lock. "
3945  "Keeping status of '%2'")
3947  .arg(RecStatus::toString(newRecStatus, kSingleRecord)));
3948  LOG(VB_GENERAL, LOG_WARNING, LOC +
3949  "See 'Tuning timeout' in mythtv-setup for this input");
3950  }
3951  else
3952  {
3953  newRecStatus = RecStatus::Recording;
3954  }
3955  }
3956  else if (m_signalMonitor->IsErrored() || current_time > m_signalMonitorDeadline)
3957  {
3958  LOG(VB_GENERAL, LOG_ERR, LOC + "TuningSignalCheck: SignalMonitor " +
3959  (m_signalMonitor->IsErrored() ? "failed" : "timed out"));
3960 
3961  ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3962  newRecStatus = RecStatus::Failed;
3963 
3965  {
3967  }
3968  }
3969  else if (m_curRecording && !m_reachedPreFail && current_time > m_preFailDeadline)
3970  {
3971  LOG(VB_GENERAL, LOG_ERR, LOC +
3972  "TuningSignalCheck: Hit pre-fail timeout");
3973  SendMythSystemRecEvent("REC_PREFAIL", m_curRecording);
3974  m_reachedPreFail = true;
3975  return nullptr;
3976  }
3978  current_time > m_startRecordingDeadline)
3979  {
3980  newRecStatus = RecStatus::Failing;
3982  keep_trying = true;
3983 
3984  SendMythSystemRecEvent("REC_FAILING", m_curRecording);
3985 
3986  QString desc = tr("Taking more than %1 ms to get a lock.")
3987  .arg(m_genOpt.m_channelTimeout);
3988  QString title = m_curRecording->GetTitle();
3989  if (!m_curRecording->GetSubtitle().isEmpty())
3990  title += " - " + m_curRecording->GetSubtitle();
3991 
3993  "Recording", title,
3994  tr("See 'Tuning timeout' in mythtv-setup "
3995  "for this input."));
3996  mn.SetDuration(30);
3998 
3999  LOG(VB_GENERAL, LOG_WARNING, LOC +
4000  QString("TuningSignalCheck: taking more than %1 ms to get a lock. "
4001  "marking this recording as '%2'.")
4003  .arg(RecStatus::toString(newRecStatus, kSingleRecord)));
4004  LOG(VB_GENERAL, LOG_WARNING, LOC +
4005  "See 'Tuning timeout' in mythtv-setup for this input");
4006  }
4007  else
4008  {
4009  if (m_signalMonitorCheckCnt) // Don't flood log file
4011  else
4012  {
4013  LOG(VB_RECORD, LOG_INFO, LOC +
4014  QString("TuningSignalCheck: Still waiting. Will timeout @ %1")
4015  .arg(m_signalMonitorDeadline.toLocalTime()
4016  .toString("hh:mm:ss.zzz")));
4018  }
4019  return nullptr;
4020  }
4021 
4022  SetRecordingStatus(newRecStatus, __LINE__);
4023 
4024  if (m_curRecording)
4025  {
4026  m_curRecording->SetRecordingStatus(newRecStatus);
4027  MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
4029  .arg(m_curRecording->GetChanID())
4031  .arg(newRecStatus)
4033  gCoreContext->dispatch(me);
4034  }
4035 
4036  if (keep_trying)
4037  return nullptr;
4038 
4039  // grab useful data from DTV signal monitor before we kill it...
4040  MPEGStreamData *streamData = nullptr;
4041  if (GetDTVSignalMonitor())
4042  streamData = GetDTVSignalMonitor()->GetStreamData();
4043 
4045  {
4046  // shut down signal monitoring
4048  ClearFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
4049  }
4050  ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
4051 
4052  if (streamData)
4053  {
4054  auto *dsd = dynamic_cast<DVBStreamData*>(streamData);
4055  if (dsd)
4057  if (!get_use_eit(GetInputId()))
4058  {
4059  LOG(VB_EIT, LOG_INFO, LOC +
4060  "EIT scanning disabled for all sources on this input.");
4061  }
4062  else if (m_scanner)
4063  m_scanner->StartPassiveScan(m_channel, streamData);
4064  }
4065 
4066  return streamData;
4067 }
4068 
4070  bool on_host, bool transcode_bfr_comm, bool on_line_comm)
4071 {
4072  if (!rec)
4073  return 0; // no jobs for Live TV recordings..
4074 
4075  int jobs = 0; // start with no jobs
4076 
4077  // grab standard jobs flags from program info
4079 
4080  // disable commercial flagging on PBS, BBC, etc.
4081  if (rec->IsCommercialFree())
4083 
4084  // disable transcoding if the profile does not allow auto transcoding
4085  const StandardSetting *autoTrans = profile.byName("autotranscode");
4086  if ((!autoTrans) || (autoTrans->getValue().toInt() == 0))
4088 
4089  bool ml = JobQueue::JobIsInMask(JOB_METADATA, jobs);
4090  if (ml)
4091  {
4092  // When allowed, metadata lookup should occur at the
4093  // start of a recording to make the additional info
4094  // available immediately (and for use in future jobs).
4095  QString host = (on_host) ? gCoreContext->GetHostName() : "";
4097  rec->GetChanID(),
4098  rec->GetRecordingStartTime(), "", "",
4099  host, JOB_LIVE_REC);
4100 
4101  // don't do regular metadata lookup, we won't need it.
4103  }
4104 
4105  // is commercial flagging enabled, and is on-line comm flagging enabled?
4106  bool rt = JobQueue::JobIsInMask(JOB_COMMFLAG, jobs) && on_line_comm;
4107  // also, we either need transcoding to be disabled or
4108  // we need to be allowed to commercial flag before transcoding?
4109  rt &= JobQueue::JobIsNotInMask(JOB_TRANSCODE, jobs) ||
4110  !transcode_bfr_comm;
4111  if (rt)
4112  {
4113  // queue up real-time (i.e. on-line) commercial flagging.
4114  QString host = (on_host) ? gCoreContext->GetHostName() : "";
4116  rec->GetChanID(),
4117  rec->GetRecordingStartTime(), "", "",
4118  host, JOB_LIVE_REC);
4119 
4120  // don't do regular comm flagging, we won't need it.
4122  }
4123 
4124  return jobs;
4125 }
4126 
4127 QString TVRec::LoadProfile(void *tvchain, RecordingInfo *rec,
4128  RecordingProfile &profile) const
4129 {
4130  // Determine the correct recording profile.
4131  // In LiveTV mode use "Live TV" profile, otherwise use the
4132  // recording's specified profile. If the desired profile can't
4133  // be found, fall back to the "Default" profile for input type.
4134  QString profileName = "Live TV";
4135  if (!tvchain && rec)
4136  profileName = rec->GetRecordingRule()->m_recProfile;
4137 
4138  QString profileRequested = profileName;
4139 
4140  if (profile.loadByType(profileName, m_genOpt.m_inputType,
4142  {
4143  LOG(VB_RECORD, LOG_INFO, LOC +
4144  QString("Using profile '%1' to record")
4145  .arg(profileName));
4146  }
4147  else
4148  {
4149  profileName = "Default";
4150  if (profile.loadByType(profileName, m_genOpt.m_inputType, m_genOpt.m_videoDev))
4151  {
4152  LOG(VB_RECORD, LOG_INFO, LOC +
4153  QString("Profile '%1' not found, using "
4154  "fallback profile '%2' to record")
4155  .arg(profileRequested).arg(profileName));
4156  }
4157  else
4158  {
4159  LOG(VB_RECORD, LOG_ERR, LOC +
4160  QString("Profile '%1' not found, and unable "
4161  "to load fallback profile '%2'. Results "
4162  "may be unpredicable")
4163  .arg(profileRequested).arg(profileName));
4164  }
4165  }
4166 
4167  return profileName;
4168 }
4169 
4174 {
4175  LOG(VB_RECORD, LOG_INFO, LOC + "Starting Recorder");
4176 
4177  bool had_dummyrec = false;
4179  {
4181  ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
4183  had_dummyrec = true;
4184  }
4185 
4187 
4190 
4191  if (m_tvChain)
4192  {
4193  bool ok = false;
4194  if (!m_buffer)
4195  {
4197  SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4198  }
4199  else
4201  true, !had_dummyrec && m_recorder);
4202  if (!ok)
4203  {
4204  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RingBuffer 2");
4205  goto err_ret;
4206  }
4207  rec = m_curRecording; // new'd in Create/SwitchLiveTVRingBuffer()
4208  }
4209 
4211  {
4212  bool write = m_genOpt.m_inputType != "IMPORT";
4213  LOG(VB_GENERAL, LOG_INFO, LOC + QString("rec->GetPathname(): '%1'")
4214  .arg(rec->GetPathname()));
4216  if (!m_buffer->IsOpen() && write)
4217  {
4218  LOG(VB_GENERAL, LOG_ERR, LOC +
4219  QString("RingBuffer '%1' not open...")
4220  .arg(rec->GetPathname()));
4221  SetRingBuffer(nullptr);
4222  ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
4223  goto err_ret;
4224  }
4225  }
4226 
4227  if (!m_buffer)
4228  {
4229  LOG(VB_GENERAL, LOG_ERR, LOC +
4230  QString("Failed to start recorder! ringBuffer is NULL\n"
4231  "\t\t\t\t Tuning request was %1\n")
4233 
4234  if (HasFlags(kFlagLiveTV))
4235  {
4236  QString message = QString("QUIT_LIVETV %1").arg(m_inputId);
4237  MythEvent me(message);
4238  gCoreContext->dispatch(me);
4239  }
4240  goto err_ret;
4241  }
4242 
4243  if (m_channel && m_genOpt.m_inputType == "MJPEG")
4244  m_channel->Close(); // Needed because of NVR::MJPEGInit()
4245 
4246  LOG(VB_GENERAL, LOG_INFO, LOC + "TuningNewRecorder - CreateRecorder()");
4248 
4249  if (m_recorder)
4250  {
4253  if (m_recorder->IsErrored())
4254  {
4255  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialize recorder!");
4256  delete m_recorder;
4257  m_recorder = nullptr;
4258  }
4259  }
4260 
4261  if (!m_recorder)
4262  {
4263  LOG(VB_GENERAL, LOG_ERR, LOC +
4264  QString("Failed to start recorder!\n"
4265  "\t\t\t\t Tuning request was %1\n")
4267 
4268  if (HasFlags(kFlagLiveTV))
4269  {
4270  QString message = QString("QUIT_LIVETV %1").arg(m_inputId);
4271  MythEvent me(message);
4272  gCoreContext->dispatch(me);
4273  }
4275  goto err_ret;
4276  }
4277 
4278  if (rec)
4279  m_recorder->SetRecording(rec);
4280 
4281  if (GetDTVRecorder() && streamData)
4282  {
4283  const StandardSetting *setting = profile.byName("recordingtype");
4284  if (setting)
4285  streamData->SetRecordingType(setting->getValue());
4286  GetDTVRecorder()->SetStreamData(streamData);
4287  }
4288 
4289  if (m_channel && m_genOpt.m_inputType == "MJPEG")
4290  m_channel->Open(); // Needed because of NVR::MJPEGInit()
4291 
4292  // Setup for framebuffer capture devices..
4293  if (m_channel)
4294  {
4297  }
4298 
4299  if (GetV4LChannel())
4300  {
4302  CloseChannel();
4303  }
4304 
4305  m_recorderThread = new MThread("RecThread", m_recorder);
4307 
4308  // Wait for recorder to start.
4309  m_stateChangeLock.unlock();
4310  while (!m_recorder->IsRecording() && !m_recorder->IsErrored())
4311  std::this_thread::sleep_for(std::chrono::microseconds(5));
4312  m_stateChangeLock.lock();
4313 
4314  if (GetV4LChannel())
4316 
4317  SetFlags(kFlagRecorderRunning | kFlagRingBufferReady, __FILE__, __LINE__);
4318 
4319  ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4320 
4321  //workaround for failed import recordings, no signal monitor means we never
4322  //go to recording state and the status here seems to override the status
4323  //set in the importrecorder and backend via setrecordingstatus
4324  if (m_genOpt.m_inputType == "IMPORT")
4325  {
4327  if (m_curRecording)
4329  }
4330  return;
4331 
4332  err_ret:
4333  SetRecordingStatus(RecStatus::Failed, __LINE__, true);
4335 
4336  if (rec)
4337  {
4338  // Make sure the scheduler knows...
4340  LOG(VB_RECORD, LOG_INFO, LOC +
4341  QString("TuningNewRecorder -- UPDATE_RECORDING_STATUS: %1")
4343  MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
4344  .arg(rec->GetInputID())
4345  .arg(rec->GetChanID())
4347  .arg(RecStatus::Failed)
4349  gCoreContext->dispatch(me);
4350  }
4351 
4352  if (m_tvChain)
4353  delete rec;
4354 }
4355 
4360 {
4361  LOG(VB_RECORD, LOG_INFO, LOC + "Restarting Recorder");
4362 
4363  bool had_dummyrec = false;
4364 
4365  if (m_curRecording)
4366  {
4369  }
4370 
4372  {
4373  ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
4374  had_dummyrec = true;
4375  }
4376 
4377  SwitchLiveTVRingBuffer(m_channel->GetChannelName(), true, !had_dummyrec);
4378 
4379  if (had_dummyrec)
4380  {
4382  ProgramInfo *progInfo = m_tvChain->GetProgramAt(-1);
4383  RecordingInfo recinfo(*progInfo);
4384  delete progInfo;
4385  recinfo.SetInputID(m_inputId);
4386  m_recorder->SetRecording(&recinfo);
4387  }
4388  m_recorder->Reset();
4389 
4390  // Set file descriptor of channel from recorder for V4L
4391  if (GetV4LChannel())
4393 
4394  // Some recorders unpause on Reset, others do not...
4395  m_recorder->Unpause();
4396 
4398  {
4400  QString msg1 = QString("Recording: %1 %2 %3 %4")
4401  .arg(rcinfo1->GetTitle()).arg(rcinfo1->GetChanID())
4403  .arg(rcinfo1->GetRecordingEndTime(MythDate::ISODate));
4404  ProgramInfo *rcinfo2 = m_tvChain->GetProgramAt(-1);
4405  QString msg2 = QString("Recording: %1 %2 %3 %4")
4406  .arg(rcinfo2->GetTitle()).arg(rcinfo2->GetChanID())
4408  .arg(rcinfo2->GetRecordingEndTime(MythDate::ISODate));
4409  delete rcinfo2;
4410  LOG(VB_RECORD, LOG_INFO, LOC + "Pseudo LiveTV recording starting." +
4411  "\n\t\t\t" + msg1 + "\n\t\t\t" + msg2);
4412 
4415 
4417 
4418  InitAutoRunJobs(m_curRecording, kAutoRunProfile, nullptr, __LINE__);
4419  }
4420 
4421  ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4422 }
4423 
4424 void TVRec::SetFlags(uint f, const QString & file, int line)
4425 {
4426  QMutexLocker lock(&m_stateChangeLock);
4427  m_stateFlags |= f;
4428  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetFlags(%1) -> %2 @ %3:%4")
4430  WakeEventLoop();
4431 }
4432 
4433 void TVRec::ClearFlags(uint f, const QString & file, int line)
4434 {
4435  QMutexLocker lock(&m_stateChangeLock);
4436  m_stateFlags &= ~f;
4437  LOG(VB_RECORD, LOG_INFO, LOC + QString("ClearFlags(%1) -> %2 @ %3:%4")
4439  WakeEventLoop();
4440 }
4441 
4443 {
4444  QString msg("");
4445 
4446  // General flags
4447  if (kFlagFrontendReady & f)
4448  msg += "FrontendReady,";
4449  if (kFlagRunMainLoop & f)
4450  msg += "RunMainLoop,";
4451  if (kFlagExitPlayer & f)
4452  msg += "ExitPlayer,";
4453  if (kFlagFinishRecording & f)
4454  msg += "FinishRecording,";
4455  if (kFlagErrored & f)
4456  msg += "Errored,";
4458  msg += "CancelNextRecording,";
4459 
4460  // Tuning flags
4461  if ((kFlagRec & f) == kFlagRec)
4462  msg += "REC,";
4463  else
4464  {
4465  if (kFlagLiveTV & f)
4466  msg += "LiveTV,";
4467  if (kFlagRecording & f)
4468  msg += "Recording,";
4469  }
4470  if ((kFlagNoRec & f) == kFlagNoRec)
4471  msg += "NOREC,";
4472  else
4473  {
4474  if (kFlagEITScan & f)
4475  msg += "EITScan,";
4476  if (kFlagCloseRec & f)
4477  msg += "CloseRec,";
4478  if (kFlagKillRec & f)
4479  msg += "KillRec,";
4480  if (kFlagAntennaAdjust & f)
4481  msg += "AntennaAdjust,";
4482  }
4484  msg += "PENDINGACTIONS,";
4485  else
4486  {
4487  if (kFlagWaitingForRecPause & f)
4488  msg += "WaitingForRecPause,";
4489  if (kFlagWaitingForSignal & f)
4490  msg += "WaitingForSignal,";
4492  msg += "NeedToStartRecorder,";
4493  if (kFlagKillRingBuffer & f)
4494  msg += "KillRingBuffer,";
4495  }
4496  if ((kFlagAnyRunning & f) == kFlagAnyRunning)
4497  msg += "ANYRUNNING,";
4498  else
4499  {
4501  msg += "SignalMonitorRunning,";
4502  if (kFlagEITScannerRunning & f)
4503  msg += "EITScannerRunning,";
4505  msg += "ANYRECRUNNING,";
4506  else
4507  {
4509  msg += "DummyRecorderRunning,";
4510  if (kFlagRecorderRunning & f)
4511  msg += "RecorderRunning,";
4512  }
4513  }
4514  if (kFlagRingBufferReady & f)
4515  msg += "RingBufferReady,";
4516 
4517  if (msg.isEmpty())
4518  msg = QString("0x%1").arg(f,0,16);
4519 
4520  return msg;
4521 }
4522 
4524 {
4525  QMutexLocker lock(&m_nextLiveTVDirLock);
4526 
4527  bool found = !m_nextLiveTVDir.isEmpty();
4528  if (!found && m_triggerLiveTVDir.wait(&m_nextLiveTVDirLock, 500))
4529  {
4530  found = !m_nextLiveTVDir.isEmpty();
4531  }
4532 
4533  return found;
4534 }
4535 
4537 {
4538  QMutexLocker lock(&m_nextLiveTVDirLock);
4539 
4540  m_nextLiveTVDir = std::move(dir);
4541  m_triggerLiveTVDir.wakeAll();
4542 }
4543 
4546  const QString & channum)
4547 {
4548  LOG(VB_RECORD, LOG_INFO, LOC + "GetProgramRingBufferForLiveTV()");
4549  if (!m_channel || !m_tvChain || !pginfo || !Buffer)
4550  return false;
4551 
4552  m_nextLiveTVDirLock.lock();
4553  m_nextLiveTVDir.clear();
4554  m_nextLiveTVDirLock.unlock();
4555 
4556  // Dispatch this early, the response can take a while.
4557  MythEvent me(QString("QUERY_NEXT_LIVETV_DIR %1").arg(m_inputId));
4558  gCoreContext->dispatch(me);
4559 
4560  uint sourceid = m_channel->GetSourceID();
4561  int chanid = ChannelUtil::GetChanID(sourceid, channum);
4562 
4563  if (chanid < 0)
4564  {
4565  // Test setups might have zero channels
4566  if (m_genOpt.m_inputType == "IMPORT" || m_genOpt.m_inputType == "DEMO")
4567  chanid = 9999;
4568  else
4569  {
4570  LOG(VB_GENERAL, LOG_ERR, LOC +
4571  QString("Channel: \'%1\' was not found in the database.\n"
4572  "\t\tMost likely, the 'starting channel' for this "
4573  "Input Connection is invalid.\n"
4574  "\t\tCould not start livetv.").arg(channum));
4575  return false;
4576  }
4577  }
4578 
4579  int hoursMax = gCoreContext->GetNumSetting("MaxHoursPerLiveTVRecording", 8);
4580  if (hoursMax <= 0)
4581  hoursMax = 8;
4582 
4583  RecordingInfo *prog = nullptr;
4586  else
4587  {
4588  prog = new RecordingInfo(
4589  chanid, MythDate::current(true), true, hoursMax);
4590  }
4591 
4592  prog->SetInputID(m_inputId);
4593 
4594  if (prog->GetRecordingStartTime() == prog->GetRecordingEndTime())
4595  {
4596  LOG(VB_GENERAL, LOG_ERR, LOC + "GetProgramRingBufferForLiveTV()"
4597  "\n\t\t\tProgramInfo is invalid."
4598  "\n" + prog->toString());
4599  prog->SetScheduledEndTime(prog->GetRecordingStartTime().addSecs(3600));
4601 
4602  prog->SetChanID(chanid);
4603  }
4604 
4607 
4608  prog->SetStorageGroup("LiveTV");
4609 
4610  if (WaitForNextLiveTVDir())
4611  {
4612  QMutexLocker lock(&m_nextLiveTVDirLock);
4614  }
4615  else
4616  {
4617  StorageGroup sgroup("LiveTV", gCoreContext->GetHostName());
4618  prog->SetPathname(sgroup.FindNextDirMostFree());
4619  }
4620 
4622  prog->SetRecordingGroup("LiveTV");
4623 
4624  StartedRecording(prog);
4625 
4626  *Buffer = MythMediaBuffer::Create(prog->GetPathname(), true);
4627  if (!(*Buffer) || !(*Buffer)->IsOpen())
4628  {
4629  LOG(VB_GENERAL, LOG_ERR, LOC + QString("RingBuffer '%1' not open...")
4630  .arg(prog->GetPathname()));
4631 
4632  delete *Buffer;
4633  delete prog;
4634 
4635  return false;
4636  }
4637 
4638  *pginfo = prog;
4639  return true;
4640 }
4641 
4642 bool TVRec::CreateLiveTVRingBuffer(const QString & channum)
4643 {
4644  LOG(VB_RECORD, LOG_INFO, LOC + QString("CreateLiveTVRingBuffer(%1)")
4645  .arg(channum));
4646 
4647  RecordingInfo *pginfo = nullptr;
4648  MythMediaBuffer *buffer = nullptr;
4649  QString inputName;
4650 
4651  if (!m_channel ||
4652  !m_channel->CheckChannel(channum))
4653  {
4655  return false;
4656  }
4657 
4658  if (!GetProgramRingBufferForLiveTV(&pginfo, &buffer, channum))
4659  {
4660  ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
4662  LOG(VB_GENERAL, LOG_ERR, LOC +
4663  QString("CreateLiveTVRingBuffer(%1) failed").arg(channum));
4664  return false;
4665  }
4666 
4667  SetRingBuffer(buffer);
4668 
4672 
4673  bool discont = (m_tvChain->TotalSize() > 0);
4675  m_channel->GetInputName(), discont);
4676 
4677  if (m_curRecording)
4678  {
4680  delete m_curRecording;
4681  }
4682 
4683  m_curRecording = pginfo;
4685 
4686  return true;
4687 }
4688 
4689 bool TVRec::SwitchLiveTVRingBuffer(const QString & channum,
4690  bool discont, bool set_rec)
4691 {
4692  QString msg;
4693  if (m_curRecording)
4694  {
4695  msg = QString(" curRec(%1) curRec.size(%2)")
4696  .arg(m_curRecording->MakeUniqueKey())
4697  .arg(m_curRecording->GetFilesize());
4698  }
4699  LOG(VB_RECORD, LOG_INFO, LOC +
4700  QString("SwitchLiveTVRingBuffer(discont %1, set_next_rec %2)")
4701  .arg(discont).arg(set_rec) + msg);
4702 
4703  RecordingInfo *pginfo = nullptr;
4704  MythMediaBuffer *buffer = nullptr;
4705  QString inputName;
4706 
4707  if (!m_channel ||
4708  !m_channel->CheckChannel(channum))
4709  {
4711  return false;
4712  }
4713 
4714  if (!GetProgramRingBufferForLiveTV(&pginfo, &buffer, channum))
4715  {
4717  return false;
4718  }
4719 
4720  QString oldinputtype = m_tvChain->GetInputType(-1);
4721 
4722  pginfo->MarkAsInUse(true, kRecorderInUseID);
4727  m_channel->GetInputName(), discont);
4728 
4729  if (set_rec && m_recorder)
4730  {
4731  m_recorder->SetNextRecording(pginfo, buffer);
4732  if (discont)
4734  delete pginfo;
4735  SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4736  }
4737  else if (!set_rec)
4738  {
4739  // dummy recordings are finished before this
4740  // is called and other recordings must be finished..
4741  if (m_curRecording && oldinputtype != "DUMMY")
4742  {
4745  delete m_curRecording;
4746  }
4747  m_curRecording = pginfo;
4748  SetRingBuffer(buffer);
4749  }
4750  else
4751  {
4752  delete buffer;
4753  }
4754 
4755  return true;
4756 }
4757 
4759 {
4760  LOG(VB_RECORD, LOG_INFO, LOC + "SwitchRecordingRingBuffer()");
4761 
4762  if (m_switchingBuffer)
4763  {
4764  LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4765  "already switching.");
4766  return nullptr;
4767  }
4768 
4769  if (!m_recorder)
4770  {
4771  LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4772  "invalid recorder.");
4773  return nullptr;
4774  }
4775 
4776  if (!m_curRecording)
4777  {
4778  LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4779  "invalid recording.");
4780  return nullptr;
4781  }
4782 
4783  if (rcinfo.GetChanID() != m_curRecording->GetChanID())
4784  {
4785  LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4786  "Not the same channel.");
4787  return nullptr;
4788  }
4789 
4790  auto *ri = new RecordingInfo(rcinfo);
4792 
4793  QString pn = LoadProfile(nullptr, ri, profile);
4794 
4795  if (pn != m_recProfileName)
4796  {
4797  LOG(VB_RECORD, LOG_ERR, LOC +
4798  QString("SwitchRecordingRingBuffer() -> "
4799  "cannot switch profile '%1' to '%2'")
4800  .arg(m_recProfileName).arg(pn));
4801  return nullptr;
4802  }
4803 
4805 
4806  ri->MarkAsInUse(true, kRecorderInUseID);
4807  StartedRecording(ri);
4808 
4809  bool write = m_genOpt.m_inputType != "IMPORT";
4810  MythMediaBuffer *buffer = MythMediaBuffer::Create(ri->GetPathname(), write);
4811  if (!buffer || !buffer->IsOpen())
4812  {
4813  delete buffer;
4814  ri->SetRecordingStatus(RecStatus::Failed);
4815  FinishedRecording(ri, nullptr);
4816  ri->MarkAsInUse(false, kRecorderInUseID);
4817  delete ri;
4818  LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer() -> "
4819  "Failed to create new RB.");
4820  return nullptr;
4821  }
4822 
4823  m_recorder->SetNextRecording(ri, buffer);
4824  SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4826  m_switchingBuffer = true;
4827  ri->SetRecordingStatus(RecStatus::Recording);
4828  LOG(VB_RECORD, LOG_INFO, LOC + "SwitchRecordingRingBuffer -> done");
4829  return ri;
4830 }
4831 
4833 {
4834  QMap<uint,TVRec*>::const_iterator it = s_inputs.find(inputid);
4835  if (it == s_inputs.end())
4836  return nullptr;
4837  return *it;
4838 }
4839 
4840 QString TuningRequest::toString(void) const
4841 {
4842  return QString("Program(%1) channel(%2) input(%3) flags(%4)")
4843  .arg((m_program == nullptr) ? QString("NULL") : m_program->toString())
4844  .arg(m_channel).arg(m_input)
4846 }
4847 
4848 #ifdef USING_DVB
4849 #include "dvbchannel.h"
4851 {
4852  // Some DVB devices munge the PMT and/or PAT so the CRC check fails.
4853  // We need to tell the stream data class to not check the CRC on
4854  // these devices. This can cause segfaults.
4855  if (dynamic_cast<DVBChannel*>(c))
4856  s->SetIgnoreCRC(dynamic_cast<DVBChannel*>(c)->HasCRCBug());
4857 }
4858 #else
4860 #endif // USING_DVB
4861 
4862 /* vim: set expandtab tabstop=4 shiftwidth=4: */
GeneralDBOptions
Definition: tv_rec.h:65
TVRec::CreateLiveTVRingBuffer
bool CreateLiveTVRingBuffer(const QString &channum)
Definition: tv_rec.cpp:4642
kLiveTVAutoExpire
@ kLiveTVAutoExpire
Definition: programtypes.h:239
DTVChannel::GetMajorChannel
uint GetMajorChannel(void) const
Returns major channel, 0 if unknown.
Definition: dtvchannel.h:95
MSqlQuery::isActive
bool isActive(void) const
Definition: mythdbcon.h:204
BROWSE_LEFT
@ BROWSE_LEFT
Fetch information on current channel in the past.
Definition: tv.h:43
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
RecStatus::Type
Type
Definition: recStatus.h:16
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
ChannelBase::Close
virtual void Close(void)=0
Closes the channel changing hardware to use.
TVRec::SetFlags
void SetFlags(uint f, const QString &file, int line)
Definition: tv_rec.cpp:4424
TVRec::GetKeyframeDurations
bool GetKeyframeDurations(int64_t start, int64_t end, frm_pos_map_t &map) const
Definition: tv_rec.cpp:2628
DTVSignalMonitor::AddFlags
void AddFlags(uint64_t _flags) override
Definition: dtvsignalmonitor.cpp:143
dtvchannel.h
channel
QDomElement channel
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:498
TVRec::IsReallyRecording
bool IsReallyRecording(void)
Returns true if frontend can consider the recorder started.
Definition: tv_rec.cpp:2474
ProgramInfo::MakeUniqueKey
QString MakeUniqueKey(void) const
Creates a unique string that can be used to identify an existing recording.
Definition: programinfo.h:333
TVRec::m_runJobOnHostOnly
bool m_runJobOnHostOnly
Definition: tv_rec.h:360
ChannelBase::Init
virtual bool Init(QString &startchannel, bool setchan)
Definition: channelbase.cpp:57
MythDate::toString
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:80
MSqlQuery::size
int size(void) const
Definition: mythdbcon.h:203
TVRec::kFlagNoRec
static const uint kFlagNoRec
Definition: tv_rec.h:457
TVRec::kFlagErrored
static const uint kFlagErrored
Definition: tv_rec.h:437
TVRec::m_eitCrawlIdleStart
int m_eitCrawlIdleStart
Definition: tv_rec.h:361
MThread::start
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:292
TVRec::m_startRecordingDeadline
QDateTime m_startRecordingDeadline
Definition: tv_rec.h:344
TVRec::m_channel
ChannelBase * m_channel
Definition: tv_rec.h:337
TableID::TVCT
@ TVCT
Definition: mpegtables.h:339
hardwareprofile.smolt.timeout
float timeout
Definition: smolt.py:103
MARK_ASPECT_16_9
@ MARK_ASPECT_16_9
Definition: programtypes.h:67
TableID::CVCT
@ CVCT
Definition: mpegtables.h:340
JobQueue::RemoveJobsFromMask
static void RemoveJobsFromMask(int jobs, int &mask)
Definition: jobqueue.h:198
TVRec::kFlagEITScannerRunning
static const uint kFlagEITScannerRunning
Definition: tv_rec.h:468
RecordingInfo::FinishedRecording
void FinishedRecording(bool allowReRecord)
If not a premature stop, adds program to history of recorded programs.
Definition: recordinginfo.cpp:1185
InputInfo::m_chanId
uint m_chanId
chanid restriction if applicable
Definition: inputinfo.h:51
BROWSE_UP
@ BROWSE_UP
Fetch information on previous channel.
Definition: tv.h:41
JobQueue::JobIsInMask
static bool JobIsInMask(int job, int mask)
Definition: jobqueue.h:194
TVRec::SetPseudoLiveTVRecording
void SetPseudoLiveTVRecording(RecordingInfo *pi)
Sets the pseudo LiveTV RecordingInfo.
Definition: tv_rec.cpp:324
MPEGStreamData::SetVideoStreamsRequired
void SetVideoStreamsRequired(uint num)
Definition: mpegstreamdata.h:245
TVRec::m_pendingRecLock
QMutex m_pendingRecLock
Definition: tv_rec.h:383
MARK_ASPECT_2_21_1
@ MARK_ASPECT_2_21_1
Definition: programtypes.h:68
DTVSignalMonitor::SetDVBService
void SetDVBService(uint network_id, uint transport_id, int service_id)
Definition: dtvsignalmonitor.cpp:231
TuningRequest
Definition: tv_rec.h:101
error
static void error(const char *str,...)
Definition: vbi.cpp:42
ReferenceCounter::DecrRef
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
Definition: referencecounter.cpp:125
ProgramInfo::SetRecordingStatus
void SetRecordingStatus(RecStatus::Type status)
Definition: programinfo.h:567
CHANNEL_DIRECTION_DOWN
@ CHANNEL_DIRECTION_DOWN
Definition: tv.h:31
dtvrecorder.h
ProgramInfo::QueryMplexID
uint QueryMplexID(void) const
Queries multiplex any recording would be made on, zero if unknown.
Definition: programinfo.cpp:2551
TVRec::m_recProfileName
QString m_recProfileName
Definition: tv_rec.h:378
ChannelChangeDirection
ChannelChangeDirection
ChannelChangeDirection is an enumeration of possible channel changing directions.
Definition: tv.h:28
TVRec::m_audioSampleRateDB
int m_audioSampleRateDB
Definition: tv_rec.h:363
mythdb.h
MPEGStreamData::SetDesiredProgram
void SetDesiredProgram(int p)
Definition: mpegstreamdata.cpp:67
RecorderBase::CreateRecorder
static RecorderBase * CreateRecorder(TVRec *tvrec, ChannelBase *channel, RecordingProfile &profile, const GeneralDBOptions &genOpt)
Definition: recorderbase.cpp:849
TVRec::ShouldSwitchToAnotherInput
bool ShouldSwitchToAnotherInput(const QString &chanid) const
Checks if named channel exists on current tuner, or another tuner.
Definition: tv_rec.cpp:2182
TVRec::kFlagRecording
static const uint kFlagRecording
final result desired is a timed recording
Definition: tv_rec.h:444
RecordingRule::Save
bool Save(bool sendSig=true)
Definition: recordingrule.cpp:387
SignalMonitor::IsAllGood
virtual bool IsAllGood(void) const
Definition: signalmonitor.h:83
CHANNEL_DIRECTION_UP
@ CHANNEL_DIRECTION_UP
Definition: tv.h:30
JobQueue::QueueJob
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:521
CardUtil::GetInputName
static QString GetInputName(uint inputid)
Definition: cardutil.cpp:1700
EITScanner::StopPassiveScan
void StopPassiveScan(void)
Stops inserting Event Information Tables into DB.
Definition: eitscanner.cpp:214
PendingInfo::m_possibleConflicts
vector< uint > m_possibleConflicts
Definition: tv_rec.h:138
kRecorderInUseID
const char * kRecorderInUseID
Definition: programtypes.cpp:17
MythTimer
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
TVRec::m_triggerEventSleepLock
QMutex m_triggerEventSleepLock
Definition: tv_rec.h:395
ChannelBase::Open
virtual bool Open(void)=0
Opens the channel changing hardware for use.
TVRec::m_tuningRequests
TuningQueue m_tuningRequests
Definition: tv_rec.h:389
RemoteGetState
uint RemoteGetState(uint inputid)
Definition: tvremoteutil.cpp:30
ChannelBase::GetInputName
virtual QString GetInputName(void) const
Definition: channelbase.h:69
RecordingInfo::ApplyRecordRecID
void ApplyRecordRecID(void)
Sets recordid to match RecordingRule recordid.
Definition: recordinginfo.cpp:532
PictureAdjustType
PictureAdjustType
Definition: tv.h:120
MPEGStreamData::SetCaching
void SetCaching(bool cacheTables)
Definition: mpegstreamdata.h:91
ProgramInfo::SetRecordingID
virtual void SetRecordingID(uint _recordedid)
Definition: programinfo.h:565
TVRec::Init
bool Init(void)
Performs instance initialization, returns true on success.
Definition: tv_rec.cpp:139
SignalMonitor::IsSupported
static bool IsSupported(const QString &cardtype)
Definition: signalmonitor.h:317
title
QString title
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:633
MythMediaBuffer::StopReads
void StopReads(void)
Definition: mythmediabuffer.cpp:686
VID_DAMAGED
@ VID_DAMAGED
Definition: programtypes.h:197
eitscanner.h
RecStatus::Tuning
@ Tuning
Definition: recStatus.h:22
pid_cache_item_t
Definition: channelutil.h:24
pid_cache_t
vector< pid_cache_item_t > pid_cache_t
Definition: channelutil.h:43
RecordingInfo
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:34
TVRec::ToggleChannelFavorite
void ToggleChannelFavorite(const QString &changroupname)
Toggles whether the current channel should be on our favorites list.
Definition: tv_rec.cpp:2956
GeneralDBOptions::m_signalTimeout
uint m_signalTimeout
Definition: tv_rec.h:76
MythMediaBuffer::Create
static MythMediaBuffer * Create(const QString &Filename, bool Write, bool UseReadAhead=true, int Timeout=kDefaultOpenTimeout, bool StreamOnly=false)
Creates a RingBuffer instance.
Definition: mythmediabuffer.cpp:104
RecordingInfo::GetFilesize
uint64_t GetFilesize(void) const override
Definition: recordinginfo.cpp:1674
ChannelBase::IsOpen
virtual bool IsOpen(void) const =0
Reports whether channel is already open.
ProgramInfo::QueryAverageScanProgressive
bool QueryAverageScanProgressive(void) const
If present in recording this loads average video scan type of the main video stream from database's s...
Definition: programinfo.cpp:4353
RecorderBase::Unpause
virtual void Unpause(void)
Unpause tells recorder to unpause.
Definition: recorderbase.cpp:274
TVRec::kFlagRingBufferReady
static const uint kFlagRingBufferReady
Definition: tv_rec.h:476
ProgramInfo::GetRecordingID
uint GetRecordingID(void) const
Definition: programinfo.h:440
MythMediaBuffer::GetWritePosition
long long GetWritePosition(void) const
Returns how far into a ThreadedFileWriter file we have written.
Definition: mythmediabuffer.cpp:1795
TVRec::m_buffer
MythMediaBuffer * m_buffer
Definition: tv_rec.h:422
RecorderBase::IsErrored
virtual bool IsErrored(void)=0
Tells us whether an unrecoverable error has been encountered.
LiveTVChain::GetID
QString GetID(void) const
Definition: livetvchain.h:53
TVRec::m_triggerLiveTVDir
QWaitCondition m_triggerLiveTVDir
Definition: tv_rec.h:415
RecStatus::Cancelled
@ Cancelled
Definition: recStatus.h:26
TVRec::m_eventThread
MThread * m_eventThread
Event processing thread, runs TVRec::run().
Definition: tv_rec.h:353
TVRec::kFlagRecorderRunning
static const uint kFlagRecorderRunning
Definition: tv_rec.h:471
GeneralDBOptions::m_inputType
QString m_inputType
Definition: tv_rec.h:73
MythEvent
This class is used as a container for messages.
Definition: mythevent.h:16
TVRec::kFlagRec
static const uint kFlagRec
Definition: tv_rec.h:447
EITScanner::StartActiveScan
void StartActiveScan(TVRec *_rec, uint max_seconds_per_source)
Definition: eitscanner.cpp:231
JOB_COMMFLAG
@ JOB_COMMFLAG
Definition: jobqueue.h:75
ProgramInfo::kTitleSubtitle
@ kTitleSubtitle
Definition: programinfo.h:496
TVRec::m_recStatus
RecStatus::Type m_recStatus
Definition: tv_rec.h:399
ChannelUtil::GetATSCChannel
static bool GetATSCChannel(uint sourceid, const QString &channum, uint &major, uint &minor)
Definition: channelutil.cpp:1855
TVRec::SetInput
QString SetInput(QString input)
Changes to the specified input.
Definition: tv_rec.cpp:3064
mythburn.write
def write(text, progress=True)
Definition: mythburn.py:308
ChannelUtil::GetProgramNumber
static int GetProgramNumber(uint sourceid, const QString &channum)
Definition: channelutil.h:187
SignalMonitor::kDTVSigMon_WaitForMGT
static const uint64_t kDTVSigMon_WaitForMGT
Definition: signalmonitor.h:183
TVRec::GetSourceID
uint GetSourceID(void) const
Returns current source id.
Definition: tv_rec.cpp:3049
EITScanner::StartPassiveScan
void StartPassiveScan(ChannelBase *channel, EITSource *eitSource)
Start inserting Event Information Tables from the multiplex we happen to be tuned to into the databas...
Definition: eitscanner.cpp:195
JobQueue::QueueRecordingJobs
static bool QueueRecordingJobs(const RecordingInfo &recinfo, int jobTypes=JOB_NONE)
Definition: jobqueue.cpp:499
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:198
arg
arg(title).arg(filename).arg(doDelete))
TVRec::m_overRecordCategory
QString m_overRecordCategory
Definition: tv_rec.h:366
PendingInfo::m_hasLaterShowing
bool m_hasLaterShowing
Definition: tv_rec.h:134
ProgramInfo::GetCategory
QString GetCategory(void) const
Definition: programinfo.h:363
RecStatus::TunerBusy
@ TunerBusy
Definition: recStatus.h:24
DTVRecorder::SetStreamData
virtual void SetStreamData(MPEGStreamData *data)
Definition: dtvrecorder.cpp:211
TVRec::GetProgramRingBufferForLiveTV
bool GetProgramRingBufferForLiveTV(RecordingInfo **pginfo, MythMediaBuffer **Buffer, const QString &channum)
Definition: tv_rec.cpp:4544
JOB_NONE
@ JOB_NONE
Definition: jobqueue.h:71
PendingInfo::m_info
ProgramInfo * m_info
Definition: tv_rec.h:132
MythNotification
Definition: mythnotification.h:26
DTVChannel::GetMinorChannel
uint GetMinorChannel(void) const
Returns minor channel, 0 if unknown.
Definition: dtvchannel.h:99
RecStatus::Unknown
@ Unknown
Definition: recStatus.h:32
apply_broken_dvb_driver_crc_hack
static void apply_broken_dvb_driver_crc_hack(ChannelBase *, MPEGStreamData *)
Definition: tv_rec.cpp:4850
TuningRequest::toString
QString toString(void) const
Definition: tv_rec.cpp:4840
RecordingRule::m_endOffset
int m_endOffset
Definition: recordingrule.h:112
ChannelBase::CreateChannel
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)
Definition: channelbase.cpp:694
DTVSignalMonitor::GetStreamData
MPEGStreamData * GetStreamData()
Returns the MPEG stream data if it exists.
Definition: dtvsignalmonitor.h:60
LiveTVChain::TotalSize
int TotalSize(void) const
Definition: livetvchain.cpp:387
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
osd.h
atscstreamdata.h
TVRec::m_curRecording
RecordingInfo * m_curRecording
Definition: tv_rec.h:402
ProgramInfo::SaveCommFlagged
void SaveCommFlagged(CommFlagStatus flag)
Set "commflagged" field in "recorded" table to "flag".
Definition: programinfo.cpp:3169
RecStatus::Recorded
@ Recorded
Definition: recStatus.h:29
TVRec::StopRecording
void StopRecording(bool killFile=false)
Changes from a recording state to kState_None.
Definition: tv_rec.cpp:705
category
QString category
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:1419
RecorderBase::GetKeyframePositions
bool GetKeyframePositions(long long start, long long end, frm_pos_map_t &map) const
Definition: recorderbase.cpp:537
MythMediaBuffer
Definition: mythmediabuffer.h:49
DTVChannel::GetFormat
QString GetFormat(void)
Definition: dtvchannel.h:47
MythTimer::start
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
ProgramInfo::GetScheduledEndTime
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:391
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
FireWireDBOptions::m_speed
int m_speed
Definition: tv_rec.h:96
RecordingInfo::kLiveTVRecGroup
@ kLiveTVRecGroup
Definition: recordinginfo.h:191
TVRec::GetFramesWritten
long long GetFramesWritten(void)
Returns number of frames written to disk by recorder.
Definition: tv_rec.cpp:2569
TVRec::GetDevices
static bool GetDevices(uint inputid, uint &parentid, GeneralDBOptions &gen_opts, DVBDBOptions &dvb_opts, FireWireDBOptions &firewire_opts)
Definition: tv_rec.cpp:1632
channelbase.h
ProgramInfo::UpdateInUseMark
void UpdateInUseMark(bool force=false)
Definition: programinfo.cpp:4711
num_inputs
static int num_inputs(void)
Definition: tv_rec.cpp:1235
TVRec::GetInput
QString GetInput(void) const
Returns current input.
Definition: tv_rec.cpp:3039
ChannelUtil::GetMplexID
static uint GetMplexID(uint sourceid, const QString &channum)
Definition: channelutil.cpp:461
TVRec::kFlagDummyRecorderRunning
static const uint kFlagDummyRecorderRunning
Definition: tv_rec.h:470
ChannelBase::GetChannelName
virtual QString GetChannelName(void) const
Definition: channelbase.h:64
TVRec::m_setChannelLock
QMutex m_setChannelLock
Definition: tv_rec.h:381
RecordingQuality::IsDamaged
bool IsDamaged(void) const
Definition: recordingquality.cpp:109
mythsystemevent.h
build_compdb.file
file
Definition: build_compdb.py:55
RecordingInfo::UpdateRecordingEnd
void UpdateRecordingEnd(void)
Update information in the recorded table when the end-time of a recording is changed.
Definition: recordinginfo.cpp:1228
TVRec::GetDTVChannel
DTVChannel * GetDTVChannel(void)
Definition: tv_rec.cpp:1181
TVRec::kFlagPendingActions
static const uint kFlagPendingActions
Definition: tv_rec.h:464
TVRec::CheckChannelPrefix
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:2318
RecorderBase::Pause
virtual void Pause(bool clear=true)
Pause tells recorder to pause, it should not block.
Definition: recorderbase.cpp:263
MasterGuideTable::TableCount
uint TableCount() const
Definition: atsctables.h:114
TVRec::IsBusy
bool IsBusy(InputInfo *busy_input=nullptr, int time_buffer=5) const
Returns true if the recorder is busy, or will be within the next time_buffer seconds.
Definition: tv_rec.cpp:2485
ProgramInfo::GetRecordingEndTime
QDateTime GetRecordingEndTime(void) const
Approximate time the recording should have ended, did end, or is intended to end.
Definition: programinfo.h:406
ChannelBase::GetPictureAttribute
virtual int GetPictureAttribute(PictureAttribute) const
Definition: channelbase.h:94
ProgramInfo::GetRecordingGroup
QString GetRecordingGroup(void) const
Definition: programinfo.h:413
TVRec::GetChainID
QString GetChainID(void)
Get the chainid of the livetv instance.
Definition: tv_rec.cpp:2702
remoteutil.h
SignalMonitor::SetUpdateRate
void SetUpdateRate(int msec)
Sets the number of milliseconds between signal monitoring attempts in the signal monitoring thread.
Definition: signalmonitor.h:111
hardwareprofile.distros.mythtv_data.data_mythtv.prefix
string prefix
Definition: data_mythtv.py:40
RecorderBase::GetKeyframeDurations
bool GetKeyframeDurations(long long start, long long end, frm_pos_map_t &map) const
Definition: recorderbase.cpp:559
ProgramInfo::QueryTuningInfo
bool QueryTuningInfo(QString &channum, QString &input) const
Returns the channel and input needed to record the program.
Definition: programinfo.cpp:5097
GetPidsToCache
static void GetPidsToCache(DTVSignalMonitor *dtvMon, pid_cache_t &pid_cache)
Definition: tv_rec.cpp:1807
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
TVRec::m_pauseNotify
bool m_pauseNotify
Definition: tv_rec.h:387
dvbchannel.h
TVRec::m_stateChangeLock
QMutex m_stateChangeLock
Definition: tv_rec.h:382
TVRec::TuningOnSameMultiplex
bool TuningOnSameMultiplex(TuningRequest &request)
Definition: tv_rec.cpp:3448
TVRec::TeardownRecorder
void TeardownRecorder(uint request_flags)
Tears down the recorder.
Definition: tv_rec.cpp:1108
kState_Error
@ kState_Error
Error State, if we ever try to enter this state errored is set.
Definition: tv.h:54
ProgramInfo::GetRecordingStartTime
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:398
TVRec::GetFilePosition
long long GetFilePosition(void)
Returns total number of bytes written by RingBuffer.
Definition: tv_rec.cpp:2584
ProgramInfo::GetPathname
QString GetPathname(void) const
Definition: programinfo.h:337
TVRec::kFlagCancelNextRecording
static const uint kFlagCancelNextRecording
Definition: tv_rec.h:438
RecordingQuality::toStringXML
QString toStringXML(void) const
Definition: recordingquality.cpp:115
ChannelBase::Renumber
virtual void Renumber(uint sourceid, const QString &oldChanNum, const QString &newChanNum)
Changes a channum if we have it cached anywhere.
Definition: channelbase.cpp:625
TVRec::SwitchRecordingRingBuffer
RecordingInfo * SwitchRecordingRingBuffer(const RecordingInfo &rcinfo)
Definition: tv_rec.cpp:4758
tmp
static guint32 * tmp
Definition: goom_core.cpp:30
TVRec::GetRecordEndTime
QDateTime GetRecordEndTime(const ProgramInfo *pi) const
Returns recording end time with proper post-roll.
Definition: tv_rec.cpp:334
VID_720
@ VID_720
Definition: programtypes.h:192
TVRec::m_recordEndTime
QDateTime m_recordEndTime
Definition: tv_rec.h:403
DTVChannel::GetTuningMode
QString GetTuningMode(void) const
Returns tuning mode last set by SetTuningMode().
Definition: dtvchannel.cpp:72
kState_ChangingState
@ kState_ChangingState
This is a placeholder state which we never actually enter, but is returned by GetState() when we are ...
Definition: tv.h:89
DTVChannel::GetOriginalNetworkID
uint GetOriginalNetworkID(void) const
Returns DVB original_network_id, 0 if unknown.
Definition: dtvchannel.h:103
LiveTVChain::GetProgramAt
ProgramInfo * GetProgramAt(int at) const
Returns program at the desired location.
Definition: livetvchain.cpp:320
kState_None
@ 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:58
EITScanner::StopActiveScan
void StopActiveScan(void)
Definition: eitscanner.cpp:296
SignalMonitor::IsRequired
static bool IsRequired(const QString &cardtype)
Returns true iff the card type supports signal monitoring.
Definition: signalmonitor.h:312
TVRec::TeardownAll
void TeardownAll(void)
Definition: tv_rec.cpp:195
GeneralDBOptions::m_channelTimeout
uint m_channelTimeout
Definition: tv_rec.h:77
MPEGStreamData::SetIgnoreCRC
void SetIgnoreCRC(bool haveCRCbug)
Definition: mpegstreamdata.h:111
TVRec::m_inputId
uint m_inputId
Definition: tv_rec.h:369
GeneralDBOptions::m_vbiDev
QString m_vbiDev
Definition: tv_rec.h:71
TVRec::RecorderPaused
void RecorderPaused(void)
This is a callback, called by the "recorder" instance when it has actually paused.
Definition: tv_rec.cpp:2947
JOB_LIVE_REC
@ JOB_LIVE_REC
Definition: jobqueue.h:57
TVRec::m_pendingRecordings
PendingMap m_pendingRecordings
Definition: tv_rec.h:409
TRANSITION
#define TRANSITION(ASTATE, BSTATE)
Definition: tv_rec.cpp:990
TVRec::kFlagAnyRecRunning
static const uint kFlagAnyRecRunning
Definition: tv_rec.h:472
ChannelBase::InitPictureAttributes
virtual bool InitPictureAttributes(void)
Definition: channelbase.h:93
TVRec::GetInputId
uint GetInputId(void) const
Returns the inputid.
Definition: tv_rec.h:234
ProgramInfo::MarkAsInUse
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...
Definition: programinfo.cpp:4903
DTVChannel::GetTransportID
uint GetTransportID(void) const
Returns DVB transport_stream_id, 0 if unknown.
Definition: dtvchannel.h:107
MPEGStreamData::ReturnCachedTable
virtual void ReturnCachedTable(const PSIPTable *psip) const
Definition: mpegstreamdata.cpp:1439
mythdate.h
TVRec::kFlagWaitingForSignal
static const uint kFlagWaitingForSignal
Definition: tv_rec.h:462
TVRec::kFlagDetect
static const uint kFlagDetect
Definition: tv_rec.h:477
ProgramInfo::SetPathname
void SetPathname(const QString &pn)
Definition: programinfo.cpp:2337
MythNotification::Error
static Type Error
Definition: mythnotification.h:33
ProgramInfo::SaveVideoProperties
void SaveVideoProperties(uint mask, uint video_property_flags)
Definition: programinfo.cpp:4660
ProgramInfo::SetRecordingEndTime
void SetRecordingEndTime(const QDateTime &dt)
Definition: programinfo.h:513
TVRec::CreateChannel
bool CreateChannel(const QString &startchannel, bool enter_power_save_mode)
Definition: tv_rec.cpp:94
TVRec::m_autoRunJobs
QHash< QString, int > m_autoRunJobs
Definition: tv_rec.h:405
VID_4K
@ VID_4K
Definition: programtypes.h:194
minor
#define minor(X)
Definition: compat.h:138
RecordingInfo::GetRecordingFile
RecordingFile * GetRecordingFile() const
Definition: recordinginfo.h:271
TVRec::PauseRecorder
void PauseRecorder(void)
Tells "recorder" to pause, used for channel and input changes.
Definition: tv_rec.cpp:2928
programinfo.h
TVRec::NotifySchedulerOfRecording
void NotifySchedulerOfRecording(RecordingInfo *rec)
Tell scheduler about the recording.
Definition: tv_rec.cpp:2751
ATSCStreamData::SetDesiredChannel
void SetDesiredChannel(int major, int minor)
Definition: atscstreamdata.cpp:58
ChannelUtil::GetChanID
static int GetChanID(int db_mplexid, int service_transport_id, int major_channel, int minor_channel, int program_number)
Definition: channelutil.cpp:1310
ProgramInfo::GetScheduledStartTime
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:384
mythlogging.h
SignalMonitor::kDVBSigMon_WaitForPos
static const uint64_t kDVBSigMon_WaitForPos
Wait for rotor to complete turning the antenna.
Definition: signalmonitor.h:200
ProgramInfo::GetRecordingStatus
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:441
TVRec::SetRecordingStatus
void SetRecordingStatus(RecStatus::Type new_status, int line, bool have_lock=false)
Definition: tv_rec.cpp:675
ProgramInfo::QueryAverageAspectRatio
MarkTypes QueryAverageAspectRatio(void) const
Definition: programinfo.cpp:4303
is_dishnet_eit
static bool is_dishnet_eit(uint inputid)
Definition: tv_rec.cpp:1215
DVBStreamData::SetDishNetEIT
void SetDishNetEIT(bool use_dishnet_eit)
Definition: dvbstreamdata.h:164
LiveTVChain::SetHostPrefix
void SetHostPrefix(const QString &prefix)
Definition: livetvchain.cpp:44
MythCoreContext::GenMythURL
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
Definition: mythcorecontext.cpp:763
RemoteStopRecording
bool RemoteStopRecording(uint inputid)
Definition: tvremoteutil.cpp:97
ChannelBase
Abstract class providing a generic interface to tuning hardware.
Definition: channelbase.h:31
RecorderBase::SetRingBuffer
void SetRingBuffer(MythMediaBuffer *Buffer)
Tells recorder to use an externally created ringbuffer.
Definition: recorderbase.cpp:75
hardwareprofile.scan.profile
profile
Definition: scan.py:99
MasterGuideTable::TablePID
uint TablePID(uint i) const
Definition: atsctables.h:128
CardUtil::IsEncoder
static bool IsEncoder(const QString &rawtype)
Definition: cardutil.h:127
RemoteIsBusy
bool RemoteIsBusy(uint inputid, InputInfo &busy_input)
Definition: tvremoteutil.cpp:363
TVRec::kFlagKillRec
static const uint kFlagKillRec
close recorder, discard recording
Definition: tv_rec.h:455
ProgramInfo::SetScheduledEndTime
void SetScheduledEndTime(const QDateTime &dt)
Definition: programinfo.h:511
CardUtil::GetInputInfo
static bool GetInputInfo(InputInfo &input, vector< uint > *groupids=nullptr)
Definition: cardutil.cpp:1630
TuningRequest::m_progNum
int m_progNum
Definition: tv_rec.h:122
ProgramInfo::SetInputID
void SetInputID(uint id)
Definition: programinfo.h:527
DTVChannel::SaveCachedPids
void SaveCachedPids(const pid_cache_t &pid_cache) const
Saves MPEG PIDs to cache to database.
Definition: dtvchannel.cpp:106
MythCoreContext::GetBackendServerPort
int GetBackendServerPort(void)
Returns the locally defined backend control port.
Definition: mythcorecontext.cpp:1055
TVRec::m_recorder
RecorderBase * m_recorder
Definition: tv_rec.h:336
MythCoreContext::SendSystemEvent
void SendSystemEvent(const QString &msg)
Definition: mythcorecontext.cpp:1526
ChannelBase::StoreInputChannels
virtual void StoreInputChannels(void)
Saves current channel as the default channel for the current input.
Definition: channelbase.cpp:645
TVRec::GetMaxBitrate
long long GetMaxBitrate(void) const
Returns the maximum bits per second this recorder can produce.
Definition: tv_rec.cpp:2644
MythCoreContext::SendEvent
void SendEvent(const MythEvent &event)
Definition: mythcorecontext.cpp:1512
eit_start_rand
static int eit_start_rand(int eitTransportTimeout)
Definition: tv_rec.cpp:1255
TVRec::kFlagRunMainLoop
static const uint kFlagRunMainLoop
Definition: tv_rec.h:434
ProgramInfo::SetRecordingRuleType
void SetRecordingRuleType(RecordingType type)
Definition: programinfo.h:568
TVRec::RemoveRecording
TVState RemoveRecording(TVState state) const
If "state" is kState_RecordingOnly or kState_WatchingLiveTV, returns a kState_None,...
Definition: tv_rec.cpp:752
TVRec::WaitForEventThreadSleep
bool WaitForEventThreadSleep(bool wake=true, ulong time=ULONG_MAX)
Definition: tv_rec.cpp:1519
TVRec::m_pseudoLiveTVRecording
RecordingInfo * m_pseudoLiveTVRecording
Definition: tv_rec.h:412
MPEGStreamData::SetRecordingType
void SetRecordingType(const QString &recording_type)
Definition: mpegstreamdata.cpp:102
hardwareprofile.i18n.t
t
Definition: i18n.py:36
TVRec::FlagToString
static QString FlagToString(uint f)
Definition: tv_rec.cpp:4442
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
ProgramInfo::GetTitle
QString GetTitle(void) const
Definition: programinfo.h:355
CardUtil::GetConflictingInputs
static vector< uint > GetConflictingInputs(uint inputid)
Definition: cardutil.cpp:2051
compat.h
TVRec::HandleStateChange
void HandleStateChange(void)
Changes the internalState to the desiredNextState if possible.
Definition: tv_rec.cpp:1002
TVRec::kAutoRunNone
@ kAutoRunNone
Definition: tv_rec.h:325
LiveTVChain::SetInputType
void SetInputType(const QString &type)
Definition: livetvchain.cpp:49
ATSCStreamData
Encapsulates data about ATSC stream and emits events for most tables.
Definition: atscstreamdata.h:28
v4lchannel.h
TVRec::m_tvChain
LiveTVChain * m_tvChain
Definition: tv_rec.h:419
DVBDBOptions::m_dvbOnDemand
bool m_dvbOnDemand
Definition: tv_rec.h:86
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
TVRec::CheckChannel
bool CheckChannel(const QString &name) const
Checks if named channel exists on current tuner.
Definition: tv_rec.cpp:2272
SignalMonitor::kDTVSigMon_WaitForPMT
static const uint64_t kDTVSigMon_WaitForPMT
Definition: signalmonitor.h:182
RecorderBase::Reset
virtual void Reset(void)=0
Reset the recorder to the startup state.
DVBDBOptions::m_dvbTuningDelay
uint m_dvbTuningDelay
Definition: tv_rec.h:87
TVRec::kFlagSignalMonitorRunning
static const uint kFlagSignalMonitorRunning
Definition: tv_rec.h:467
ProgramInfo::SetRecordingGroup
void SetRecordingGroup(const QString &group)
Definition: programinfo.h:514
TVRec::SetupSignalMonitor
bool SetupSignalMonitor(bool tablemon, bool EITscan, bool notify)
This creates a SignalMonitor instance and begins signal monitoring.
Definition: tv_rec.cpp:2036
SignalMonitor::kDTVSigMon_WaitForPAT
static const uint64_t kDTVSigMon_WaitForPAT
Definition: signalmonitor.h:181
TVRec::TuningGetChanNum
QString TuningGetChanNum(const TuningRequest &request, QString &input) const
Definition: tv_rec.cpp:3409
TVRec::kFlagEITScan
static const uint kFlagEITScan
final result desired is an EIT Scan
Definition: tv_rec.h:451
SignalMonitor::kDTVSigMon_WaitForSDT
static const uint64_t kDTVSigMon_WaitForSDT
Definition: signalmonitor.h:186
TVRec::TeardownSignalMonitor
void TeardownSignalMonitor(void)
If a SignalMonitor instance exists, the monitoring thread is stopped and the instance is deleted.
Definition: tv_rec.cpp:2090
RecorderBase::GetRecordingQuality
virtual RecordingQuality * GetRecordingQuality(const RecordingInfo *ri) const
Returns a report about the current recordings quality.
Definition: recorderbase.cpp:503
TVRec::WaitForNextLiveTVDir
bool WaitForNextLiveTVDir(void)
Definition: tv_rec.cpp:4523
RecStatus::Failing
@ Failing
Definition: recStatus.h:18
RecordingInfo::SetDesiredStartTime
void SetDesiredStartTime(const QDateTime &dt)
Definition: recordinginfo.h:236
TVRec::kSignalMonitoringRate
static const uint kSignalMonitoringRate
How many milliseconds the signal monitor should wait between checks.
Definition: tv_rec.h:430
TVRec::kFlagCloseRec
static const uint kFlagCloseRec
close recorder, keep recording
Definition: tv_rec.h:453
RecordingInfo::GetAutoRunJobs
int GetAutoRunJobs(void) const
Returns a bitmap of which jobs are attached to this RecordingInfo.
Definition: recordinginfo.cpp:500
ProgramInfo::GetSourceID
uint GetSourceID(void) const
Definition: programinfo.h:456
MPEGStreamData
Encapsulates data about MPEG stream and emits events for each table.
Definition: mpegstreamdata.h:85
RecordingRule::m_recProfile
QString m_recProfile
Definition: recordingrule.h:121
TVRec::m_nextLiveTVDir
QString m_nextLiveTVDir
Definition: tv_rec.h:413
TVRec::m_triggerEventSleepWait
QWaitCondition m_triggerEventSleepWait
Definition: tv_rec.h:396
RecorderBase::GetFrameRate
double GetFrameRate(void) const
Returns the latest frame rate.
Definition: recorderbase.h:233
JobQueue::JobIsNotInMask
static bool JobIsNotInMask(int job, int mask)
Definition: jobqueue.h:195
TVRec::CloseChannel
void CloseChannel(void)
Definition: tv_rec.cpp:1168
RecordingRule::m_type
RecordingType m_type
Definition: recordingrule.h:113
RecStatus::Aborted
@ Aborted
Definition: recStatus.h:28
TVRec::kFlagKillRingBuffer
static const uint kFlagKillRingBuffer
Definition: tv_rec.h:458
TVRec::ChangeState
void ChangeState(TVState nextState)
Puts a state change on the nextState queue.
Definition: tv_rec.cpp:1086
DVBStreamData
Definition: dvbstreamdata.h:32
TVRec::m_desiredNextState
TVState m_desiredNextState
Definition: tv_rec.h:385
ProgramInfo::toString
QString toString(Verbosity v=kLongDescription, const QString &sep=":", const QString &grp="\"") const
Definition: programinfo.cpp:1830
TVRec::m_parentId
uint m_parentId
Definition: tv_rec.h:370
TVRec::run
void run(void) override
Event handling method, contains event loop.
Definition: tv_rec.cpp:1268
RecStatus::Failed
@ Failed
Definition: recStatus.h:23
TVRec::m_triggerEventLoopSignal
bool m_triggerEventLoopSignal
Definition: tv_rec.h:394
DTVRecorder::GetStreamData
MPEGStreamData * GetStreamData(void) const
Definition: dtvrecorder.h:59
add_spacer
static QString add_spacer(const QString &channel, const QString &spacer)
Adds the spacer before the last character in chan.
Definition: tv_rec.cpp:2283
RecorderBase::IsRecording
virtual bool IsRecording(void)
Tells whether the StartRecorder() loop is running.
Definition: recorderbase.cpp:243
RecordingRule::GetAutoExpire
AutoExpireType GetAutoExpire(void) const
Definition: recordingrule.h:63
TVRec::TuningShutdowns
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:3572
TVRec::m_liveTVStartChannel
QString m_liveTVStartChannel
Definition: tv_rec.h:416
storagegroup.h
TVRec::TuningRequest
friend class TuningRequest
Definition: tv_rec.h:146
f
QTextStream t & f
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:600
jobqueue.h
RecorderBase::GetVideoFd
virtual int GetVideoFd(void)=0
Returns file descriptor of recorder device.
TVRec::m_scanner
EITScanner * m_scanner
Definition: tv_rec.h:339
TVRec::m_rbFileExt
QString m_rbFileExt
Definition: tv_rec.h:423
StandardSetting::getValue
virtual QString getValue(void) const
Definition: standardsettings.h:51
LiveTVChain::ReloadAll
void ReloadAll(const QStringList &data=QStringList())
Definition: livetvchain.cpp:209
TVRec::SetSignalMonitoringRate
int SetSignalMonitoringRate(int rate, int notifyFrontend=1)
Sets the signal monitoring rate.
Definition: tv_rec.cpp:2128
TVRec::m_eitScanStartTime
QDateTime m_eitScanStartTime
Definition: tv_rec.h:391
TVRec::GetPictureAttribute
int GetPictureAttribute(PictureAttribute attr)
Definition: tv_rec.cpp:3005
LOC2
#define LOC2
Definition: tv_rec.cpp:47
TVRec::TuningNewRecorder
void TuningNewRecorder(MPEGStreamData *streamData)
Creates a recorder instance.
Definition: tv_rec.cpp:4173
RecordingInfo::StartedRecording
void StartedRecording(const QString &ext)
Inserts this RecordingInfo into the database as an existing recording.
Definition: recordinginfo.cpp:931
TVRec::GetKeyframePositions
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:2617
TVRec::s_inputsLock
static QReadWriteLock s_inputsLock
Definition: tv_rec.h:426
TVRec::kFlagWaitingForRecPause
static const uint kFlagWaitingForRecPause
Definition: tv_rec.h:461
RecordingQuality
Definition: recordingquality.h:34
DTVRecorder
This is a specialization of RecorderBase used to handle MPEG-2, MPEG-4, MPEG-4 AVC,...
Definition: dtvrecorder.h:27
CHANNEL_DIRECTION_FAVORITE
@ CHANNEL_DIRECTION_FAVORITE
Definition: tv.h:32
MSqlQuery::isConnected
bool isConnected(void) const
Only updated once during object creation.
Definition: mythdbcon.h:135
uint
unsigned int uint
Definition: compat.h:140
TVRec::kFlagLiveTV
static const uint kFlagLiveTV
final result desired is LiveTV recording
Definition: tv_rec.h:442
TVRec::SetNextLiveTVDir
void SetNextLiveTVDir(QString dir)
Definition: tv_rec.cpp:4536
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:57
TVRec::GetFramerate
float GetFramerate(void)
Returns recordering frame rate from the recorder.
Definition: tv_rec.cpp:2554
TVRec::m_transcodeFirst
bool m_transcodeFirst
Definition: tv_rec.h:358
TVRec::m_signalEventCmdSent
bool m_signalEventCmdSent
Definition: tv_rec.h:342
TVRec::GetRecordingStatus
RecStatus::Type GetRecordingStatus(void) const
Definition: tv_rec.cpp:669
TVRec::QueueEITChannelChange
bool QueueEITChannelChange(const QString &name)
Queues up a channel change for the EITScanner.
Definition: tv_rec.cpp:3139
CardUtil::IsVBoxPresent
static bool IsVBoxPresent(uint inputid)
TuningRequest::m_minorChan
uint m_minorChan
Definition: tv_rec.h:121
DTVSignalMonitor::GetATSCStreamData
ATSCStreamData * GetATSCStreamData()
Returns the ATSC stream data if it exists.
Definition: dtvsignalmonitor.cpp:528
TVRec::m_triggerEventLoopWait
QWaitCondition m_triggerEventLoopWait
Definition: tv_rec.h:393
DTVChannel::EnterPowerSavingMode
virtual bool EnterPowerSavingMode(void)
Enters power saving mode if the card supports it.
Definition: dtvchannel.h:66
BROWSE_DOWN
@ BROWSE_DOWN
Fetch information on next channel.
Definition: tv.h:42
InputInfo::Clear
virtual void Clear(void)
Definition: inputinfo.cpp:6
CardUtil::IsV4L
static bool IsV4L(const QString &rawtype)
Definition: cardutil.h:137
TVRec::IsErrored
bool IsErrored(void) const
Returns true is "errored" is true, false otherwise.
Definition: tv_rec.h:238
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:913
TVRec::m_overRecordSecCat
int m_overRecordSecCat
Definition: tv_rec.h:365
RecorderBase::GetFramesWritten
virtual long long GetFramesWritten(void)=0
Returns number of frames written to disk.
states
states
Definition: mythmiscutil.cpp:1245
TVRec::m_overRecordSecNrml
int m_overRecordSecNrml
Definition: tv_rec.h:364
ProgramInfo::SaveAutoExpire
void SaveAutoExpire(AutoExpireType autoExpire, bool updateDelete=false)
Set "autoexpire" field in "recorded" table to "autoExpire".
Definition: programinfo.cpp:3222
ChannelBase::SetFd
virtual void SetFd(int fd)
Sets file descriptor.
Definition: channelbase.h:55
BROWSE_RIGHT
@ BROWSE_RIGHT
Fetch information on current channel in the future.
Definition: tv.h:44
RecordingInfo::LoadRecordingFile
void LoadRecordingFile()
Definition: recordinginfo.cpp:1638
mythmediabuffer.h
TVRec::GetRecording
ProgramInfo * GetRecording(void)
Allocates and returns a ProgramInfo for the current recording.
Definition: tv_rec.cpp:239
TVRec::SpawnLiveTV
void SpawnLiveTV(LiveTVChain *newchain, bool pip, QString startchan)
Tells TVRec to spawn a "Live TV" recorder.
Definition: tv_rec.cpp:2672
GeneralDBOptions::m_skipBtAudio
bool m_skipBtAudio
Definition: tv_rec.h:75
TVRec::CancelNextRecording
void CancelNextRecording(bool cancel)
Tells TVRec to cancel the upcoming recording.
Definition: tv_rec.cpp:347
frm_pos_map_t
QMap< long long, long long > frm_pos_map_t
Frame # -> File offset map.
Definition: programtypes.h:46
tvremoteutil.h
SignalMonitor::Init
static SignalMonitor * Init(const QString &cardtype, int db_cardnum, ChannelBase *channel, bool release_stream)
Definition: signalmonitor.cpp:85
InputInfo::m_inputId
uint m_inputId
unique key in DB for this input
Definition: inputinfo.h:49
PictureAttribute
PictureAttribute
Definition: videoouttypes.h:87
RecorderBase::StopRecording
virtual void StopRecording(void)
StopRecording() signals to the recorder that it should stop recording and exit cleanly.
Definition: recorderbase.cpp:224
MythNotification::Check
static Type Check
Definition: mythnotification.h:35
TVRec::m_triggerEventSleepSignal
bool m_triggerEventSleepSignal
Definition: tv_rec.h:397
ChannelUtil::GetVideoFilters
static QString GetVideoFilters(uint sourceid, const QString &channum)
Definition: channelutil.h:189
RecorderBase::Initialize
virtual void Initialize(void)=0
This is called between SetOptionsFromProfile() and run() to initialize any devices,...
DTVChannel::GetSIStandard
QString GetSIStandard(void) const
Returns PSIP table standard: MPEG, DVB, ATSC, or OpenCable.
Definition: dtvchannel.cpp:44
TVRec::m_eitTransportTimeout
int m_eitTransportTimeout
Definition: tv_rec.h:362
RecordingRule::m_recGroupID
uint m_recGroupID
Definition: recordingrule.h:124
SET_NEXT
#define SET_NEXT()
Definition: tv_rec.cpp:992