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