MythTV  master
tv_rec.cpp
Go to the documentation of this file.
1 // C headers
2 #include <cstdio>
3 #include <cstdlib>
4 #include <cstring>
5 #include <sched.h> // for sched_yield
6 #include <chrono> // for milliseconds
7 #include <thread> // for sleep_for
8 
9 // MythTV headers
10 
11 #include "compat.h"
12 #include "previewgeneratorqueue.h"
13 #include "dtvsignalmonitor.h"
14 #include "recordingprofile.h"
15 #include "mythcorecontext.h"
16 #include "mythsystemevent.h"
17 #include "atscstreamdata.h"
18 #include "dvbstreamdata.h"
19 #include "recordingrule.h"
20 #include "channelgroup.h"
21 #include "storagegroup.h"
22 #include "tvremoteutil.h"
23 #include "dtvrecorder.h"
24 #include "livetvchain.h"
25 #include "programinfo.h"
26 #include "mythlogging.h"
27 #include "channelbase.h"
28 #include "atsctables.h"
29 #include "dtvchannel.h"
30 #include "eitscanner.h"
31 #include "mythconfig.h"
32 #include "remoteutil.h"
33 #include "ringbuffer.h"
34 #include "v4lchannel.h"
35 #include "dialogbox.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(inputid)
47 
49 const uint TVRec::kSignalMonitoringRate = 50; /* msec */
50 
51 QReadWriteLock TVRec::inputsLock;
52 QMap<uint,TVRec*> TVRec::inputs;
53 
54 static bool is_dishnet_eit(uint inputid);
55 static int init_jobs(const RecordingInfo *rec, RecordingProfile &profile,
56  bool on_host, bool transcode_bfr_comm, bool on_line_comm);
58 static int eit_start_rand(int eitTransportTimeout);
59 
84 TVRec::TVRec(int _inputid)
85  // Various components TVRec coordinates
86  : recorder(nullptr), channel(nullptr), signalMonitor(nullptr),
87  scanner(nullptr),
88  signalEventCmdSent(false),
89  signalMonitorCheckCnt(0),
90  reachedRecordingDeadline(false),
91  reachedPreFail(false),
92  // Various threads
93  eventThread(new MThread("TVRecEvent", this)),
94  recorderThread(nullptr),
95  // Configuration variables from database
96  transcodeFirst(false),
97  earlyCommFlag(false), runJobOnHostOnly(false),
98  eitCrawlIdleStart(60), eitTransportTimeout(5*60),
99  audioSampleRateDB(0),
100  overRecordSecNrml(0), overRecordSecCat(0),
101  overRecordCategory(""),
102  // Configuration variables from setup rutines
103  inputid(_inputid), parentid(0), ispip(false),
104  // State variables
105  stateChangeLock(QMutex::Recursive),
106  pendingRecLock(QMutex::Recursive),
107  internalState(kState_None), desiredNextState(kState_None),
108  changeState(false), pauseNotify(true),
109  stateFlags(0), lastTuningRequest(0),
110  triggerEventLoopLock(QMutex::NonRecursive),
111  triggerEventLoopSignal(false),
112  triggerEventSleepLock(QMutex::NonRecursive),
113  triggerEventSleepSignal(false),
114  switchingBuffer(false),
115  m_recStatus(RecStatus::Unknown),
116  // Current recording info
117  curRecording(nullptr),
118  overrecordseconds(0),
119  // Pseudo LiveTV recording
120  pseudoLiveTVRecording(nullptr),
121  nextLiveTVDir(""), nextLiveTVDirLock(),
122  // tvchain
123  tvchain(nullptr),
124  // RingBuffer info
125  ringBuffer(nullptr), rbFileExt("ts")
126 {
127  inputs[inputid] = this;
128 }
129 
130 bool TVRec::CreateChannel(const QString &startchannel,
131  bool enter_power_save_mode)
132 {
133  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("CreateChannel(%1)")
134  .arg(startchannel));
135  // If this recorder is a child and its parent is not in error, we
136  // do not need nor want to set the channel.
137  bool setchan = true;
138  if (parentid)
139  {
140  TVRec *parentTV = GetTVRec(parentid);
141  if (parentTV && parentTV->GetState() == kState_Error)
142  setchan = false;
143  }
145  this, genOpt, dvbOpt, fwOpt,
146  startchannel, enter_power_save_mode, rbFileExt, setchan);
147 
148  if (genOpt.inputtype == "VBOX")
149  {
151  {
152  // VBOX presence failed recorder is marked errored
153  LOG(VB_GENERAL, LOG_ERR, LOC +
154  QString("CreateChannel(%1) failed due to VBOX not responding "
155  "to network check on inputid [%2]")
156  .arg(startchannel).arg(inputid));
157  channel = nullptr;
158  }
159  }
160 
161  if (!channel)
162  {
163  SetFlags(kFlagErrored, __FILE__, __LINE__);
164  return false;
165  }
166 
167  return true;
168 }
169 
175 bool TVRec::Init(void)
176 {
177  QMutexLocker lock(&stateChangeLock);
178 
180  return false;
181 
183 
184  // configure the Channel instance
185  QString startchannel = GetStartChannel(inputid);
186  if (!CreateChannel(startchannel, true))
187  return false;
188 
190  gCoreContext->GetBoolSetting("AutoTranscodeBeforeAutoCommflag", false);
191  earlyCommFlag = gCoreContext->GetBoolSetting("AutoCommflagWhileRecording", false);
192  runJobOnHostOnly = gCoreContext->GetBoolSetting("JobsRunOnRecordHost", false);
194  max(gCoreContext->GetNumSetting("EITTransportTimeout", 5) * 60, 6);
195  eitCrawlIdleStart = gCoreContext->GetNumSetting("EITCrawIdleStart", 60);
196  audioSampleRateDB = gCoreContext->GetNumSetting("AudioSampleRate");
197  overRecordSecNrml = gCoreContext->GetNumSetting("RecordOverTime");
198  overRecordSecCat = gCoreContext->GetNumSetting("CategoryOverTime") * 60;
199  overRecordCategory= gCoreContext->GetSetting("OverTimeCategory");
200 
201  eventThread->start();
202 
204 
205  return true;
206 }
207 
213 {
214  inputs.remove(inputid);
215 
217  {
218  ClearFlags(kFlagRunMainLoop, __FILE__, __LINE__);
219  eventThread->wait();
220  delete eventThread;
221  eventThread = nullptr;
222  }
223 
224  if (channel)
225  {
226  delete channel;
227  channel = nullptr;
228  }
229 }
230 
232 {
233  LOG(VB_RECORD, LOG_INFO, LOC + "TeardownAll");
234 
236 
237  if (scanner)
238  {
239  delete scanner;
240  scanner = nullptr;
241  }
242 
244 
245  SetRingBuffer(nullptr);
246 }
247 
249 {
250  QMutexLocker locker(&triggerEventLoopLock);
251  triggerEventLoopSignal = true;
252  triggerEventLoopWait.wakeAll();
253 }
254 
262 {
263  if (changeState)
264  return kState_ChangingState;
265  return internalState;
266 }
267 
276 {
277  QMutexLocker lock(&stateChangeLock);
278 
279  ProgramInfo *tmppginfo = nullptr;
280 
281  if (curRecording && !changeState)
282  {
283  tmppginfo = new ProgramInfo(*curRecording);
285  }
286  else
287  tmppginfo = new ProgramInfo();
288  tmppginfo->SetInputID(inputid);
289 
290  return tmppginfo;
291 }
292 
307 void TVRec::RecordPending(const ProgramInfo *rcinfo, int secsleft,
308  bool hasLater)
309 {
310  QMutexLocker statelock(&stateChangeLock);
311  QMutexLocker pendlock(&pendingRecLock);
312 
313  if (secsleft < 0)
314  {
315  LOG(VB_RECORD, LOG_INFO, LOC + "Pending recording revoked on " +
316  QString("inputid [%1]").arg(rcinfo->GetInputID()));
317 
318  PendingMap::iterator it = pendingRecordings.find(rcinfo->GetInputID());
319  if (it != pendingRecordings.end())
320  {
321  (*it).ask = false;
322  (*it).doNotAsk = (*it).canceled = true;
323  }
324  return;
325  }
326 
327  LOG(VB_RECORD, LOG_INFO, LOC +
328  QString("RecordPending on inputid [%1]").arg(rcinfo->GetInputID()));
329 
330  PendingInfo pending;
331  pending.info = new ProgramInfo(*rcinfo);
332  pending.recordingStart = MythDate::current().addSecs(secsleft);
333  pending.hasLaterShowing = hasLater;
334  pending.ask = true;
335  pending.doNotAsk = false;
336 
337  pendingRecordings[rcinfo->GetInputID()] = pending;
338 
339  // If this isn't a recording for this instance to make, we are done
340  if (rcinfo->GetInputID() != inputid)
341  return;
342 
343  // We also need to check our input groups
344  vector<uint> inputids = CardUtil::GetConflictingInputs(
345  rcinfo->GetInputID());
346 
347  pendingRecordings[rcinfo->GetInputID()].possibleConflicts = inputids;
348 
349  pendlock.unlock();
350  statelock.unlock();
351  for (uint i = 0; i < inputids.size(); i++)
352  RemoteRecordPending(inputids[i], rcinfo, secsleft, hasLater);
353  statelock.relock();
354  pendlock.relock();
355 }
356 
361 {
364  if (old_rec)
365  delete old_rec;
366 }
367 
371 QDateTime TVRec::GetRecordEndTime(const ProgramInfo *pi) const
372 {
373  bool spcat = (!overRecordCategory.isEmpty() &&
375  int secs = (spcat) ? overRecordSecCat : overRecordSecNrml;
376  return pi->GetRecordingEndTime().addSecs(secs);
377 }
378 
384 void TVRec::CancelNextRecording(bool cancel)
385 {
386  QMutexLocker pendlock(&pendingRecLock);
387  LOG(VB_RECORD, LOG_INFO, LOC +
388  QString("CancelNextRecording(%1) -- begin").arg(cancel));
389 
390  PendingMap::iterator it = pendingRecordings.find(inputid);
391  if (it == pendingRecordings.end())
392  {
393  LOG(VB_RECORD, LOG_INFO, LOC + QString("CancelNextRecording(%1) -- "
394  "error, unknown recording").arg(cancel));
395  return;
396  }
397 
398  if (cancel)
399  {
400  vector<uint> &inputids = (*it).possibleConflicts;
401  for (uint i = 0; i < inputids.size(); i++)
402  {
403  LOG(VB_RECORD, LOG_INFO, LOC +
404  QString("CancelNextRecording -- inputid 0x%1")
405  .arg((uint64_t)inputids[i],0,16));
406 
407  pendlock.unlock();
408  RemoteRecordPending(inputids[i], (*it).info, -1, false);
409  pendlock.relock();
410  }
411 
412  LOG(VB_RECORD, LOG_INFO, LOC +
413  QString("CancelNextRecording -- inputid [%1]")
414  .arg(inputid));
415 
416  RecordPending((*it).info, -1, false);
417  }
418  else
419  {
420  (*it).canceled = false;
421  }
422 
423  LOG(VB_RECORD, LOG_INFO, LOC +
424  QString("CancelNextRecording(%1) -- end").arg(cancel));
425 }
426 
435 {
436  RecordingInfo ri1(*pginfo);
439  RecordingInfo *rcinfo = &ri1;
440 
441  LOG(VB_RECORD, LOG_INFO, LOC + QString("StartRecording(%1)")
442  .arg(rcinfo->toString(ProgramInfo::kTitleSubtitle)));
443 
444  QMutexLocker lock(&stateChangeLock);
445  QString msg("");
446 
449 
450  // Flush out any pending state changes
452 
453  // We need to do this check early so we don't cancel an overrecord
454  // that we're trying to extend.
458  {
459  int post_roll_seconds = curRecording->GetRecordingEndTime()
460  .secsTo(recordEndTime);
461 
466 
468  .addSecs(post_roll_seconds);
469 
470  msg = QString("updating recording: %1 %2 %3 %4")
474  LOG(VB_RECORD, LOG_INFO, LOC + msg);
475 
476  ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
477 
479  return RecStatus::Recording;
480  }
481 
482  bool cancelNext = false;
483  PendingInfo pendinfo;
484  PendingMap::iterator it;
485  bool has_pending;
486 
487  pendingRecLock.lock();
488  if ((it = pendingRecordings.find(inputid)) != pendingRecordings.end())
489  {
490  (*it).ask = (*it).doNotAsk = false;
491  cancelNext = (*it).canceled;
492  }
493  pendingRecLock.unlock();
494 
495  // Flush out events...
497 
498  // Rescan pending recordings since the event loop may have deleted
499  // a stale entry. If this happens the info pointer will not be valid
500  // since the HandlePendingRecordings loop will have deleted it.
501  pendingRecLock.lock();
502  it = pendingRecordings.find(inputid);
503  has_pending = (it != pendingRecordings.end());
504  if (has_pending)
505  pendinfo = *it;
506  pendingRecLock.unlock();
507 
508  // If the needed input is in a shared input group, and we are
509  // not canceling the recording anyway, check other recorders
510  if (!cancelNext && has_pending && !pendinfo.possibleConflicts.empty())
511  {
512  LOG(VB_RECORD, LOG_INFO, LOC +
513  "Checking input group recorders - begin");
514  vector<uint> &inputids = pendinfo.possibleConflicts;
515 
516  uint mplexid = 0, chanid = 0, sourceid = 0;
517  vector<uint> inputids2;
518  vector<TVState> states;
519 
520  // Stop remote recordings if needed
521  for (uint i = 0; i < inputids.size(); i++)
522  {
523  InputInfo busy_input;
524  bool is_busy = RemoteIsBusy(inputids[i], busy_input);
525 
526  if (is_busy && !sourceid)
527  {
528  mplexid = pendinfo.info->QueryMplexID();
529  chanid = pendinfo.info->GetChanID();
530  sourceid = pendinfo.info->GetSourceID();
531  }
532 
533  if (is_busy &&
534  ((sourceid != busy_input.sourceid) ||
535  (mplexid != busy_input.mplexid) ||
536  ((mplexid == 0 || mplexid == 32767) &&
537  chanid != busy_input.chanid)))
538  {
539  states.push_back((TVState) RemoteGetState(inputids[i]));
540  inputids2.push_back(inputids[i]);
541  }
542  }
543 
544  bool ok = true;
545  for (uint i = 0; (i < inputids2.size()) && ok; i++)
546  {
547  LOG(VB_RECORD, LOG_INFO, LOC +
548  QString("Attempting to stop input [%1] in state %2")
549  .arg(inputids2[i]).arg(StateToString(states[i])));
550 
551  bool success = RemoteStopRecording(inputids2[i]);
552  if (success)
553  {
554  uint state = RemoteGetState(inputids2[i]);
555  LOG(VB_GENERAL, LOG_INFO, LOC + QString("a [%1]: %2")
556  .arg(inputids2[i]).arg(StateToString((TVState)state)));
557  success = (kState_None == state);
558  }
559 
560  // If we managed to stop LiveTV recording, restart playback..
561  if (success && states[i] == kState_WatchingLiveTV)
562  {
563  QString message = QString("QUIT_LIVETV %1").arg(inputids2[i]);
564  MythEvent me(message);
565  gCoreContext->dispatch(me);
566  }
567 
568  LOG(VB_RECORD, LOG_INFO, LOC +
569  QString("Stopping recording on [%1], %2") .arg(inputids2[i])
570  .arg(success ? "succeeded" : "failed"));
571 
572  ok &= success;
573  }
574 
575  // If we failed to stop the remote recordings, don't record
576  if (!ok)
577  {
578  CancelNextRecording(true);
579  cancelNext = true;
580  }
581 
582  inputids.clear();
583 
584  LOG(VB_RECORD, LOG_INFO, LOC + "Checking input group recorders - done");
585  }
586 
587  bool did_switch = false;
588  if (!cancelNext && (GetState() == kState_RecordingOnly))
589  {
591  did_switch = (nullptr != ri2);
592  if (did_switch)
593  {
594  // Make sure scheduler is allowed to end this recording
595  ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
596 
598  }
599  else
600  {
601  // If in post-roll, end recording
602  stateChangeLock.unlock();
603  StopRecording();
604  stateChangeLock.lock();
605  }
606  }
607 
608  if (!cancelNext && (GetState() == kState_None))
609  {
610  if (tvchain)
611  {
612  QString message = QString("LIVETV_EXITED");
613  MythEvent me(message, tvchain->GetID());
614  gCoreContext->dispatch(me);
615  tvchain->DecrRef();
616  tvchain = nullptr;
617  }
618 
620 
621  // Tell event loop to begin recording.
622  curRecording = new RecordingInfo(*rcinfo);
627 
628  // Make sure scheduler is allowed to end this recording
629  ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
630 
633  else
634  LOG(VB_RECORD, LOG_WARNING, LOC + "Still failing.");
636  }
637  else if (!cancelNext && (GetState() == kState_WatchingLiveTV))
638  {
642 
643  // We want the frontend to change channel for recording
644  // and disable the UI for channel change, PiP, etc.
645 
646  QString message = QString("LIVETV_WATCH %1 1").arg(inputid);
647  QStringList prog;
648  rcinfo->ToStringList(prog);
649  MythEvent me(message, prog);
650  gCoreContext->dispatch(me);
651  }
652  else if (!did_switch)
653  {
654  msg = QString("Wanted to record: %1 %2 %3 %4\n\t\t\t")
655  .arg(rcinfo->GetTitle()).arg(rcinfo->GetChanID())
657  .arg(rcinfo->GetRecordingEndTime(MythDate::ISODate));
658 
659  if (cancelNext)
660  {
661  msg += "But a user has canceled this recording";
663  }
664  else
665  {
666  msg += QString("But the current state is: %1")
669  }
670 
672  msg += QString("\n\t\t\tCurrently recording: %1 %2 %3 %4")
676 
677  LOG(VB_GENERAL, LOG_INFO, LOC + msg);
678  }
679 
680  for (int i = 0; i < pendingRecordings.size(); i++)
681  delete pendingRecordings[i].info;
682  pendingRecordings.clear();
683 
684  if (!did_switch)
685  {
687 
688  QMutexLocker locker(&pendingRecLock);
689  if ((curRecording) &&
694  {
695  SetRecordingStatus(RecStatus::Failed, __LINE__, true);
696  }
697  return m_recStatus;
698  }
699 
700  return GetRecordingStatus();
701 }
702 
704 {
705  QMutexLocker pendlock(&pendingRecLock);
706  return m_recStatus;
707 }
708 
710  RecStatus::Type new_status, int line, bool have_lock)
711 {
712  RecStatus::Type old_status;
713  if (have_lock)
714  {
715  old_status = m_recStatus;
716  m_recStatus = new_status;
717  }
718  else
719  {
720  pendingRecLock.lock();
721  old_status = m_recStatus;
722  m_recStatus = new_status;
723  pendingRecLock.unlock();
724  }
725 
726  LOG(VB_RECORD, LOG_INFO, LOC +
727  QString("SetRecordingStatus(%1->%2) on line %3")
728  .arg(RecStatus::toString(old_status, kSingleRecord))
729  .arg(RecStatus::toString(new_status, kSingleRecord))
730  .arg(line));
731 }
732 
739 void TVRec::StopRecording(bool killFile)
740 {
741  if (StateIsRecording(GetState()))
742  {
743  QMutexLocker lock(&stateChangeLock);
744  if (killFile)
745  SetFlags(kFlagKillRec, __FILE__, __LINE__);
746  else if (curRecording)
747  {
748  QDateTime now = MythDate::current(true);
749  if (now < curRecording->GetDesiredEndTime())
751  }
753  // wait for state change to take effect
755  ClearFlags(kFlagCancelNextRecording|kFlagKillRec, __FILE__, __LINE__);
756 
758  }
759 }
760 
767 {
768  return (state == kState_RecordingOnly ||
769  state == kState_WatchingLiveTV);
770 }
771 
777 {
778  return (state == kState_WatchingPreRecorded);
779 }
780 
787 {
788  if (StateIsRecording(state))
789  return kState_None;
790 
791  LOG(VB_GENERAL, LOG_ERR, LOC +
792  QString("Unknown state in RemoveRecording: %1")
793  .arg(StateToString(state)));
794  return kState_Error;
795 }
796 
803 {
804  if (StateIsPlaying(state))
805  {
806  if (state == kState_WatchingPreRecorded)
807  return kState_None;
808  return kState_RecordingOnly;
809  }
810 
811  QString msg = "Unknown state in RemovePlaying: %1";
812  LOG(VB_GENERAL, LOG_ERR, LOC + msg.arg(StateToString(state)));
813 
814  return kState_Error;
815 }
816 
823 {
824  if (!curRec)
825  return;
826 
827  curRec->StartedRecording(rbFileExt);
828  LOG(VB_RECORD, LOG_INFO, LOC + QString("StartedRecording(%1) fn(%2)")
829  .arg(curRec->MakeUniqueKey()).arg(curRec->GetPathname()));
830 
831  if (curRec->IsCommercialFree())
833 
834  AutoRunInitType t = (curRec->GetRecordingGroup() == "LiveTV") ?
836  InitAutoRunJobs(curRec, t, nullptr, __LINE__);
837 
838  SendMythSystemRecEvent("REC_STARTED", curRec);
839 }
840 
849 {
850  if (!curRec)
851  return;
852 
853  // Make sure the recording group is up to date
854  const QString recgrp = curRec->QueryRecordingGroup();
855  curRec->SetRecordingGroup(recgrp);
856 
857  bool is_good = true;
858  if (recq)
859  {
860  LOG((recq->IsDamaged()) ? VB_GENERAL : VB_RECORD, LOG_INFO,
861  LOC + QString("FinishedRecording(%1) %2 recq:%3\n")
862  .arg(curRec->MakeUniqueKey())
863  .arg((recq->IsDamaged()) ? "damaged" : "good")
864  .arg(recq->toStringXML()));
865  is_good = !recq->IsDamaged();
866  delete recq;
867  recq = nullptr;
868  }
869 
870  RecStatus::Type ors = curRec->GetRecordingStatus();
871  // Set the final recording status
872  if (curRec->GetRecordingStatus() == RecStatus::Recording)
874  else if (curRec->GetRecordingStatus() != RecStatus::Recorded)
876  curRec->SetRecordingEndTime(MythDate::current(true));
877  is_good &= (curRec->GetRecordingStatus() == RecStatus::Recorded);
878 
879  // Figure out if this was already done for this recording
880  bool was_finished = false;
881  static QMutex finRecLock;
882  static QHash<QString,QDateTime> finRecMap;
883  {
884  QMutexLocker locker(&finRecLock);
885  QDateTime now = MythDate::current();
886  QDateTime expired = now.addSecs(-60*5);
887  QHash<QString,QDateTime>::iterator it = finRecMap.begin();
888  while (it != finRecMap.end())
889  {
890  if ((*it) < expired)
891  it = finRecMap.erase(it);
892  else
893  ++it;
894  }
895  QString key = curRec->MakeUniqueKey();
896  it = finRecMap.find(key);
897  if (it != finRecMap.end())
898  was_finished = true;
899  else
900  finRecMap[key] = now;
901  }
902 
903  // Print something informative to the log
904  LOG(VB_RECORD, LOG_INFO, LOC +
905  QString("FinishedRecording(%1) %2 quality"
906  "\n\t\t\ttitle: %3\n\t\t\t"
907  "in recgroup: %4 status: %5:%6 %7 %8")
908  .arg(curRec->MakeUniqueKey())
909  .arg(is_good ? "Good" : "Bad")
910  .arg(curRec->GetTitle())
911  .arg(recgrp)
914  .arg(HasFlags(kFlagDummyRecorderRunning)?"is_dummy":"not_dummy")
915  .arg(was_finished?"already_finished":"finished_now"));
916 
917  // This has already been called on this recording..
918  if (was_finished)
919  return;
920 
921  // Notify the frontend watching live tv that this file is final
922  if (tvchain)
923  tvchain->FinishedRecording(curRec);
924 
925  // if this is a dummy recorder, do no more..
927  {
928  curRec->FinishedRecording(true); // so end time is updated
929  SendMythSystemRecEvent("REC_FINISHED", curRec);
930  return;
931  }
932 
933  // Get the width and set the videoprops
934  MarkTypes aspectRatio = curRec->QueryAverageAspectRatio();
935  uint avg_height = curRec->QueryAverageHeight();
936  curRec->SaveVideoProperties(
938  ((avg_height > 1000) ? VID_1080 : ((avg_height > 700) ? VID_720 : 0)) |
939  ((is_good) ? 0 : VID_DAMAGED) |
940  (((aspectRatio == MARK_ASPECT_16_9) ||
941  (aspectRatio == MARK_ASPECT_2_21_1)) ? VID_WIDESCREEN : 0));
942 
943  // Make sure really short recordings have positive run time.
944  if (curRec->GetRecordingEndTime() <= curRec->GetRecordingStartTime())
945  {
946  curRec->SetRecordingEndTime(
947  curRec->GetRecordingStartTime().addSecs(60));
948  }
949 
950  // HACK Temporary hack, ensure we've loaded the recording file info, do it now
951  // so that it contains the final filesize information
952  if (!curRec->GetRecordingFile())
953  curRec->LoadRecordingFile();
954 
955  // Generate a preview
956  uint64_t fsize = curRec->GetFilesize();
957  if (curRec->IsLocal() && (fsize >= 1000) &&
959  {
961  }
962 
963  // store recording in recorded table
964  curRec->FinishedRecording(!is_good || (recgrp == "LiveTV"));
965 
966  // send out UPDATE_RECORDING_STATUS message
967  if (recgrp != "LiveTV")
968  {
969  LOG(VB_RECORD, LOG_INFO, LOC +
970  QString("FinishedRecording -- UPDATE_RECORDING_STATUS: %1")
971  .arg(RecStatus::toString(is_good ? curRec->GetRecordingStatus()
973  MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
974  .arg(curRec->GetInputID())
975  .arg(curRec->GetChanID())
977  .arg(is_good ? curRec->GetRecordingStatus() : RecStatus::Failed)
978  .arg(curRec->GetRecordingEndTime(MythDate::ISODate)));
979  gCoreContext->dispatch(me);
980  }
981 
982  // send out REC_FINISHED message
983  SendMythSystemRecEvent("REC_FINISHED", curRec);
984 
985  // send out DONE_RECORDING message
986  int secsSince = curRec->GetRecordingStartTime()
987  .secsTo(MythDate::current());
988  QString message = QString("DONE_RECORDING %1 %2 %3")
989  .arg(inputid).arg(secsSince).arg(GetFramesWritten());
990  MythEvent me(message);
991  gCoreContext->dispatch(me);
992 
993  // Handle JobQueue
994  QHash<QString,int>::iterator autoJob =
995  autoRunJobs.find(curRec->MakeUniqueKey());
996  if (autoJob == autoRunJobs.end())
997  {
998  LOG(VB_GENERAL, LOG_INFO,
999  "autoRunJobs not initialized until FinishedRecording()");
1000  AutoRunInitType t =
1001  (recgrp == "LiveTV") ? kAutoRunNone : kAutoRunProfile;
1002  InitAutoRunJobs(curRec, t, nullptr, __LINE__);
1003  autoJob = autoRunJobs.find(curRec->MakeUniqueKey());
1004  }
1005  LOG(VB_JOBQUEUE, LOG_INFO, QString("AutoRunJobs 0x%1").arg(*autoJob,0,16));
1006  if ((recgrp == "LiveTV") || (fsize < 1000) ||
1007  (curRec->GetRecordingStatus() != RecStatus::Recorded) ||
1008  (curRec->GetRecordingStartTime().secsTo(
1009  MythDate::current()) < 120))
1010  {
1013  }
1014  if (*autoJob != JOB_NONE)
1015  JobQueue::QueueRecordingJobs(*curRec, *autoJob);
1016  autoRunJobs.erase(autoJob);
1017 }
1018 
1019 #define TRANSITION(ASTATE,BSTATE) \
1020  ((internalState == (ASTATE)) && (desiredNextState == (BSTATE)))
1021 #define SET_NEXT() do { nextState = desiredNextState; changed = true; } while(0)
1022 #define SET_LAST() do { nextState = internalState; changed = true; } while(0)
1023 
1032 {
1033  TVState nextState = internalState;
1034 
1035  bool changed = false;
1036 
1037  QString transMsg = QString(" %1 to %2")
1038  .arg(StateToString(nextState))
1040 
1042  {
1043  LOG(VB_GENERAL, LOG_ERR, LOC +
1044  "HandleStateChange(): Null transition" + transMsg);
1045  changeState = false;
1046  return;
1047  }
1048 
1049  // Make sure EIT scan is stopped before any tuning,
1050  // to avoid race condition with it's tuning requests.
1052  {
1054  ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1055  eitScanStartTime = MythDate::current().addSecs(
1057  }
1058 
1059  // Handle different state transitions
1061  {
1063  SET_NEXT();
1064  }
1066  {
1068  SET_NEXT();
1069  }
1071  {
1072  SetPseudoLiveTVRecording(nullptr);
1073 
1074  SET_NEXT();
1075  }
1077  {
1078  SetPseudoLiveTVRecording(nullptr);
1080  SET_NEXT();
1081  }
1083  {
1086  (GetFlags()&kFlagKillRec)));
1087  SET_NEXT();
1088  }
1089 
1090  QString msg = (changed) ? "Changing from" : "Unknown state transition:";
1091  LOG(VB_GENERAL, LOG_INFO, LOC + msg + transMsg);
1092 
1093  // update internal state variable
1094  internalState = nextState;
1095  changeState = false;
1096 
1098  if (scanner && (internalState == kState_None))
1099  {
1102  }
1103  else
1104  {
1105  eitScanStartTime = eitScanStartTime.addYears(1);
1106  }
1107 }
1108 #undef TRANSITION
1109 #undef SET_NEXT
1110 #undef SET_LAST
1111 
1116 {
1117  QMutexLocker lock(&stateChangeLock);
1118  desiredNextState = nextState;
1119  changeState = true;
1120  WakeEventLoop();
1121 }
1122 
1137 void TVRec::TeardownRecorder(uint request_flags)
1138 {
1139  LOG(VB_RECORD, LOG_INFO, LOC + QString("TeardownRecorder(%1)")
1140  .arg((request_flags & kFlagKillRec) ? "kFlagKillRec" : ""));
1141 
1142  pauseNotify = false;
1143  ispip = false;
1144 
1146  {
1148  recorderThread->wait();
1149  delete recorderThread;
1150  recorderThread = nullptr;
1151  }
1153  __FILE__, __LINE__);
1154 
1155  RecordingQuality *recq = nullptr;
1156  if (recorder)
1157  {
1158  if (GetV4LChannel())
1159  channel->SetFd(-1);
1160 
1162 
1163  QMutexLocker locker(&stateChangeLock);
1164  delete recorder;
1165  recorder = nullptr;
1166  }
1167 
1168  if (ringBuffer)
1169  {
1170  LOG(VB_FILE, LOG_INFO, LOC + "calling StopReads()");
1171  ringBuffer->StopReads();
1172  }
1173 
1174  if (curRecording)
1175  {
1176  if (!!(request_flags & kFlagKillRec))
1178 
1180 
1182  delete curRecording;
1183  curRecording = nullptr;
1184  }
1185 
1186  pauseNotify = true;
1187 
1188  if (GetDTVChannel())
1190 }
1191 
1193 {
1194  return dynamic_cast<DTVRecorder*>(recorder);
1195 }
1196 
1198 {
1199  if (channel &&
1200  ((genOpt.inputtype == "DVB" && dvbOpt.dvb_on_demand) ||
1201  genOpt.inputtype == "FREEBOX" ||
1202  genOpt.inputtype == "VBOX" ||
1203  genOpt.inputtype == "HDHOMERUN" ||
1205  {
1206  channel->Close();
1207  }
1208 }
1209 
1211 {
1212  return dynamic_cast<DTVChannel*>(channel);
1213 }
1214 
1216 {
1217 #ifdef USING_V4L2
1218  return dynamic_cast<V4LChannel*>(channel);
1219 #else
1220  return nullptr;
1221 #endif // USING_V4L2
1222 }
1223 
1224 static bool get_use_eit(uint inputid)
1225 {
1226  MSqlQuery query(MSqlQuery::InitCon());
1227  query.prepare(
1228  "SELECT SUM(useeit) "
1229  "FROM videosource, capturecard "
1230  "WHERE videosource.sourceid = capturecard.sourceid AND"
1231  " capturecard.cardid = :INPUTID");
1232  query.bindValue(":INPUTID", inputid);
1233 
1234  if (!query.exec() || !query.isActive())
1235  {
1236  MythDB::DBError("get_use_eit", query);
1237  return false;
1238  }
1239  else if (query.next())
1240  return query.value(0).toBool();
1241  return false;
1242 }
1243 
1244 static bool is_dishnet_eit(uint inputid)
1245 {
1246  MSqlQuery query(MSqlQuery::InitCon());
1247  query.prepare(
1248  "SELECT SUM(dishnet_eit) "
1249  "FROM videosource, capturecard "
1250  "WHERE videosource.sourceid = capturecard.sourceid AND"
1251  " capturecard.cardid = :INPUTID");
1252  query.bindValue(":INPUTID", inputid);
1253 
1254  if (!query.exec() || !query.isActive())
1255  {
1256  MythDB::DBError("is_dishnet_eit", query);
1257  return false;
1258  }
1259  else if (query.next())
1260  return query.value(0).toBool();
1261  return false;
1262 }
1263 
1264 static int num_inputs(void)
1265 {
1266  MSqlQuery query(MSqlQuery::InitCon());
1267 
1268  QString str =
1269  "SELECT COUNT(cardid) "
1270  "FROM capturecard ";
1271 
1272  query.prepare(str);
1273 
1274  if (!query.exec() || !query.isActive())
1275  {
1276  MythDB::DBError("num_inputs", query);
1277  return -1;
1278  }
1279  else if (query.next())
1280  return query.value(0).toInt();
1281  return -1;
1282 }
1283 
1284 static int eit_start_rand(int eitTransportTimeout)
1285 {
1286  // randomize start time a bit
1287  int timeout = random() % (eitTransportTimeout / 3);
1288  // get the number of inputs and the position of the current input
1289  // to distribute the the scan start evenly over eitTransportTimeout
1290  int no_inputs = num_inputs();
1291  if (no_inputs > 0)
1292  timeout += eitTransportTimeout / no_inputs;
1293  return timeout;
1294 }
1295 
1297 void TVRec::run(void)
1298 {
1299  QMutexLocker lock(&stateChangeLock);
1300  SetFlags(kFlagRunMainLoop, __FILE__, __LINE__);
1301  ClearFlags(kFlagExitPlayer | kFlagFinishRecording, __FILE__, __LINE__);
1302 
1304  // check whether we should use the EITScanner in this TVRec instance
1306  (!GetDTVChannel() || GetDTVChannel()->IsMaster()) &&
1308  {
1309  scanner = new EITScanner(inputid);
1312  }
1313  else
1314  {
1315  eitScanStartTime = eitScanStartTime.addYears(1);
1316  }
1317 
1318  while (HasFlags(kFlagRunMainLoop))
1319  {
1320  // If there is a state change queued up, do it...
1321  if (changeState)
1322  {
1325  __FILE__, __LINE__);
1326  }
1327 
1328  // Quick exit on fatal errors.
1329  if (IsErrored())
1330  {
1331  LOG(VB_GENERAL, LOG_ERR, LOC +
1332  "RunTV encountered fatal error, exiting event thread.");
1333  ClearFlags(kFlagRunMainLoop, __FILE__, __LINE__);
1334  TeardownAll();
1335  return;
1336  }
1337 
1338  // Handle any tuning events.. Blindly grabbing the lock here
1339  // can sometimes cause a deadlock with Init() while it waits
1340  // to make sure this thread starts. Until a better solution
1341  // is found, don't run HandleTuning unless we can safely get
1342  // the lock.
1343  if (inputsLock.tryLockForRead())
1344  {
1345  HandleTuning();
1346  inputsLock.unlock();
1347  }
1348 
1349  // Tell frontends about pending recordings
1351 
1352  // If we are recording a program, check if the recording is
1353  // over or someone has asked us to finish the recording.
1354  // Add an extra 60 seconds to the recording end time if we
1355  // might want a back to back recording.
1356  QDateTime recEnd = (!pendingRecordings.empty()) ?
1357  recordEndTime.addSecs(60) : recordEndTime;
1358  if ((GetState() == kState_RecordingOnly) &&
1359  (MythDate::current() > recEnd ||
1361  {
1363  ClearFlags(kFlagFinishRecording, __FILE__, __LINE__);
1364  }
1365 
1366  if (curRecording)
1367  {
1369 
1370  if (recorder)
1371  {
1373 
1374  // Check for recorder errors
1375  if (recorder->IsErrored())
1376  {
1378 
1380  {
1381  QString message = QString("QUIT_LIVETV %1").arg(inputid);
1382  MythEvent me(message);
1383  gCoreContext->dispatch(me);
1384  }
1385  else
1387  }
1388  }
1389  }
1390 
1391  // Check for the end of the current program..
1393  {
1394  QDateTime now = MythDate::current();
1395  bool has_finish = HasFlags(kFlagFinishRecording);
1396  bool has_rec = pseudoLiveTVRecording;
1397  bool enable_ui = true;
1398 
1399  pendingRecLock.lock();
1400  bool rec_soon =
1402  pendingRecLock.unlock();
1403 
1404  if (has_rec && (has_finish || (now > recordEndTime)))
1405  {
1406  SetPseudoLiveTVRecording(nullptr);
1407  }
1408  else if (!has_rec && !rec_soon && curRecording &&
1409  (now >= curRecording->GetScheduledEndTime()))
1410  {
1411  if (!switchingBuffer)
1412  {
1413  LOG(VB_RECORD, LOG_INFO, LOC +
1414  "Switching Buffer (" +
1415  QString("!has_rec(%1) && ").arg(has_rec) +
1416  QString("!rec_soon(%1) && (").arg(rec_soon) +
1417  MythDate::toString(now, MythDate::ISODate) + " >= " +
1419  QString("(%1) ))")
1420  .arg(now >= curRecording->GetScheduledEndTime()));
1421 
1422  switchingBuffer = true;
1423 
1425  false, true);
1426  }
1427  else
1428  {
1429  LOG(VB_RECORD, LOG_INFO, "Waiting for ringbuffer switch");
1430  }
1431  }
1432  else
1433  enable_ui = false;
1434 
1435  if (enable_ui)
1436  {
1437  LOG(VB_RECORD, LOG_INFO, LOC + "Enabling Full LiveTV UI.");
1438  QString message = QString("LIVETV_WATCH %1 0").arg(inputid);
1439  MythEvent me(message);
1440  gCoreContext->dispatch(me);
1441  }
1442  }
1443 
1444  // Check for ExitPlayer flag, and if set change to a non-watching
1445  // state (either kState_RecordingOnly or kState_None).
1447  {
1450  else if (StateIsPlaying(internalState))
1452  ClearFlags(kFlagExitPlayer, __FILE__, __LINE__);
1453  }
1454 
1455  if (scanner && channel &&
1457  {
1458  if (!dvbOpt.dvb_eitscan)
1459  {
1460  LOG(VB_EIT, LOG_INFO, LOC +
1461  "EIT scanning disabled for this input.");
1462  eitScanStartTime = eitScanStartTime.addYears(1);
1463  }
1464  else if (!get_use_eit(GetInputId()))
1465  {
1466  LOG(VB_EIT, LOG_INFO, LOC +
1467  "EIT scanning disabled for all sources on this input.");
1468  eitScanStartTime = eitScanStartTime.addYears(1);
1469  }
1470  else
1471  {
1472  // Check if another card in the same input group is
1473  // busy. This could be either virtual DVB-devices or
1474  // a second tuner on a single card
1475  inputsLock.lockForRead();
1476  bool allow_eit = true;
1477  vector<uint> inputids =
1479  InputInfo busy_input;
1480  for (uint i = 0; i < inputids.size() && allow_eit; ++i)
1481  allow_eit = !RemoteIsBusy(inputids[i], busy_input);
1482  if (allow_eit)
1483  {
1485  SetFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1487  QDateTime::currentDateTime().addYears(1);
1488  }
1489  else
1490  {
1491  LOG(VB_CHANNEL, LOG_INFO, LOC + QString(
1492  "Postponing EIT scan on input [%1] "
1493  "because input %2 is busy")
1494  .arg(inputid).arg(busy_input.inputid));
1495  eitScanStartTime = eitScanStartTime.addSecs(300);
1496  }
1497  inputsLock.unlock();
1498  }
1499  }
1500 
1501  // We should be no more than a few thousand milliseconds,
1502  // as the end recording code does not have a trigger...
1503  // NOTE: If you change anything here, make sure that
1504  // WaitforEventThreadSleep() will still work...
1505  if (tuningRequests.empty() && !changeState)
1506  {
1507  lock.unlock(); // stateChangeLock
1508 
1509  {
1510  QMutexLocker locker(&triggerEventSleepLock);
1511  triggerEventSleepSignal = true;
1512  triggerEventSleepWait.wakeAll();
1513  }
1514 
1515  sched_yield();
1516 
1517  {
1518  QMutexLocker locker(&triggerEventLoopLock);
1519  // We check triggerEventLoopSignal because it is possible
1520  // that WakeEventLoop() was called since we
1521  // unlocked the stateChangeLock
1523  {
1524  triggerEventLoopWait.wait(
1525  &triggerEventLoopLock, 1000 /* ms */);
1526  }
1527  triggerEventLoopSignal = false;
1528  }
1529 
1530  lock.relock(); // stateChangeLock
1531  }
1532  }
1533 
1534  if (GetState() != kState_None)
1535  {
1538  }
1539 
1540  TeardownAll();
1541 }
1542 
1548 bool TVRec::WaitForEventThreadSleep(bool wake, ulong time)
1549 {
1550  bool ok = false;
1551  MythTimer t;
1552  t.start();
1553 
1554  while (!ok && ((unsigned long) t.elapsed()) < time)
1555  {
1556  MythTimer t2;
1557  t2.start();
1558 
1559  if (wake)
1560  WakeEventLoop();
1561 
1562  stateChangeLock.unlock();
1563 
1564  sched_yield();
1565 
1566  {
1567  QMutexLocker locker(&triggerEventSleepLock);
1570  triggerEventSleepSignal = false;
1571  }
1572 
1573  stateChangeLock.lock();
1574 
1575  // verify that we were triggered.
1576  ok = (tuningRequests.empty() && !changeState);
1577 
1578  int te = t2.elapsed();
1579  if (!ok && te < 10)
1580  std::this_thread::sleep_for(std::chrono::microseconds(10-te));
1581  }
1582  return ok;
1583 }
1584 
1586 {
1587  QMutexLocker pendlock(&pendingRecLock);
1588 
1589  PendingMap::iterator it, next;
1590 
1591  for (it = pendingRecordings.begin(); it != pendingRecordings.end();)
1592  {
1593  next = it; ++next;
1594  if (MythDate::current() > (*it).recordingStart.addSecs(30))
1595  {
1596  LOG(VB_RECORD, LOG_INFO, LOC + "Deleting stale pending recording " +
1597  QString("[%1] '%2'")
1598  .arg((*it).info->GetInputID())
1599  .arg((*it).info->GetTitle()));
1600 
1601  delete (*it).info;
1602  pendingRecordings.erase(it);
1603  }
1604  it = next;
1605  }
1606 
1607  if (pendingRecordings.empty())
1608  return;
1609 
1610  // Make sure EIT scan is stopped so it does't interfere
1612  {
1613  LOG(VB_CHANNEL, LOG_INFO,
1614  LOC + "Stopping active EIT scan for pending recording.");
1616  }
1617 
1618  // If we have a pending recording and AskAllowRecording
1619  // or DoNotAskAllowRecording is set and the frontend is
1620  // ready send an ASK_RECORDING query to frontend.
1621 
1622  bool has_rec = false;
1623  it = pendingRecordings.begin();
1624  if ((1 == pendingRecordings.size()) &&
1625  (*it).ask &&
1626  ((*it).info->GetInputID() == inputid) &&
1628  {
1630  has_rec = pseudoLiveTVRecording &&
1632  (*it).recordingStart);
1633  }
1634 
1635  for (it = pendingRecordings.begin(); it != pendingRecordings.end(); ++it)
1636  {
1637  if (!(*it).ask && !(*it).doNotAsk)
1638  continue;
1639 
1640  int timeuntil = ((*it).doNotAsk) ?
1641  -1: MythDate::current().secsTo((*it).recordingStart);
1642 
1643  if (has_rec)
1644  (*it).canceled = true;
1645 
1646  QString query = QString("ASK_RECORDING %1 %2 %3 %4")
1647  .arg(inputid)
1648  .arg(timeuntil)
1649  .arg(has_rec ? 1 : 0)
1650  .arg((*it).hasLaterShowing ? 1 : 0);
1651 
1652  LOG(VB_GENERAL, LOG_INFO, LOC + query);
1653 
1654  QStringList msg;
1655  (*it).info->ToStringList(msg);
1656  MythEvent me(query, msg);
1657  gCoreContext->dispatch(me);
1658 
1659  (*it).ask = (*it).doNotAsk = false;
1660  }
1661 }
1662 
1664  uint &parentid,
1665  GeneralDBOptions &gen_opts,
1666  DVBDBOptions &dvb_opts,
1667  FireWireDBOptions &firewire_opts)
1668 {
1669  int testnum = 0;
1670  QString test;
1671 
1672  MSqlQuery query(MSqlQuery::InitCon());
1673  query.prepare(
1674  "SELECT videodevice, vbidevice, audiodevice, "
1675  " audioratelimit, cardtype, "
1676  " skipbtaudio, signal_timeout, channel_timeout, "
1677  " dvb_wait_for_seqstart, "
1678  ""
1679  " dvb_on_demand, dvb_tuning_delay, dvb_eitscan,"
1680  ""
1681  " firewire_speed, firewire_model, firewire_connection, "
1682  " parentid "
1683  ""
1684  "FROM capturecard "
1685  "WHERE cardid = :INPUTID");
1686  query.bindValue(":INPUTID", inputid);
1687 
1688  if (!query.exec() || !query.isActive())
1689  {
1690  MythDB::DBError("getdevices", query);
1691  return false;
1692  }
1693 
1694  if (!query.next())
1695  return false;
1696 
1697  // General options
1698  test = query.value(0).toString();
1699  if (!test.isEmpty())
1700  gen_opts.videodev = test;
1701 
1702  test = query.value(1).toString();
1703  if (!test.isEmpty())
1704  gen_opts.vbidev = test;
1705 
1706  test = query.value(2).toString();
1707  if (!test.isEmpty())
1708  gen_opts.audiodev = test;
1709 
1710  gen_opts.audiosamplerate = max(testnum, query.value(3).toInt());
1711 
1712  test = query.value(4).toString();
1713  if (!test.isEmpty())
1714  gen_opts.inputtype = test;
1715 
1716  gen_opts.skip_btaudio = query.value(5).toUInt();
1717 
1718  gen_opts.signal_timeout = (uint) max(query.value(6).toInt(), 0);
1719  gen_opts.channel_timeout = (uint) max(query.value(7).toInt(), 0);
1720 
1721  // We should have at least 100 ms to acquire tables...
1722  int table_timeout = ((int)gen_opts.channel_timeout -
1723  (int)gen_opts.signal_timeout);
1724  if (table_timeout < 100)
1725  gen_opts.channel_timeout = gen_opts.signal_timeout + 2500;
1726 
1727  gen_opts.wait_for_seqstart = query.value(8).toUInt();
1728 
1729  // DVB options
1730  uint dvboff = 9;
1731  dvb_opts.dvb_on_demand = query.value(dvboff + 0).toUInt();
1732  dvb_opts.dvb_tuning_delay = query.value(dvboff + 1).toUInt();
1733  dvb_opts.dvb_eitscan = query.value(dvboff + 2).toUInt();
1734 
1735  // Firewire options
1736  uint fireoff = dvboff + 3;
1737  firewire_opts.speed = query.value(fireoff + 0).toUInt();
1738 
1739  test = query.value(fireoff + 1).toString();
1740  if (!test.isEmpty())
1741  firewire_opts.model = test;
1742 
1743  firewire_opts.connection = query.value(fireoff + 2).toUInt();
1744 
1745  parentid = query.value(15).toUInt();
1746 
1747  return true;
1748 }
1749 
1751 {
1752  QString startchan;
1753 
1754  LOG(VB_RECORD, LOG_INFO, LOC + QString("GetStartChannel[%1]")
1755  .arg(inputid));
1756 
1757  // Get last tuned channel from database, to use as starting channel
1758  MSqlQuery query(MSqlQuery::InitCon());
1759  query.prepare(
1760  "SELECT startchan "
1761  "FROM capturecard "
1762  "WHERE capturecard.cardid = :INPUTID");
1763  query.bindValue(":INPUTID", inputid);
1764 
1765  if (!query.exec() || !query.isActive())
1766  {
1767  MythDB::DBError("getstartchan", query);
1768  }
1769  else if (query.next())
1770  {
1771  startchan = query.value(0).toString();
1772  if (!startchan.isEmpty())
1773  {
1774  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Start channel: %1.")
1775  .arg(startchan));
1776  return startchan;
1777  }
1778  }
1779 
1780  // If we failed to get the last tuned channel,
1781  // get a valid channel on our current input.
1782  query.prepare(
1783  "SELECT channum "
1784  "FROM capturecard, channel "
1785  "WHERE channel.sourceid = capturecard.sourceid AND "
1786  " capturecard.cardid = :INPUTID");
1787  query.bindValue(":INPUTID", inputid);
1788 
1789  if (!query.exec() || !query.isActive())
1790  {
1791  MythDB::DBError("getstartchan2", query);
1792  }
1793  while (query.next())
1794  {
1795  startchan = query.value(0).toString();
1796  if (!startchan.isEmpty())
1797  {
1798  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Start channel from DB is "
1799  "empty, setting to '%1' instead.").arg(startchan));
1800  return startchan;
1801  }
1802  }
1803 
1804  // If we failed to get a channel on our current input,
1805  // widen search to any input.
1806  query.prepare(
1807  "SELECT channum, inputname "
1808  "FROM capturecard, channel "
1809  "WHERE channel.sourceid = capturecard.sourceid AND "
1810  " capturecard.cardid = :INPUTID");
1811  query.bindValue(":INPUTID", inputid);
1812 
1813  if (!query.exec() || !query.isActive())
1814  {
1815  MythDB::DBError("getstartchan3", query);
1816  }
1817  while (query.next())
1818  {
1819  startchan = query.value(0).toString();
1820  if (!startchan.isEmpty())
1821  {
1822  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Start channel invalid, "
1823  "setting to '%1' on input %2 instead.").arg(startchan)
1824  .arg(query.value(1).toString()));
1825  return startchan;
1826  }
1827  }
1828 
1829  // If there are no valid channels, just use a random channel
1830  startchan = "3";
1831  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Problem finding starting channel, "
1832  "setting to default of '%1'.").arg(startchan));
1833  return startchan;
1834 }
1835 
1836 static void GetPidsToCache(DTVSignalMonitor *dtvMon, pid_cache_t &pid_cache)
1837 {
1838  if (!dtvMon->GetATSCStreamData())
1839  return;
1840 
1841  const MasterGuideTable *mgt = dtvMon->GetATSCStreamData()->GetCachedMGT();
1842  if (!mgt)
1843  return;
1844 
1845  for (uint i = 0; i < mgt->TableCount(); ++i)
1846  {
1847  pid_cache_item_t item(mgt->TablePID(i), mgt->TableType(i));
1848  pid_cache.push_back(item);
1849  }
1850  dtvMon->GetATSCStreamData()->ReturnCachedTable(mgt);
1851 }
1852 
1853 static bool ApplyCachedPids(DTVSignalMonitor *dtvMon, const DTVChannel* channel)
1854 {
1855  pid_cache_t pid_cache;
1856  channel->GetCachedPids(pid_cache);
1857  pid_cache_t::const_iterator it = pid_cache.begin();
1858  bool vctpid_cached = false;
1859  for (; it != pid_cache.end(); ++it)
1860  {
1861  if ((it->GetTableID() == TableID::TVCT) ||
1862  (it->GetTableID() == TableID::CVCT))
1863  {
1864  vctpid_cached = true;
1865  dtvMon->GetATSCStreamData()->AddListeningPID(it->GetPID());
1866  }
1867  }
1868  return vctpid_cached;
1869 }
1870 
1887 {
1888  LOG(VB_RECORD, LOG_INFO, LOC + "Setting up table monitoring.");
1889 
1891  DTVChannel *dtvchan = GetDTVChannel();
1892  if (!sm || !dtvchan)
1893  {
1894  LOG(VB_GENERAL, LOG_ERR, LOC + "Setting up table monitoring.");
1895  return false;
1896  }
1897 
1898  MPEGStreamData *sd = nullptr;
1899  if (GetDTVRecorder())
1900  {
1901  sd = GetDTVRecorder()->GetStreamData();
1902  sd->SetCaching(true);
1903  }
1904 
1905  QString recording_type = "all";
1909  const StandardSetting *setting = profile.byName("recordingtype");
1910  if (setting)
1911  recording_type = setting->getValue();
1912 
1913  const QString tuningmode = dtvchan->GetTuningMode();
1914 
1915  // Check if this is an ATSC Channel
1916  int major = dtvchan->GetMajorChannel();
1917  int minor = dtvchan->GetMinorChannel();
1918  if ((minor > 0) && (tuningmode == "atsc"))
1919  {
1920  QString msg = QString("ATSC channel: %1_%2").arg(major).arg(minor);
1921  LOG(VB_RECORD, LOG_INFO, LOC + msg);
1922 
1923  ATSCStreamData *asd = dynamic_cast<ATSCStreamData*>(sd);
1924  if (!asd)
1925  {
1926  sd = asd = new ATSCStreamData(major, minor, inputid);
1927  sd->SetCaching(true);
1928  if (GetDTVRecorder())
1929  GetDTVRecorder()->SetStreamData(asd);
1930  }
1931 
1932  asd->Reset();
1933  sm->SetStreamData(sd);
1934  sm->SetChannel(major, minor);
1935  sd->SetRecordingType(recording_type);
1936 
1937  // Try to get pid of VCT from cache and
1938  // require MGT if we don't have VCT pid.
1939  if (!ApplyCachedPids(sm, dtvchan))
1941 
1942  LOG(VB_RECORD, LOG_INFO, LOC +
1943  "Successfully set up ATSC table monitoring.");
1944  return true;
1945  }
1946 
1947  // Check if this is an DVB channel
1948  int progNum = dtvchan->GetProgramNumber();
1949  if ((progNum >= 0) && (tuningmode == "dvb") && (genOpt.inputtype != "VBOX"))
1950  {
1951  int netid = dtvchan->GetOriginalNetworkID();
1952  int tsid = dtvchan->GetTransportID();
1953 
1954  DVBStreamData *dsd = dynamic_cast<DVBStreamData*>(sd);
1955  if (!dsd)
1956  {
1957  sd = dsd = new DVBStreamData(netid, tsid, progNum, inputid);
1958  sd->SetCaching(true);
1959  if (GetDTVRecorder())
1960  GetDTVRecorder()->SetStreamData(dsd);
1961  }
1962 
1963  LOG(VB_RECORD, LOG_INFO, LOC +
1964  QString("DVB service_id %1 on net_id %2 tsid %3")
1965  .arg(progNum).arg(netid).arg(tsid));
1966 
1968 
1969  dsd->Reset();
1970  sm->SetStreamData(sd);
1971  sm->SetDVBService(netid, tsid, progNum);
1972  sd->SetRecordingType(recording_type);
1973 
1977  sm->SetRotorTarget(1.0f);
1978 
1979  if (EITscan)
1980  {
1982  sm->IgnoreEncrypted(true);
1983  }
1984 
1985  LOG(VB_RECORD, LOG_INFO, LOC +
1986  "Successfully set up DVB table monitoring.");
1987  return true;
1988  }
1989 
1990  // Check if this is an MPEG channel
1991  if (progNum >= 0)
1992  {
1993  if (!sd)
1994  {
1995  sd = new MPEGStreamData(progNum, inputid, true);
1996  sd->SetCaching(true);
1997  if (GetDTVRecorder())
1999  }
2000 
2001  QString msg = QString("MPEG program number: %1").arg(progNum);
2002  LOG(VB_RECORD, LOG_INFO, LOC + msg);
2003 
2005 
2006  sd->Reset();
2007  sm->SetStreamData(sd);
2008  sm->SetProgramNumber(progNum);
2009  sd->SetRecordingType(recording_type);
2010 
2014  sm->SetRotorTarget(1.0f);
2015 
2016  if (EITscan)
2017  {
2019  sm->IgnoreEncrypted(true);
2020  }
2021 
2022  LOG(VB_RECORD, LOG_INFO, LOC +
2023  "Successfully set up MPEG table monitoring.");
2024  return true;
2025  }
2026 
2027  // If this is not an ATSC, DVB or MPEG channel then check to make sure
2028  // that we have permanent pidcache entries.
2029  bool ok = false;
2030  if (GetDTVChannel())
2031  {
2032  pid_cache_t pid_cache;
2033  GetDTVChannel()->GetCachedPids(pid_cache);
2034  pid_cache_t::const_iterator it = pid_cache.begin();
2035  for (; !ok && it != pid_cache.end(); ++it)
2036  ok |= it->IsPermanent();
2037  }
2038 
2039  if (!ok)
2040  {
2041  QString msg = "No valid DTV info, ATSC maj(%1) min(%2), MPEG pn(%3)";
2042  LOG(VB_GENERAL, LOG_ERR, LOC + msg.arg(major).arg(minor).arg(progNum));
2043  }
2044  else
2045  {
2046  LOG(VB_RECORD, LOG_INFO, LOC +
2047  "Successfully set up raw pid monitoring.");
2048  }
2049 
2050  return ok;
2051 }
2052 
2067 bool TVRec::SetupSignalMonitor(bool tablemon, bool EITscan, bool notify)
2068 {
2069  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetupSignalMonitor(%1, %2)")
2070  .arg(tablemon).arg(notify));
2071 
2072  // if it already exists, there no need to initialize it
2073  if (signalMonitor)
2074  return true;
2075 
2076  // if there is no channel object we can't monitor it
2077  if (!channel)
2078  return false;
2079 
2080  // nothing to monitor here either (DummyChannel)
2081  if (genOpt.inputtype == "IMPORT" || genOpt.inputtype == "DEMO")
2082  return true;
2083 
2084  // make sure statics are initialized
2086 
2089  channel, false);
2090 
2091  if (signalMonitor)
2092  {
2093  LOG(VB_RECORD, LOG_INFO, LOC + "Signal monitor successfully created");
2094  // If this is a monitor for Digital TV, initialize table monitors
2095  if (GetDTVSignalMonitor() && tablemon &&
2096  !SetupDTVSignalMonitor(EITscan))
2097  {
2098  LOG(VB_GENERAL, LOG_ERR, LOC +
2099  "Failed to setup digital signal monitoring");
2100 
2101  return false;
2102  }
2103 
2104  signalMonitor->AddListener(this);
2106  kSignalMonitoringRate * 5 :
2109 
2110  // Start the monitoring thread
2111  signalMonitor->Start();
2112  }
2113 
2114  return true;
2115 }
2116 
2122 {
2123  if (!signalMonitor)
2124  return;
2125 
2126  LOG(VB_RECORD, LOG_INFO, LOC + "TeardownSignalMonitor() -- begin");
2127 
2128  // If this is a DTV signal monitor, save any pids we know about.
2130  DTVChannel *dtvChan = GetDTVChannel();
2131  if (dtvMon && dtvChan)
2132  {
2133  pid_cache_t pid_cache;
2134  GetPidsToCache(dtvMon, pid_cache);
2135  if (!pid_cache.empty())
2136  dtvChan->SaveCachedPids(pid_cache);
2137  }
2138 
2139  if (signalMonitor)
2140  {
2141  delete signalMonitor;
2142  signalMonitor = nullptr;
2143  }
2144 
2145  LOG(VB_RECORD, LOG_INFO, LOC + "TeardownSignalMonitor() -- end");
2146 }
2147 
2159 int TVRec::SetSignalMonitoringRate(int rate, int notifyFrontend)
2160 {
2161  QString msg = "SetSignalMonitoringRate(%1, %2)";
2162  LOG(VB_RECORD, LOG_INFO, LOC +
2163  msg.arg(rate).arg(notifyFrontend) + "-- start");
2164 
2165  QMutexLocker lock(&stateChangeLock);
2166 
2168  {
2169  LOG(VB_GENERAL, LOG_ERR, LOC +
2170  "Signal Monitoring is notsupported by your hardware.");
2171  return 0;
2172  }
2173 
2175  {
2176  LOG(VB_GENERAL, LOG_ERR, LOC +
2177  "Signal can only be monitored in LiveTV Mode.");
2178  return 0;
2179  }
2180 
2181  ClearFlags(kFlagRingBufferReady, __FILE__, __LINE__);
2182 
2183  TuningRequest req = (rate > 0) ?
2186 
2187  tuningRequests.enqueue(req);
2188 
2189  // Wait for RingBuffer reset
2190  while (!HasFlags(kFlagRingBufferReady))
2192  LOG(VB_RECORD, LOG_INFO, LOC +
2193  msg.arg(rate).arg(notifyFrontend) + " -- end");
2194  return 1;
2195 }
2196 
2198 {
2199  return dynamic_cast<DTVSignalMonitor*>(signalMonitor);
2200 }
2201 
2214 {
2215  QString msg("");
2216  MSqlQuery query(MSqlQuery::InitCon());
2217 
2218  if (!query.isConnected())
2219  return false;
2220 
2221  query.prepare("SELECT channel.channum, channel.callsign "
2222  "FROM channel "
2223  "WHERE channel.chanid = :CHANID");
2224  query.bindValue(":CHANID", chanid);
2225  if (!query.exec() || !query.next())
2226  {
2227  MythDB::DBError("ShouldSwitchToAnotherInput", query);
2228  return false;
2229  }
2230 
2231  QString channelname = query.value(0).toString();
2232  QString callsign = query.value(1).toString();
2233 
2234  query.prepare(
2235  "SELECT channel.channum "
2236  "FROM channel, capturecard "
2237  "WHERE ( channel.chanid = :CHANID OR "
2238  " ( channel.channum = :CHANNUM AND "
2239  " channel.callsign = :CALLSIGN ) "
2240  " ) AND "
2241  " channel.sourceid = capturecard.sourceid AND "
2242  " capturecard.cardid = :INPUTID");
2243  query.bindValue(":CHANID", chanid);
2244  query.bindValue(":CHANNUM", channelname);
2245  query.bindValue(":CALLSIGN", callsign);
2246  query.bindValue(":INPUTID", inputid);
2247 
2248  if (!query.exec() || !query.isActive())
2249  {
2250  MythDB::DBError("ShouldSwitchToAnotherInput", query);
2251  }
2252  else if (query.size() > 0)
2253  {
2254  msg = "Found channel (%1) on current input[%2].";
2255  LOG(VB_RECORD, LOG_INFO, LOC + msg.arg(channelname).arg(inputid));
2256  return false;
2257  }
2258 
2259  // We didn't find it on the current input, so now we check other inputs.
2260  query.prepare(
2261  "SELECT channel.channum, capturecard.cardid "
2262  "FROM channel, capturecard "
2263  "WHERE ( channel.chanid = :CHANID OR "
2264  " ( channel.channum = :CHANNUM AND "
2265  " channel.callsign = :CALLSIGN ) "
2266  " ) AND "
2267  " channel.sourceid = capturecard.sourceid AND "
2268  " capturecard.cardid != :INPUTID");
2269  query.bindValue(":CHANID", chanid);
2270  query.bindValue(":CHANNUM", channelname);
2271  query.bindValue(":CALLSIGN", callsign);
2272  query.bindValue(":INPUTID", inputid);
2273 
2274  if (!query.exec() || !query.isActive())
2275  {
2276  MythDB::DBError("ShouldSwitchToAnotherInput", query);
2277  }
2278  else if (query.next())
2279  {
2280  msg = QString("Found channel (%1) on different input(%2).")
2281  .arg(query.value(0).toString()).arg(query.value(1).toString());
2282  LOG(VB_RECORD, LOG_INFO, LOC + msg);
2283  return true;
2284  }
2285 
2286  msg = QString("Did not find channel(%1) on any input.").arg(channelname);
2287  LOG(VB_RECORD, LOG_ERR, LOC + msg);
2288  return false;
2289 }
2290 
2301 bool TVRec::CheckChannel(QString name) const
2302 {
2303  if (!channel)
2304  return false;
2305 
2306  return channel->CheckChannel(name);
2307 }
2308 
2312 static QString add_spacer(const QString &channel, const QString &spacer)
2313 {
2314  QString chan = channel;
2315  if ((chan.length() >= 2) && !spacer.isEmpty())
2316  return chan.left(chan.length()-1) + spacer + chan.right(1);
2317  return chan;
2318 }
2319 
2347 bool TVRec::CheckChannelPrefix(const QString &prefix,
2348  uint &is_complete_valid_channel_on_rec,
2349  bool &is_extra_char_useful,
2350  QString &needed_spacer)
2351 {
2352 #if DEBUG_CHANNEL_PREFIX
2353  LOG(VB_GENERAL, LOG_DEBUG, QString("CheckChannelPrefix(%1)").arg(prefix));
2354 #endif
2355 
2356  static const uint kSpacerListSize = 5;
2357  static const char* spacers[kSpacerListSize] = { "", "_", "-", "#", "." };
2358 
2359  MSqlQuery query(MSqlQuery::InitCon());
2360  QString basequery = QString(
2361  "SELECT channel.chanid, channel.channum, capturecard.cardid "
2362  "FROM channel, capturecard "
2363  "WHERE channel.channum LIKE '%1%' AND "
2364  " channel.sourceid = capturecard.sourceid");
2365 
2366  QString inputquery[2] =
2367  {
2368  QString(" AND capturecard.cardid = '%1'").arg(inputid),
2369  QString(" AND capturecard.cardid != '%1'").arg(inputid),
2370  };
2371 
2372  vector<uint> fchanid;
2373  vector<QString> fchannum;
2374  vector<uint> finputid;
2375  vector<QString> fspacer;
2376 
2377  for (uint i = 0; i < 2; i++)
2378  {
2379  for (uint j = 0; j < kSpacerListSize; j++)
2380  {
2381  QString qprefix = add_spacer(
2382  prefix, (QString(spacers[j]) == "_") ? "\\_" : spacers[j]);
2383  query.prepare(basequery.arg(qprefix) + inputquery[i]);
2384 
2385  if (!query.exec() || !query.isActive())
2386  {
2387  MythDB::DBError("checkchannel -- locate channum", query);
2388  }
2389  else if (query.size())
2390  {
2391  while (query.next())
2392  {
2393  fchanid.push_back(query.value(0).toUInt());
2394  fchannum.push_back(query.value(1).toString());
2395  finputid.push_back(query.value(2).toUInt());
2396  fspacer.push_back(spacers[j]);
2397 #if DEBUG_CHANNEL_PREFIX
2398  LOG(VB_GENERAL, LOG_DEBUG,
2399  QString("(%1,%2) Adding %3 rec %4")
2400  .arg(i).arg(j).arg(query.value(1).toString(),6)
2401  .arg(query.value(2).toUInt()));
2402 #endif
2403  }
2404  }
2405 
2406  if (prefix.length() < 2)
2407  break;
2408  }
2409  }
2410 
2411  // Now process the lists for the info we need...
2412  is_extra_char_useful = false;
2413  is_complete_valid_channel_on_rec = 0;
2414  needed_spacer.clear();
2415 
2416  if (fchanid.empty())
2417  return false;
2418 
2419  if (fchanid.size() == 1) // Unique channel...
2420  {
2421  needed_spacer = fspacer[0];
2422  bool nc = (fchannum[0] != add_spacer(prefix, fspacer[0]));
2423 
2424  is_complete_valid_channel_on_rec = (nc) ? 0 : finputid[0];
2425  is_extra_char_useful = nc;
2426  return true;
2427  }
2428 
2429  // If we get this far there is more than one channel
2430  // sharing the prefix we were given.
2431 
2432  // Is an extra characher useful for disambiguation?
2433  is_extra_char_useful = false;
2434  for (uint i = 0; (i < fchannum.size()) && !is_extra_char_useful; i++)
2435  {
2436  is_extra_char_useful = (fchannum[i] != add_spacer(prefix, fspacer[i]));
2437 #if DEBUG_CHANNEL_PREFIX
2438  LOG(VB_GENERAL, LOG_DEBUG, QString("is_extra_char_useful(%1!=%2): %3")
2439  .arg(fchannum[i]).arg(add_spacer(prefix, fspacer[i]))
2440  .arg(is_extra_char_useful));
2441 #endif
2442  }
2443 
2444  // Are any of the channels complete w/o spacer?
2445  // If so set is_complete_valid_channel_on_rec,
2446  // with a preference for our inputid.
2447  for (uint i = 0; i < fchannum.size(); i++)
2448  {
2449  if (fchannum[i] == prefix)
2450  {
2451  is_complete_valid_channel_on_rec = finputid[i];
2452  if (finputid[i] == inputid)
2453  break;
2454  }
2455  }
2456 
2457  if (is_complete_valid_channel_on_rec)
2458  return true;
2459 
2460  // Add a spacer, if one is needed to select a valid channel.
2461  bool spacer_needed = true;
2462  for (uint i = 0; (i < fspacer.size() && spacer_needed); i++)
2463  spacer_needed = !fspacer[i].isEmpty();
2464  if (spacer_needed)
2465  needed_spacer = fspacer[0];
2466 
2467  // If it isn't useful to wait for more characters,
2468  // then try to commit to any true match immediately.
2469  for (uint i = 0; i < ((is_extra_char_useful) ? 0 : fchanid.size()); i++)
2470  {
2471  if (fchannum[i] == add_spacer(prefix, fspacer[i]))
2472  {
2473  needed_spacer = fspacer[i];
2474  is_complete_valid_channel_on_rec = finputid[i];
2475  return true;
2476  }
2477  }
2478 
2479  return true;
2480 }
2481 
2483  const QString &channum)
2484 {
2485  if (!recorder)
2486  return false;
2487 
2488  QString videoFilters = ChannelUtil::GetVideoFilters(sourceid, channum);
2489  if (!videoFilters.isEmpty())
2490  {
2491  recorder->SetVideoFilters(videoFilters);
2492  return true;
2493  }
2494 
2495  return false;
2496 }
2497 
2503 {
2504  return ((recorder && recorder->IsRecording()) ||
2506 }
2507 
2513 bool TVRec::IsBusy(InputInfo *busy_input, int time_buffer) const
2514 {
2515  InputInfo dummy;
2516  if (!busy_input)
2517  busy_input = &dummy;
2518 
2519  busy_input->Clear();
2520 
2521  if (!channel)
2522  return false;
2523 
2524  if (!channel->GetInputID())
2525  return false;
2526 
2527  uint chanid = 0;
2528 
2529  if (GetState() != kState_None)
2530  {
2531  busy_input->inputid = channel->GetInputID();
2532  chanid = channel->GetChanID();
2533  }
2534 
2535  PendingInfo pendinfo;
2536  bool has_pending;
2537  {
2538  pendingRecLock.lock();
2539  PendingMap::const_iterator it = pendingRecordings.find(inputid);
2540  has_pending = (it != pendingRecordings.end());
2541  if (has_pending)
2542  pendinfo = *it;
2543  pendingRecLock.unlock();
2544  }
2545 
2546  if (!busy_input->inputid && has_pending)
2547  {
2548  int timeLeft = MythDate::current()
2549  .secsTo(pendinfo.recordingStart);
2550 
2551  if (timeLeft <= time_buffer)
2552  {
2553  QString channum, input;
2554  if (pendinfo.info->QueryTuningInfo(channum, input))
2555  {
2556  busy_input->inputid = channel->GetInputID();
2557  chanid = pendinfo.info->GetChanID();
2558  }
2559  }
2560  }
2561 
2562  if (busy_input->inputid)
2563  {
2564  CardUtil::GetInputInfo(*busy_input);
2565  busy_input->chanid = chanid;
2566  busy_input->mplexid = ChannelUtil::GetMplexID(busy_input->chanid);
2567  busy_input->mplexid =
2568  (32767 == busy_input->mplexid) ? 0 : busy_input->mplexid;
2569  }
2570 
2571  return busy_input->inputid;
2572 }
2573 
2574 
2582 {
2583  QMutexLocker lock(&stateChangeLock);
2584 
2585  if (recorder)
2586  return recorder->GetFrameRate();
2587  return -1.0f;
2588 }
2589 
2597 {
2598  QMutexLocker lock(&stateChangeLock);
2599 
2600  if (recorder)
2601  return recorder->GetFramesWritten();
2602  return -1;
2603 }
2604 
2611 long long TVRec::GetFilePosition(void)
2612 {
2613  QMutexLocker lock(&stateChangeLock);
2614 
2615  if (ringBuffer)
2616  return ringBuffer->GetWritePosition();
2617  return -1;
2618 }
2619 
2627 int64_t TVRec::GetKeyframePosition(uint64_t desired) const
2628 {
2629  QMutexLocker lock(&stateChangeLock);
2630 
2631  if (recorder)
2632  return recorder->GetKeyframePosition(desired);
2633  return -1;
2634 }
2635 
2645  int64_t start, int64_t end, frm_pos_map_t &map) const
2646 {
2647  QMutexLocker lock(&stateChangeLock);
2648 
2649  if (recorder)
2650  return recorder->GetKeyframePositions(start, end, map);
2651 
2652  return false;
2653 }
2654 
2656  int64_t start, int64_t end, frm_pos_map_t &map) const
2657 {
2658  QMutexLocker lock(&stateChangeLock);
2659 
2660  if (recorder)
2661  return recorder->GetKeyframeDurations(start, end, map);
2662 
2663  return false;
2664 }
2665 
2671 long long TVRec::GetMaxBitrate(void) const
2672 {
2673  long long bitrate;
2674  if (genOpt.inputtype == "MPEG")
2675  bitrate = 10080000LL; // use DVD max bit rate
2676  if (genOpt.inputtype == "HDPVR")
2677  bitrate = 20200000LL; // Peek bit rate for HD-PVR
2679  bitrate = 22200000LL; // 1080i
2680  else // frame grabber
2681  bitrate = 10080000LL; // use DVD max bit rate, probably too big
2682 
2683  return bitrate;
2684 }
2685 
2691 void TVRec::SpawnLiveTV(LiveTVChain *newchain, bool pip, QString startchan)
2692 {
2693  QMutexLocker lock(&stateChangeLock);
2694 
2695  tvchain = newchain;
2696  tvchain->IncrRef(); // mark it for TVRec use
2697  tvchain->ReloadAll();
2698 
2699  QString hostprefix = gCoreContext->GenMythURL(
2702 
2703  tvchain->SetHostPrefix(hostprefix);
2705 
2706  ispip = pip;
2707  LiveTVStartChannel = startchan;
2708 
2709  // Change to WatchingLiveTV
2711  // Wait for state change to take effect
2713 
2714  // Make sure StartRecording can't steal our tuner
2715  SetFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2716 }
2717 
2721 QString TVRec::GetChainID(void)
2722 {
2723  if (tvchain)
2724  return tvchain->GetID();
2725  return "";
2726 }
2727 
2737 {
2738  QMutexLocker lock(&stateChangeLock);
2739 
2740  if (internalState == kState_None)
2741  return; // already stopped
2742 
2743  if (!curRecording)
2744  return;
2745 
2746  const QString recgrp = curRecording->QueryRecordingGroup();
2748 
2749  if (recgrp != "LiveTV" && !pseudoLiveTVRecording)
2750  {
2751  // User wants this recording to continue
2753  }
2754  else if (recgrp == "LiveTV" && pseudoLiveTVRecording)
2755  {
2756  // User wants to abandon scheduled recording
2757  SetPseudoLiveTVRecording(nullptr);
2758  }
2759 }
2760 
2771 {
2772  if (!channel)
2773  return;
2774 
2775  // Notify scheduler of the recording.
2776  // + set up recording so it can be resumed
2777  rec->SetInputID(inputid);
2779 
2780  if (rec->GetRecordingRuleType() == kNotRecording)
2781  {
2784  }
2785 
2786  // + remove any end offset which would mismatch the live session
2787  rec->GetRecordingRule()->m_endOffset = 0;
2788 
2789  // + save RecStatus::Inactive recstatus to so that a reschedule call
2790  // doesn't start recording this on another input before we
2791  // send the SCHEDULER_ADD_RECORDING message to the scheduler.
2793  rec->AddHistory(false);
2794 
2795  // + save RecordingRule so that we get a recordid
2796  // (don't allow RescheduleMatch(), avoiding unneeded reschedule)
2797  rec->GetRecordingRule()->Save(false);
2798 
2799  // + save recordid to recorded entry
2800  rec->ApplyRecordRecID();
2801 
2802  // + set proper recstatus (saved later)
2804 
2805  // + pass proginfo to scheduler and reschedule
2806  QStringList prog;
2807  rec->ToStringList(prog);
2808  MythEvent me("SCHEDULER_ADD_RECORDING", prog);
2809  gCoreContext->dispatch(me);
2810 
2811  // Allow scheduler to end this recording before post-roll,
2812  // if it has another recording for this recorder.
2813  ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2814 }
2815 
2817  RecordingProfile *recpro, int line)
2818 {
2819  if (kAutoRunProfile == t)
2820  {
2822  if (!recpro)
2823  {
2824  LoadProfile(nullptr, rec, profile);
2825  recpro = &profile;
2826  }
2827  autoRunJobs[rec->MakeUniqueKey()] =
2828  init_jobs(rec, *recpro, runJobOnHostOnly,
2830  }
2831  else
2832  {
2834  }
2835  LOG(VB_JOBQUEUE, LOG_INFO,
2836  QString("InitAutoRunJobs for %1, line %2 -> 0x%3")
2837  .arg(rec->MakeUniqueKey()).arg(line)
2838  .arg(autoRunJobs[rec->MakeUniqueKey()],0,16));
2839 }
2840 
2852 void TVRec::SetLiveRecording(int recording)
2853 {
2854  LOG(VB_GENERAL, LOG_INFO, LOC +
2855  QString("SetLiveRecording(%1)").arg(recording));
2856  QMutexLocker locker(&stateChangeLock);
2857 
2858  (void) recording;
2859 
2861  bool was_rec = pseudoLiveTVRecording;
2863  if (was_rec && !pseudoLiveTVRecording)
2864  {
2865  LOG(VB_GENERAL, LOG_INFO, LOC + "SetLiveRecording() -- cancel");
2866  // cancel -- 'recording' should be 0 or -1
2867  SetFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2868  curRecording->SetRecordingGroup("LiveTV");
2869  InitAutoRunJobs(curRecording, kAutoRunNone, nullptr, __LINE__);
2870  }
2871  else if (!was_rec && pseudoLiveTVRecording)
2872  {
2873  LOG(VB_GENERAL, LOG_INFO, LOC + "SetLiveRecording() -- record");
2874  // record -- 'recording' should be 1 or -1
2875 
2876  // If the last recording was flagged for keeping
2877  // in the frontend, then add the recording rule
2878  // so that transcode, commfrag, etc can be run.
2881  recstat = curRecording->GetRecordingStatus();
2882  curRecording->SetRecordingGroup("Default");
2883  InitAutoRunJobs(curRecording, kAutoRunProfile, nullptr, __LINE__);
2884  }
2885 
2886  MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
2887  .arg(curRecording->GetInputID())
2888  .arg(curRecording->GetChanID())
2890  .arg(recstat)
2892 
2893  gCoreContext->dispatch(me);
2894 }
2895 
2901 {
2902  QMutexLocker lock(&stateChangeLock);
2903  LOG(VB_RECORD, LOG_INFO, LOC +
2904  QString("StopLiveTV(void) curRec: 0x%1 pseudoRec: 0x%2")
2905  .arg((uint64_t)curRecording,0,16)
2906  .arg((uint64_t)pseudoLiveTVRecording,0,16));
2907 
2909  return;
2910 
2911  bool hadPseudoLiveTVRec = pseudoLiveTVRecording;
2913 
2914  if (!hadPseudoLiveTVRec && pseudoLiveTVRecording)
2916 
2917  // Figure out next state and if needed recording end time.
2918  TVState next_state = kState_None;
2920  {
2922  next_state = kState_RecordingOnly;
2923  }
2924 
2925  // Change to the appropriate state
2926  ChangeState(next_state);
2927 
2928  // Wait for state change to take effect...
2930 
2931  // We are done with the tvchain...
2932  if (tvchain)
2933  {
2934  tvchain->DecrRef();
2935  }
2936  tvchain = nullptr;
2937 }
2938 
2948 {
2949  QMutexLocker lock(&stateChangeLock);
2950 
2951  if (!recorder)
2952  {
2953  LOG(VB_GENERAL, LOG_ERR, LOC +
2954  "PauseRecorder() called with no recorder");
2955  return;
2956  }
2957 
2958  recorder->Pause();
2959 }
2960 
2967 {
2968  if (pauseNotify)
2969  WakeEventLoop();
2970 }
2971 
2975 void TVRec::ToggleChannelFavorite(QString changroupname)
2976 {
2977  QMutexLocker lock(&stateChangeLock);
2978 
2979  if (!channel)
2980  return;
2981 
2982  // Get current channel id...
2983  uint sourceid = channel->GetSourceID();
2984  QString channum = channel->GetChannelName();
2985  uint chanid = ChannelUtil::GetChanID(sourceid, channum);
2986 
2987  if (!chanid)
2988  {
2989  LOG(VB_GENERAL, LOG_ERR, LOC +
2990  QString("Channel: \'%1\' was not found in the database.\n"
2991  "\t\tMost likely, the 'starting channel' for this "
2992  "Input Connection is invalid.\n"
2993  "\t\tCould not toggle favorite.").arg(channum));
2994  return;
2995  }
2996 
2997  int changrpid;
2998 
2999  changrpid = ChannelGroup::GetChannelGroupId(changroupname);
3000 
3001  if (changrpid <1)
3002  {
3003  LOG(VB_RECORD, LOG_ERR, LOC +
3004  QString("ToggleChannelFavorite: Invalid channel group name %1,")
3005  .arg(changroupname));
3006  }
3007  else
3008  {
3009  bool result = ChannelGroup::ToggleChannel(chanid, changrpid, true);
3010 
3011  if (!result)
3012  LOG(VB_RECORD, LOG_ERR, LOC + "Unable to toggle channel favorite.");
3013  else
3014  LOG(VB_RECORD, LOG_INFO, LOC +
3015  QString("Toggled channel favorite.channum %1, chan group %2")
3016  .arg(channum).arg(changroupname));
3017  }
3018 }
3019 
3026 {
3027  QMutexLocker lock(&stateChangeLock);
3028  if (!channel)
3029  return -1;
3030 
3031  int ret = channel->GetPictureAttribute(attr);
3032 
3033  return (ret < 0) ? -1 : ret / 655;
3034 }
3035 
3044  PictureAttribute attr,
3045  bool direction)
3046 {
3047  QMutexLocker lock(&stateChangeLock);
3048  if (!channel)
3049  return -1;
3050 
3051  int ret = channel->ChangePictureAttribute(type, attr, direction);
3052 
3053  return (ret < 0) ? -1 : ret / 655;
3054 }
3055 
3059 QString TVRec::GetInput(void) const
3060 {
3061  if (channel)
3062  return channel->GetInputName();
3063  return QString();
3064 }
3065 
3070 {
3071  if (channel)
3072  return channel->GetSourceID();
3073  return 0;
3074 }
3075 
3084 QString TVRec::SetInput(QString input)
3085 {
3086  QMutexLocker lock(&stateChangeLock);
3087  QString origIn = input;
3088  LOG(VB_RECORD, LOG_INFO, LOC + "SetInput(" + input + ") -- begin");
3089 
3090  if (!channel)
3091  {
3092  LOG(VB_RECORD, LOG_INFO, LOC + "SetInput() -- end no channel class");
3093  return QString();
3094  }
3095 
3096  LOG(VB_RECORD, LOG_INFO, LOC + "SetInput(" + origIn + ":" + input +
3097  ") -- end nothing to do");
3098  return input;
3099 }
3100 
3110 void TVRec::SetChannel(QString name, uint requestType)
3111 {
3112  QMutexLocker locker1(&setChannelLock);
3113  QMutexLocker locker2(&stateChangeLock);
3114 
3115  LOG(VB_CHANNEL, LOG_INFO, LOC +
3116  QString("SetChannel(%1) -- begin").arg(name));
3117 
3118  // Detect tuning request type if needed
3119  if (requestType & kFlagDetect)
3120  {
3122  requestType = lastTuningRequest.flags & (kFlagRec | kFlagNoRec);
3123  }
3124 
3125  // Clear the RingBuffer reset flag, in case we wait for a reset below
3126  ClearFlags(kFlagRingBufferReady, __FILE__, __LINE__);
3127 
3128  // Clear out any EITScan channel change requests
3130  while (it != tuningRequests.end())
3131  {
3132  if ((*it).flags & kFlagEITScan)
3133  it = tuningRequests.erase(it);
3134  else
3135  ++it;
3136  }
3137 
3138  // Actually add the tuning request to the queue, and
3139  // then wait for it to start tuning
3140  tuningRequests.enqueue(TuningRequest(requestType, name));
3142 
3143  // If we are using a recorder, wait for a RingBuffer reset
3144  if (requestType & kFlagRec)
3145  {
3146  while (!HasFlags(kFlagRingBufferReady))
3148  }
3149  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("SetChannel(%1) -- end").arg(name));
3150 }
3151 
3160 {
3161  LOG(VB_CHANNEL, LOG_INFO, LOC +
3162  QString("QueueEITChannelChange(%1) -- begin").arg(name));
3163 
3164  bool ok = false;
3165  if (setChannelLock.tryLock())
3166  {
3167  if (stateChangeLock.tryLock())
3168  {
3169  if (tuningRequests.empty())
3170  {
3172  ok = true;
3173  }
3174  stateChangeLock.unlock();
3175  }
3176  setChannelLock.unlock();
3177  }
3178 
3179  LOG(VB_CHANNEL, LOG_INFO, LOC +
3180  QString("QueueEITChannelChange(%1) -- end --> %2").arg(name).arg(ok));
3181 
3182  return ok;
3183 }
3184 
3186  QString &title, QString &subtitle,
3187  QString &desc, QString &category,
3188  QString &starttime, QString &endtime,
3189  QString &callsign, QString &iconpath,
3190  QString &channum, uint &sourceChanid,
3191  QString &seriesid, QString &programid)
3192 {
3193  QString compare = "<=";
3194  QString sortorder = "desc";
3195  uint chanid = 0;
3196 
3197  if (sourceChanid)
3198  {
3199  chanid = sourceChanid;
3200 
3201  if (BROWSE_UP == direction)
3202  chanid = channel->GetNextChannel(chanid, CHANNEL_DIRECTION_UP);
3203  else if (BROWSE_DOWN == direction)
3204  chanid = channel->GetNextChannel(chanid, CHANNEL_DIRECTION_DOWN);
3205  else if (BROWSE_FAVORITE == direction)
3206  chanid = channel->GetNextChannel(
3207  chanid, CHANNEL_DIRECTION_FAVORITE);
3208 
3209  else if (BROWSE_LEFT == direction)
3210  {
3211  compare = "<";
3212  }
3213  else if (BROWSE_RIGHT == direction)
3214  {
3215  compare = ">";
3216  sortorder = "asc";
3217  }
3218  }
3219 
3220  if (!chanid)
3221  {
3222  if (BROWSE_SAME == direction)
3223  chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
3224  else if (BROWSE_UP == direction)
3225  chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_UP);
3226  else if (BROWSE_DOWN == direction)
3227  chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_DOWN);
3228  else if (BROWSE_FAVORITE == direction)
3229  chanid = channel->GetNextChannel(channum,
3231  else if (BROWSE_LEFT == direction)
3232  {
3233  chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
3234  compare = "<";
3235  }
3236  else if (BROWSE_RIGHT == direction)
3237  {
3238  chanid = channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
3239  compare = ">";
3240  sortorder = "asc";
3241  }
3242  }
3243 
3244  QString querystr = QString(
3245  "SELECT title, subtitle, description, category, "
3246  " starttime, endtime, callsign, icon, "
3247  " channum, seriesid, programid "
3248  "FROM program, channel "
3249  "WHERE program.chanid = channel.chanid AND "
3250  " channel.chanid = :CHANID AND "
3251  " starttime %1 :STARTTIME "
3252  "ORDER BY starttime %2 "
3253  "LIMIT 1").arg(compare).arg(sortorder);
3254 
3255  MSqlQuery query(MSqlQuery::InitCon());
3256  query.prepare(querystr);
3257  query.bindValue(":CHANID", chanid);
3258  query.bindValue(":STARTTIME", starttime);
3259 
3260  // Clear everything now in case either query fails.
3261  title = subtitle = desc = category = "";
3262  starttime = endtime = callsign = iconpath = "";
3263  channum = seriesid = programid = "";
3264  sourceChanid = 0;
3265 
3266  // Try to get the program info
3267  if (!query.exec() && !query.isActive())
3268  {
3269  MythDB::DBError("GetNextProgram -- get program info", query);
3270  }
3271  else if (query.next())
3272  {
3273  title = query.value(0).toString();
3274  subtitle = query.value(1).toString();
3275  desc = query.value(2).toString();
3276  category = query.value(3).toString();
3277  starttime = query.value(4).toString();
3278  endtime = query.value(5).toString();
3279  callsign = query.value(6).toString();
3280  iconpath = query.value(7).toString();
3281  channum = query.value(8).toString();
3282  seriesid = query.value(9).toString();
3283  programid = query.value(10).toString();
3284  sourceChanid = chanid;
3285  return;
3286  }
3287 
3288  // Couldn't get program info, so get the channel info instead
3289  query.prepare(
3290  "SELECT channum, callsign, icon "
3291  "FROM channel "
3292  "WHERE chanid = :CHANID");
3293  query.bindValue(":CHANID", chanid);
3294 
3295  if (!query.exec() || !query.isActive())
3296  {
3297  MythDB::DBError("GetNextProgram -- get channel info", query);
3298  }
3299  else if (query.next())
3300  {
3301  sourceChanid = chanid;
3302  channum = query.value(0).toString();
3303  callsign = query.value(1).toString();
3304  iconpath = query.value(2).toString();
3305  }
3306 }
3307 
3308 bool TVRec::GetChannelInfo(uint &chanid, uint &sourceid,
3309  QString &callsign, QString &channum,
3310  QString &channame, QString &xmltvid) const
3311 {
3312  callsign.clear();
3313  channum.clear();
3314  channame.clear();
3315  xmltvid.clear();
3316 
3317  if ((!chanid || !sourceid) && !channel)
3318  return false;
3319 
3320  if (!chanid)
3321  chanid = (uint) max(channel->GetChanID(), 0);
3322 
3323  if (!sourceid)
3324  sourceid = channel->GetSourceID();
3325 
3326  MSqlQuery query(MSqlQuery::InitCon());
3327  query.prepare(
3328  "SELECT callsign, channum, name, xmltvid "
3329  "FROM channel "
3330  "WHERE chanid = :CHANID");
3331  query.bindValue(":CHANID", chanid);
3332  if (!query.exec() || !query.isActive())
3333  {
3334  MythDB::DBError("GetChannelInfo", query);
3335  return false;
3336  }
3337 
3338  if (!query.next())
3339  return false;
3340 
3341  callsign = query.value(0).toString();
3342  channum = query.value(1).toString();
3343  channame = query.value(2).toString();
3344  xmltvid = query.value(3).toString();
3345 
3346  return true;
3347 }
3348 
3349 bool TVRec::SetChannelInfo(uint chanid, uint sourceid,
3350  QString oldchannum,
3351  QString callsign, QString channum,
3352  QString channame, QString xmltvid)
3353 {
3354  if (!chanid || !sourceid || channum.isEmpty())
3355  return false;
3356 
3357  MSqlQuery query(MSqlQuery::InitCon());
3358  query.prepare(
3359  "UPDATE channel "
3360  "SET callsign = :CALLSIGN, "
3361  " channum = :CHANNUM, "
3362  " name = :CHANNAME, "
3363  " xmltvid = :XMLTVID "
3364  "WHERE chanid = :CHANID AND "
3365  " sourceid = :SOURCEID");
3366  query.bindValue(":CALLSIGN", callsign);
3367  query.bindValue(":CHANNUM", channum);
3368  query.bindValue(":CHANNAME", channame);
3369  query.bindValue(":XMLTVID", xmltvid);
3370  query.bindValue(":CHANID", chanid);
3371  query.bindValue(":SOURCEID", sourceid);
3372 
3373  if (!query.exec())
3374  {
3375  MythDB::DBError("SetChannelInfo", query);
3376  return false;
3377  }
3378 
3379  if (channel)
3380  channel->Renumber(sourceid, oldchannum, channum);
3381 
3382  return true;
3383 }
3384 
3389 {
3390  QMutexLocker lock(&stateChangeLock);
3391 
3392  RingBuffer *rb_old = ringBuffer;
3393  ringBuffer = rb;
3394 
3395  if (rb_old && (rb_old != rb))
3396  {
3398  ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3399  delete rb_old;
3400  }
3401 
3402  switchingBuffer = false;
3403 }
3404 
3406  RingBuffer *rb, RecordingInfo *pginfo, RecordingQuality *recq)
3407 {
3408  LOG(VB_GENERAL, LOG_INFO, LOC + "RingBufferChanged()");
3409 
3410  if (pginfo)
3411  {
3412  if (curRecording)
3413  {
3416  delete curRecording;
3417  }
3418  recordEndTime = GetRecordEndTime(pginfo);
3419  curRecording = new RecordingInfo(*pginfo);
3422  }
3423 
3424  SetRingBuffer(rb);
3425 }
3426 
3428  QString &input) const
3429 {
3430  QString channum;
3431 
3432  if (request.program)
3433  {
3434  request.program->QueryTuningInfo(channum, input);
3435  return channum;
3436  }
3437 
3438  channum = request.channel;
3439  input = request.input;
3440 
3441  // If this is Live TV startup, we need a channel...
3442  if (channum.isEmpty() && (request.flags & kFlagLiveTV))
3443  {
3444  if (!LiveTVStartChannel.isEmpty())
3445  channum = LiveTVStartChannel;
3446  else
3447  {
3449  channum = GetStartChannel(inputid);
3450  }
3451  }
3452  if (request.flags & kFlagLiveTV)
3453  channel->Init(channum, false);
3454 
3455  if (channel && !channum.isEmpty() && (channum.indexOf("NextChannel") >= 0))
3456  {
3457  // FIXME This is just horrible
3458  int dir = channum.right(channum.length() - 12).toInt();
3459  uint chanid = channel->GetNextChannel(0, static_cast<ChannelChangeDirection>(dir));
3460  channum = ChannelUtil::GetChanNum(chanid);
3461  }
3462 
3463  return channum;
3464 }
3465 
3467 {
3468  if ((request.flags & kFlagAntennaAdjust) || request.input.isEmpty() ||
3470  {
3471  return false;
3472  }
3473 
3474  uint sourceid = channel->GetSourceID();
3475  QString oldchannum = channel->GetChannelName();
3476  QString newchannum = request.channel;
3477 
3478  if (ChannelUtil::IsOnSameMultiplex(sourceid, newchannum, oldchannum))
3479  {
3481  ATSCStreamData *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
3482 
3483  if (atsc)
3484  {
3485  uint major, minor = 0;
3486  ChannelUtil::GetATSCChannel(sourceid, newchannum, major, minor);
3487 
3488  if (minor && atsc->HasChannel(major, minor))
3489  {
3490  request.majorChan = major;
3491  request.minorChan = minor;
3492  return true;
3493  }
3494  }
3495 
3496  if (mpeg)
3497  {
3498  uint progNum = ChannelUtil::GetProgramNumber(sourceid, newchannum);
3499  if (mpeg->HasProgram(progNum))
3500  {
3501  request.progNum = progNum;
3502  return true;
3503  }
3504  }
3505  }
3506 
3507  return false;
3508 }
3509 
3518 {
3519  if (tuningRequests.size())
3520  {
3521  TuningRequest request = tuningRequests.front();
3522  LOG(VB_RECORD, LOG_INFO, LOC +
3523  "HandleTuning Request: " + request.toString());
3524 
3525  QString input;
3526  request.channel = TuningGetChanNum(request, input);
3527  request.input = input;
3528 
3529  if (TuningOnSameMultiplex(request))
3530  LOG(VB_CHANNEL, LOG_INFO, LOC + "On same multiplex");
3531 
3532  TuningShutdowns(request);
3533 
3534  // The dequeue isn't safe to do until now because we
3535  // release the stateChangeLock to teardown a recorder
3537 
3538  // Now we start new stuff
3539  if (request.flags & (kFlagRecording|kFlagLiveTV|
3541  {
3542  if (!recorder)
3543  {
3544  LOG(VB_RECORD, LOG_INFO, LOC +
3545  "No recorder yet, calling TuningFrequency");
3546  TuningFrequency(request);
3547  }
3548  else
3549  {
3550  LOG(VB_RECORD, LOG_INFO, LOC + "Waiting for recorder pause..");
3551  SetFlags(kFlagWaitingForRecPause, __FILE__, __LINE__);
3552  }
3553  }
3554  lastTuningRequest = request;
3555  }
3556 
3558  {
3559  if (!recorder->IsPaused())
3560  return;
3561 
3562  ClearFlags(kFlagWaitingForRecPause, __FILE__, __LINE__);
3563  LOG(VB_RECORD, LOG_INFO, LOC +
3564  "Recorder paused, calling TuningFrequency");
3566  }
3567 
3568  MPEGStreamData *streamData = nullptr;
3569  if (HasFlags(kFlagWaitingForSignal) && !(streamData = TuningSignalCheck()))
3570  return;
3571 
3573  {
3574  if (recorder)
3576  else
3577  TuningNewRecorder(streamData);
3578 
3579  // If we got this far it is safe to set a new starting channel...
3580  if (channel)
3582  }
3583 }
3584 
3590 {
3591  LOG(VB_RECORD, LOG_INFO, LOC + QString("TuningShutdowns(%1)")
3592  .arg(request.toString()));
3593 
3594  QString channum, inputname;
3595 
3596  if (scanner && !(request.flags & kFlagEITScan) &&
3598  {
3600  ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
3601  eitScanStartTime = MythDate::current().addSecs(
3603  }
3604 
3605  if (scanner && !request.IsOnSameMultiplex())
3607 
3609  {
3610  MPEGStreamData *sd = nullptr;
3611  if (GetDTVSignalMonitor())
3614  ClearFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3615 
3616  // Delete StreamData if it is not in use by the recorder.
3617  MPEGStreamData *rec_sd = nullptr;
3618  if (GetDTVRecorder())
3619  rec_sd = GetDTVRecorder()->GetStreamData();
3620  if (sd && (sd != rec_sd))
3621  delete sd;
3622  }
3624  ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3625 
3626  // At this point any waits are canceled.
3627 
3628  if (request.flags & kFlagNoRec)
3629  {
3631  {
3632  FinishedRecording(curRecording, nullptr);
3633  ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3635  }
3636 
3638  (curRecording &&
3641  {
3642  stateChangeLock.unlock();
3643  TeardownRecorder(request.flags);
3644  stateChangeLock.lock();
3645  }
3646  // At this point the recorders are shut down
3647 
3648  CloseChannel();
3649  // At this point the channel is shut down
3650  }
3651 
3652  if (ringBuffer && (request.flags & kFlagKillRingBuffer))
3653  {
3654  LOG(VB_RECORD, LOG_INFO, LOC + "Tearing down RingBuffer");
3655  SetRingBuffer(nullptr);
3656  // At this point the ringbuffer is shut down
3657  }
3658 
3659  // Clear pending actions from last request
3660  ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
3661 }
3662 
3681 {
3682  LOG(VB_GENERAL, LOG_INFO, LOC + "TuningFrequency");
3683 
3684  DTVChannel *dtvchan = GetDTVChannel();
3685  if (dtvchan)
3686  {
3687  MPEGStreamData *mpeg = nullptr;
3688 
3689  if (GetDTVRecorder())
3690  mpeg = GetDTVRecorder()->GetStreamData();
3691 
3692  const QString tuningmode = (HasFlags(kFlagEITScannerRunning)) ?
3693  dtvchan->GetSIStandard() :
3694  dtvchan->GetSuggestedTuningMode(
3696 
3697  dtvchan->SetTuningMode(tuningmode);
3698 
3699  if (request.minorChan && (tuningmode == "atsc"))
3700  {
3702 
3703  ATSCStreamData *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
3704  if (atsc)
3705  atsc->SetDesiredChannel(request.majorChan, request.minorChan);
3706  }
3707  else if (request.progNum >= 0)
3708  {
3710 
3711  if (mpeg)
3712  mpeg->SetDesiredProgram(request.progNum);
3713  }
3714  }
3715 
3716  if (request.IsOnSameMultiplex())
3717  {
3718  // Update the channel number for SwitchLiveTVRingBuffer (called from
3719  // TuningRestartRecorder). This ensures that the livetvchain will be
3720  // updated with the new channel number
3721  if (channel)
3722  {
3724  channel->GetChannelName(), request.channel );
3725  }
3726 
3727  QStringList slist;
3728  slist<<"message"<<QObject::tr("On known multiplex...");
3729  MythEvent me(QString("SIGNAL %1").arg(inputid), slist);
3730  gCoreContext->dispatch(me);
3731 
3732  SetFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3733  return;
3734  }
3735 
3736  QString input = request.input;
3737  QString channum = request.channel;
3738 
3739  bool ok1;
3740  if (channel)
3741  {
3742  channel->Open();
3743  if (!channum.isEmpty())
3744  ok1 = channel->SetChannelByString(channum);
3745  else
3746  ok1 = false;
3747  }
3748  else
3749  ok1 = true;
3750 
3751  if (!ok1)
3752  {
3753  if (!(request.flags & kFlagLiveTV) || !(request.flags & kFlagEITScan))
3754  {
3755  if (curRecording)
3757 
3758  LOG(VB_GENERAL, LOG_ERR, LOC +
3759  QString("Failed to set channel to %1. Reverting to kState_None")
3760  .arg(channum));
3761  if (kState_None != internalState)
3763  else
3765  return;
3766  }
3767  else
3768  {
3769  LOG(VB_GENERAL, LOG_ERR, LOC +
3770  QString("Failed to set channel to %1.").arg(channum));
3771  }
3772  }
3773 
3774 
3775  bool mpts_only = GetDTVChannel() &&
3776  GetDTVChannel()->GetFormat().compare("MPTS") == 0;
3777  if (mpts_only)
3778  {
3779  // Not using a signal monitor, so just set the status to recording
3781  }
3782 
3783 
3784  bool livetv = request.flags & kFlagLiveTV;
3785  bool antadj = request.flags & kFlagAntennaAdjust;
3786  bool use_sm = !mpts_only && SignalMonitor::IsRequired(genOpt.inputtype);
3787  bool use_dr = use_sm && (livetv || antadj);
3788  bool has_dummy = false;
3789 
3790  if (use_dr)
3791  {
3792  // We need there to be a ringbuffer for these modes
3793  bool ok2;
3795  pseudoLiveTVRecording = nullptr;
3796 
3797  tvchain->SetInputType("DUMMY");
3798 
3799  if (!ringBuffer)
3800  ok2 = CreateLiveTVRingBuffer(channum);
3801  else
3802  ok2 = SwitchLiveTVRingBuffer(channum, true, false);
3804 
3806 
3807  if (!ok2)
3808  {
3809  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RingBuffer 1");
3810  return;
3811  }
3812 
3813  has_dummy = true;
3814  }
3815 
3816  // Start signal monitoring for devices capable of monitoring
3817  if (use_sm)
3818  {
3819  LOG(VB_RECORD, LOG_INFO, LOC + "Starting Signal Monitor");
3820  bool error = false;
3821  if (!SetupSignalMonitor(
3822  !antadj, request.flags & kFlagEITScan, livetv | antadj))
3823  {
3824  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to setup signal monitor");
3825  if (signalMonitor)
3826  {
3827  delete signalMonitor;
3828  signalMonitor = nullptr;
3829  }
3830 
3831  // pretend the signal monitor is running to prevent segfault
3832  SetFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3833  ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3834  error = true;
3835  }
3836 
3837  if (signalMonitor)
3838  {
3839  if (request.flags & kFlagEITScan)
3840  {
3842  SetVideoStreamsRequired(0);
3844  }
3845 
3846  SetFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3847  ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3848  if (!antadj)
3849  {
3850  QDateTime expire = MythDate::current();
3851 
3852  SetFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3853  if (curRecording)
3854  {
3856  // If startRecordingDeadline is passed, this
3857  // recording is marked as failed, so the scheduler
3858  // can try another showing.
3860  expire.addMSecs(genOpt.channel_timeout);
3861  preFailDeadline =
3862  expire.addMSecs(genOpt.channel_timeout * 2 / 3);
3863  // Keep trying to record this showing (even if it
3864  // has been marked as failed) until the scheduled
3865  // end time.
3867  curRecording->GetRecordingEndTime().addSecs(-10);
3868 
3869  LOG(VB_CHANNEL, LOG_DEBUG, LOC +
3870  QString("Pre-fail start deadline: %1 "
3871  "Start recording deadline: %2 "
3872  "Good signal deadline: %3")
3873  .arg(preFailDeadline.toLocalTime()
3874  .toString("hh:mm:ss.zzz"))
3875  .arg(startRecordingDeadline.toLocalTime()
3876  .toString("hh:mm:ss.zzz"))
3877  .arg(signalMonitorDeadline.toLocalTime()
3878  .toString("hh:mm:ss.zzz")));
3879  }
3880  else
3881  {
3883  expire.addMSecs(genOpt.channel_timeout);
3884  }
3886 
3887  //System Event TUNING_TIMEOUT deadline
3888  signalEventCmdTimeout = expire.addMSecs(genOpt.channel_timeout);
3889  signalEventCmdSent = false;
3890  }
3891  }
3892 
3893  if (has_dummy && ringBuffer)
3894  {
3895  // Make sure recorder doesn't point to bogus ringbuffer before
3896  // it is potentially restarted without a new ringbuffer, if
3897  // the next channel won't tune and the user exits LiveTV.
3898  if (recorder)
3899  recorder->SetRingBuffer(nullptr);
3900 
3901  SetFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3902  LOG(VB_RECORD, LOG_INFO, "DummyDTVRecorder -- started");
3903  SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
3904  }
3905 
3906  // if we had problems starting the signal monitor,
3907  // we don't want to start the recorder...
3908  if (error)
3909  return;
3910  }
3911 
3912  // Request a recorder, if the command is a recording command
3913  ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3914  if (request.flags & kFlagRec && !antadj)
3915  SetFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3916 }
3917 
3926 {
3927  RecStatus::Type newRecStatus;
3928  bool keep_trying = false;
3929  QDateTime current_time = MythDate::current();
3930 
3931  if ((signalMonitor->IsErrored() || current_time > signalEventCmdTimeout) &&
3933  {
3934  gCoreContext->SendSystemEvent(QString("TUNING_SIGNAL_TIMEOUT CARDID %1")
3935  .arg(inputid));
3936  signalEventCmdSent = true;
3937  }
3938 
3939  if (signalMonitor->IsAllGood())
3940  {
3941  LOG(VB_RECORD, LOG_INFO, LOC + "TuningSignalCheck: Good signal");
3942  if (curRecording && (current_time > startRecordingDeadline))
3943  {
3944  newRecStatus = RecStatus::Failing;
3946 
3947  QString desc = tr("Good signal seen after %1 ms")
3948  .arg(genOpt.channel_timeout +
3949  startRecordingDeadline.msecsTo(current_time));
3950  QString title = curRecording->GetTitle();
3951  if (!curRecording->GetSubtitle().isEmpty())
3952  title += " - " + curRecording->GetSubtitle();
3953 
3955  "Recording", title,
3956  tr("See 'Tuning timeout' in mythtv-setup "
3957  "for this input."));
3959 
3960  LOG(VB_GENERAL, LOG_WARNING, LOC +
3961  QString("It took longer than %1 ms to get a signal lock. "
3962  "Keeping status of '%2'")
3963  .arg(genOpt.channel_timeout)
3964  .arg(RecStatus::toString(newRecStatus, kSingleRecord)));
3965  LOG(VB_GENERAL, LOG_WARNING, LOC +
3966  "See 'Tuning timeout' in mythtv-setup for this input");
3967  }
3968  else
3969  {
3970  newRecStatus = RecStatus::Recording;
3971  }
3972  }
3973  else if (signalMonitor->IsErrored() || current_time > signalMonitorDeadline)
3974  {
3975  LOG(VB_GENERAL, LOG_ERR, LOC + "TuningSignalCheck: SignalMonitor " +
3976  (signalMonitor->IsErrored() ? "failed" : "timed out"));
3977 
3978  ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3979  newRecStatus = RecStatus::Failed;
3980 
3982  {
3984  }
3985  }
3986  else if (curRecording && !reachedPreFail && current_time > preFailDeadline)
3987  {
3988  LOG(VB_GENERAL, LOG_ERR, LOC +
3989  "TuningSignalCheck: Hit pre-fail timeout");
3990  SendMythSystemRecEvent("REC_PREFAIL", curRecording);
3991  reachedPreFail = true;
3992  return nullptr;
3993  }
3994  else if (curRecording && !reachedRecordingDeadline &&
3995  current_time > startRecordingDeadline)
3996  {
3997  newRecStatus = RecStatus::Failing;
3998  reachedRecordingDeadline = true;
3999  keep_trying = true;
4000 
4001  SendMythSystemRecEvent("REC_FAILING", curRecording);
4002 
4003  QString desc = tr("Taking more than %1 ms to get a lock.")
4004  .arg(genOpt.channel_timeout);
4005  QString title = curRecording->GetTitle();
4006  if (!curRecording->GetSubtitle().isEmpty())
4007  title += " - " + curRecording->GetSubtitle();
4008 
4010  "Recording", title,
4011  tr("See 'Tuning timeout' in mythtv-setup "
4012  "for this input."));
4013  mn.SetDuration(30);
4015 
4016  LOG(VB_GENERAL, LOG_WARNING, LOC +
4017  QString("TuningSignalCheck: taking more than %1 ms to get a lock. "
4018  "marking this recording as '%2'.")
4019  .arg(genOpt.channel_timeout)
4020  .arg(RecStatus::toString(newRecStatus, kSingleRecord)));
4021  LOG(VB_GENERAL, LOG_WARNING, LOC +
4022  "See 'Tuning timeout' in mythtv-setup for this input");
4023  }
4024  else
4025  {
4026  if (signalMonitorCheckCnt) // Don't flood log file
4028  else
4029  {
4030  LOG(VB_RECORD, LOG_INFO, LOC +
4031  QString("TuningSignalCheck: Still waiting. Will timeout @ %1")
4032  .arg(signalMonitorDeadline.toLocalTime()
4033  .toString("hh:mm:ss.zzz")));
4035  }
4036  return nullptr;
4037  }
4038 
4039  SetRecordingStatus(newRecStatus, __LINE__);
4040 
4041  if (curRecording)
4042  {
4043  curRecording->SetRecordingStatus(newRecStatus);
4044  MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
4045  .arg(curRecording->GetInputID())
4046  .arg(curRecording->GetChanID())
4048  .arg(newRecStatus)
4050  gCoreContext->dispatch(me);
4051  }
4052 
4053  if (keep_trying)
4054  return nullptr;
4055 
4056  // grab useful data from DTV signal monitor before we kill it...
4057  MPEGStreamData *streamData = nullptr;
4058  if (GetDTVSignalMonitor())
4059  streamData = GetDTVSignalMonitor()->GetStreamData();
4060 
4062  {
4063  // shut down signal monitoring
4065  ClearFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
4066  }
4067  ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
4068 
4069  if (streamData)
4070  {
4071  DVBStreamData *dsd = dynamic_cast<DVBStreamData*>(streamData);
4072  if (dsd)
4074  if (!get_use_eit(GetInputId()))
4075  {
4076  LOG(VB_EIT, LOG_INFO, LOC +
4077  "EIT scanning disabled for all sources on this input.");
4078  }
4079  else if (scanner)
4080  scanner->StartPassiveScan(channel, streamData);
4081  }
4082 
4083  return streamData;
4084 }
4085 
4087  bool on_host, bool transcode_bfr_comm, bool on_line_comm)
4088 {
4089  if (!rec)
4090  return 0; // no jobs for Live TV recordings..
4091 
4092  int jobs = 0; // start with no jobs
4093 
4094  // grab standard jobs flags from program info
4096 
4097  // disable commercial flagging on PBS, BBC, etc.
4098  if (rec->IsCommercialFree())
4100 
4101  // disable transcoding if the profile does not allow auto transcoding
4102  const StandardSetting *autoTrans = profile.byName("autotranscode");
4103  if ((!autoTrans) || (autoTrans->getValue().toInt() == 0))
4105 
4106  bool ml = JobQueue::JobIsInMask(JOB_METADATA, jobs);
4107  if (ml)
4108  {
4109  // When allowed, metadata lookup should occur at the
4110  // start of a recording to make the additional info
4111  // available immediately (and for use in future jobs).
4112  QString host = (on_host) ? gCoreContext->GetHostName() : "";
4114  rec->GetChanID(),
4115  rec->GetRecordingStartTime(), "", "",
4116  host, JOB_LIVE_REC);
4117 
4118  // don't do regular metadata lookup, we won't need it.
4120  }
4121 
4122  // is commercial flagging enabled, and is on-line comm flagging enabled?
4123  bool rt = JobQueue::JobIsInMask(JOB_COMMFLAG, jobs) && on_line_comm;
4124  // also, we either need transcoding to be disabled or
4125  // we need to be allowed to commercial flag before transcoding?
4126  rt &= JobQueue::JobIsNotInMask(JOB_TRANSCODE, jobs) ||
4127  !transcode_bfr_comm;
4128  if (rt)
4129  {
4130  // queue up real-time (i.e. on-line) commercial flagging.
4131  QString host = (on_host) ? gCoreContext->GetHostName() : "";
4133  rec->GetChanID(),
4134  rec->GetRecordingStartTime(), "", "",
4135  host, JOB_LIVE_REC);
4136 
4137  // don't do regular comm flagging, we won't need it.
4139  }
4140 
4141  return jobs;
4142 }
4143 
4144 QString TVRec::LoadProfile(void *tvchain, RecordingInfo *rec,
4146 {
4147  // Determine the correct recording profile.
4148  // In LiveTV mode use "Live TV" profile, otherwise use the
4149  // recording's specified profile. If the desired profile can't
4150  // be found, fall back to the "Default" profile for input type.
4151  QString profileName = "Live TV";
4152  if (!tvchain && rec)
4153  profileName = rec->GetRecordingRule()->m_recProfile;
4154 
4155  QString profileRequested = profileName;
4156 
4157  if (profile.loadByType(profileName, genOpt.inputtype,
4158  genOpt.videodev))
4159  {
4160  LOG(VB_RECORD, LOG_INFO, LOC +
4161  QString("Using profile '%1' to record")
4162  .arg(profileName));
4163  }
4164  else
4165  {
4166  profileName = "Default";
4167  if (profile.loadByType(profileName, genOpt.inputtype, genOpt.videodev))
4168  {
4169  LOG(VB_RECORD, LOG_INFO, LOC +
4170  QString("Profile '%1' not found, using "
4171  "fallback profile '%2' to record")
4172  .arg(profileRequested).arg(profileName));
4173  }
4174  else
4175  {
4176  LOG(VB_RECORD, LOG_ERR, LOC +
4177  QString("Profile '%1' not found, and unable "
4178  "to load fallback profile '%2'. Results "
4179  "may be unpredicable")
4180  .arg(profileRequested).arg(profileName));
4181  }
4182  }
4183 
4184  return profileName;
4185 }
4186 
4191 {
4192  LOG(VB_RECORD, LOG_INFO, LOC + "Starting Recorder");
4193 
4194  bool had_dummyrec = false;
4196  {
4197  FinishedRecording(curRecording, nullptr);
4198  ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
4200  had_dummyrec = true;
4201  }
4202 
4204 
4207 
4208  if (tvchain)
4209  {
4210  bool ok;
4211  if (!ringBuffer)
4212  {
4214  SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4215  }
4216  else
4218  true, !had_dummyrec && recorder);
4219  if (!ok)
4220  {
4221  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RingBuffer 2");
4222  goto err_ret;
4223  }
4224  rec = curRecording; // new'd in Create/SwitchLiveTVRingBuffer()
4225  }
4226 
4228  {
4229  bool write = genOpt.inputtype != "IMPORT";
4230  LOG(VB_GENERAL, LOG_INFO, LOC + QString("rec->GetPathname(): '%1'")
4231  .arg(rec->GetPathname()));
4233  if (!ringBuffer->IsOpen() && write)
4234  {
4235  LOG(VB_GENERAL, LOG_ERR, LOC +
4236  QString("RingBuffer '%1' not open...")
4237  .arg(rec->GetPathname()));
4238  SetRingBuffer(nullptr);
4239  ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
4240  goto err_ret;
4241  }
4242  }
4243 
4244  if (!ringBuffer)
4245  {
4246  LOG(VB_GENERAL, LOG_ERR, LOC +
4247  QString("Failed to start recorder! ringBuffer is NULL\n"
4248  "\t\t\t\t Tuning request was %1\n")
4249  .arg(lastTuningRequest.toString()));
4250 
4251  if (HasFlags(kFlagLiveTV))
4252  {
4253  QString message = QString("QUIT_LIVETV %1").arg(inputid);
4254  MythEvent me(message);
4255  gCoreContext->dispatch(me);
4256  }
4257  goto err_ret;
4258  }
4259 
4260  if (channel && genOpt.inputtype == "MJPEG")
4261  channel->Close(); // Needed because of NVR::MJPEGInit()
4262 
4263  LOG(VB_GENERAL, LOG_INFO, LOC + "TuningNewRecorder - CreateRecorder()");
4265 
4266  if (recorder)
4267  {
4269  recorder->Initialize();
4270  if (recorder->IsErrored())
4271  {
4272  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialize recorder!");
4273  delete recorder;
4274  recorder = nullptr;
4275  }
4276  }
4277 
4278  if (!recorder)
4279  {
4280  LOG(VB_GENERAL, LOG_ERR, LOC +
4281  QString("Failed to start recorder!\n"
4282  "\t\t\t\t Tuning request was %1\n")
4283  .arg(lastTuningRequest.toString()));
4284 
4285  if (HasFlags(kFlagLiveTV))
4286  {
4287  QString message = QString("QUIT_LIVETV %1").arg(inputid);
4288  MythEvent me(message);
4289  gCoreContext->dispatch(me);
4290  }
4292  goto err_ret;
4293  }
4294 
4295  if (rec)
4296  recorder->SetRecording(rec);
4297 
4298  if (GetDTVRecorder() && streamData)
4299  {
4300  const StandardSetting *setting = profile.byName("recordingtype");
4301  if (setting)
4302  streamData->SetRecordingType(setting->getValue());
4303  GetDTVRecorder()->SetStreamData(streamData);
4304  }
4305 
4306  if (channel && genOpt.inputtype == "MJPEG")
4307  channel->Open(); // Needed because of NVR::MJPEGInit()
4308 
4309  // Setup for framebuffer capture devices..
4310  if (channel)
4311  {
4313  channel->GetChannelName());
4314  }
4315 
4316  if (GetV4LChannel())
4317  {
4319  CloseChannel();
4320  }
4321 
4322  recorderThread = new MThread("RecThread", recorder);
4323  recorderThread->start();
4324 
4325  // Wait for recorder to start.
4326  stateChangeLock.unlock();
4327  while (!recorder->IsRecording() && !recorder->IsErrored())
4328  std::this_thread::sleep_for(std::chrono::microseconds(5));
4329  stateChangeLock.lock();
4330 
4331  if (GetV4LChannel())
4333 
4334  SetFlags(kFlagRecorderRunning | kFlagRingBufferReady, __FILE__, __LINE__);
4335 
4336  ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4337 
4338  //workaround for failed import recordings, no signal monitor means we never
4339  //go to recording state and the status here seems to override the status
4340  //set in the importrecorder and backend via setrecordingstatus
4341  if (genOpt.inputtype == "IMPORT")
4342  {
4344  if (curRecording)
4346  }
4347  return;
4348 
4349  err_ret:
4350  SetRecordingStatus(RecStatus::Failed, __LINE__, true);
4352 
4353  if (rec)
4354  {
4355  // Make sure the scheduler knows...
4357  LOG(VB_RECORD, LOG_INFO, LOC +
4358  QString("TuningNewRecorder -- UPDATE_RECORDING_STATUS: %1")
4360  MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
4361  .arg(rec->GetInputID())
4362  .arg(rec->GetChanID())
4364  .arg(RecStatus::Failed)
4366  gCoreContext->dispatch(me);
4367  }
4368 
4369  if (tvchain)
4370  delete rec;
4371 }
4372 
4377 {
4378  LOG(VB_RECORD, LOG_INFO, LOC + "Restarting Recorder");
4379 
4380  bool had_dummyrec = false;
4381 
4382  if (curRecording)
4383  {
4384  FinishedRecording(curRecording, nullptr);
4386  }
4387 
4389  {
4390  ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
4391  had_dummyrec = true;
4392  }
4393 
4394  SwitchLiveTVRingBuffer(channel->GetChannelName(), true, !had_dummyrec);
4395 
4396  if (had_dummyrec)
4397  {
4399  ProgramInfo *progInfo = tvchain->GetProgramAt(-1);
4400  RecordingInfo recinfo(*progInfo);
4401  delete progInfo;
4402  recinfo.SetInputID(inputid);
4403  recorder->SetRecording(&recinfo);
4404  }
4405  recorder->Reset();
4406 
4407  // Set file descriptor of channel from recorder for V4L
4408  if (GetV4LChannel())
4410 
4411  // Some recorders unpause on Reset, others do not...
4412  recorder->Unpause();
4413 
4415  {
4417  QString msg1 = QString("Recording: %1 %2 %3 %4")
4418  .arg(rcinfo1->GetTitle()).arg(rcinfo1->GetChanID())
4420  .arg(rcinfo1->GetRecordingEndTime(MythDate::ISODate));
4421  ProgramInfo *rcinfo2 = tvchain->GetProgramAt(-1);
4422  QString msg2 = QString("Recording: %1 %2 %3 %4")
4423  .arg(rcinfo2->GetTitle()).arg(rcinfo2->GetChanID())
4425  .arg(rcinfo2->GetRecordingEndTime(MythDate::ISODate));
4426  delete rcinfo2;
4427  LOG(VB_RECORD, LOG_INFO, LOC + "Pseudo LiveTV recording starting." +
4428  "\n\t\t\t" + msg1 + "\n\t\t\t" + msg2);
4429 
4432 
4434 
4435  InitAutoRunJobs(curRecording, kAutoRunProfile, nullptr, __LINE__);
4436  }
4437 
4438  ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4439 }
4440 
4441 void TVRec::SetFlags(uint f, const QString & file, int line)
4442 {
4443  QMutexLocker lock(&stateChangeLock);
4444  stateFlags |= f;
4445  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetFlags(%1) -> %2 @ %3:%4")
4446  .arg(FlagToString(f)).arg(FlagToString(stateFlags)).arg(file).arg(line));
4447  WakeEventLoop();
4448 }
4449 
4450 void TVRec::ClearFlags(uint f, const QString & file, int line)
4451 {
4452  QMutexLocker lock(&stateChangeLock);
4453  stateFlags &= ~f;
4454  LOG(VB_RECORD, LOG_INFO, LOC + QString("ClearFlags(%1) -> %2 @ %3:%4")
4455  .arg(FlagToString(f)).arg(FlagToString(stateFlags)).arg(file).arg(line));
4456  WakeEventLoop();
4457 }
4458 
4460 {
4461  QString msg("");
4462 
4463  // General flags
4464  if (kFlagFrontendReady & f)
4465  msg += "FrontendReady,";
4466  if (kFlagRunMainLoop & f)
4467  msg += "RunMainLoop,";
4468  if (kFlagExitPlayer & f)
4469  msg += "ExitPlayer,";
4470  if (kFlagFinishRecording & f)
4471  msg += "FinishRecording,";
4472  if (kFlagErrored & f)
4473  msg += "Errored,";
4474  if (kFlagCancelNextRecording & f)
4475  msg += "CancelNextRecording,";
4476 
4477  // Tuning flags
4478  if ((kFlagRec & f) == kFlagRec)
4479  msg += "REC,";
4480  else
4481  {
4482  if (kFlagLiveTV & f)
4483  msg += "LiveTV,";
4484  if (kFlagRecording & f)
4485  msg += "Recording,";
4486  }
4487  if ((kFlagNoRec & f) == kFlagNoRec)
4488  msg += "NOREC,";
4489  else
4490  {
4491  if (kFlagEITScan & f)
4492  msg += "EITScan,";
4493  if (kFlagCloseRec & f)
4494  msg += "CloseRec,";
4495  if (kFlagKillRec & f)
4496  msg += "KillRec,";
4497  if (kFlagAntennaAdjust & f)
4498  msg += "AntennaAdjust,";
4499  }
4501  msg += "PENDINGACTIONS,";
4502  else
4503  {
4504  if (kFlagWaitingForRecPause & f)
4505  msg += "WaitingForRecPause,";
4506  if (kFlagWaitingForSignal & f)
4507  msg += "WaitingForSignal,";
4508  if (kFlagNeedToStartRecorder & f)
4509  msg += "NeedToStartRecorder,";
4510  if (kFlagKillRingBuffer & f)
4511  msg += "KillRingBuffer,";
4512  }
4513  if ((kFlagAnyRunning & f) == kFlagAnyRunning)
4514  msg += "ANYRUNNING,";
4515  else
4516  {
4517  if (kFlagSignalMonitorRunning & f)
4518  msg += "SignalMonitorRunning,";
4519  if (kFlagEITScannerRunning & f)
4520  msg += "EITScannerRunning,";
4522  msg += "ANYRECRUNNING,";
4523  else
4524  {
4525  if (kFlagDummyRecorderRunning & f)
4526  msg += "DummyRecorderRunning,";
4527  if (kFlagRecorderRunning & f)
4528  msg += "RecorderRunning,";
4529  }
4530  }
4531  if (kFlagRingBufferReady & f)
4532  msg += "RingBufferReady,";
4533 
4534  if (msg.isEmpty())
4535  msg = QString("0x%1").arg(f,0,16);
4536 
4537  return msg;
4538 }
4539 
4541 {
4542  QMutexLocker lock(&nextLiveTVDirLock);
4543 
4544  bool found = !nextLiveTVDir.isEmpty();
4545  if (!found && triggerLiveTVDir.wait(&nextLiveTVDirLock, 500))
4546  {
4547  found = !nextLiveTVDir.isEmpty();
4548  }
4549 
4550  return found;
4551 }
4552 
4553 void TVRec::SetNextLiveTVDir(QString dir)
4554 {
4555  QMutexLocker lock(&nextLiveTVDirLock);
4556 
4557  nextLiveTVDir = dir;
4558  triggerLiveTVDir.wakeAll();
4559 }
4560 
4562  RingBuffer **rb,
4563  const QString & channum)
4564 {
4565  LOG(VB_RECORD, LOG_INFO, LOC + "GetProgramRingBufferForLiveTV()");
4566  if (!channel || !tvchain || !pginfo || !rb)
4567  return false;
4568 
4569  nextLiveTVDirLock.lock();
4570  nextLiveTVDir.clear();
4571  nextLiveTVDirLock.unlock();
4572 
4573  // Dispatch this early, the response can take a while.
4574  MythEvent me(QString("QUERY_NEXT_LIVETV_DIR %1").arg(inputid));
4575  gCoreContext->dispatch(me);
4576 
4577  uint sourceid = channel->GetSourceID();
4578  int chanid = ChannelUtil::GetChanID(sourceid, channum);
4579 
4580  if (chanid < 0)
4581  {
4582  // Test setups might have zero channels
4583  if (genOpt.inputtype == "IMPORT" || genOpt.inputtype == "DEMO")
4584  chanid = 9999;
4585  else
4586  {
4587  LOG(VB_GENERAL, LOG_ERR, LOC +
4588  QString("Channel: \'%1\' was not found in the database.\n"
4589  "\t\tMost likely, the 'starting channel' for this "
4590  "Input Connection is invalid.\n"
4591  "\t\tCould not start livetv.").arg(channum));
4592  return false;
4593  }
4594  }
4595 
4596  int hoursMax = gCoreContext->GetNumSetting("MaxHoursPerLiveTVRecording", 8);
4597  if (hoursMax <= 0)
4598  hoursMax = 8;
4599 
4600  RecordingInfo *prog = nullptr;
4602  prog = new RecordingInfo(*pseudoLiveTVRecording);
4603  else
4604  {
4605  prog = new RecordingInfo(
4606  chanid, MythDate::current(true), true, hoursMax);
4607  }
4608 
4609  prog->SetInputID(inputid);
4610 
4611  if (prog->GetRecordingStartTime() == prog->GetRecordingEndTime())
4612  {
4613  LOG(VB_GENERAL, LOG_ERR, LOC + "GetProgramRingBufferForLiveTV()"
4614  "\n\t\t\tProgramInfo is invalid."
4615  "\n" + prog->toString());
4616  prog->SetScheduledEndTime(prog->GetRecordingStartTime().addSecs(3600));
4618 
4619  prog->SetChanID(chanid);
4620  }
4621 
4622  if (!pseudoLiveTVRecording)
4624 
4625  prog->SetStorageGroup("LiveTV");
4626 
4627  if (WaitForNextLiveTVDir())
4628  {
4629  QMutexLocker lock(&nextLiveTVDirLock);
4630  prog->SetPathname(nextLiveTVDir);
4631  }
4632  else
4633  {
4634  StorageGroup sgroup("LiveTV", gCoreContext->GetHostName());
4635  prog->SetPathname(sgroup.FindNextDirMostFree());
4636  }
4637 
4638  if (!pseudoLiveTVRecording)
4639  prog->SetRecordingGroup("LiveTV");
4640 
4641  StartedRecording(prog);
4642 
4643  *rb = RingBuffer::Create(prog->GetPathname(), true);
4644  if (!(*rb) || !(*rb)->IsOpen())
4645  {
4646  LOG(VB_GENERAL, LOG_ERR, LOC + QString("RingBuffer '%1' not open...")
4647  .arg(prog->GetPathname()));
4648 
4649  delete *rb;
4650  delete prog;
4651 
4652  return false;
4653  }
4654 
4655  *pginfo = prog;
4656  return true;
4657 }
4658 
4659 bool TVRec::CreateLiveTVRingBuffer(const QString & channum)
4660 {
4661  LOG(VB_RECORD, LOG_INFO, LOC + QString("CreateLiveTVRingBuffer(%1)")
4662  .arg(channum));
4663 
4664  RecordingInfo *pginfo = nullptr;
4665  RingBuffer *rb = nullptr;
4666  QString inputName;
4667 
4668  if (!channel ||
4669  !channel->CheckChannel(channum))
4670  {
4672  return false;
4673  }
4674 
4675  if (!GetProgramRingBufferForLiveTV(&pginfo, &rb, channum))
4676  {
4677  ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
4679  LOG(VB_GENERAL, LOG_ERR, LOC +
4680  QString("CreateLiveTVRingBuffer(%1) failed").arg(channum));
4681  return false;
4682  }
4683 
4684  SetRingBuffer(rb);
4685 
4687  if (!pseudoLiveTVRecording)
4689 
4690  bool discont = (tvchain->TotalSize() > 0);
4692  channel->GetInputName(), discont);
4693 
4694  if (curRecording)
4695  {
4697  delete curRecording;
4698  }
4699 
4700  curRecording = pginfo;
4702 
4703  return true;
4704 }
4705 
4706 bool TVRec::SwitchLiveTVRingBuffer(const QString & channum,
4707  bool discont, bool set_rec)
4708 {
4709  QString msg;
4710  if (curRecording)
4711  {
4712  msg = QString(" curRec(%1) curRec.size(%2)")
4713  .arg(curRecording->MakeUniqueKey())
4714  .arg(curRecording->GetFilesize());
4715  }
4716  LOG(VB_RECORD, LOG_INFO, LOC +
4717  QString("SwitchLiveTVRingBuffer(discont %1, set_next_rec %2)")
4718  .arg(discont).arg(set_rec) + msg);
4719 
4720  RecordingInfo *pginfo = nullptr;
4721  RingBuffer *rb = nullptr;
4722  QString inputName;
4723 
4724  if (!channel ||
4725  !channel->CheckChannel(channum))
4726  {
4728  return false;
4729  }
4730 
4731  if (!GetProgramRingBufferForLiveTV(&pginfo, &rb, channum))
4732  {
4734  return false;
4735  }
4736 
4737  QString oldinputtype = tvchain->GetInputType(-1);
4738 
4739  pginfo->MarkAsInUse(true, kRecorderInUseID);
4741  if (!pseudoLiveTVRecording)
4744  channel->GetInputName(), discont);
4745 
4746  if (set_rec && recorder)
4747  {
4748  recorder->SetNextRecording(pginfo, rb);
4749  if (discont)
4751  delete pginfo;
4752  SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4753  }
4754  else if (!set_rec)
4755  {
4756  // dummy recordings are finished before this
4757  // is called and other recordings must be finished..
4758  if (curRecording && oldinputtype != "DUMMY")
4759  {
4760  FinishedRecording(curRecording, nullptr);
4762  delete curRecording;
4763  }
4764  curRecording = pginfo;
4765  SetRingBuffer(rb);
4766  }
4767  else
4768  {
4769  delete rb;
4770  }
4771 
4772  return true;
4773 }
4774 
4776 {
4777  LOG(VB_RECORD, LOG_INFO, LOC + "SwitchRecordingRingBuffer()");
4778 
4779  if (switchingBuffer)
4780  {
4781  LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4782  "already switching.");
4783  return nullptr;
4784  }
4785 
4786  if (!recorder)
4787  {
4788  LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4789  "invalid recorder.");
4790  return nullptr;
4791  }
4792 
4793  if (!curRecording)
4794  {
4795  LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4796  "invalid recording.");
4797  return nullptr;
4798  }
4799 
4800  if (rcinfo.GetChanID() != curRecording->GetChanID())
4801  {
4802  LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4803  "Not the same channel.");
4804  return nullptr;
4805  }
4806 
4807  RecordingInfo *ri = new RecordingInfo(rcinfo);
4809 
4810  QString pn = LoadProfile(nullptr, ri, profile);
4811 
4812  if (pn != recProfileName)
4813  {
4814  LOG(VB_RECORD, LOG_ERR, LOC +
4815  QString("SwitchRecordingRingBuffer() -> "
4816  "cannot switch profile '%1' to '%2'")
4817  .arg(recProfileName).arg(pn));
4818  return nullptr;
4819  }
4820 
4822 
4823  ri->MarkAsInUse(true, kRecorderInUseID);
4824  StartedRecording(ri);
4825 
4826  bool write = genOpt.inputtype != "IMPORT";
4828  if (!rb || !rb->IsOpen())
4829  {
4830  delete rb;
4832  FinishedRecording(ri, nullptr);
4833  ri->MarkAsInUse(false, kRecorderInUseID);
4834  delete ri;
4835  LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer() -> "
4836  "Failed to create new RB.");
4837  return nullptr;
4838  }
4839  else
4840  {
4841  recorder->SetNextRecording(ri, rb);
4842  SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4844  switchingBuffer = true;
4846  LOG(VB_RECORD, LOG_INFO, LOC + "SwitchRecordingRingBuffer -> done");
4847  return ri;
4848  }
4849 }
4850 
4852 {
4853  QMap<uint,TVRec*>::const_iterator it = inputs.find(inputid);
4854  if (it == inputs.end())
4855  return nullptr;
4856  return *it;
4857 }
4858 
4859 QString TuningRequest::toString(void) const
4860 {
4861  return QString("Program(%1) channel(%2) input(%3) flags(%4)")
4862  .arg((program == nullptr) ? QString("NULL") : program->toString())
4863  .arg(channel).arg(input)
4864  .arg(TVRec::FlagToString(flags));
4865 }
4866 
4867 #ifdef USING_DVB
4868 #include "dvbchannel.h"
4870 {
4871  // Some DVB devices munge the PMT and/or PAT so the CRC check fails.
4872  // We need to tell the stream data class to not check the CRC on
4873  // these devices. This can cause segfaults.
4874  if (dynamic_cast<DVBChannel*>(c))
4875  s->SetIgnoreCRC(dynamic_cast<DVBChannel*>(c)->HasCRCBug());
4876 }
4877 #else
4879 #endif // USING_DVB
4880 
4881 /* vim: set expandtab tabstop=4 shiftwidth=4: */
uint flags
Definition: tv_rec.h:123
uint dvb_tuning_delay
Definition: tv_rec.h:91
DTVSignalMonitor * GetDTVSignalMonitor(void)
Definition: tv_rec.cpp:2197
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:794
void SetStorageGroup(const QString &group)
Definition: programinfo.h:517
bool GetKeyframeDurations(int64_t start, int64_t end, frm_pos_map_t &) const
Definition: tv_rec.cpp:2655
QString GetID(void) const
Definition: livetvchain.h:53
bool GetProgramRingBufferForLiveTV(RecordingInfo **pginfo, RingBuffer **rb, const QString &channum)
Definition: tv_rec.cpp:4561
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:295
virtual int ChangePictureAttribute(PictureAdjustType, PictureAttribute, bool)
Definition: channelbase.h:95
QString recProfileName
Definition: tv_rec.h:385
void AddListener(SignalMonitorListener *listener)
def write(text, progress=True)
Definition: mythburn.py:279
int audiosamplerate
Definition: tv_rec.h:79
virtual void SetRotorTarget(float)
Sets rotor target pos from 0.0 to 1.0.
QString TuningGetChanNum(const TuningRequest &, QString &input) const
Definition: tv_rec.cpp:3427
ProgramInfo * info
Definition: tv_rec.h:140
Error State, if we ever try to enter this state errored is set.
Definition: tv.h:54
virtual int GetChanID(void) const
void SetVideoStreamsRequired(uint num)
virtual void Reset(void)
uint inputid
Definition: tv_rec.h:376
Fetch information on previous channel.
Definition: tv.h:41
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
void bindValue(const QString &placeholder, const QVariant &val)
Definition: mythdbcon.cpp:875
uint GetInputID(void) const
Definition: programinfo.h:457
QString LiveTVStartChannel
Definition: tv_rec.h:422
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
#define LOC
Definition: tv_rec.cpp:46
This is a specialization of RecorderBase used to handle MPEG-2, MPEG-4, MPEG-4 AVC,...
Definition: dtvrecorder.h:28
long long GetKeyframePosition(long long desired) const
Returns closest keyframe position before the desired frame.
int TotalSize(void) const
static QString GetInputName(uint inputid)
Definition: cardutil.cpp:1228
virtual void Close(void)=0
Closes the channel changing hardware to use.
void StopRecording(bool killFile=false)
Changes from a recording state to kState_None.
Definition: tv_rec.cpp:739
Watching LiveTV is the state for when we are watching a recording and the user has control over the c...
Definition: tv.h:63
uint GetInputId(void)
Returns the inputid.
Definition: tv_rec.h:241
int ChangePictureAttribute(PictureAdjustType type, PictureAttribute attr, bool direction)
Returns current value [0,100] if it succeeds, -1 otherwise.
Definition: tv_rec.cpp:3043
void ToggleChannelFavorite(QString)
Toggles whether the current channel should be on our favorites list.
Definition: tv_rec.cpp:2975
static RecorderBase * CreateRecorder(TVRec *tvrec, ChannelBase *channel, const RecordingProfile &profile, const GeneralDBOptions &genOpt)
QString GenMythURL(QString host=QString(), QString port=QString(), QString path=QString(), QString storageGroup=QString())
bool earlyCommFlag
Definition: tv_rec.h:366
virtual void ReturnCachedTable(const PSIPTable *psip) const
void SavePositionMap(bool force=false, bool finished=false)
Save the seektable to the DB.
AutoRunInitType
Definition: tv_rec.h:332
RecordingInfo * pseudoLiveTVRecording
Definition: tv_rec.h:418
long long GetFramesWritten(void)
Returns number of frames written to disk by recorder.
Definition: tv_rec.cpp:2596
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:91
static bool ToggleChannel(uint chanid, int changrpid, int delete_chan)
QString model
Definition: tv_rec.h:102
QString GetInput(void) const
Returns current input.
Definition: tv_rec.cpp:3059
static const uint kFlagNeedToStartRecorder
Definition: tv_rec.h:469
QMutex triggerEventSleepLock
Definition: tv_rec.h:402
virtual void Start()
Start signal monitoring thread.
virtual QString GetChannelName(void) const
Definition: channelbase.h:64
static const uint64_t kDVBSigMon_WaitForPos
Wait for rotor to complete turning the antenna.
virtual bool IsOpen(void) const =0
Reports whether channel is already open.
virtual void SetStreamData(MPEGStreamData *data)
Sets the MPEG stream data for DTVSignalMonitor to use, and connects the table signals to the monitor.
int overRecordSecNrml
Definition: tv_rec.h:371
void SetNotifyFrontend(bool notify)
Enables or disables frontend notification of the current signal value.
Definition: signalmonitor.h:90
static const uint kFlagRecorderRunning
Definition: tv_rec.h:477
QDateTime recordEndTime
Definition: tv_rec.h:410
void SetPathname(const QString &) const
uint GetTransportID(void) const
Returns DVB transport_stream_id, 0 if unknown.
Definition: dtvchannel.h:107
void RecordPending(const ProgramInfo *rcinfo, int secsleft, bool hasLater)
Tells TVRec "rcinfo" is the next pending recording.
Definition: tv_rec.cpp:307
RecStatus::Type m_recStatus
Definition: tv_rec.h:406
int GetProgramNumber(void) const
Returns program number in PAT, -1 if unknown.
Definition: dtvchannel.h:91
static vector< uint > GetConflictingInputs(uint inputid)
Definition: cardutil.cpp:1558
const char * kRecorderInUseID
void RingBufferChanged(RingBuffer *, RecordingInfo *, RecordingQuality *)
Definition: tv_rec.cpp:3405
uint minorChan
Definition: tv_rec.h:128
static const uint kFlagRecording
final result desired is a timed recording
Definition: tv_rec.h:450
void SaveCommFlagged(CommFlagStatus flag)
Set "commflagged" field in "recorded" table to "flag".
static void error(const char *str,...)
Definition: vbi.c:41
static const uint64_t kDTVSigMon_WaitForPMT
void FinishedRecording(RecordingInfo *, RecordingQuality *)
If not a premature stop, adds program to history of recorded programs.
Definition: tv_rec.cpp:848
uint GetSourceID(void) const
Definition: programinfo.h:456
virtual void Initialize(void)=0
This is called between SetOptionsFromProfile() and run() to initialize any devices,...
uint sourceid
associated channel listings source
Definition: inputinfo.h:75
bool TuningOnSameMultiplex(TuningRequest &request)
Definition: tv_rec.cpp:3466
bool RemoteIsBusy(uint inputid, InputInfo &busy_input)
volatile bool switchingBuffer
Definition: tv_rec.h:405
static const uint64_t kDTVSigMon_WaitForPAT
uint TableCount() const
Definition: atsctables.h:114
RecordingInfo * SwitchRecordingRingBuffer(const RecordingInfo &rcinfo)
Definition: tv_rec.cpp:4775
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
void MarkAsInUse(bool inuse, QString usedFor="")
Tracks a recording's in use status, to prevent deletion and to allow the storage scheduler to perform...
uint channel_timeout
Definition: tv_rec.h:82
void ToStringList(QStringList &list) const
Serializes ProgramInfo into a QStringList which can be passed over a socket.
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:312
void TuningNewRecorder(MPEGStreamData *)
Creates a recorder instance.
Definition: tv_rec.cpp:4190
bool SetChannelInfo(uint chanid, uint sourceid, QString oldchannum, QString callsign, QString channum, QString channame, QString xmltvid)
Definition: tv_rec.cpp:3349
RecordingRule * GetRecordingRule(void)
Returns the "record" field, creating it if necessary.
virtual bool Init(QString &startchannel, bool setchan)
Definition: channelbase.cpp:65
ProgramInfo * GetProgramAt(int at) const
Returns program at the desired location.
QString StateToString(TVState state)
Returns a human readable QString representing a TVState.
Definition: tv.cpp:10
QString vbidev
Definition: tv_rec.h:76
QString toString(Verbosity v=kLongDescription, QString sep=":", QString grp="\"") const
QString GetTitle(void) const
Definition: programinfo.h:355
bool GetKeyframePositions(int64_t start, int64_t end, frm_pos_map_t &) const
Returns byte position in RingBuffer of a keyframes according to recorder.
Definition: tv_rec.cpp:2644
static const uint kFlagEITScannerRunning
Definition: tv_rec.h:474
static const uint kFlagDummyRecorderRunning
Definition: tv_rec.h:476
void SetInputType(const QString &type)
Definition: livetvchain.cpp:56
void SetRecordingRuleID(uint id)
Definition: programinfo.h:525
vector< pid_cache_item_t > pid_cache_t
Definition: channelutil.h:43
static const uint kFlagRingBufferReady
Definition: tv_rec.h:482
int size(void) const
Definition: mythdbcon.h:187
bool isConnected(void)
Only updated once during object creation.
Definition: mythdbcon.h:135
static ChannelBase * CreateChannel(TVRec *tv_rec, const GeneralDBOptions &genOpt, const DVBDBOptions &dvbOpt, const FireWireDBOptions &fwOpt, const QString &startchannel, bool enter_power_save_mode, QString &rbFileExt, bool setchan)
static QMap< uint, TVRec * > inputs
Definition: tv_rec.h:433
QString nextLiveTVDir
Definition: tv_rec.h:419
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:34
DTVChannel * GetDTVChannel(void)
Definition: tv_rec.cpp:1210
void SetChannel(QString name, uint requestType=kFlagDetect)
Changes to a named channel on the current tuner.
Definition: tv_rec.cpp:3110
bool dvb_eitscan
Definition: tv_rec.h:92
static const uint kFlagWaitingForSignal
Definition: tv_rec.h:468
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:2513
QString channel
Definition: tv_rec.h:125
static const uint kFlagRec
Definition: tv_rec.h:453
Fetch browse information on current channel and time.
Definition: tv.h:40
virtual bool EnterPowerSavingMode(void)
Enters power saving mode if the card supports it.
Definition: dtvchannel.h:66
MarkTypes QueryAverageAspectRatio(void) const
QString GetSuggestedTuningMode(bool is_live_tv) const
Returns suggested tuning mode: "mpeg", "dvb", or "atsc".
Definition: dtvchannel.cpp:67
static SignalMonitor * Init(QString cardtype, int db_cardnum, ChannelBase *channel, bool release_stream)
static int GetProgramNumber(uint sourceid, const QString &channum)
Definition: channelutil.h:181
QString SetInput(QString input)
Changes to the specified input.
Definition: tv_rec.cpp:3084
void StartedRecording(QString ext)
Inserts this RecordingInfo into the database as an existing recording.
uint GetSourceID(void) const
Returns current source id.
Definition: tv_rec.cpp:3069
bool IsCommercialFree(void) const
Definition: programinfo.h:472
void SetRecordingStatus(RecStatus::Type new_status, int line, bool have_lock=false)
Definition: tv_rec.cpp:709
unsigned int uint
Definition: compat.h:140
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static RingBuffer * Create(const QString &xfilename, bool write, bool usereadahead=true, int timeout_ms=kDefaultOpenTimeout, bool stream_only=false)
Creates a RingBuffer instance.
Definition: ringbuffer.cpp:112
QHash< QString, int > autoRunJobs
Definition: tv_rec.h:411
uint signal_timeout
Definition: tv_rec.h:81
Watching Pre-recorded is a TV only state for when we are watching a pre-existing recording.
Definition: tv.h:67
virtual void SetVideoFilters(QString &filters)=0
Tells recorder which filters to use.
RecordingType m_type
RecStatus::Type StartRecording(ProgramInfo *rcinfo)
Tells TVRec to Start recording the program "rcinfo" as soon as possible.
Definition: tv_rec.cpp:434
void AddHistory(bool resched=true, bool forcedup=false, bool future=false)
Adds recording history, creating "record" it if necessary.
void TeardownRecorder(uint request_flags)
Tears down the recorder.
Definition: tv_rec.cpp:1137
virtual bool Open(void)=0
Opens the channel changing hardware for use.
QMutex nextLiveTVDirLock
Definition: tv_rec.h:420
static const uint kFlagCancelNextRecording
Definition: tv_rec.h:444
void UpdateInUseMark(bool force=false)
static QReadWriteLock inputsLock
Definition: tv_rec.h:432
bool GetKeyframeDurations(long long start, long long end, frm_pos_map_t &) const
static void apply_broken_dvb_driver_crc_hack(ChannelBase *, MPEGStreamData *)
Definition: tv_rec.cpp:4869
RecordingFile * GetRecordingFile() const
bool Init(void)
Performs instance initialization, returns true on success.
Definition: tv_rec.cpp:175
void SetDesiredEndTime(const QDateTime &dt)
virtual void Clear(void)
Definition: inputinfo.cpp:6
bool IsOnSameMultiplex(void) const
Definition: tv_rec.h:120
virtual void CheckForRingBufferSwitch(void)
If requested, switch to new RingBuffer/ProgramInfo objects.
TVState
TVState is an enumeration of the states used by TV and TVRec.
Definition: tv.h:50
QWaitCondition triggerEventSleepWait
Definition: tv_rec.h:403
TVState RemoveRecording(TVState state)
If "state" is kState_RecordingOnly or kState_WatchingLiveTV, returns a kState_None,...
Definition: tv_rec.cpp:786
QDateTime signalMonitorDeadline
Definition: tv_rec.h:352
~TVRec(void)
Stops the event and scanning threads and deletes any ChannelBase, RingBuffer, and RecorderBase instan...
Definition: tv_rec.cpp:212
bool runJobOnHostOnly
Definition: tv_rec.h:367
static guint32 * tmp
Definition: goom_core.c:35
static uint GetMplexID(uint sourceid, const QString &channum)
void enqueue(T d)
Adds item to the back of the list. O(1).
Definition: mythdeque.h:42
static const uint64_t kDTVSigMon_WaitForMGT
void StartedRecording(RecordingInfo *)
Inserts a "curRec" into the database.
Definition: tv_rec.cpp:822
static const uint kFlagSignalMonitorRunning
Definition: tv_rec.h:473
uint RemoteGetState(uint inputid)
void SetPseudoLiveTVRecording(RecordingInfo *)
Sets the pseudo LiveTV RecordingInfo.
Definition: tv_rec.cpp:360
QDateTime preFailDeadline
Definition: tv_rec.h:355
bool changeState
Definition: tv_rec.h:393
static const uint kFlagEITScan
final result desired is an EIT Scan
Definition: tv_rec.h:457
void LoadRecordingFile()
static const uint kFlagCloseRec
close recorder, keep recording
Definition: tv_rec.h:459
GeneralDBOptions genOpt
Definition: tv_rec.h:381
virtual int GetPictureAttribute(PictureAttribute) const
Definition: channelbase.h:94
static const uint kFlagDetect
Definition: tv_rec.h:483
uint mplexid
mplexid restriction if applicable
Definition: inputinfo.h:77
static bool GetATSCChannel(uint sourceid, const QString &channum, uint &major, uint &minor)
bool reachedPreFail
Definition: tv_rec.h:356
bool IsErrored(void) const
Definition: signalmonitor.h:80
bool IsSameProgramWeakCheck(const ProgramInfo &other) const
Checks for duplicate using only title, chanid and startts.
virtual QString getValue(void) const
static bool IsEncoder(const QString &rawtype)
Definition: cardutil.h:120
bool dvb_on_demand
Definition: tv_rec.h:90
int GetBackendServerPort(void)
Returns the locally defined backend control port.
void ChangeState(TVState nextState)
Puts a state change on the nextState queue.
Definition: tv_rec.cpp:1115
void FinishedRecording(bool allowReRecord)
If not a premature stop, adds program to history of recorded programs.
bool wait_for_seqstart
Definition: tv_rec.h:83
RecordingInfo * curRecording
Definition: tv_rec.h:409
bool QueryTuningInfo(QString &channum, QString &input) const
Returns the channel and input needed to record the program.
static const uint kFlagKillRec
close recorder, discard recording
Definition: tv_rec.h:461
Overall structure.
int GetAutoRunJobs(void) const
Returns a bitmap of which jobs are attached to this RecordingInfo.
void StopPassiveScan(void)
Stops inserting Event Information Tables into DB.
Definition: eitscanner.cpp:224
Fetch information on next channel.
Definition: tv.h:42
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:384
bool triggerEventLoopSignal
Definition: tv_rec.h:401
uint GetMajorChannel(void) const
Returns major channel, 0 if unknown.
Definition: dtvchannel.h:95
QVariant value(int i) const
Definition: mythdbcon.h:182
static void GetPidsToCache(DTVSignalMonitor *dtvMon, pid_cache_t &pid_cache)
Definition: tv_rec.cpp:1836
virtual bool IsPaused(bool holding_lock=false) const
Returns true iff recorder is paused.
QString overRecordCategory
Definition: tv_rec.h:373
bool transcodeFirst
Definition: tv_rec.h:365
static int GetChannelGroupId(QString changroupname)
QString MakeUniqueKey(void) const
Creates a unique string that can be used to identify an existing recording.
Definition: programinfo.h:333
PendingMap pendingRecordings
Definition: tv_rec.h:415
bool Save(bool sendSig=true)
RecordingInfo * program
Definition: tv_rec.h:124
MarkTypes
Definition: programtypes.h:48
long long GetMaxBitrate(void) const
Returns the maximum bits per second this recorder can produce.
Definition: tv_rec.cpp:2671
void AppendNewProgram(ProgramInfo *pginfo, QString channum, QString inputname, bool discont)
Definition: livetvchain.cpp:67
enum BrowseDirections BrowseDirection
Used to request ProgramInfo for channel browsing.
Holds information on recordings and videos.
Definition: programinfo.h:66
uint QueryAverageHeight(void) const
If present in recording this loads average height of the main video stream from database's stream mar...
friend class TuningRequest
Definition: tv_rec.h:154
virtual int GetVideoFd(void)=0
Returns file descriptor of recorder device.
bool signalEventCmdSent
Definition: tv_rec.h:349
static QString FlagToString(uint)
Definition: tv_rec.cpp:4459
virtual long long GetFramesWritten(void)=0
Returns number of frames written to disk.
static bool GetDevices(uint inputid, uint &parentid, GeneralDBOptions &general_opts, DVBDBOptions &dvb_opts, FireWireDBOptions &firewire_opts)
Definition: tv_rec.cpp:1663
FireWireDBOptions fwOpt
Definition: tv_rec.h:383
void ReloadAll(const QStringList &data=QStringList())
void SetScheduledEndTime(const QDateTime &dt)
Definition: programinfo.h:511
Recording Only is a TVRec only state for when we are recording a program, but there is no one current...
Definition: tv.h:84
void SetChannel(int major, int minor)
bool HasProgram(uint progNum) const
virtual int IncrRef(void)
Increments reference count.
This class is used as a container for messages.
Definition: mythevent.h:15
void SetDVBService(uint network_id, uint transport_id, int service_id)
static const uint kFlagAnyRecRunning
Definition: tv_rec.h:478
int progNum
Definition: tv_rec.h:129
static const uint kFlagLiveTV
final result desired is LiveTV recording
Definition: tv_rec.h:448
static const uint kFlagPendingActions
Definition: tv_rec.h:470
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
static bool QueueJob(int jobType, uint chanid, const QDateTime &recstartts, QString args="", QString comment="", QString host="", int flags=0, int status=JOB_QUEUED, QDateTime schedruntime=QDateTime())
Definition: jobqueue.cpp:525
bool skip_btaudio
Definition: tv_rec.h:80
uint signalMonitorCheckCnt
Definition: tv_rec.h:353
bool QueueEITChannelChange(const QString &name)
Queues up a channel change for the EITScanner.
Definition: tv_rec.cpp:3159
Class providing a generic interface to digital tuning hardware.
Definition: dtvchannel.h:34
RecorderBase * recorder
Definition: tv_rec.h:343
QString GetChainID(void)
Get the chainid of the livetv instance.
Definition: tv_rec.cpp:2721
static int GetChanID(int db_mplexid, int service_transport_id, int major_channel, int minor_channel, int program_number)
void CloseChannel(void)
Definition: tv_rec.cpp:1197
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:150
void SaveVideoProperties(uint mask, uint video_property_flags)
bool hasLaterShowing
Definition: tv_rec.h:142
static bool GetInputInfo(InputInfo &info, vector< uint > *groupids=nullptr)
Definition: cardutil.cpp:1154
static bool QueueRecordingJobs(const RecordingInfo &, int jobTypes=JOB_NONE)
Definition: jobqueue.cpp:500
TuningQueue tuningRequests
Definition: tv_rec.h:396
void SpawnLiveTV(LiveTVChain *newchain, bool pip, QString startchan)
Tells TVRec to spawn a "Live TV" recorder.
Definition: tv_rec.cpp:2691
long long GetFilePosition(void)
Returns total number of bytes written by RingBuffer.
Definition: tv_rec.cpp:2611
ProgramInfo * GetRecording(void)
Allocates and returns a ProgramInfo for the current recording.
Definition: tv_rec.cpp:275
static bool IsRequired(const QString &cardtype)
Returns true iff the card type supports signal monitoring.
void run(void) override
Event handling method, contains event loop.
Definition: tv_rec.cpp:1297
deque< TuningRequest >::iterator iterator
Definition: mythdeque.h:44
uint GetMinorChannel(void) const
Returns minor channel, 0 if unknown.
Definition: dtvchannel.h:99
static int num_inputs(void)
Definition: tv_rec.cpp:1264
static QString GetVideoFilters(uint sourceid, const QString &channum)
Definition: channelutil.h:183
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
T dequeue()
Removes item from front of list and returns a copy. O(1).
Definition: mythdeque.h:32
QString GetSetting(const QString &key, const QString &defaultval="")
void SetDesiredProgram(int p)
static const uint kFlagRunMainLoop
Definition: tv_rec.h:440
void NotifySchedulerOfRecording(RecordingInfo *)
Tell scheduler about the recording.
Definition: tv_rec.cpp:2770
QDateTime startRecordingDeadline
Definition: tv_rec.h:351
QMutex setChannelLock
Definition: tv_rec.h:388
unsigned char t
Definition: ParseText.cpp:340
static bool IsEITCapable(const QString &rawtype)
Definition: cardutil.h:155
bool pauseNotify
Definition: tv_rec.h:394
uint TablePID(uint i) const
Definition: atsctables.h:128
static void GetPreviewImage(const ProgramInfo &pginfo, QString token)
Submit a request for the generation of a preview image.
QString LoadProfile(void *, RecordingInfo *, RecordingProfile &)
Definition: tv_rec.cpp:4144
QString rbFileExt
Definition: tv_rec.h:429
void SetDuration(int duration)
contains a duration during which the notification will be displayed for.
static QString toString(Type, uint id)
Converts "recstatus" into a short (unreadable) string.
Definition: recStatus.cpp:39
QString GetFormat(void)
Definition: dtvchannel.h:47
int overRecordSecCat
Definition: tv_rec.h:372
int SetSignalMonitoringRate(int rate, int notifyFrontend=1)
Sets the signal monitoring rate.
Definition: tv_rec.cpp:2159
#define minor(X)
Definition: compat.h:138
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
void TeardownAll(void)
Definition: tv_rec.cpp:231
QString GetInputType(int pos=-1) const
bool ShouldSwitchToAnotherInput(QString chanid)
Checks if named channel exists on current tuner, or another tuner.
Definition: tv_rec.cpp:2213
QString GetSubtitle(void) const
Definition: programinfo.h:357
bool isActive(void) const
Definition: mythdbcon.h:188
bool SetupSignalMonitor(bool tablemon, bool EITscan, bool notify)
This creates a SignalMonitor instance and begins signal monitoring.
Definition: tv_rec.cpp:2067
bool CheckChannel(QString name) const
Checks if named channel exists on current tuner.
Definition: tv_rec.cpp:2301
bool IsLocal(void) const
Definition: programinfo.h:345
void RecorderPaused(void)
This is a callback, called by the "recorder" instance when it has actually paused.
Definition: tv_rec.cpp:2966
bool ask
Definition: tv_rec.h:144
void GetCachedPids(pid_cache_t &pid_cache) const
Returns cached MPEG PIDs for last tuned channel.
Definition: dtvchannel.cpp:107
virtual bool InitPictureAttributes(void)
Definition: channelbase.h:93
float GetFramerate(void)
Returns recordering frame rate from the recorder.
Definition: tv_rec.cpp:2581
static bool IsVBoxPresent(uint inputid)
uint stateFlags
Definition: tv_rec.h:395
RingBuffer * ringBuffer
Definition: tv_rec.h:428
Acts as glue between ChannelBase, EITSource, and EITHelper.
Definition: eitscanner.h:30
QString GetRecordingGroup(void) const
Definition: programinfo.h:413
QDateTime eitScanStartTime
Definition: tv_rec.h:398
static const uint kSignalMonitoringRate
How many milliseconds the signal monitor should wait between checks.
Definition: tv_rec.h:436
MPEGStreamData * GetStreamData()
Returns the MPEG stream data if it exists.
void PauseRecorder(void)
Tells "recorder" to pause, used for channel and input changes.
Definition: tv_rec.cpp:2947
uint GetRecordingID(void) const
Definition: programinfo.h:440
bool HasChannel(uint major, uint minor) const
virtual bool IsOpen(void) const =0
Returns true if open for either reading or writing.
PictureAttribute
Definition: videoouttypes.h:89
void SendMythSystemRecEvent(const QString &msg, const RecordingInfo *pginfo)
V4LChannel * GetV4LChannel(void)
Definition: tv_rec.cpp:1215
void SetRecordingStatus(RecStatus::Type status)
Definition: programinfo.h:566
static const uint kFlagKillRingBuffer
Definition: tv_rec.h:464
bool RemoteRecordPending(uint inputid, const ProgramInfo *pginfo, int secsleft, bool hasLater)
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:547
static bool IsOnSameMultiplex(uint sourceid, const QString &new_channum, const QString &old_channum)
void SetTuningMode(const QString &tuningmode)
Sets tuning mode: "mpeg", "dvb", "atsc", etc.
Definition: dtvchannel.cpp:97
QString QueryRecordingGroup(void) const
Query recgroup from recorded.
#define TRANSITION(ASTATE, BSTATE)
Definition: tv_rec.cpp:1019
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:391
void CancelNextRecording(bool cancel)
Tells TVRec to cancel the upcoming recording.
Definition: tv_rec.cpp:384
static void RemoveJobsFromMask(int jobs, int &mask)
Definition: jobqueue.h:201
void SetIgnoreCRC(bool haveCRCbug)
void TuningRestartRecorder(void)
Restarts a stopped recorder or unpauses a paused recorder.
Definition: tv_rec.cpp:4376
void UpdateRecordingEnd(void)
Update information in the recorded table when the end-time of a recording is changed.
static bool JobIsNotInMask(int job, int mask)
Definition: jobqueue.h:198
bool CheckChannel(const QString &channum) const
MPEGStreamData * GetStreamData(void) const
Definition: dtvrecorder.h:60
const char * name
Definition: ParseText.cpp:339
virtual void StoreInputChannels(void)
Saves current channel as the default channel for the current input.
Implements tuning for TV cards using the V4L driver API, both versions 1 and 2.
Definition: v4lchannel.h:32
virtual void Unpause(void)
Unpause tells recorder to unpause.
void SetDesiredChannel(int major, int minor)
uint inputid
unique key in DB for this input
Definition: inputinfo.h:76
bool WaitForEventThreadSleep(bool wake=true, ulong time=ULONG_MAX)
You MUST HAVE the stateChange-lock locked when you call this method!
Definition: tv_rec.cpp:1548
void ApplyRecordRecGroupChange(const QString &newrecgroup)
Sets the recording group, both in this RecordingInfo and in the database.
int64_t GetKeyframePosition(uint64_t desired) const
Returns byte position in RingBuffer of a keyframe according to recorder.
Definition: tv_rec.cpp:2627
uint GetOriginalNetworkID(void) const
Returns DVB original_network_id, 0 if unknown.
Definition: dtvchannel.h:103
virtual bool IsErrored(void)=0
Tells us whether an unrecoverable error has been encountered.
virtual void SetRecordingID(uint _recordedid)
Definition: programinfo.h:565
QString GetTuningMode(void) const
Returns tuning mode last set by SetTuningMode().
Definition: dtvchannel.cpp:83
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:101
#define SET_NEXT()
Definition: tv_rec.cpp:1021
void HandleStateChange(void)
Changes the internalState to the desiredNextState if possible.
Definition: tv_rec.cpp:1031
void dispatch(const MythEvent &event)
void WakeEventLoop(void)
Definition: tv_rec.cpp:248
void TeardownSignalMonitor(void)
If a SignalMonitor instance exists, the monitoring thread is stopped and the instance is deleted.
Definition: tv_rec.cpp:2121
TVState GetState(void) const
Returns the TVState of the recorder.
Definition: tv_rec.cpp:261
bool WaitForNextLiveTVDir(void)
Definition: tv_rec.cpp:4540
QDateTime signalEventCmdTimeout
Definition: tv_rec.h:348
void StartPassiveScan(ChannelBase *, EITSource *)
Start inserting Event Information Tables from the multiplex we happen to be tuned to into the databas...
Definition: eitscanner.cpp:205
const MasterGuideTable * GetCachedMGT(bool current=true) const
int elapsed(void) const
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:90
virtual void AddListeningPID(uint pid, PIDPriority priority=kPIDPriorityNormal)
void StopReads(void)
????
Definition: ringbuffer.cpp:746
static int eit_start_rand(int eitTransportTimeout)
Definition: tv_rec.cpp:1284
bool RemoteStopRecording(uint inputid)
None State, this is the initial state in both TV and TVRec, it indicates that we are ready to change ...
Definition: tv.h:58
This class is intended to detect the presence of needed tables.
uint64_t GetFilesize(void) const override
void SetHostPrefix(const QString &prefix)
Definition: livetvchain.cpp:51
PictureAttribute next(PictureAttributeSupported supported, PictureAttribute attribute)
ATSCStreamData * GetATSCStreamData()
Returns the ATSC stream data if it exists.
QWaitCondition triggerEventLoopWait
Definition: tv_rec.h:400
void SetChanID(uint _chanid)
Definition: programinfo.h:509
int GetNumSetting(const QString &key, int defaultval=0)
void SetRecordingType(const QString &recording_type)
void AddFlags(uint64_t _flags) override
QString audiodev
Definition: tv_rec.h:77
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:819
void SetInputID(uint id)
Definition: programinfo.h:527
bool SetupDTVSignalMonitor(bool EITscan)
Tells DTVSignalMonitor what channel to look for.
Definition: tv_rec.cpp:1886
MThread * eventThread
Event processing thread, runs TVRec::run().
Definition: tv_rec.h:360
long long GetWritePosition(void) const
Returns how far into a ThreadedFileWriter file we have written.
void SaveCachedPids(const pid_cache_t &pid_cache) const
Saves MPEG PIDs to cache to database.
Definition: dtvchannel.cpp:117
bool CreateChannel(const QString &startChanNum, bool enter_power_save_mode)
Definition: tv_rec.cpp:130
uint GetFlags(void) const
Definition: tv_rec.h:252
bool triggerEventSleepSignal
Definition: tv_rec.h:404
TVRec(int _inputid)
Performs instance initialiation not requiring access to database.
Definition: tv_rec.cpp:84
virtual bool IsRecording(void)
Tells whether the StartRecorder() loop is running.
QDateTime GetRecordEndTime(const ProgramInfo *) const
Returns recording end time with proper post-roll.
Definition: tv_rec.cpp:371
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void SetCaching(bool cacheTables)
bool reachedRecordingDeadline
Definition: tv_rec.h:354
Abstract class providing a generic interface to tuning hardware.
Definition: channelbase.h:31
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:441
int GetPictureAttribute(PictureAttribute attr)
Definition: tv_rec.cpp:3025
PictureAdjustType
Definition: tv.h:120
virtual uint GetNextChannel(uint chanid, ChannelChangeDirection direction) const
TVState RemovePlaying(TVState state)
Returns TVState that would remove the playing, but potentially keep recording if we are watching an i...
Definition: tv_rec.cpp:802
bool GetBoolSetting(const QString &key, bool defaultval=false)
This table tells the decoder on which PIDs to find other tables, and their sizes and each table's cur...
Definition: atsctables.h:74
void InitAutoRunJobs(RecordingInfo *, AutoRunInitType, RecordingProfile *, int line)
Definition: tv_rec.cpp:2816
void Reset(void) override
Definition: dvbstreamdata.h:35
bool SetVideoFiltersForChannel(uint sourceid, const QString &channum)
Definition: tv_rec.cpp:2482
static const uint kFlagWaitingForRecPause
Definition: tv_rec.h:467
bool HasFlags(uint f) const
Definition: tv_rec.h:294
TVState desiredNextState
Definition: tv_rec.h:392
void FinishedRecording(ProgramInfo *pginfo)
Fetch information on current channel in the past.
Definition: tv.h:43
QString inputtype
Definition: tv_rec.h:78
static void Init()
Initializes the some static constants needed by SignalMonitorValue.
void SetNextLiveTVDir(QString dir)
Definition: tv_rec.cpp:4553
RecordingType GetRecordingRuleType(void) const
Definition: programinfo.h:445
bool SwitchLiveTVRingBuffer(const QString &channum, bool discont, bool set_rec)
Definition: tv_rec.cpp:4706
This is a placeholder state which we never actually enter, but is returned by GetState() when we are ...
Definition: tv.h:89
void SetRecording(const RecordingInfo *pginfo)
Changes the Recording from the one set initially with SetOptionsFromProfile().
QDateTime recordingStart
Definition: tv_rec.h:141
RecStatus::Type GetRecordingStatus(void) const
Definition: tv_rec.cpp:703
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:366
static bool is_dishnet_eit(uint inputid)
Definition: tv_rec.cpp:1244
static const uint kFlagFrontendReady
Definition: tv_rec.h:439
virtual RecordingQuality * GetRecordingQuality(const RecordingInfo *) const
Returns a report about the current recordings quality.
bool IsErrored(void) const
Returns true is "errored" is true, false otherwise.
Definition: tv_rec.h:245
void StartActiveScan(TVRec *, uint max_seconds_per_source)
Definition: eitscanner.cpp:240
void HandlePendingRecordings(void)
Definition: tv_rec.cpp:1585
vector< uint > possibleConflicts
Definition: tv_rec.h:146
void SetRecordingRuleType(RecordingType type)
Definition: programinfo.h:567
QString m_recProfile
virtual void SetFd(int fd)
Sets file descriptor.
Definition: channelbase.h:55
static const uint64_t kDTVSigMon_WaitForSDT
uint GetRecordingRuleID(void) const
Definition: programinfo.h:443
QString toStringXML(void) const
Fetch information on current channel in the future.
Definition: tv.h:44
QString input
Definition: tv_rec.h:126
static const uint kFlagAnyRunning
Definition: tv_rec.h:479
void SetDishNetEIT(bool)
static QString GetStartChannel(uint inputid)
Definition: tv_rec.cpp:1750
GoomState states[STATES_NB]
Definition: goom_core.c:52
void SetRecordingStartTime(const QDateTime &dt)
Definition: programinfo.h:512
virtual void Pause(bool clear=true)
Pause tells recorder to pause, it should not block.
static bool ApplyCachedPids(DTVSignalMonitor *dtvMon, const DTVChannel *channel)
Definition: tv_rec.cpp:1853
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:398
void SetUpdateRate(int msec)
Sets the number of milliseconds between signal monitoring attempts in the signal monitoring thread.
static TVRec * GetTVRec(uint inputid)
Definition: tv_rec.cpp:4851
bool ispip
Definition: tv_rec.h:378
void SetRecordingEndTime(const QDateTime &dt)
Definition: programinfo.h:513
EITScanner * scanner
Definition: tv_rec.h:346
void ApplyRecordRecID(void)
Sets recordid to match RecordingRule recordid.
QDateTime GetRecordingEndTime(void) const
Approximate time the recording should have ended, did end, or is intended to end.
Definition: programinfo.h:406
uint TableType(uint i) const
Definition: atsctables.h:120
RemoteEncoder * recorder
void SetNextRecording(const RecordingInfo *, RingBuffer *)
Sets next recording info, to be applied as soon as practical.
AutoExpireType GetAutoExpire(void) const
Definition: recordingrule.h:62
void TuningShutdowns(const TuningRequest &)
This shuts down anything that needs to be shut down before handling the passed in tuning request.
Definition: tv_rec.cpp:3589
virtual bool SetChannelByString(const QString &chan)=0
uint QueryMplexID(void) const
Queries multiplex any recording would be made on, zero if unknown.
static uint GetSourceID(uint inputid)
Definition: cardutil.cpp:1276
void CheckForRecGroupChange(void)
Check if frontend changed the recording group.
Definition: tv_rec.cpp:2736