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.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).ask = false;
286  (*it).doNotAsk = (*it).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.info = new ProgramInfo(*rcinfo);
296  pending.recordingStart = MythDate::current().addSecs(secsleft);
297  pending.hasLaterShowing = hasLater;
298  pending.ask = true;
299  pending.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()].possibleConflicts = inputids;
312 
313  pendlock.unlock();
314  statelock.unlock();
315  for (size_t i = 0; i < inputids.size(); i++)
316  RemoteRecordPending(inputids[i], 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).possibleConflicts;
364  for (size_t i = 0; i < inputids.size(); i++)
365  {
366  LOG(VB_RECORD, LOG_INFO, LOC +
367  QString("CancelNextRecording -- inputid 0x%1")
368  .arg((uint64_t)inputids[i],0,16));
369 
370  pendlock.unlock();
371  RemoteRecordPending(inputids[i], (*it).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).info, -1, false);
380  }
381  else
382  {
383  (*it).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  bool has_pending;
449 
450  m_pendingRecLock.lock();
451  if ((it = m_pendingRecordings.find(m_inputid)) != m_pendingRecordings.end())
452  {
453  (*it).ask = (*it).doNotAsk = false;
454  cancelNext = (*it).canceled;
455  }
456  m_pendingRecLock.unlock();
457 
458  // Flush out events...
460 
461  // Rescan pending recordings since the event loop may have deleted
462  // a stale entry. If this happens the info pointer will not be valid
463  // since the HandlePendingRecordings loop will have deleted it.
464  m_pendingRecLock.lock();
465  it = m_pendingRecordings.find(m_inputid);
466  has_pending = (it != m_pendingRecordings.end());
467  if (has_pending)
468  pendinfo = *it;
469  m_pendingRecLock.unlock();
470 
471  // If the needed input is in a shared input group, and we are
472  // not canceling the recording anyway, check other recorders
473  if (!cancelNext && has_pending && !pendinfo.possibleConflicts.empty())
474  {
475  LOG(VB_RECORD, LOG_INFO, LOC +
476  "Checking input group recorders - begin");
477  vector<uint> &inputids = pendinfo.possibleConflicts;
478 
479  uint mplexid = 0, chanid = 0, sourceid = 0;
480  vector<uint> inputids2;
481  vector<TVState> states;
482 
483  // Stop remote recordings if needed
484  for (size_t i = 0; i < inputids.size(); i++)
485  {
486  InputInfo busy_input;
487  bool is_busy = RemoteIsBusy(inputids[i], busy_input);
488 
489  if (is_busy && !sourceid)
490  {
491  mplexid = pendinfo.info->QueryMplexID();
492  chanid = pendinfo.info->GetChanID();
493  sourceid = pendinfo.info->GetSourceID();
494  }
495 
496  if (is_busy &&
497  ((sourceid != busy_input.m_sourceid) ||
498  (mplexid != busy_input.m_mplexid) ||
499  ((mplexid == 0 || mplexid == 32767) &&
500  chanid != busy_input.m_chanid)))
501  {
502  states.push_back((TVState) RemoteGetState(inputids[i]));
503  inputids2.push_back(inputids[i]);
504  }
505  }
506 
507  bool ok = true;
508  for (uint i = 0; (i < inputids2.size()) && ok; i++)
509  {
510  LOG(VB_RECORD, LOG_INFO, LOC +
511  QString("Attempting to stop input [%1] in state %2")
512  .arg(inputids2[i]).arg(StateToString(states[i])));
513 
514  bool success = RemoteStopRecording(inputids2[i]);
515  if (success)
516  {
517  uint state = RemoteGetState(inputids2[i]);
518  LOG(VB_GENERAL, LOG_INFO, LOC + QString("a [%1]: %2")
519  .arg(inputids2[i]).arg(StateToString((TVState)state)));
520  success = (kState_None == state);
521  }
522 
523  // If we managed to stop LiveTV recording, restart playback..
524  if (success && states[i] == kState_WatchingLiveTV)
525  {
526  QString message = QString("QUIT_LIVETV %1").arg(inputids2[i]);
527  MythEvent me(message);
528  gCoreContext->dispatch(me);
529  }
530 
531  LOG(VB_RECORD, LOG_INFO, LOC +
532  QString("Stopping recording on [%1], %2") .arg(inputids2[i])
533  .arg(success ? "succeeded" : "failed"));
534 
535  ok &= success;
536  }
537 
538  // If we failed to stop the remote recordings, don't record
539  if (!ok)
540  {
541  CancelNextRecording(true);
542  cancelNext = true;
543  }
544 
545  inputids.clear();
546 
547  LOG(VB_RECORD, LOG_INFO, LOC + "Checking input group recorders - done");
548  }
549 
550  bool did_switch = false;
551  if (!cancelNext && (GetState() == kState_RecordingOnly))
552  {
554  did_switch = (nullptr != ri2);
555  if (did_switch)
556  {
557  // Make sure scheduler is allowed to end this recording
558  ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
559 
561  }
562  else
563  {
564  // If in post-roll, end recording
565  m_stateChangeLock.unlock();
566  StopRecording();
567  m_stateChangeLock.lock();
568  }
569  }
570 
571  if (!cancelNext && (GetState() == kState_None))
572  {
573  if (m_tvChain)
574  {
575  QString message = QString("LIVETV_EXITED");
576  MythEvent me(message, m_tvChain->GetID());
577  gCoreContext->dispatch(me);
578  m_tvChain->DecrRef();
579  m_tvChain = nullptr;
580  }
581 
583 
584  // Tell event loop to begin recording.
585  m_curRecording = new RecordingInfo(*rcinfo);
590 
591  // Make sure scheduler is allowed to end this recording
592  ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
593 
596  else
597  LOG(VB_RECORD, LOG_WARNING, LOC + "Still failing.");
599  }
600  else if (!cancelNext && (GetState() == kState_WatchingLiveTV))
601  {
605 
606  // We want the frontend to change channel for recording
607  // and disable the UI for channel change, PiP, etc.
608 
609  QString message = QString("LIVETV_WATCH %1 1").arg(m_inputid);
610  QStringList prog;
611  rcinfo->ToStringList(prog);
612  MythEvent me(message, prog);
613  gCoreContext->dispatch(me);
614  }
615  else if (!did_switch)
616  {
617  msg = QString("Wanted to record: %1 %2 %3 %4\n\t\t\t")
618  .arg(rcinfo->GetTitle()).arg(rcinfo->GetChanID())
620  .arg(rcinfo->GetRecordingEndTime(MythDate::ISODate));
621 
622  if (cancelNext)
623  {
624  msg += "But a user has canceled this recording";
626  }
627  else
628  {
629  msg += QString("But the current state is: %1")
632  }
633 
635  msg += QString("\n\t\t\tCurrently recording: %1 %2 %3 %4")
639 
640  LOG(VB_GENERAL, LOG_INFO, LOC + msg);
641  }
642 
643  for (int i = 0; i < m_pendingRecordings.size(); i++)
644  delete m_pendingRecordings[i].info;
645  m_pendingRecordings.clear();
646 
647  if (!did_switch)
648  {
650 
651  QMutexLocker locker(&m_pendingRecLock);
652  if ((m_curRecording) &&
657  {
658  SetRecordingStatus(RecStatus::Failed, __LINE__, true);
659  }
660  return m_recStatus;
661  }
662 
663  return GetRecordingStatus();
664 }
665 
667 {
668  QMutexLocker pendlock(&m_pendingRecLock);
669  return m_recStatus;
670 }
671 
673  RecStatus::Type new_status, int line, bool have_lock)
674 {
675  RecStatus::Type old_status;
676  if (have_lock)
677  {
678  old_status = m_recStatus;
679  m_recStatus = new_status;
680  }
681  else
682  {
683  m_pendingRecLock.lock();
684  old_status = m_recStatus;
685  m_recStatus = new_status;
686  m_pendingRecLock.unlock();
687  }
688 
689  LOG(VB_RECORD, LOG_INFO, LOC +
690  QString("SetRecordingStatus(%1->%2) on line %3")
691  .arg(RecStatus::toString(old_status, kSingleRecord))
692  .arg(RecStatus::toString(new_status, kSingleRecord))
693  .arg(line));
694 }
695 
702 void TVRec::StopRecording(bool killFile)
703 {
704  if (StateIsRecording(GetState()))
705  {
706  QMutexLocker lock(&m_stateChangeLock);
707  if (killFile)
708  SetFlags(kFlagKillRec, __FILE__, __LINE__);
709  else if (m_curRecording)
710  {
711  QDateTime now = MythDate::current(true);
712  if (now < m_curRecording->GetDesiredEndTime())
714  }
716  // wait for state change to take effect
718  ClearFlags(kFlagCancelNextRecording|kFlagKillRec, __FILE__, __LINE__);
719 
721  }
722 }
723 
730 {
731  return (state == kState_RecordingOnly ||
732  state == kState_WatchingLiveTV);
733 }
734 
740 {
741  return (state == kState_WatchingPreRecorded);
742 }
743 
750 {
751  if (StateIsRecording(state))
752  return kState_None;
753 
754  LOG(VB_GENERAL, LOG_ERR, LOC +
755  QString("Unknown state in RemoveRecording: %1")
756  .arg(StateToString(state)));
757  return kState_Error;
758 }
759 
766 {
767  if (StateIsPlaying(state))
768  {
769  if (state == kState_WatchingPreRecorded)
770  return kState_None;
771  return kState_RecordingOnly;
772  }
773 
774  QString msg = "Unknown state in RemovePlaying: %1";
775  LOG(VB_GENERAL, LOG_ERR, LOC + msg.arg(StateToString(state)));
776 
777  return kState_Error;
778 }
779 
786 {
787  if (!curRec)
788  return;
789 
790  curRec->StartedRecording(m_rbFileExt);
791  LOG(VB_RECORD, LOG_INFO, LOC + QString("StartedRecording(%1) fn(%2)")
792  .arg(curRec->MakeUniqueKey()).arg(curRec->GetPathname()));
793 
794  if (curRec->IsCommercialFree())
796 
797  AutoRunInitType t = (curRec->GetRecordingGroup() == "LiveTV") ?
799  InitAutoRunJobs(curRec, t, nullptr, __LINE__);
800 
801  SendMythSystemRecEvent("REC_STARTED", curRec);
802 }
803 
812 {
813  if (!curRec)
814  return;
815 
816  // Make sure the recording group is up to date
817  const QString recgrp = curRec->QueryRecordingGroup();
818  curRec->SetRecordingGroup(recgrp);
819 
820  bool is_good = true;
821  if (recq)
822  {
823  LOG((recq->IsDamaged()) ? VB_GENERAL : VB_RECORD, LOG_INFO,
824  LOC + QString("FinishedRecording(%1) %2 recq:%3\n")
825  .arg(curRec->MakeUniqueKey())
826  .arg((recq->IsDamaged()) ? "damaged" : "good")
827  .arg(recq->toStringXML()));
828  is_good = !recq->IsDamaged();
829  delete recq;
830  recq = nullptr;
831  }
832 
833  RecStatus::Type ors = curRec->GetRecordingStatus();
834  // Set the final recording status
835  if (curRec->GetRecordingStatus() == RecStatus::Recording)
837  else if (curRec->GetRecordingStatus() != RecStatus::Recorded)
839  curRec->SetRecordingEndTime(MythDate::current(true));
840  is_good &= (curRec->GetRecordingStatus() == RecStatus::Recorded);
841 
842  // Figure out if this was already done for this recording
843  bool was_finished = false;
844  static QMutex finRecLock;
845  static QHash<QString,QDateTime> finRecMap;
846  {
847  QMutexLocker locker(&finRecLock);
848  QDateTime now = MythDate::current();
849  QDateTime expired = now.addSecs(-60*5);
850  QHash<QString,QDateTime>::iterator it = finRecMap.begin();
851  while (it != finRecMap.end())
852  {
853  if ((*it) < expired)
854  it = finRecMap.erase(it);
855  else
856  ++it;
857  }
858  QString key = curRec->MakeUniqueKey();
859  it = finRecMap.find(key);
860  if (it != finRecMap.end())
861  was_finished = true;
862  else
863  finRecMap[key] = now;
864  }
865 
866  // Print something informative to the log
867  LOG(VB_RECORD, LOG_INFO, LOC +
868  QString("FinishedRecording(%1) %2 quality"
869  "\n\t\t\ttitle: %3\n\t\t\t"
870  "in recgroup: %4 status: %5:%6 %7 %8")
871  .arg(curRec->MakeUniqueKey())
872  .arg(is_good ? "Good" : "Bad")
873  .arg(curRec->GetTitle())
874  .arg(recgrp)
877  .arg(HasFlags(kFlagDummyRecorderRunning)?"is_dummy":"not_dummy")
878  .arg(was_finished?"already_finished":"finished_now"));
879 
880  // This has already been called on this recording..
881  if (was_finished)
882  return;
883 
884  // Notify the frontend watching live tv that this file is final
885  if (m_tvChain)
886  m_tvChain->FinishedRecording(curRec);
887 
888  // if this is a dummy recorder, do no more..
890  {
891  curRec->FinishedRecording(true); // so end time is updated
892  SendMythSystemRecEvent("REC_FINISHED", curRec);
893  return;
894  }
895 
896  // Get the width and set the videoprops
897  MarkTypes aspectRatio = curRec->QueryAverageAspectRatio();
898  uint avg_height = curRec->QueryAverageHeight();
899  curRec->SaveVideoProperties(
901  ((avg_height > 1000) ? VID_1080 : ((avg_height > 700) ? VID_720 : 0)) |
902  ((is_good) ? 0 : VID_DAMAGED) |
903  (((aspectRatio == MARK_ASPECT_16_9) ||
904  (aspectRatio == MARK_ASPECT_2_21_1)) ? VID_WIDESCREEN : 0));
905 
906  // Make sure really short recordings have positive run time.
907  if (curRec->GetRecordingEndTime() <= curRec->GetRecordingStartTime())
908  {
909  curRec->SetRecordingEndTime(
910  curRec->GetRecordingStartTime().addSecs(60));
911  }
912 
913  // HACK Temporary hack, ensure we've loaded the recording file info, do it now
914  // so that it contains the final filesize information
915  if (!curRec->GetRecordingFile())
916  curRec->LoadRecordingFile();
917 
918  // Generate a preview
919  uint64_t fsize = curRec->GetFilesize();
920  if (curRec->IsLocal() && (fsize >= 1000) &&
922  {
924  }
925 
926  // store recording in recorded table
927  curRec->FinishedRecording(!is_good || (recgrp == "LiveTV"));
928 
929  // send out UPDATE_RECORDING_STATUS message
930  if (recgrp != "LiveTV")
931  {
932  LOG(VB_RECORD, LOG_INFO, LOC +
933  QString("FinishedRecording -- UPDATE_RECORDING_STATUS: %1")
934  .arg(RecStatus::toString(is_good ? curRec->GetRecordingStatus()
936  MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
937  .arg(curRec->GetInputID())
938  .arg(curRec->GetChanID())
940  .arg(is_good ? curRec->GetRecordingStatus() : RecStatus::Failed)
941  .arg(curRec->GetRecordingEndTime(MythDate::ISODate)));
942  gCoreContext->dispatch(me);
943  }
944 
945  // send out REC_FINISHED message
946  SendMythSystemRecEvent("REC_FINISHED", curRec);
947 
948  // send out DONE_RECORDING message
949  int secsSince = curRec->GetRecordingStartTime()
950  .secsTo(MythDate::current());
951  QString message = QString("DONE_RECORDING %1 %2 %3")
952  .arg(m_inputid).arg(secsSince).arg(GetFramesWritten());
953  MythEvent me(message);
954  gCoreContext->dispatch(me);
955 
956  // Handle JobQueue
957  QHash<QString,int>::iterator autoJob =
958  m_autoRunJobs.find(curRec->MakeUniqueKey());
959  if (autoJob == m_autoRunJobs.end())
960  {
961  LOG(VB_GENERAL, LOG_INFO,
962  "autoRunJobs not initialized until FinishedRecording()");
964  (recgrp == "LiveTV") ? kAutoRunNone : kAutoRunProfile;
965  InitAutoRunJobs(curRec, t, nullptr, __LINE__);
966  autoJob = m_autoRunJobs.find(curRec->MakeUniqueKey());
967  }
968  LOG(VB_JOBQUEUE, LOG_INFO, QString("AutoRunJobs 0x%1").arg(*autoJob,0,16));
969  if ((recgrp == "LiveTV") || (fsize < 1000) ||
970  (curRec->GetRecordingStatus() != RecStatus::Recorded) ||
971  (curRec->GetRecordingStartTime().secsTo(
972  MythDate::current()) < 120))
973  {
976  }
977  if (*autoJob != JOB_NONE)
978  JobQueue::QueueRecordingJobs(*curRec, *autoJob);
979  m_autoRunJobs.erase(autoJob);
980 }
981 
982 #define TRANSITION(ASTATE,BSTATE) \
983  ((m_internalState == (ASTATE)) && (m_desiredNextState == (BSTATE)))
984 #define SET_NEXT() do { nextState = m_desiredNextState; changed = true; } while(false)
985 #define SET_LAST() do { nextState = m_internalState; changed = true; } while(false)
986 
995 {
996  TVState nextState = m_internalState;
997 
998  bool changed = false;
999 
1000  QString transMsg = QString(" %1 to %2")
1001  .arg(StateToString(nextState))
1003 
1005  {
1006  LOG(VB_GENERAL, LOG_ERR, LOC +
1007  "HandleStateChange(): Null transition" + transMsg);
1008  m_changeState = false;
1009  return;
1010  }
1011 
1012  // Make sure EIT scan is stopped before any tuning,
1013  // to avoid race condition with it's tuning requests.
1015  {
1017  ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1020  }
1021 
1022  // Handle different state transitions
1024  {
1026  SET_NEXT();
1027  }
1029  {
1031  SET_NEXT();
1032  }
1034  {
1035  SetPseudoLiveTVRecording(nullptr);
1036 
1037  SET_NEXT();
1038  }
1040  {
1041  SetPseudoLiveTVRecording(nullptr);
1043  SET_NEXT();
1044  }
1046  {
1049  (GetFlags()&kFlagKillRec)));
1050  SET_NEXT();
1051  }
1052 
1053  QString msg = (changed) ? "Changing from" : "Unknown state transition:";
1054  LOG(VB_GENERAL, LOG_INFO, LOC + msg + transMsg);
1055 
1056  // update internal state variable
1057  m_internalState = nextState;
1058  m_changeState = false;
1059 
1062  {
1065  }
1066  else
1067  {
1068  m_eitScanStartTime = m_eitScanStartTime.addYears(1);
1069  }
1070 }
1071 #undef TRANSITION
1072 #undef SET_NEXT
1073 #undef SET_LAST
1074 
1079 {
1080  QMutexLocker lock(&m_stateChangeLock);
1081  m_desiredNextState = nextState;
1082  m_changeState = true;
1083  WakeEventLoop();
1084 }
1085 
1100 void TVRec::TeardownRecorder(uint request_flags)
1101 {
1102  LOG(VB_RECORD, LOG_INFO, LOC + QString("TeardownRecorder(%1)")
1103  .arg((request_flags & kFlagKillRec) ? "kFlagKillRec" : ""));
1104 
1105  m_pauseNotify = false;
1106  m_ispip = false;
1107 
1109  {
1112  delete m_recorderThread;
1113  m_recorderThread = nullptr;
1114  }
1116  __FILE__, __LINE__);
1117 
1118  RecordingQuality *recq = nullptr;
1119  if (m_recorder)
1120  {
1121  if (GetV4LChannel())
1122  m_channel->SetFd(-1);
1123 
1125 
1126  QMutexLocker locker(&m_stateChangeLock);
1127  delete m_recorder;
1128  m_recorder = nullptr;
1129  }
1130 
1131  if (m_ringBuffer)
1132  {
1133  LOG(VB_FILE, LOG_INFO, LOC + "calling StopReads()");
1135  }
1136 
1137  if (m_curRecording)
1138  {
1139  if (!!(request_flags & kFlagKillRec))
1141 
1143 
1145  delete m_curRecording;
1146  m_curRecording = nullptr;
1147  }
1148 
1149  m_pauseNotify = true;
1150 
1151  if (GetDTVChannel())
1153 }
1154 
1156 {
1157  return dynamic_cast<DTVRecorder*>(m_recorder);
1158 }
1159 
1161 {
1162  if (m_channel &&
1163  ((m_genOpt.inputtype == "DVB" && m_dvbOpt.dvb_on_demand) ||
1164  m_genOpt.inputtype == "FREEBOX" ||
1165  m_genOpt.inputtype == "VBOX" ||
1166  m_genOpt.inputtype == "HDHOMERUN" ||
1168  {
1169  m_channel->Close();
1170  }
1171 }
1172 
1174 {
1175  return dynamic_cast<DTVChannel*>(m_channel);
1176 }
1177 
1179 {
1180 #ifdef USING_V4L2
1181  return dynamic_cast<V4LChannel*>(m_channel);
1182 #else
1183  return nullptr;
1184 #endif // USING_V4L2
1185 }
1186 
1187 static bool get_use_eit(uint inputid)
1188 {
1189  MSqlQuery query(MSqlQuery::InitCon());
1190  query.prepare(
1191  "SELECT SUM(useeit) "
1192  "FROM videosource, capturecard "
1193  "WHERE videosource.sourceid = capturecard.sourceid AND"
1194  " capturecard.cardid = :INPUTID");
1195  query.bindValue(":INPUTID", inputid);
1196 
1197  if (!query.exec() || !query.isActive())
1198  {
1199  MythDB::DBError("get_use_eit", query);
1200  return false;
1201  }
1202  if (query.next())
1203  return query.value(0).toBool();
1204  return false;
1205 }
1206 
1207 static bool is_dishnet_eit(uint inputid)
1208 {
1209  MSqlQuery query(MSqlQuery::InitCon());
1210  query.prepare(
1211  "SELECT SUM(dishnet_eit) "
1212  "FROM videosource, capturecard "
1213  "WHERE videosource.sourceid = capturecard.sourceid AND"
1214  " capturecard.cardid = :INPUTID");
1215  query.bindValue(":INPUTID", inputid);
1216 
1217  if (!query.exec() || !query.isActive())
1218  {
1219  MythDB::DBError("is_dishnet_eit", query);
1220  return false;
1221  }
1222  if (query.next())
1223  return query.value(0).toBool();
1224  return false;
1225 }
1226 
1227 static int num_inputs(void)
1228 {
1229  MSqlQuery query(MSqlQuery::InitCon());
1230 
1231  QString str =
1232  "SELECT COUNT(cardid) "
1233  "FROM capturecard ";
1234 
1235  query.prepare(str);
1236 
1237  if (!query.exec() || !query.isActive())
1238  {
1239  MythDB::DBError("num_inputs", query);
1240  return -1;
1241  }
1242  if (query.next())
1243  return query.value(0).toInt();
1244  return -1;
1245 }
1246 
1247 static int eit_start_rand(int eitTransportTimeout)
1248 {
1249  // randomize start time a bit
1250  int timeout = random() % (eitTransportTimeout / 3);
1251  // get the number of inputs and the position of the current input
1252  // to distribute the the scan start evenly over eitTransportTimeout
1253  int no_inputs = num_inputs();
1254  if (no_inputs > 0)
1255  timeout += eitTransportTimeout / no_inputs;
1256  return timeout;
1257 }
1258 
1260 void TVRec::run(void)
1261 {
1262  QMutexLocker lock(&m_stateChangeLock);
1263  SetFlags(kFlagRunMainLoop, __FILE__, __LINE__);
1264  ClearFlags(kFlagExitPlayer | kFlagFinishRecording, __FILE__, __LINE__);
1265 
1267  // check whether we should use the EITScanner in this TVRec instance
1269  (!GetDTVChannel() || GetDTVChannel()->IsMaster()) &&
1271  {
1275  }
1276  else
1277  {
1278  m_eitScanStartTime = m_eitScanStartTime.addYears(1);
1279  }
1280 
1281  while (HasFlags(kFlagRunMainLoop))
1282  {
1283  // If there is a state change queued up, do it...
1284  if (m_changeState)
1285  {
1288  __FILE__, __LINE__);
1289  }
1290 
1291  // Quick exit on fatal errors.
1292  if (IsErrored())
1293  {
1294  LOG(VB_GENERAL, LOG_ERR, LOC +
1295  "RunTV encountered fatal error, exiting event thread.");
1296  ClearFlags(kFlagRunMainLoop, __FILE__, __LINE__);
1297  TeardownAll();
1298  return;
1299  }
1300 
1301  // Handle any tuning events.. Blindly grabbing the lock here
1302  // can sometimes cause a deadlock with Init() while it waits
1303  // to make sure this thread starts. Until a better solution
1304  // is found, don't run HandleTuning unless we can safely get
1305  // the lock.
1306  if (s_inputsLock.tryLockForRead())
1307  {
1308  HandleTuning();
1309  s_inputsLock.unlock();
1310  }
1311 
1312  // Tell frontends about pending recordings
1314 
1315  // If we are recording a program, check if the recording is
1316  // over or someone has asked us to finish the recording.
1317  // Add an extra 60 seconds to the recording end time if we
1318  // might want a back to back recording.
1319  QDateTime recEnd = (!m_pendingRecordings.empty()) ?
1320  m_recordEndTime.addSecs(60) : m_recordEndTime;
1321  if ((GetState() == kState_RecordingOnly) &&
1322  (MythDate::current() > recEnd ||
1324  {
1326  ClearFlags(kFlagFinishRecording, __FILE__, __LINE__);
1327  }
1328 
1329  if (m_curRecording)
1330  {
1332 
1333  if (m_recorder)
1334  {
1336 
1337  // Check for recorder errors
1338  if (m_recorder->IsErrored())
1339  {
1341 
1343  {
1344  QString message = QString("QUIT_LIVETV %1").arg(m_inputid);
1345  MythEvent me(message);
1346  gCoreContext->dispatch(me);
1347  }
1348  else
1350  }
1351  }
1352  }
1353 
1354  // Check for the end of the current program..
1356  {
1357  QDateTime now = MythDate::current();
1358  bool has_finish = HasFlags(kFlagFinishRecording);
1359  bool has_rec = m_pseudoLiveTVRecording;
1360  bool enable_ui = true;
1361 
1362  m_pendingRecLock.lock();
1363  bool rec_soon =
1365  m_pendingRecLock.unlock();
1366 
1367  if (has_rec && (has_finish || (now > m_recordEndTime)))
1368  {
1369  SetPseudoLiveTVRecording(nullptr);
1370  }
1371  else if (!has_rec && !rec_soon && m_curRecording &&
1372  (now >= m_curRecording->GetScheduledEndTime()))
1373  {
1374  if (!m_switchingBuffer)
1375  {
1376  LOG(VB_RECORD, LOG_INFO, LOC +
1377  "Switching Buffer (" +
1378  QString("!has_rec(%1) && ").arg(has_rec) +
1379  QString("!rec_soon(%1) && (").arg(rec_soon) +
1380  MythDate::toString(now, MythDate::ISODate) + " >= " +
1382  QString("(%1) ))")
1383  .arg(now >= m_curRecording->GetScheduledEndTime()));
1384 
1385  m_switchingBuffer = true;
1386 
1388  false, true);
1389  }
1390  else
1391  {
1392  LOG(VB_RECORD, LOG_INFO, "Waiting for ringbuffer switch");
1393  }
1394  }
1395  else
1396  enable_ui = false;
1397 
1398  if (enable_ui)
1399  {
1400  LOG(VB_RECORD, LOG_INFO, LOC + "Enabling Full LiveTV UI.");
1401  QString message = QString("LIVETV_WATCH %1 0").arg(m_inputid);
1402  MythEvent me(message);
1403  gCoreContext->dispatch(me);
1404  }
1405  }
1406 
1407  // Check for ExitPlayer flag, and if set change to a non-watching
1408  // state (either kState_RecordingOnly or kState_None).
1410  {
1413  else if (StateIsPlaying(m_internalState))
1415  ClearFlags(kFlagExitPlayer, __FILE__, __LINE__);
1416  }
1417 
1418  if (m_scanner && m_channel &&
1420  {
1421  if (!m_dvbOpt.dvb_eitscan)
1422  {
1423  LOG(VB_EIT, LOG_INFO, LOC +
1424  "EIT scanning disabled for this input.");
1425  m_eitScanStartTime = m_eitScanStartTime.addYears(1);
1426  }
1427  else if (!get_use_eit(GetInputId()))
1428  {
1429  LOG(VB_EIT, LOG_INFO, LOC +
1430  "EIT scanning disabled for all sources on this input.");
1431  m_eitScanStartTime = m_eitScanStartTime.addYears(1);
1432  }
1433  else
1434  {
1435  // Check if another card in the same input group is
1436  // busy. This could be either virtual DVB-devices or
1437  // a second tuner on a single card
1438  s_inputsLock.lockForRead();
1439  bool allow_eit = true;
1440  vector<uint> inputids =
1442  InputInfo busy_input;
1443  for (uint i = 0; i < inputids.size() && allow_eit; ++i)
1444  allow_eit = !RemoteIsBusy(inputids[i], busy_input);
1445  if (allow_eit)
1446  {
1448  SetFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
1450  QDateTime::currentDateTime().addYears(1);
1451  }
1452  else
1453  {
1454  LOG(VB_CHANNEL, LOG_INFO, LOC + QString(
1455  "Postponing EIT scan on input [%1] "
1456  "because input %2 is busy")
1457  .arg(m_inputid).arg(busy_input.m_inputid));
1458  m_eitScanStartTime = m_eitScanStartTime.addSecs(300);
1459  }
1460  s_inputsLock.unlock();
1461  }
1462  }
1463 
1464  // We should be no more than a few thousand milliseconds,
1465  // as the end recording code does not have a trigger...
1466  // NOTE: If you change anything here, make sure that
1467  // WaitforEventThreadSleep() will still work...
1468  if (m_tuningRequests.empty() && !m_changeState)
1469  {
1470  lock.unlock(); // stateChangeLock
1471 
1472  {
1473  QMutexLocker locker(&m_triggerEventSleepLock);
1475  m_triggerEventSleepWait.wakeAll();
1476  }
1477 
1478  sched_yield();
1479 
1480  {
1481  QMutexLocker locker(&m_triggerEventLoopLock);
1482  // We check triggerEventLoopSignal because it is possible
1483  // that WakeEventLoop() was called since we
1484  // unlocked the stateChangeLock
1486  {
1488  &m_triggerEventLoopLock, 1000 /* ms */);
1489  }
1490  m_triggerEventLoopSignal = false;
1491  }
1492 
1493  lock.relock(); // stateChangeLock
1494  }
1495  }
1496 
1497  if (GetState() != kState_None)
1498  {
1501  }
1502 
1503  TeardownAll();
1504 }
1505 
1511 bool TVRec::WaitForEventThreadSleep(bool wake, ulong time)
1512 {
1513  bool ok = false;
1514  MythTimer t;
1515  t.start();
1516 
1517  while (!ok && ((unsigned long) t.elapsed()) < time)
1518  {
1519  MythTimer t2;
1520  t2.start();
1521 
1522  if (wake)
1523  WakeEventLoop();
1524 
1525  m_stateChangeLock.unlock();
1526 
1527  sched_yield();
1528 
1529  {
1530  QMutexLocker locker(&m_triggerEventSleepLock);
1533  m_triggerEventSleepSignal = false;
1534  }
1535 
1536  m_stateChangeLock.lock();
1537 
1538  // verify that we were triggered.
1539  ok = (m_tuningRequests.empty() && !m_changeState);
1540 
1541  int te = t2.elapsed();
1542  if (!ok && te < 10)
1543  std::this_thread::sleep_for(std::chrono::microseconds(10-te));
1544  }
1545  return ok;
1546 }
1547 
1549 {
1550  QMutexLocker pendlock(&m_pendingRecLock);
1551 
1552  PendingMap::iterator it, next;
1553 
1554  for (it = m_pendingRecordings.begin(); it != m_pendingRecordings.end();)
1555  {
1556  next = it; ++next;
1557  if (MythDate::current() > (*it).recordingStart.addSecs(30))
1558  {
1559  LOG(VB_RECORD, LOG_INFO, LOC + "Deleting stale pending recording " +
1560  QString("[%1] '%2'")
1561  .arg((*it).info->GetInputID())
1562  .arg((*it).info->GetTitle()));
1563 
1564  delete (*it).info;
1565  m_pendingRecordings.erase(it);
1566  }
1567  it = next;
1568  }
1569 
1570  if (m_pendingRecordings.empty())
1571  return;
1572 
1573  // Make sure EIT scan is stopped so it does't interfere
1575  {
1576  LOG(VB_CHANNEL, LOG_INFO,
1577  LOC + "Stopping active EIT scan for pending recording.");
1579  }
1580 
1581  // If we have a pending recording and AskAllowRecording
1582  // or DoNotAskAllowRecording is set and the frontend is
1583  // ready send an ASK_RECORDING query to frontend.
1584 
1585  bool has_rec = false;
1586  it = m_pendingRecordings.begin();
1587  if ((1 == m_pendingRecordings.size()) &&
1588  (*it).ask &&
1589  ((*it).info->GetInputID() == m_inputid) &&
1591  {
1593  has_rec = m_pseudoLiveTVRecording &&
1595  (*it).recordingStart);
1596  }
1597 
1598  for (it = m_pendingRecordings.begin(); it != m_pendingRecordings.end(); ++it)
1599  {
1600  if (!(*it).ask && !(*it).doNotAsk)
1601  continue;
1602 
1603  int timeuntil = ((*it).doNotAsk) ?
1604  -1: MythDate::current().secsTo((*it).recordingStart);
1605 
1606  if (has_rec)
1607  (*it).canceled = true;
1608 
1609  QString query = QString("ASK_RECORDING %1 %2 %3 %4")
1610  .arg(m_inputid)
1611  .arg(timeuntil)
1612  .arg(has_rec ? 1 : 0)
1613  .arg((*it).hasLaterShowing ? 1 : 0);
1614 
1615  LOG(VB_GENERAL, LOG_INFO, LOC + query);
1616 
1617  QStringList msg;
1618  (*it).info->ToStringList(msg);
1619  MythEvent me(query, msg);
1620  gCoreContext->dispatch(me);
1621 
1622  (*it).ask = (*it).doNotAsk = false;
1623  }
1624 }
1625 
1627  uint &parentid,
1628  GeneralDBOptions &gen_opts,
1629  DVBDBOptions &dvb_opts,
1630  FireWireDBOptions &firewire_opts)
1631 {
1632  int testnum = 0;
1633  QString test;
1634 
1635  MSqlQuery query(MSqlQuery::InitCon());
1636  query.prepare(
1637  "SELECT videodevice, vbidevice, audiodevice, "
1638  " audioratelimit, cardtype, "
1639  " skipbtaudio, signal_timeout, channel_timeout, "
1640  " dvb_wait_for_seqstart, "
1641  ""
1642  " dvb_on_demand, dvb_tuning_delay, dvb_eitscan,"
1643  ""
1644  " firewire_speed, firewire_model, firewire_connection, "
1645  " parentid "
1646  ""
1647  "FROM capturecard "
1648  "WHERE cardid = :INPUTID");
1649  query.bindValue(":INPUTID", inputid);
1650 
1651  if (!query.exec() || !query.isActive())
1652  {
1653  MythDB::DBError("getdevices", query);
1654  return false;
1655  }
1656 
1657  if (!query.next())
1658  return false;
1659 
1660  // General options
1661  test = query.value(0).toString();
1662  if (!test.isEmpty())
1663  gen_opts.videodev = test;
1664 
1665  test = query.value(1).toString();
1666  if (!test.isEmpty())
1667  gen_opts.vbidev = test;
1668 
1669  test = query.value(2).toString();
1670  if (!test.isEmpty())
1671  gen_opts.audiodev = test;
1672 
1673  gen_opts.audiosamplerate = max(testnum, query.value(3).toInt());
1674 
1675  test = query.value(4).toString();
1676  if (!test.isEmpty())
1677  gen_opts.inputtype = test;
1678 
1679  gen_opts.skip_btaudio = query.value(5).toBool();
1680 
1681  gen_opts.signal_timeout = (uint) max(query.value(6).toInt(), 0);
1682  gen_opts.channel_timeout = (uint) max(query.value(7).toInt(), 0);
1683 
1684  // We should have at least 100 ms to acquire tables...
1685  int table_timeout = ((int)gen_opts.channel_timeout -
1686  (int)gen_opts.signal_timeout);
1687  if (table_timeout < 100)
1688  gen_opts.channel_timeout = gen_opts.signal_timeout + 2500;
1689 
1690  gen_opts.wait_for_seqstart = query.value(8).toBool();
1691 
1692  // DVB options
1693  uint dvboff = 9;
1694  dvb_opts.dvb_on_demand = query.value(dvboff + 0).toBool();
1695  dvb_opts.dvb_tuning_delay = query.value(dvboff + 1).toUInt();
1696  dvb_opts.dvb_eitscan = query.value(dvboff + 2).toBool();
1697 
1698  // Firewire options
1699  uint fireoff = dvboff + 3;
1700  firewire_opts.speed = query.value(fireoff + 0).toUInt();
1701 
1702  test = query.value(fireoff + 1).toString();
1703  if (!test.isEmpty())
1704  firewire_opts.model = test;
1705 
1706  firewire_opts.connection = query.value(fireoff + 2).toUInt();
1707 
1708  parentid = query.value(15).toUInt();
1709 
1710  return true;
1711 }
1712 
1714 {
1715  QString startchan;
1716 
1717  LOG(VB_RECORD, LOG_INFO, LOC2 + QString("GetStartChannel[%1]")
1718  .arg(inputid));
1719 
1720  // Get last tuned channel from database, to use as starting channel
1721  MSqlQuery query(MSqlQuery::InitCon());
1722  query.prepare(
1723  "SELECT startchan "
1724  "FROM capturecard "
1725  "WHERE capturecard.cardid = :INPUTID");
1726  query.bindValue(":INPUTID", inputid);
1727 
1728  if (!query.exec() || !query.isActive())
1729  {
1730  MythDB::DBError("getstartchan", query);
1731  }
1732  else if (query.next())
1733  {
1734  startchan = query.value(0).toString();
1735  if (!startchan.isEmpty())
1736  {
1737  LOG(VB_CHANNEL, LOG_INFO, LOC2 + QString("Start channel: %1")
1738  .arg(startchan));
1739  return startchan;
1740  }
1741  }
1742 
1743  // If we failed to get the last tuned channel,
1744  // get a valid channel on our current input.
1745  query.prepare(
1746  "SELECT channum "
1747  "FROM capturecard, channel "
1748  "WHERE channel.sourceid = capturecard.sourceid AND "
1749  " capturecard.cardid = :INPUTID");
1750  query.bindValue(":INPUTID", inputid);
1751 
1752  if (!query.exec() || !query.isActive())
1753  {
1754  MythDB::DBError("getstartchan2", query);
1755  }
1756  while (query.next())
1757  {
1758  startchan = query.value(0).toString();
1759  if (!startchan.isEmpty())
1760  {
1761  LOG(VB_GENERAL, LOG_ERR, LOC2 + QString("Start channel from DB is "
1762  "empty, setting to '%1' instead.").arg(startchan));
1763  return startchan;
1764  }
1765  }
1766 
1767  // If we failed to get a channel on our current input,
1768  // widen search to any input.
1769  query.prepare(
1770  "SELECT channum, inputname "
1771  "FROM capturecard, channel "
1772  "WHERE channel.sourceid = capturecard.sourceid AND "
1773  " capturecard.cardid = :INPUTID");
1774  query.bindValue(":INPUTID", inputid);
1775 
1776  if (!query.exec() || !query.isActive())
1777  {
1778  MythDB::DBError("getstartchan3", query);
1779  }
1780  while (query.next())
1781  {
1782  startchan = query.value(0).toString();
1783  if (!startchan.isEmpty())
1784  {
1785  LOG(VB_GENERAL, LOG_ERR, LOC2 + QString("Start channel invalid, "
1786  "setting to '%1' on input %2 instead.").arg(startchan)
1787  .arg(query.value(1).toString()));
1788  return startchan;
1789  }
1790  }
1791 
1792  // If there are no valid channels, just use a random channel
1793  startchan = "3";
1794  LOG(VB_GENERAL, LOG_ERR, LOC2 + QString("Problem finding starting channel, "
1795  "setting to default of '%1'.").arg(startchan));
1796  return startchan;
1797 }
1798 
1799 static void GetPidsToCache(DTVSignalMonitor *dtvMon, pid_cache_t &pid_cache)
1800 {
1801  if (!dtvMon->GetATSCStreamData())
1802  return;
1803 
1804  const MasterGuideTable *mgt = dtvMon->GetATSCStreamData()->GetCachedMGT();
1805  if (!mgt)
1806  return;
1807 
1808  for (uint i = 0; i < mgt->TableCount(); ++i)
1809  {
1810  pid_cache_item_t item(mgt->TablePID(i), mgt->TableType(i));
1811  pid_cache.push_back(item);
1812  }
1813  dtvMon->GetATSCStreamData()->ReturnCachedTable(mgt);
1814 }
1815 
1816 static bool ApplyCachedPids(DTVSignalMonitor *dtvMon, const DTVChannel* channel)
1817 {
1818  pid_cache_t pid_cache;
1819  channel->GetCachedPids(pid_cache);
1820  pid_cache_t::const_iterator it = pid_cache.begin();
1821  bool vctpid_cached = false;
1822  for (; it != pid_cache.end(); ++it)
1823  {
1824  if ((it->GetTableID() == TableID::TVCT) ||
1825  (it->GetTableID() == TableID::CVCT))
1826  {
1827  vctpid_cached = true;
1828  dtvMon->GetATSCStreamData()->AddListeningPID(it->GetPID());
1829  }
1830  }
1831  return vctpid_cached;
1832 }
1833 
1850 {
1851  LOG(VB_RECORD, LOG_INFO, LOC + "Setting up table monitoring.");
1852 
1854  DTVChannel *dtvchan = GetDTVChannel();
1855  if (!sm || !dtvchan)
1856  {
1857  LOG(VB_GENERAL, LOG_ERR, LOC + "Setting up table monitoring.");
1858  return false;
1859  }
1860 
1861  MPEGStreamData *sd = nullptr;
1862  if (GetDTVRecorder())
1863  {
1864  sd = GetDTVRecorder()->GetStreamData();
1865  sd->SetCaching(true);
1866  }
1867 
1868  QString recording_type = "all";
1872  const StandardSetting *setting = profile.byName("recordingtype");
1873  if (setting)
1874  recording_type = setting->getValue();
1875 
1876  const QString tuningmode = dtvchan->GetTuningMode();
1877 
1878  // Check if this is an ATSC Channel
1879  int major = dtvchan->GetMajorChannel();
1880  int minor = dtvchan->GetMinorChannel();
1881  if ((minor > 0) && (tuningmode == "atsc"))
1882  {
1883  QString msg = QString("ATSC channel: %1_%2").arg(major).arg(minor);
1884  LOG(VB_RECORD, LOG_INFO, LOC + msg);
1885 
1886  ATSCStreamData *asd = dynamic_cast<ATSCStreamData*>(sd);
1887  if (!asd)
1888  {
1889  sd = asd = new ATSCStreamData(major, minor, m_inputid);
1890  sd->SetCaching(true);
1891  if (GetDTVRecorder())
1892  GetDTVRecorder()->SetStreamData(asd);
1893  }
1894 
1895  asd->Reset();
1896  sm->SetStreamData(sd);
1897  sm->SetChannel(major, minor);
1898  sd->SetRecordingType(recording_type);
1899 
1900  // Try to get pid of VCT from cache and
1901  // require MGT if we don't have VCT pid.
1902  if (!ApplyCachedPids(sm, dtvchan))
1904 
1905  LOG(VB_RECORD, LOG_INFO, LOC +
1906  "Successfully set up ATSC table monitoring.");
1907  return true;
1908  }
1909 
1910  // Check if this is an DVB channel
1911  int progNum = dtvchan->GetProgramNumber();
1912  if ((progNum >= 0) && (tuningmode == "dvb") && (m_genOpt.inputtype != "VBOX"))
1913  {
1914  int netid = dtvchan->GetOriginalNetworkID();
1915  int tsid = dtvchan->GetTransportID();
1916 
1917  DVBStreamData *dsd = dynamic_cast<DVBStreamData*>(sd);
1918  if (!dsd)
1919  {
1920  sd = dsd = new DVBStreamData(netid, tsid, progNum, m_inputid);
1921  sd->SetCaching(true);
1922  if (GetDTVRecorder())
1923  GetDTVRecorder()->SetStreamData(dsd);
1924  }
1925 
1926  LOG(VB_RECORD, LOG_INFO, LOC +
1927  QString("DVB service_id %1 on net_id %2 tsid %3")
1928  .arg(progNum).arg(netid).arg(tsid));
1929 
1931 
1932  dsd->Reset();
1933  sm->SetStreamData(sd);
1934  sm->SetDVBService(netid, tsid, progNum);
1935  sd->SetRecordingType(recording_type);
1936 
1940  sm->SetRotorTarget(1.0F);
1941 
1942  if (EITscan)
1943  {
1945  sm->IgnoreEncrypted(true);
1946  }
1947 
1948  LOG(VB_RECORD, LOG_INFO, LOC +
1949  "Successfully set up DVB table monitoring.");
1950  return true;
1951  }
1952 
1953  // Check if this is an MPEG channel
1954  if (progNum >= 0)
1955  {
1956  if (!sd)
1957  {
1958  sd = new MPEGStreamData(progNum, m_inputid, true);
1959  sd->SetCaching(true);
1960  if (GetDTVRecorder())
1962  }
1963 
1964  QString msg = QString("MPEG program number: %1").arg(progNum);
1965  LOG(VB_RECORD, LOG_INFO, LOC + msg);
1966 
1968 
1969  sd->Reset();
1970  sm->SetStreamData(sd);
1971  sm->SetProgramNumber(progNum);
1972  sd->SetRecordingType(recording_type);
1973 
1977  sm->SetRotorTarget(1.0F);
1978 
1979  if (EITscan)
1980  {
1982  sm->IgnoreEncrypted(true);
1983  }
1984 
1985  LOG(VB_RECORD, LOG_INFO, LOC +
1986  "Successfully set up MPEG table monitoring.");
1987  return true;
1988  }
1989 
1990  // If this is not an ATSC, DVB or MPEG channel then check to make sure
1991  // that we have permanent pidcache entries.
1992  bool ok = false;
1993  if (GetDTVChannel())
1994  {
1995  pid_cache_t pid_cache;
1996  GetDTVChannel()->GetCachedPids(pid_cache);
1997  pid_cache_t::const_iterator it = pid_cache.begin();
1998  for (; !ok && it != pid_cache.end(); ++it)
1999  ok |= it->IsPermanent();
2000  }
2001 
2002  if (!ok)
2003  {
2004  QString msg = "No valid DTV info, ATSC maj(%1) min(%2), MPEG pn(%3)";
2005  LOG(VB_GENERAL, LOG_ERR, LOC + msg.arg(major).arg(minor).arg(progNum));
2006  }
2007  else
2008  {
2009  LOG(VB_RECORD, LOG_INFO, LOC +
2010  "Successfully set up raw pid monitoring.");
2011  }
2012 
2013  return ok;
2014 }
2015 
2030 bool TVRec::SetupSignalMonitor(bool tablemon, bool EITscan, bool notify)
2031 {
2032  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetupSignalMonitor(%1, %2)")
2033  .arg(tablemon).arg(notify));
2034 
2035  // if it already exists, there no need to initialize it
2036  if (m_signalMonitor)
2037  return true;
2038 
2039  // if there is no channel object we can't monitor it
2040  if (!m_channel)
2041  return false;
2042 
2043  // nothing to monitor here either (DummyChannel)
2044  if (m_genOpt.inputtype == "IMPORT" || m_genOpt.inputtype == "DEMO")
2045  return true;
2046 
2047  // make sure statics are initialized
2049 
2052  m_channel, false);
2053 
2054  if (m_signalMonitor)
2055  {
2056  LOG(VB_RECORD, LOG_INFO, LOC + "Signal monitor successfully created");
2057  // If this is a monitor for Digital TV, initialize table monitors
2058  if (GetDTVSignalMonitor() && tablemon &&
2059  !SetupDTVSignalMonitor(EITscan))
2060  {
2061  LOG(VB_GENERAL, LOG_ERR, LOC +
2062  "Failed to setup digital signal monitoring");
2063 
2064  return false;
2065  }
2066 
2069  kSignalMonitoringRate * 5 :
2072 
2073  // Start the monitoring thread
2075  }
2076 
2077  return true;
2078 }
2079 
2085 {
2086  if (!m_signalMonitor)
2087  return;
2088 
2089  LOG(VB_RECORD, LOG_INFO, LOC + "TeardownSignalMonitor() -- begin");
2090 
2091  // If this is a DTV signal monitor, save any pids we know about.
2093  DTVChannel *dtvChan = GetDTVChannel();
2094  if (dtvMon && dtvChan)
2095  {
2096  pid_cache_t pid_cache;
2097  GetPidsToCache(dtvMon, pid_cache);
2098  if (!pid_cache.empty())
2099  dtvChan->SaveCachedPids(pid_cache);
2100  }
2101 
2102  if (m_signalMonitor)
2103  {
2104  delete m_signalMonitor;
2105  m_signalMonitor = nullptr;
2106  }
2107 
2108  LOG(VB_RECORD, LOG_INFO, LOC + "TeardownSignalMonitor() -- end");
2109 }
2110 
2122 int TVRec::SetSignalMonitoringRate(int rate, int notifyFrontend)
2123 {
2124  QString msg = "SetSignalMonitoringRate(%1, %2)";
2125  LOG(VB_RECORD, LOG_INFO, LOC +
2126  msg.arg(rate).arg(notifyFrontend) + "-- start");
2127 
2128  QMutexLocker lock(&m_stateChangeLock);
2129 
2131  {
2132  LOG(VB_GENERAL, LOG_ERR, LOC +
2133  "Signal Monitoring is notsupported by your hardware.");
2134  return 0;
2135  }
2136 
2138  {
2139  LOG(VB_GENERAL, LOG_ERR, LOC +
2140  "Signal can only be monitored in LiveTV Mode.");
2141  return 0;
2142  }
2143 
2144  ClearFlags(kFlagRingBufferReady, __FILE__, __LINE__);
2145 
2146  TuningRequest req = (rate > 0) ?
2149 
2151 
2152  // Wait for RingBuffer reset
2153  while (!HasFlags(kFlagRingBufferReady))
2155  LOG(VB_RECORD, LOG_INFO, LOC +
2156  msg.arg(rate).arg(notifyFrontend) + " -- end");
2157  return 1;
2158 }
2159 
2161 {
2162  return dynamic_cast<DTVSignalMonitor*>(m_signalMonitor);
2163 }
2164 
2176 bool TVRec::ShouldSwitchToAnotherInput(const QString& chanid)
2177 {
2178  QString msg("");
2179  MSqlQuery query(MSqlQuery::InitCon());
2180 
2181  if (!query.isConnected())
2182  return false;
2183 
2184  query.prepare("SELECT channel.channum, channel.callsign "
2185  "FROM channel "
2186  "WHERE channel.chanid = :CHANID");
2187  query.bindValue(":CHANID", chanid);
2188  if (!query.exec() || !query.next())
2189  {
2190  MythDB::DBError("ShouldSwitchToAnotherInput", query);
2191  return false;
2192  }
2193 
2194  QString channelname = query.value(0).toString();
2195  QString callsign = query.value(1).toString();
2196 
2197  query.prepare(
2198  "SELECT channel.channum "
2199  "FROM channel, capturecard "
2200  "WHERE ( channel.chanid = :CHANID OR "
2201  " ( channel.channum = :CHANNUM AND "
2202  " channel.callsign = :CALLSIGN ) "
2203  " ) AND "
2204  " channel.sourceid = capturecard.sourceid AND "
2205  " capturecard.cardid = :INPUTID");
2206  query.bindValue(":CHANID", chanid);
2207  query.bindValue(":CHANNUM", channelname);
2208  query.bindValue(":CALLSIGN", callsign);
2209  query.bindValue(":INPUTID", m_inputid);
2210 
2211  if (!query.exec() || !query.isActive())
2212  {
2213  MythDB::DBError("ShouldSwitchToAnotherInput", query);
2214  }
2215  else if (query.size() > 0)
2216  {
2217  msg = "Found channel (%1) on current input[%2].";
2218  LOG(VB_RECORD, LOG_INFO, LOC + msg.arg(channelname).arg(m_inputid));
2219  return false;
2220  }
2221 
2222  // We didn't find it on the current input, so now we check other inputs.
2223  query.prepare(
2224  "SELECT channel.channum, capturecard.cardid "
2225  "FROM channel, capturecard "
2226  "WHERE ( channel.chanid = :CHANID OR "
2227  " ( channel.channum = :CHANNUM AND "
2228  " channel.callsign = :CALLSIGN ) "
2229  " ) AND "
2230  " channel.sourceid = capturecard.sourceid AND "
2231  " capturecard.cardid != :INPUTID");
2232  query.bindValue(":CHANID", chanid);
2233  query.bindValue(":CHANNUM", channelname);
2234  query.bindValue(":CALLSIGN", callsign);
2235  query.bindValue(":INPUTID", m_inputid);
2236 
2237  if (!query.exec() || !query.isActive())
2238  {
2239  MythDB::DBError("ShouldSwitchToAnotherInput", query);
2240  }
2241  else if (query.next())
2242  {
2243  msg = QString("Found channel (%1) on different input(%2).")
2244  .arg(query.value(0).toString()).arg(query.value(1).toString());
2245  LOG(VB_RECORD, LOG_INFO, LOC + msg);
2246  return true;
2247  }
2248 
2249  msg = QString("Did not find channel(%1) on any input.").arg(channelname);
2250  LOG(VB_RECORD, LOG_ERR, LOC + msg);
2251  return false;
2252 }
2253 
2264 bool TVRec::CheckChannel(const QString& name) const
2265 {
2266  if (!m_channel)
2267  return false;
2268 
2269  return m_channel->CheckChannel(name);
2270 }
2271 
2275 static QString add_spacer(const QString &channel, const QString &spacer)
2276 {
2277  QString chan = channel;
2278  if ((chan.length() >= 2) && !spacer.isEmpty())
2279  return chan.left(chan.length()-1) + spacer + chan.right(1);
2280  return chan;
2281 }
2282 
2310 bool TVRec::CheckChannelPrefix(const QString &prefix,
2311  uint &complete_valid_channel_on_rec,
2312  bool &is_extra_char_useful,
2313  QString &needed_spacer)
2314 {
2315 #if DEBUG_CHANNEL_PREFIX
2316  LOG(VB_GENERAL, LOG_DEBUG, QString("CheckChannelPrefix(%1)").arg(prefix));
2317 #endif
2318 
2319  static const uint kSpacerListSize = 5;
2320  static const char* spacers[kSpacerListSize] = { "", "_", "-", "#", "." };
2321 
2322  MSqlQuery query(MSqlQuery::InitCon());
2323  QString basequery = QString(
2324  "SELECT channel.chanid, channel.channum, capturecard.cardid "
2325  "FROM channel, capturecard "
2326  "WHERE channel.channum LIKE '%1%' AND "
2327  " channel.sourceid = capturecard.sourceid");
2328 
2329  QString inputquery[2] =
2330  {
2331  QString(" AND capturecard.cardid = '%1'").arg(m_inputid),
2332  QString(" AND capturecard.cardid != '%1'").arg(m_inputid),
2333  };
2334 
2335  vector<uint> fchanid;
2336  vector<QString> fchannum;
2337  vector<uint> finputid;
2338  vector<QString> fspacer;
2339 
2340  for (uint i = 0; i < 2; i++)
2341  {
2342  for (uint j = 0; j < kSpacerListSize; j++)
2343  {
2344  QString qprefix = add_spacer(
2345  prefix, (QString(spacers[j]) == "_") ? "\\_" : spacers[j]);
2346  query.prepare(basequery.arg(qprefix) + inputquery[i]);
2347 
2348  if (!query.exec() || !query.isActive())
2349  {
2350  MythDB::DBError("checkchannel -- locate channum", query);
2351  }
2352  else if (query.size())
2353  {
2354  while (query.next())
2355  {
2356  fchanid.push_back(query.value(0).toUInt());
2357  fchannum.push_back(query.value(1).toString());
2358  finputid.push_back(query.value(2).toUInt());
2359  fspacer.emplace_back(spacers[j]);
2360 #if DEBUG_CHANNEL_PREFIX
2361  LOG(VB_GENERAL, LOG_DEBUG,
2362  QString("(%1,%2) Adding %3 rec %4")
2363  .arg(i).arg(j).arg(query.value(1).toString(),6)
2364  .arg(query.value(2).toUInt()));
2365 #endif
2366  }
2367  }
2368 
2369  if (prefix.length() < 2)
2370  break;
2371  }
2372  }
2373 
2374  // Now process the lists for the info we need...
2375  is_extra_char_useful = false;
2376  complete_valid_channel_on_rec = 0;
2377  needed_spacer.clear();
2378 
2379  if (fchanid.empty())
2380  return false;
2381 
2382  if (fchanid.size() == 1) // Unique channel...
2383  {
2384  needed_spacer = fspacer[0];
2385  bool nc = (fchannum[0] != add_spacer(prefix, fspacer[0]));
2386 
2387  complete_valid_channel_on_rec = (nc) ? 0 : finputid[0];
2388  is_extra_char_useful = nc;
2389  return true;
2390  }
2391 
2392  // If we get this far there is more than one channel
2393  // sharing the prefix we were given.
2394 
2395  // Is an extra characher useful for disambiguation?
2396  is_extra_char_useful = false;
2397  for (uint i = 0; (i < fchannum.size()) && !is_extra_char_useful; i++)
2398  {
2399  is_extra_char_useful = (fchannum[i] != add_spacer(prefix, fspacer[i]));
2400 #if DEBUG_CHANNEL_PREFIX
2401  LOG(VB_GENERAL, LOG_DEBUG, QString("is_extra_char_useful(%1!=%2): %3")
2402  .arg(fchannum[i]).arg(add_spacer(prefix, fspacer[i]))
2403  .arg(is_extra_char_useful));
2404 #endif
2405  }
2406 
2407  // Are any of the channels complete w/o spacer?
2408  // If so set complete_valid_channel_on_rec,
2409  // with a preference for our inputid.
2410  for (size_t i = 0; i < fchannum.size(); i++)
2411  {
2412  if (fchannum[i] == prefix)
2413  {
2414  complete_valid_channel_on_rec = finputid[i];
2415  if (finputid[i] == m_inputid)
2416  break;
2417  }
2418  }
2419 
2420  if (complete_valid_channel_on_rec != 0)
2421  return true;
2422 
2423  // Add a spacer, if one is needed to select a valid channel.
2424  bool spacer_needed = true;
2425  for (uint i = 0; (i < fspacer.size() && spacer_needed); i++)
2426  spacer_needed = !fspacer[i].isEmpty();
2427  if (spacer_needed)
2428  needed_spacer = fspacer[0];
2429 
2430  // If it isn't useful to wait for more characters,
2431  // then try to commit to any true match immediately.
2432  for (size_t i = 0; i < ((is_extra_char_useful) ? 0 : fchanid.size()); i++)
2433  {
2434  if (fchannum[i] == add_spacer(prefix, fspacer[i]))
2435  {
2436  needed_spacer = fspacer[i];
2437  complete_valid_channel_on_rec = finputid[i];
2438  return true;
2439  }
2440  }
2441 
2442  return true;
2443 }
2444 
2446  const QString &channum)
2447 {
2448  if (!m_recorder)
2449  return false;
2450 
2451  QString videoFilters = ChannelUtil::GetVideoFilters(sourceid, channum);
2452  if (!videoFilters.isEmpty())
2453  {
2454  m_recorder->SetVideoFilters(videoFilters);
2455  return true;
2456  }
2457 
2458  return false;
2459 }
2460 
2466 {
2467  return ((m_recorder && m_recorder->IsRecording()) ||
2469 }
2470 
2476 bool TVRec::IsBusy(InputInfo *busy_input, int time_buffer) const
2477 {
2478  InputInfo dummy;
2479  if (!busy_input)
2480  busy_input = &dummy;
2481 
2482  busy_input->Clear();
2483 
2484  if (!m_channel)
2485  return false;
2486 
2487  if (!m_channel->GetInputID())
2488  return false;
2489 
2490  uint chanid = 0;
2491 
2492  if (GetState() != kState_None)
2493  {
2494  busy_input->m_inputid = m_channel->GetInputID();
2495  chanid = m_channel->GetChanID();
2496  }
2497 
2498  PendingInfo pendinfo;
2499  bool has_pending;
2500  {
2501  m_pendingRecLock.lock();
2502  PendingMap::const_iterator it = m_pendingRecordings.find(m_inputid);
2503  has_pending = (it != m_pendingRecordings.end());
2504  if (has_pending)
2505  pendinfo = *it;
2506  m_pendingRecLock.unlock();
2507  }
2508 
2509  if (!busy_input->m_inputid && has_pending)
2510  {
2511  int timeLeft = MythDate::current()
2512  .secsTo(pendinfo.recordingStart);
2513 
2514  if (timeLeft <= time_buffer)
2515  {
2516  QString channum, input;
2517  if (pendinfo.info->QueryTuningInfo(channum, input))
2518  {
2519  busy_input->m_inputid = m_channel->GetInputID();
2520  chanid = pendinfo.info->GetChanID();
2521  }
2522  }
2523  }
2524 
2525  if (busy_input->m_inputid)
2526  {
2527  CardUtil::GetInputInfo(*busy_input);
2528  busy_input->m_chanid = chanid;
2529  busy_input->m_mplexid = ChannelUtil::GetMplexID(busy_input->m_chanid);
2530  busy_input->m_mplexid =
2531  (32767 == busy_input->m_mplexid) ? 0 : busy_input->m_mplexid;
2532  }
2533 
2534  return busy_input->m_inputid != 0U;
2535 }
2536 
2537 
2545 {
2546  QMutexLocker lock(&m_stateChangeLock);
2547 
2548  if (m_recorder)
2549  return m_recorder->GetFrameRate();
2550  return -1.0F;
2551 }
2552 
2560 {
2561  QMutexLocker lock(&m_stateChangeLock);
2562 
2563  if (m_recorder)
2564  return m_recorder->GetFramesWritten();
2565  return -1;
2566 }
2567 
2574 long long TVRec::GetFilePosition(void)
2575 {
2576  QMutexLocker lock(&m_stateChangeLock);
2577 
2578  if (m_ringBuffer)
2579  return m_ringBuffer->GetWritePosition();
2580  return -1;
2581 }
2582 
2590 int64_t TVRec::GetKeyframePosition(uint64_t desired) const
2591 {
2592  QMutexLocker lock(&m_stateChangeLock);
2593 
2594  if (m_recorder)
2595  return m_recorder->GetKeyframePosition(desired);
2596  return -1;
2597 }
2598 
2608  int64_t start, int64_t end, frm_pos_map_t &map) const
2609 {
2610  QMutexLocker lock(&m_stateChangeLock);
2611 
2612  if (m_recorder)
2613  return m_recorder->GetKeyframePositions(start, end, map);
2614 
2615  return false;
2616 }
2617 
2619  int64_t start, int64_t end, frm_pos_map_t &map) const
2620 {
2621  QMutexLocker lock(&m_stateChangeLock);
2622 
2623  if (m_recorder)
2624  return m_recorder->GetKeyframeDurations(start, end, map);
2625 
2626  return false;
2627 }
2628 
2634 long long TVRec::GetMaxBitrate(void) const
2635 {
2636  long long bitrate;
2637  if (m_genOpt.inputtype == "MPEG")
2638  bitrate = 10080000LL; // use DVD max bit rate
2639  else if (m_genOpt.inputtype == "HDPVR")
2640  bitrate = 20200000LL; // Peek bit rate for HD-PVR
2642  bitrate = 22200000LL; // 1080i
2643  else // frame grabber
2644  bitrate = 10080000LL; // use DVD max bit rate, probably too big
2645 
2646  return bitrate;
2647 }
2648 
2654 void TVRec::SpawnLiveTV(LiveTVChain *newchain, bool pip, QString startchan)
2655 {
2656  QMutexLocker lock(&m_stateChangeLock);
2657 
2658  m_tvChain = newchain;
2659  m_tvChain->IncrRef(); // mark it for TVRec use
2660  m_tvChain->ReloadAll();
2661 
2662  QString hostprefix = gCoreContext->GenMythURL(
2665 
2666  m_tvChain->SetHostPrefix(hostprefix);
2668 
2669  m_ispip = pip;
2670  m_liveTVStartChannel = std::move(startchan);
2671 
2672  // Change to WatchingLiveTV
2674  // Wait for state change to take effect
2676 
2677  // Make sure StartRecording can't steal our tuner
2678  SetFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2679 }
2680 
2684 QString TVRec::GetChainID(void)
2685 {
2686  if (m_tvChain)
2687  return m_tvChain->GetID();
2688  return "";
2689 }
2690 
2700 {
2701  QMutexLocker lock(&m_stateChangeLock);
2702 
2704  return; // already stopped
2705 
2706  if (!m_curRecording)
2707  return;
2708 
2709  const QString recgrp = m_curRecording->QueryRecordingGroup();
2711 
2712  if (recgrp != "LiveTV" && !m_pseudoLiveTVRecording)
2713  {
2714  // User wants this recording to continue
2716  }
2717  else if (recgrp == "LiveTV" && m_pseudoLiveTVRecording)
2718  {
2719  // User wants to abandon scheduled recording
2720  SetPseudoLiveTVRecording(nullptr);
2721  }
2722 }
2723 
2734 {
2735  if (!m_channel)
2736  return;
2737 
2738  // Notify scheduler of the recording.
2739  // + set up recording so it can be resumed
2740  rec->SetInputID(m_inputid);
2742 
2743  if (rec->GetRecordingRuleType() == kNotRecording)
2744  {
2747  }
2748 
2749  // + remove any end offset which would mismatch the live session
2750  rec->GetRecordingRule()->m_endOffset = 0;
2751 
2752  // + save RecStatus::Inactive recstatus to so that a reschedule call
2753  // doesn't start recording this on another input before we
2754  // send the SCHEDULER_ADD_RECORDING message to the scheduler.
2756  rec->AddHistory(false);
2757 
2758  // + save RecordingRule so that we get a recordid
2759  // (don't allow RescheduleMatch(), avoiding unneeded reschedule)
2760  rec->GetRecordingRule()->Save(false);
2761 
2762  // + save recordid to recorded entry
2763  rec->ApplyRecordRecID();
2764 
2765  // + set proper recstatus (saved later)
2767 
2768  // + pass proginfo to scheduler and reschedule
2769  QStringList prog;
2770  rec->ToStringList(prog);
2771  MythEvent me("SCHEDULER_ADD_RECORDING", prog);
2772  gCoreContext->dispatch(me);
2773 
2774  // Allow scheduler to end this recording before post-roll,
2775  // if it has another recording for this recorder.
2776  ClearFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2777 }
2778 
2780  RecordingProfile *recpro, int line)
2781 {
2782  if (kAutoRunProfile == t)
2783  {
2785  if (!recpro)
2786  {
2787  LoadProfile(nullptr, rec, profile);
2788  recpro = &profile;
2789  }
2790  m_autoRunJobs[rec->MakeUniqueKey()] =
2791  init_jobs(rec, *recpro, m_runJobOnHostOnly,
2793  }
2794  else
2795  {
2797  }
2798  LOG(VB_JOBQUEUE, LOG_INFO,
2799  QString("InitAutoRunJobs for %1, line %2 -> 0x%3")
2800  .arg(rec->MakeUniqueKey()).arg(line)
2801  .arg(m_autoRunJobs[rec->MakeUniqueKey()],0,16));
2802 }
2803 
2815 void TVRec::SetLiveRecording(int recording)
2816 {
2817  LOG(VB_GENERAL, LOG_INFO, LOC +
2818  QString("SetLiveRecording(%1)").arg(recording));
2819  QMutexLocker locker(&m_stateChangeLock);
2820 
2821  (void) recording;
2822 
2824  bool was_rec = m_pseudoLiveTVRecording;
2826  if (was_rec && !m_pseudoLiveTVRecording)
2827  {
2828  LOG(VB_GENERAL, LOG_INFO, LOC + "SetLiveRecording() -- cancel");
2829  // cancel -- 'recording' should be 0 or -1
2830  SetFlags(kFlagCancelNextRecording, __FILE__, __LINE__);
2831  m_curRecording->SetRecordingGroup("LiveTV");
2832  InitAutoRunJobs(m_curRecording, kAutoRunNone, nullptr, __LINE__);
2833  }
2834  else if (!was_rec && m_pseudoLiveTVRecording)
2835  {
2836  LOG(VB_GENERAL, LOG_INFO, LOC + "SetLiveRecording() -- record");
2837  // record -- 'recording' should be 1 or -1
2838 
2839  // If the last recording was flagged for keeping
2840  // in the frontend, then add the recording rule
2841  // so that transcode, commfrag, etc can be run.
2844  recstat = m_curRecording->GetRecordingStatus();
2845  m_curRecording->SetRecordingGroup("Default");
2846  InitAutoRunJobs(m_curRecording, kAutoRunProfile, nullptr, __LINE__);
2847  }
2848 
2849  MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
2850  .arg(m_curRecording->GetInputID())
2851  .arg(m_curRecording->GetChanID())
2853  .arg(recstat)
2855 
2856  gCoreContext->dispatch(me);
2857 }
2858 
2864 {
2865  QMutexLocker lock(&m_stateChangeLock);
2866  LOG(VB_RECORD, LOG_INFO, LOC +
2867  QString("StopLiveTV(void) curRec: 0x%1 pseudoRec: 0x%2")
2868  .arg((uint64_t)m_curRecording,0,16)
2869  .arg((uint64_t)m_pseudoLiveTVRecording,0,16));
2870 
2872  return;
2873 
2874  bool hadPseudoLiveTVRec = m_pseudoLiveTVRecording;
2876 
2877  if (!hadPseudoLiveTVRec && m_pseudoLiveTVRecording)
2879 
2880  // Figure out next state and if needed recording end time.
2881  TVState next_state = kState_None;
2883  {
2885  next_state = kState_RecordingOnly;
2886  }
2887 
2888  // Change to the appropriate state
2889  ChangeState(next_state);
2890 
2891  // Wait for state change to take effect...
2893 
2894  // We are done with the tvchain...
2895  if (m_tvChain)
2896  {
2897  m_tvChain->DecrRef();
2898  }
2899  m_tvChain = nullptr;
2900 }
2901 
2911 {
2912  QMutexLocker lock(&m_stateChangeLock);
2913 
2914  if (!m_recorder)
2915  {
2916  LOG(VB_GENERAL, LOG_ERR, LOC +
2917  "PauseRecorder() called with no recorder");
2918  return;
2919  }
2920 
2921  m_recorder->Pause();
2922 }
2923 
2930 {
2931  if (m_pauseNotify)
2932  WakeEventLoop();
2933 }
2934 
2938 void TVRec::ToggleChannelFavorite(const QString& changroupname)
2939 {
2940  QMutexLocker lock(&m_stateChangeLock);
2941 
2942  if (!m_channel)
2943  return;
2944 
2945  // Get current channel id...
2946  uint sourceid = m_channel->GetSourceID();
2947  QString channum = m_channel->GetChannelName();
2948  uint chanid = ChannelUtil::GetChanID(sourceid, channum);
2949 
2950  if (!chanid)
2951  {
2952  LOG(VB_GENERAL, LOG_ERR, LOC +
2953  QString("Channel: \'%1\' was not found in the database.\n"
2954  "\t\tMost likely, the 'starting channel' for this "
2955  "Input Connection is invalid.\n"
2956  "\t\tCould not toggle favorite.").arg(channum));
2957  return;
2958  }
2959 
2960  int changrpid;
2961 
2962  changrpid = ChannelGroup::GetChannelGroupId(changroupname);
2963 
2964  if (changrpid <1)
2965  {
2966  LOG(VB_RECORD, LOG_ERR, LOC +
2967  QString("ToggleChannelFavorite: Invalid channel group name %1,")
2968  .arg(changroupname));
2969  }
2970  else
2971  {
2972  bool result = ChannelGroup::ToggleChannel(chanid, changrpid, true);
2973 
2974  if (!result)
2975  LOG(VB_RECORD, LOG_ERR, LOC + "Unable to toggle channel favorite.");
2976  else
2977  LOG(VB_RECORD, LOG_INFO, LOC +
2978  QString("Toggled channel favorite.channum %1, chan group %2")
2979  .arg(channum).arg(changroupname));
2980  }
2981 }
2982 
2989 {
2990  QMutexLocker lock(&m_stateChangeLock);
2991  if (!m_channel)
2992  return -1;
2993 
2994  int ret = m_channel->GetPictureAttribute(attr);
2995 
2996  return (ret < 0) ? -1 : ret / 655;
2997 }
2998 
3007  PictureAttribute attr,
3008  bool direction)
3009 {
3010  QMutexLocker lock(&m_stateChangeLock);
3011  if (!m_channel)
3012  return -1;
3013 
3014  int ret = m_channel->ChangePictureAttribute(type, attr, direction);
3015 
3016  return (ret < 0) ? -1 : ret / 655;
3017 }
3018 
3022 QString TVRec::GetInput(void) const
3023 {
3024  if (m_channel)
3025  return m_channel->GetInputName();
3026  return QString();
3027 }
3028 
3033 {
3034  if (m_channel)
3035  return m_channel->GetSourceID();
3036  return 0;
3037 }
3038 
3047 QString TVRec::SetInput(QString input)
3048 {
3049  QMutexLocker lock(&m_stateChangeLock);
3050  QString origIn = input;
3051  LOG(VB_RECORD, LOG_INFO, LOC + "SetInput(" + input + ") -- begin");
3052 
3053  if (!m_channel)
3054  {
3055  LOG(VB_RECORD, LOG_INFO, LOC + "SetInput() -- end no channel class");
3056  return QString();
3057  }
3058 
3059  LOG(VB_RECORD, LOG_INFO, LOC + "SetInput(" + origIn + ":" + input +
3060  ") -- end nothing to do");
3061  return input;
3062 }
3063 
3073 void TVRec::SetChannel(const QString& name, uint requestType)
3074 {
3075  QMutexLocker locker1(&m_setChannelLock);
3076  QMutexLocker locker2(&m_stateChangeLock);
3077 
3078  LOG(VB_CHANNEL, LOG_INFO, LOC +
3079  QString("SetChannel(%1) -- begin").arg(name));
3080 
3081  // Detect tuning request type if needed
3082  if (requestType & kFlagDetect)
3083  {
3085  requestType = m_lastTuningRequest.flags & (kFlagRec | kFlagNoRec);
3086  }
3087 
3088  // Clear the RingBuffer reset flag, in case we wait for a reset below
3089  ClearFlags(kFlagRingBufferReady, __FILE__, __LINE__);
3090 
3091  // Clear out any EITScan channel change requests
3093  while (it != m_tuningRequests.end())
3094  {
3095  if ((*it).flags & kFlagEITScan)
3096  it = m_tuningRequests.erase(it);
3097  else
3098  ++it;
3099  }
3100 
3101  // Actually add the tuning request to the queue, and
3102  // then wait for it to start tuning
3103  m_tuningRequests.enqueue(TuningRequest(requestType, name));
3105 
3106  // If we are using a recorder, wait for a RingBuffer reset
3107  if (requestType & kFlagRec)
3108  {
3109  while (!HasFlags(kFlagRingBufferReady))
3111  }
3112  LOG(VB_CHANNEL, LOG_INFO, LOC + QString("SetChannel(%1) -- end").arg(name));
3113 }
3114 
3123 {
3124  LOG(VB_CHANNEL, LOG_INFO, LOC +
3125  QString("QueueEITChannelChange(%1) -- begin").arg(name));
3126 
3127  bool ok = false;
3128  if (m_setChannelLock.tryLock())
3129  {
3130  if (m_stateChangeLock.tryLock())
3131  {
3132  if (m_tuningRequests.empty())
3133  {
3135  ok = true;
3136  }
3137  m_stateChangeLock.unlock();
3138  }
3139  m_setChannelLock.unlock();
3140  }
3141 
3142  LOG(VB_CHANNEL, LOG_INFO, LOC +
3143  QString("QueueEITChannelChange(%1) -- end --> %2").arg(name).arg(ok));
3144 
3145  return ok;
3146 }
3147 
3149  QString &title, QString &subtitle,
3150  QString &desc, QString &category,
3151  QString &starttime, QString &endtime,
3152  QString &callsign, QString &iconpath,
3153  QString &channum, uint &sourceChanid,
3154  QString &seriesid, QString &programid)
3155 {
3156  QString compare = "<=";
3157  QString sortorder = "desc";
3158  uint chanid = 0;
3159 
3160  if (sourceChanid)
3161  {
3162  chanid = sourceChanid;
3163 
3164  if (BROWSE_UP == direction)
3165  chanid = m_channel->GetNextChannel(chanid, CHANNEL_DIRECTION_UP);
3166  else if (BROWSE_DOWN == direction)
3167  chanid = m_channel->GetNextChannel(chanid, CHANNEL_DIRECTION_DOWN);
3168  else if (BROWSE_FAVORITE == direction)
3169  chanid = m_channel->GetNextChannel(
3170  chanid, CHANNEL_DIRECTION_FAVORITE);
3171 
3172  else if (BROWSE_LEFT == direction)
3173  {
3174  compare = "<";
3175  }
3176  else if (BROWSE_RIGHT == direction)
3177  {
3178  compare = ">";
3179  sortorder = "asc";
3180  }
3181  }
3182 
3183  if (!chanid)
3184  {
3185  if (BROWSE_SAME == direction)
3186  chanid = m_channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
3187  else if (BROWSE_UP == direction)
3188  chanid = m_channel->GetNextChannel(channum, CHANNEL_DIRECTION_UP);
3189  else if (BROWSE_DOWN == direction)
3190  chanid = m_channel->GetNextChannel(channum, CHANNEL_DIRECTION_DOWN);
3191  else if (BROWSE_FAVORITE == direction)
3192  chanid = m_channel->GetNextChannel(channum,
3194  else if (BROWSE_LEFT == direction)
3195  {
3196  chanid = m_channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
3197  compare = "<";
3198  }
3199  else if (BROWSE_RIGHT == direction)
3200  {
3201  chanid = m_channel->GetNextChannel(channum, CHANNEL_DIRECTION_SAME);
3202  compare = ">";
3203  sortorder = "asc";
3204  }
3205  }
3206 
3207  QString querystr = QString(
3208  "SELECT title, subtitle, description, category, "
3209  " starttime, endtime, callsign, icon, "
3210  " channum, seriesid, programid "
3211  "FROM program, channel "
3212  "WHERE program.chanid = channel.chanid AND "
3213  " channel.chanid = :CHANID AND "
3214  " starttime %1 :STARTTIME "
3215  "ORDER BY starttime %2 "
3216  "LIMIT 1").arg(compare).arg(sortorder);
3217 
3218  MSqlQuery query(MSqlQuery::InitCon());
3219  query.prepare(querystr);
3220  query.bindValue(":CHANID", chanid);
3221  query.bindValue(":STARTTIME", starttime);
3222 
3223  // Clear everything now in case either query fails.
3224  title = subtitle = desc = category = "";
3225  starttime = endtime = callsign = iconpath = "";
3226  channum = seriesid = programid = "";
3227  sourceChanid = 0;
3228 
3229  // Try to get the program info
3230  if (!query.exec() && !query.isActive())
3231  {
3232  MythDB::DBError("GetNextProgram -- get program info", query);
3233  }
3234  else if (query.next())
3235  {
3236  title = query.value(0).toString();
3237  subtitle = query.value(1).toString();
3238  desc = query.value(2).toString();
3239  category = query.value(3).toString();
3240  starttime = query.value(4).toString();
3241  endtime = query.value(5).toString();
3242  callsign = query.value(6).toString();
3243  iconpath = query.value(7).toString();
3244  channum = query.value(8).toString();
3245  seriesid = query.value(9).toString();
3246  programid = query.value(10).toString();
3247  sourceChanid = chanid;
3248  return;
3249  }
3250 
3251  // Couldn't get program info, so get the channel info instead
3252  query.prepare(
3253  "SELECT channum, callsign, icon "
3254  "FROM channel "
3255  "WHERE chanid = :CHANID");
3256  query.bindValue(":CHANID", chanid);
3257 
3258  if (!query.exec() || !query.isActive())
3259  {
3260  MythDB::DBError("GetNextProgram -- get channel info", query);
3261  }
3262  else if (query.next())
3263  {
3264  sourceChanid = chanid;
3265  channum = query.value(0).toString();
3266  callsign = query.value(1).toString();
3267  iconpath = query.value(2).toString();
3268  }
3269 }
3270 
3271 bool TVRec::GetChannelInfo(uint &chanid, uint &sourceid,
3272  QString &callsign, QString &channum,
3273  QString &channame, QString &xmltvid) const
3274 {
3275  callsign.clear();
3276  channum.clear();
3277  channame.clear();
3278  xmltvid.clear();
3279 
3280  if ((!chanid || !sourceid) && !m_channel)
3281  return false;
3282 
3283  if (!chanid)
3284  chanid = (uint) max(m_channel->GetChanID(), 0);
3285 
3286  if (!sourceid)
3287  sourceid = m_channel->GetSourceID();
3288 
3289  MSqlQuery query(MSqlQuery::InitCon());
3290  query.prepare(
3291  "SELECT callsign, channum, name, xmltvid "
3292  "FROM channel "
3293  "WHERE chanid = :CHANID");
3294  query.bindValue(":CHANID", chanid);
3295  if (!query.exec() || !query.isActive())
3296  {
3297  MythDB::DBError("GetChannelInfo", query);
3298  return false;
3299  }
3300 
3301  if (!query.next())
3302  return false;
3303 
3304  callsign = query.value(0).toString();
3305  channum = query.value(1).toString();
3306  channame = query.value(2).toString();
3307  xmltvid = query.value(3).toString();
3308 
3309  return true;
3310 }
3311 
3312 bool TVRec::SetChannelInfo(uint chanid, uint sourceid,
3313  const QString& oldchannum,
3314  const QString& callsign, const QString& channum,
3315  const QString& channame, const QString& xmltvid)
3316 {
3317  if (!chanid || !sourceid || channum.isEmpty())
3318  return false;
3319 
3320  MSqlQuery query(MSqlQuery::InitCon());
3321  query.prepare(
3322  "UPDATE channel "
3323  "SET callsign = :CALLSIGN, "
3324  " channum = :CHANNUM, "
3325  " name = :CHANNAME, "
3326  " xmltvid = :XMLTVID "
3327  "WHERE chanid = :CHANID AND "
3328  " sourceid = :SOURCEID");
3329  query.bindValue(":CALLSIGN", callsign);
3330  query.bindValue(":CHANNUM", channum);
3331  query.bindValue(":CHANNAME", channame);
3332  query.bindValue(":XMLTVID", xmltvid);
3333  query.bindValue(":CHANID", chanid);
3334  query.bindValue(":SOURCEID", sourceid);
3335 
3336  if (!query.exec())
3337  {
3338  MythDB::DBError("SetChannelInfo", query);
3339  return false;
3340  }
3341 
3342  if (m_channel)
3343  m_channel->Renumber(sourceid, oldchannum, channum);
3344 
3345  return true;
3346 }
3347 
3352 {
3353  QMutexLocker lock(&m_stateChangeLock);
3354 
3355  RingBuffer *rb_old = m_ringBuffer;
3356  m_ringBuffer = rb;
3357 
3358  if (rb_old && (rb_old != rb))
3359  {
3361  ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3362  delete rb_old;
3363  }
3364 
3365  m_switchingBuffer = false;
3366 }
3367 
3369  RingBuffer *rb, RecordingInfo *pginfo, RecordingQuality *recq)
3370 {
3371  LOG(VB_GENERAL, LOG_INFO, LOC + "RingBufferChanged()");
3372 
3373  if (pginfo)
3374  {
3375  if (m_curRecording)
3376  {
3379  delete m_curRecording;
3380  }
3382  m_curRecording = new RecordingInfo(*pginfo);
3385  }
3386 
3387  SetRingBuffer(rb);
3388 }
3389 
3391  QString &input) const
3392 {
3393  QString channum;
3394 
3395  if (request.program)
3396  {
3397  request.program->QueryTuningInfo(channum, input);
3398  return channum;
3399  }
3400 
3401  channum = request.channel;
3402  input = request.input;
3403 
3404  // If this is Live TV startup, we need a channel...
3405  if (channum.isEmpty() && (request.flags & kFlagLiveTV))
3406  {
3407  if (!m_liveTVStartChannel.isEmpty())
3408  channum = m_liveTVStartChannel;
3409  else
3410  {
3412  channum = GetStartChannel(m_inputid);
3413  }
3414  }
3415  if (request.flags & kFlagLiveTV)
3416  m_channel->Init(channum, false);
3417 
3418  if (m_channel && !channum.isEmpty() && (channum.indexOf("NextChannel") >= 0))
3419  {
3420  // FIXME This is just horrible
3421  int dir = channum.right(channum.length() - 12).toInt();
3422  uint chanid = m_channel->GetNextChannel(0, static_cast<ChannelChangeDirection>(dir));
3423  channum = ChannelUtil::GetChanNum(chanid);
3424  }
3425 
3426  return channum;
3427 }
3428 
3430 {
3431  if ((request.flags & kFlagAntennaAdjust) || request.input.isEmpty() ||
3433  {
3434  return false;
3435  }
3436 
3437  uint sourceid = m_channel->GetSourceID();
3438  QString oldchannum = m_channel->GetChannelName();
3439  QString newchannum = request.channel;
3440 
3441  if (ChannelUtil::IsOnSameMultiplex(sourceid, newchannum, oldchannum))
3442  {
3444  ATSCStreamData *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
3445 
3446  if (atsc)
3447  {
3448  uint major, minor = 0;
3449  ChannelUtil::GetATSCChannel(sourceid, newchannum, major, minor);
3450 
3451  if (minor && atsc->HasChannel(major, minor))
3452  {
3453  request.majorChan = major;
3454  request.minorChan = minor;
3455  return true;
3456  }
3457  }
3458 
3459  if (mpeg)
3460  {
3461  uint progNum = ChannelUtil::GetProgramNumber(sourceid, newchannum);
3462  if (mpeg->HasProgram(progNum))
3463  {
3464  request.progNum = progNum;
3465  return true;
3466  }
3467  }
3468  }
3469 
3470  return false;
3471 }
3472 
3481 {
3482  if (!m_tuningRequests.empty())
3483  {
3484  TuningRequest request = m_tuningRequests.front();
3485  LOG(VB_RECORD, LOG_INFO, LOC +
3486  "HandleTuning Request: " + request.toString());
3487 
3488  QString input;
3489  request.channel = TuningGetChanNum(request, input);
3490  request.input = input;
3491 
3492  if (TuningOnSameMultiplex(request))
3493  LOG(VB_CHANNEL, LOG_INFO, LOC + "On same multiplex");
3494 
3495  TuningShutdowns(request);
3496 
3497  // The dequeue isn't safe to do until now because we
3498  // release the stateChangeLock to teardown a recorder
3500 
3501  // Now we start new stuff
3502  if (request.flags & (kFlagRecording|kFlagLiveTV|
3504  {
3505  if (!m_recorder)
3506  {
3507  LOG(VB_RECORD, LOG_INFO, LOC +
3508  "No recorder yet, calling TuningFrequency");
3509  TuningFrequency(request);
3510  }
3511  else
3512  {
3513  LOG(VB_RECORD, LOG_INFO, LOC + "Waiting for recorder pause..");
3514  SetFlags(kFlagWaitingForRecPause, __FILE__, __LINE__);
3515  }
3516  }
3517  m_lastTuningRequest = request;
3518  }
3519 
3521  {
3522  if (!m_recorder->IsPaused())
3523  return;
3524 
3525  ClearFlags(kFlagWaitingForRecPause, __FILE__, __LINE__);
3526  LOG(VB_RECORD, LOG_INFO, LOC +
3527  "Recorder paused, calling TuningFrequency");
3529  }
3530 
3531  MPEGStreamData *streamData = nullptr;
3532  if (HasFlags(kFlagWaitingForSignal) && !(streamData = TuningSignalCheck()))
3533  return;
3534 
3536  {
3537  if (m_recorder)
3539  else
3540  TuningNewRecorder(streamData);
3541 
3542  // If we got this far it is safe to set a new starting channel...
3543  if (m_channel)
3545  }
3546 }
3547 
3553 {
3554  LOG(VB_RECORD, LOG_INFO, LOC + QString("TuningShutdowns(%1)")
3555  .arg(request.toString()));
3556 
3557  QString channum, inputname;
3558 
3559  if (m_scanner && !(request.flags & kFlagEITScan) &&
3561  {
3563  ClearFlags(kFlagEITScannerRunning, __FILE__, __LINE__);
3566  }
3567 
3568  if (m_scanner && !request.IsOnSameMultiplex())
3570 
3572  {
3573  MPEGStreamData *sd = nullptr;
3574  if (GetDTVSignalMonitor())
3577  ClearFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3578 
3579  // Delete StreamData if it is not in use by the recorder.
3580  MPEGStreamData *rec_sd = nullptr;
3581  if (GetDTVRecorder())
3582  rec_sd = GetDTVRecorder()->GetStreamData();
3583  if (sd && (sd != rec_sd))
3584  delete sd;
3585  }
3587  ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3588 
3589  // At this point any waits are canceled.
3590 
3591  if (request.flags & kFlagNoRec)
3592  {
3594  {
3596  ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3598  }
3599 
3601  (m_curRecording &&
3604  {
3605  m_stateChangeLock.unlock();
3606  TeardownRecorder(request.flags);
3607  m_stateChangeLock.lock();
3608  }
3609  // At this point the recorders are shut down
3610 
3611  CloseChannel();
3612  // At this point the channel is shut down
3613  }
3614 
3615  if (m_ringBuffer && (request.flags & kFlagKillRingBuffer))
3616  {
3617  LOG(VB_RECORD, LOG_INFO, LOC + "Tearing down RingBuffer");
3618  SetRingBuffer(nullptr);
3619  // At this point the ringbuffer is shut down
3620  }
3621 
3622  // Clear pending actions from last request
3623  ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
3624 }
3625 
3644 {
3645  LOG(VB_GENERAL, LOG_INFO, LOC + "TuningFrequency");
3646 
3647  DTVChannel *dtvchan = GetDTVChannel();
3648  if (dtvchan)
3649  {
3650  MPEGStreamData *mpeg = nullptr;
3651 
3652  if (GetDTVRecorder())
3653  mpeg = GetDTVRecorder()->GetStreamData();
3654 
3655  const QString tuningmode = (HasFlags(kFlagEITScannerRunning)) ?
3656  dtvchan->GetSIStandard() :
3657  dtvchan->GetSuggestedTuningMode(
3659 
3660  dtvchan->SetTuningMode(tuningmode);
3661 
3662  if (request.minorChan && (tuningmode == "atsc"))
3663  {
3665 
3666  ATSCStreamData *atsc = dynamic_cast<ATSCStreamData*>(mpeg);
3667  if (atsc)
3668  atsc->SetDesiredChannel(request.majorChan, request.minorChan);
3669  }
3670  else if (request.progNum >= 0)
3671  {
3673 
3674  if (mpeg)
3675  mpeg->SetDesiredProgram(request.progNum);
3676  }
3677  }
3678 
3679  if (request.IsOnSameMultiplex())
3680  {
3681  // Update the channel number for SwitchLiveTVRingBuffer (called from
3682  // TuningRestartRecorder). This ensures that the livetvchain will be
3683  // updated with the new channel number
3684  if (m_channel)
3685  {
3687  m_channel->GetChannelName(), request.channel );
3688  }
3689 
3690  QStringList slist;
3691  slist<<"message"<<QObject::tr("On known multiplex...");
3692  MythEvent me(QString("SIGNAL %1").arg(m_inputid), slist);
3693  gCoreContext->dispatch(me);
3694 
3695  SetFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3696  return;
3697  }
3698 
3699  QString channum = request.channel;
3700 
3701  bool ok1;
3702  if (m_channel)
3703  {
3704  m_channel->Open();
3705  if (!channum.isEmpty())
3706  ok1 = m_channel->SetChannelByString(channum);
3707  else
3708  ok1 = false;
3709  }
3710  else
3711  ok1 = true;
3712 
3713  if (!ok1)
3714  {
3715  if (!(request.flags & kFlagLiveTV) || !(request.flags & kFlagEITScan))
3716  {
3717  if (m_curRecording)
3719 
3720  LOG(VB_GENERAL, LOG_ERR, LOC +
3721  QString("Failed to set channel to %1. Reverting to kState_None")
3722  .arg(channum));
3725  else
3727  return;
3728  }
3729 
3730  LOG(VB_GENERAL, LOG_ERR, LOC +
3731  QString("Failed to set channel to %1.").arg(channum));
3732  }
3733 
3734 
3735  bool mpts_only = GetDTVChannel() &&
3736  GetDTVChannel()->GetFormat().compare("MPTS") == 0;
3737  if (mpts_only)
3738  {
3739  // Not using a signal monitor, so just set the status to recording
3741  if (m_curRecording)
3742  {
3744  }
3745  }
3746 
3747 
3748  bool livetv = (request.flags & kFlagLiveTV) != 0U;
3749  bool antadj = (request.flags & kFlagAntennaAdjust) != 0U;
3750  bool use_sm = !mpts_only && SignalMonitor::IsRequired(m_genOpt.inputtype);
3751  bool use_dr = use_sm && (livetv || antadj);
3752  bool has_dummy = false;
3753 
3754  if (use_dr)
3755  {
3756  // We need there to be a ringbuffer for these modes
3757  bool ok2;
3759  m_pseudoLiveTVRecording = nullptr;
3760 
3761  m_tvChain->SetInputType("DUMMY");
3762 
3763  if (!m_ringBuffer)
3764  ok2 = CreateLiveTVRingBuffer(channum);
3765  else
3766  ok2 = SwitchLiveTVRingBuffer(channum, true, false);
3768 
3770 
3771  if (!ok2)
3772  {
3773  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RingBuffer 1");
3774  return;
3775  }
3776 
3777  has_dummy = true;
3778  }
3779 
3780  // Start signal monitoring for devices capable of monitoring
3781  if (use_sm)
3782  {
3783  LOG(VB_RECORD, LOG_INFO, LOC + "Starting Signal Monitor");
3784  bool error = false;
3785  if (!SetupSignalMonitor(
3786  !antadj, (request.flags & kFlagEITScan) != 0U, livetv || antadj))
3787  {
3788  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to setup signal monitor");
3789  if (m_signalMonitor)
3790  {
3791  delete m_signalMonitor;
3792  m_signalMonitor = nullptr;
3793  }
3794 
3795  // pretend the signal monitor is running to prevent segfault
3796  SetFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3797  ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3798  error = true;
3799  }
3800 
3801  if (m_signalMonitor)
3802  {
3803  if (request.flags & kFlagEITScan)
3804  {
3806  SetVideoStreamsRequired(0);
3808  }
3809 
3810  SetFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
3811  ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3812  if (!antadj)
3813  {
3814  QDateTime expire = MythDate::current();
3815 
3816  SetFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
3817  if (m_curRecording)
3818  {
3820  // If startRecordingDeadline is passed, this
3821  // recording is marked as failed, so the scheduler
3822  // can try another showing.
3824  expire.addMSecs(m_genOpt.channel_timeout);
3826  expire.addMSecs(m_genOpt.channel_timeout * 2 / 3);
3827  // Keep trying to record this showing (even if it
3828  // has been marked as failed) until the scheduled
3829  // end time.
3831  m_curRecording->GetRecordingEndTime().addSecs(-10);
3832 
3833  LOG(VB_CHANNEL, LOG_DEBUG, LOC +
3834  QString("Pre-fail start deadline: %1 "
3835  "Start recording deadline: %2 "
3836  "Good signal deadline: %3")
3837  .arg(m_preFailDeadline.toLocalTime()
3838  .toString("hh:mm:ss.zzz"))
3839  .arg(m_startRecordingDeadline.toLocalTime()
3840  .toString("hh:mm:ss.zzz"))
3841  .arg(m_signalMonitorDeadline.toLocalTime()
3842  .toString("hh:mm:ss.zzz")));
3843  }
3844  else
3845  {
3847  expire.addMSecs(m_genOpt.channel_timeout);
3848  }
3850 
3851  //System Event TUNING_TIMEOUT deadline
3853  m_signalEventCmdSent = false;
3854  }
3855  }
3856 
3857  if (has_dummy && m_ringBuffer)
3858  {
3859  // Make sure recorder doesn't point to bogus ringbuffer before
3860  // it is potentially restarted without a new ringbuffer, if
3861  // the next channel won't tune and the user exits LiveTV.
3862  if (m_recorder)
3863  m_recorder->SetRingBuffer(nullptr);
3864 
3865  SetFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
3866  LOG(VB_RECORD, LOG_INFO, "DummyDTVRecorder -- started");
3867  SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
3868  }
3869 
3870  // if we had problems starting the signal monitor,
3871  // we don't want to start the recorder...
3872  if (error)
3873  return;
3874  }
3875 
3876  // Request a recorder, if the command is a recording command
3877  ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3878  if (request.flags & kFlagRec && !antadj)
3879  SetFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3880 }
3881 
3890 {
3891  RecStatus::Type newRecStatus;
3892  bool keep_trying = false;
3893  QDateTime current_time = MythDate::current();
3894 
3895  if ((m_signalMonitor->IsErrored() || current_time > m_signalEventCmdTimeout) &&
3897  {
3898  gCoreContext->SendSystemEvent(QString("TUNING_SIGNAL_TIMEOUT CARDID %1")
3899  .arg(m_inputid));
3900  m_signalEventCmdSent = true;
3901  }
3902 
3903  if (m_signalMonitor->IsAllGood())
3904  {
3905  LOG(VB_RECORD, LOG_INFO, LOC + "TuningSignalCheck: Good signal");
3906  if (m_curRecording && (current_time > m_startRecordingDeadline))
3907  {
3908  newRecStatus = RecStatus::Failing;
3910 
3911  QString desc = tr("Good signal seen after %1 ms")
3912  .arg(m_genOpt.channel_timeout +
3913  m_startRecordingDeadline.msecsTo(current_time));
3914  QString title = m_curRecording->GetTitle();
3915  if (!m_curRecording->GetSubtitle().isEmpty())
3916  title += " - " + m_curRecording->GetSubtitle();
3917 
3919  "Recording", title,
3920  tr("See 'Tuning timeout' in mythtv-setup "
3921  "for this input."));
3923 
3924  LOG(VB_GENERAL, LOG_WARNING, LOC +
3925  QString("It took longer than %1 ms to get a signal lock. "
3926  "Keeping status of '%2'")
3928  .arg(RecStatus::toString(newRecStatus, kSingleRecord)));
3929  LOG(VB_GENERAL, LOG_WARNING, LOC +
3930  "See 'Tuning timeout' in mythtv-setup for this input");
3931  }
3932  else
3933  {
3934  newRecStatus = RecStatus::Recording;
3935  }
3936  }
3937  else if (m_signalMonitor->IsErrored() || current_time > m_signalMonitorDeadline)
3938  {
3939  LOG(VB_GENERAL, LOG_ERR, LOC + "TuningSignalCheck: SignalMonitor " +
3940  (m_signalMonitor->IsErrored() ? "failed" : "timed out"));
3941 
3942  ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
3943  newRecStatus = RecStatus::Failed;
3944 
3946  {
3948  }
3949  }
3950  else if (m_curRecording && !m_reachedPreFail && current_time > m_preFailDeadline)
3951  {
3952  LOG(VB_GENERAL, LOG_ERR, LOC +
3953  "TuningSignalCheck: Hit pre-fail timeout");
3954  SendMythSystemRecEvent("REC_PREFAIL", m_curRecording);
3955  m_reachedPreFail = true;
3956  return nullptr;
3957  }
3959  current_time > m_startRecordingDeadline)
3960  {
3961  newRecStatus = RecStatus::Failing;
3963  keep_trying = true;
3964 
3965  SendMythSystemRecEvent("REC_FAILING", m_curRecording);
3966 
3967  QString desc = tr("Taking more than %1 ms to get a lock.")
3968  .arg(m_genOpt.channel_timeout);
3969  QString title = m_curRecording->GetTitle();
3970  if (!m_curRecording->GetSubtitle().isEmpty())
3971  title += " - " + m_curRecording->GetSubtitle();
3972 
3974  "Recording", title,
3975  tr("See 'Tuning timeout' in mythtv-setup "
3976  "for this input."));
3977  mn.SetDuration(30);
3979 
3980  LOG(VB_GENERAL, LOG_WARNING, LOC +
3981  QString("TuningSignalCheck: taking more than %1 ms to get a lock. "
3982  "marking this recording as '%2'.")
3984  .arg(RecStatus::toString(newRecStatus, kSingleRecord)));
3985  LOG(VB_GENERAL, LOG_WARNING, LOC +
3986  "See 'Tuning timeout' in mythtv-setup for this input");
3987  }
3988  else
3989  {
3990  if (m_signalMonitorCheckCnt) // Don't flood log file
3992  else
3993  {
3994  LOG(VB_RECORD, LOG_INFO, LOC +
3995  QString("TuningSignalCheck: Still waiting. Will timeout @ %1")
3996  .arg(m_signalMonitorDeadline.toLocalTime()
3997  .toString("hh:mm:ss.zzz")));
3999  }
4000  return nullptr;
4001  }
4002 
4003  SetRecordingStatus(newRecStatus, __LINE__);
4004 
4005  if (m_curRecording)
4006  {
4007  m_curRecording->SetRecordingStatus(newRecStatus);
4008  MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
4009  .arg(m_curRecording->GetInputID())
4010  .arg(m_curRecording->GetChanID())
4012  .arg(newRecStatus)
4014  gCoreContext->dispatch(me);
4015  }
4016 
4017  if (keep_trying)
4018  return nullptr;
4019 
4020  // grab useful data from DTV signal monitor before we kill it...
4021  MPEGStreamData *streamData = nullptr;
4022  if (GetDTVSignalMonitor())
4023  streamData = GetDTVSignalMonitor()->GetStreamData();
4024 
4026  {
4027  // shut down signal monitoring
4029  ClearFlags(kFlagSignalMonitorRunning, __FILE__, __LINE__);
4030  }
4031  ClearFlags(kFlagWaitingForSignal, __FILE__, __LINE__);
4032 
4033  if (streamData)
4034  {
4035  DVBStreamData *dsd = dynamic_cast<DVBStreamData*>(streamData);
4036  if (dsd)
4038  if (!get_use_eit(GetInputId()))
4039  {
4040  LOG(VB_EIT, LOG_INFO, LOC +
4041  "EIT scanning disabled for all sources on this input.");
4042  }
4043  else if (m_scanner)
4044  m_scanner->StartPassiveScan(m_channel, streamData);
4045  }
4046 
4047  return streamData;
4048 }
4049 
4051  bool on_host, bool transcode_bfr_comm, bool on_line_comm)
4052 {
4053  if (!rec)
4054  return 0; // no jobs for Live TV recordings..
4055 
4056  int jobs = 0; // start with no jobs
4057 
4058  // grab standard jobs flags from program info
4060 
4061  // disable commercial flagging on PBS, BBC, etc.
4062  if (rec->IsCommercialFree())
4064 
4065  // disable transcoding if the profile does not allow auto transcoding
4066  const StandardSetting *autoTrans = profile.byName("autotranscode");
4067  if ((!autoTrans) || (autoTrans->getValue().toInt() == 0))
4069 
4070  bool ml = JobQueue::JobIsInMask(JOB_METADATA, jobs);
4071  if (ml)
4072  {
4073  // When allowed, metadata lookup should occur at the
4074  // start of a recording to make the additional info
4075  // available immediately (and for use in future jobs).
4076  QString host = (on_host) ? gCoreContext->GetHostName() : "";
4078  rec->GetChanID(),
4079  rec->GetRecordingStartTime(), "", "",
4080  host, JOB_LIVE_REC);
4081 
4082  // don't do regular metadata lookup, we won't need it.
4084  }
4085 
4086  // is commercial flagging enabled, and is on-line comm flagging enabled?
4087  bool rt = JobQueue::JobIsInMask(JOB_COMMFLAG, jobs) && on_line_comm;
4088  // also, we either need transcoding to be disabled or
4089  // we need to be allowed to commercial flag before transcoding?
4090  rt &= JobQueue::JobIsNotInMask(JOB_TRANSCODE, jobs) ||
4091  !transcode_bfr_comm;
4092  if (rt)
4093  {
4094  // queue up real-time (i.e. on-line) commercial flagging.
4095  QString host = (on_host) ? gCoreContext->GetHostName() : "";
4097  rec->GetChanID(),
4098  rec->GetRecordingStartTime(), "", "",
4099  host, JOB_LIVE_REC);
4100 
4101  // don't do regular comm flagging, we won't need it.
4103  }
4104 
4105  return jobs;
4106 }
4107 
4108 QString TVRec::LoadProfile(void *tvchain, RecordingInfo *rec,
4110 {
4111  // Determine the correct recording profile.
4112  // In LiveTV mode use "Live TV" profile, otherwise use the
4113  // recording's specified profile. If the desired profile can't
4114  // be found, fall back to the "Default" profile for input type.
4115  QString profileName = "Live TV";
4116  if (!tvchain && rec)
4117  profileName = rec->GetRecordingRule()->m_recProfile;
4118 
4119  QString profileRequested = profileName;
4120 
4121  if (profile.loadByType(profileName, m_genOpt.inputtype,
4122  m_genOpt.videodev))
4123  {
4124  LOG(VB_RECORD, LOG_INFO, LOC +
4125  QString("Using profile '%1' to record")
4126  .arg(profileName));
4127  }
4128  else
4129  {
4130  profileName = "Default";
4131  if (profile.loadByType(profileName, m_genOpt.inputtype, m_genOpt.videodev))
4132  {
4133  LOG(VB_RECORD, LOG_INFO, LOC +
4134  QString("Profile '%1' not found, using "
4135  "fallback profile '%2' to record")
4136  .arg(profileRequested).arg(profileName));
4137  }
4138  else
4139  {
4140  LOG(VB_RECORD, LOG_ERR, LOC +
4141  QString("Profile '%1' not found, and unable "
4142  "to load fallback profile '%2'. Results "
4143  "may be unpredicable")
4144  .arg(profileRequested).arg(profileName));
4145  }
4146  }
4147 
4148  return profileName;
4149 }
4150 
4155 {
4156  LOG(VB_RECORD, LOG_INFO, LOC + "Starting Recorder");
4157 
4158  bool had_dummyrec = false;
4160  {
4162  ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
4164  had_dummyrec = true;
4165  }
4166 
4168 
4171 
4172  if (m_tvChain)
4173  {
4174  bool ok;
4175  if (!m_ringBuffer)
4176  {
4178  SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4179  }
4180  else
4182  true, !had_dummyrec && m_recorder);
4183  if (!ok)
4184  {
4185  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create RingBuffer 2");
4186  goto err_ret;
4187  }
4188  rec = m_curRecording; // new'd in Create/SwitchLiveTVRingBuffer()
4189  }
4190 
4192  {
4193  bool write = m_genOpt.inputtype != "IMPORT";
4194  LOG(VB_GENERAL, LOG_INFO, LOC + QString("rec->GetPathname(): '%1'")
4195  .arg(rec->GetPathname()));
4197  if (!m_ringBuffer->IsOpen() && write)
4198  {
4199  LOG(VB_GENERAL, LOG_ERR, LOC +
4200  QString("RingBuffer '%1' not open...")
4201  .arg(rec->GetPathname()));
4202  SetRingBuffer(nullptr);
4203  ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
4204  goto err_ret;
4205  }
4206  }
4207 
4208  if (!m_ringBuffer)
4209  {
4210  LOG(VB_GENERAL, LOG_ERR, LOC +
4211  QString("Failed to start recorder! ringBuffer is NULL\n"
4212  "\t\t\t\t Tuning request was %1\n")
4213  .arg(m_lastTuningRequest.toString()));
4214 
4215  if (HasFlags(kFlagLiveTV))
4216  {
4217  QString message = QString("QUIT_LIVETV %1").arg(m_inputid);
4218  MythEvent me(message);
4219  gCoreContext->dispatch(me);
4220  }
4221  goto err_ret;
4222  }
4223 
4224  if (m_channel && m_genOpt.inputtype == "MJPEG")
4225  m_channel->Close(); // Needed because of NVR::MJPEGInit()
4226 
4227  LOG(VB_GENERAL, LOG_INFO, LOC + "TuningNewRecorder - CreateRecorder()");
4229 
4230  if (m_recorder)
4231  {
4234  if (m_recorder->IsErrored())
4235  {
4236  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialize recorder!");
4237  delete m_recorder;
4238  m_recorder = nullptr;
4239  }
4240  }
4241 
4242  if (!m_recorder)
4243  {
4244  LOG(VB_GENERAL, LOG_ERR, LOC +
4245  QString("Failed to start recorder!\n"
4246  "\t\t\t\t Tuning request was %1\n")
4247  .arg(m_lastTuningRequest.toString()));
4248 
4249  if (HasFlags(kFlagLiveTV))
4250  {
4251  QString message = QString("QUIT_LIVETV %1").arg(m_inputid);
4252  MythEvent me(message);
4253  gCoreContext->dispatch(me);
4254  }
4256  goto err_ret;
4257  }
4258 
4259  if (rec)
4260  m_recorder->SetRecording(rec);
4261 
4262  if (GetDTVRecorder() && streamData)
4263  {
4264  const StandardSetting *setting = profile.byName("recordingtype");
4265  if (setting)
4266  streamData->SetRecordingType(setting->getValue());
4267  GetDTVRecorder()->SetStreamData(streamData);
4268  }
4269 
4270  if (m_channel && m_genOpt.inputtype == "MJPEG")
4271  m_channel->Open(); // Needed because of NVR::MJPEGInit()
4272 
4273  // Setup for framebuffer capture devices..
4274  if (m_channel)
4275  {
4278  }
4279 
4280  if (GetV4LChannel())
4281  {
4283  CloseChannel();
4284  }
4285 
4286  m_recorderThread = new MThread("RecThread", m_recorder);
4288 
4289  // Wait for recorder to start.
4290  m_stateChangeLock.unlock();
4291  while (!m_recorder->IsRecording() && !m_recorder->IsErrored())
4292  std::this_thread::sleep_for(std::chrono::microseconds(5));
4293  m_stateChangeLock.lock();
4294 
4295  if (GetV4LChannel())
4297 
4298  SetFlags(kFlagRecorderRunning | kFlagRingBufferReady, __FILE__, __LINE__);
4299 
4300  ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4301 
4302  //workaround for failed import recordings, no signal monitor means we never
4303  //go to recording state and the status here seems to override the status
4304  //set in the importrecorder and backend via setrecordingstatus
4305  if (m_genOpt.inputtype == "IMPORT")
4306  {
4308  if (m_curRecording)
4310  }
4311  return;
4312 
4313  err_ret:
4314  SetRecordingStatus(RecStatus::Failed, __LINE__, true);
4316 
4317  if (rec)
4318  {
4319  // Make sure the scheduler knows...
4321  LOG(VB_RECORD, LOG_INFO, LOC +
4322  QString("TuningNewRecorder -- UPDATE_RECORDING_STATUS: %1")
4324  MythEvent me(QString("UPDATE_RECORDING_STATUS %1 %2 %3 %4 %5")
4325  .arg(rec->GetInputID())
4326  .arg(rec->GetChanID())
4328  .arg(RecStatus::Failed)
4330  gCoreContext->dispatch(me);
4331  }
4332 
4333  if (m_tvChain)
4334  delete rec;
4335 }
4336 
4341 {
4342  LOG(VB_RECORD, LOG_INFO, LOC + "Restarting Recorder");
4343 
4344  bool had_dummyrec = false;
4345 
4346  if (m_curRecording)
4347  {
4350  }
4351 
4353  {
4354  ClearFlags(kFlagDummyRecorderRunning, __FILE__, __LINE__);
4355  had_dummyrec = true;
4356  }
4357 
4358  SwitchLiveTVRingBuffer(m_channel->GetChannelName(), true, !had_dummyrec);
4359 
4360  if (had_dummyrec)
4361  {
4363  ProgramInfo *progInfo = m_tvChain->GetProgramAt(-1);
4364  RecordingInfo recinfo(*progInfo);
4365  delete progInfo;
4366  recinfo.SetInputID(m_inputid);
4367  m_recorder->SetRecording(&recinfo);
4368  }
4369  m_recorder->Reset();
4370 
4371  // Set file descriptor of channel from recorder for V4L
4372  if (GetV4LChannel())
4374 
4375  // Some recorders unpause on Reset, others do not...
4376  m_recorder->Unpause();
4377 
4379  {
4381  QString msg1 = QString("Recording: %1 %2 %3 %4")
4382  .arg(rcinfo1->GetTitle()).arg(rcinfo1->GetChanID())
4384  .arg(rcinfo1->GetRecordingEndTime(MythDate::ISODate));
4385  ProgramInfo *rcinfo2 = m_tvChain->GetProgramAt(-1);
4386  QString msg2 = QString("Recording: %1 %2 %3 %4")
4387  .arg(rcinfo2->GetTitle()).arg(rcinfo2->GetChanID())
4389  .arg(rcinfo2->GetRecordingEndTime(MythDate::ISODate));
4390  delete rcinfo2;
4391  LOG(VB_RECORD, LOG_INFO, LOC + "Pseudo LiveTV recording starting." +
4392  "\n\t\t\t" + msg1 + "\n\t\t\t" + msg2);
4393 
4396 
4398 
4399  InitAutoRunJobs(m_curRecording, kAutoRunProfile, nullptr, __LINE__);
4400  }
4401 
4402  ClearFlags(kFlagNeedToStartRecorder, __FILE__, __LINE__);
4403 }
4404 
4405 void TVRec::SetFlags(uint f, const QString & file, int line)
4406 {
4407  QMutexLocker lock(&m_stateChangeLock);
4408  m_stateFlags |= f;
4409  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetFlags(%1) -> %2 @ %3:%4")
4410  .arg(FlagToString(f)).arg(FlagToString(m_stateFlags)).arg(file).arg(line));
4411  WakeEventLoop();
4412 }
4413 
4414 void TVRec::ClearFlags(uint f, const QString & file, int line)
4415 {
4416  QMutexLocker lock(&m_stateChangeLock);
4417  m_stateFlags &= ~f;
4418  LOG(VB_RECORD, LOG_INFO, LOC + QString("ClearFlags(%1) -> %2 @ %3:%4")
4419  .arg(FlagToString(f)).arg(FlagToString(m_stateFlags)).arg(file).arg(line));
4420  WakeEventLoop();
4421 }
4422 
4424 {
4425  QString msg("");
4426 
4427  // General flags
4428  if (kFlagFrontendReady & f)
4429  msg += "FrontendReady,";
4430  if (kFlagRunMainLoop & f)
4431  msg += "RunMainLoop,";
4432  if (kFlagExitPlayer & f)
4433  msg += "ExitPlayer,";
4434  if (kFlagFinishRecording & f)
4435  msg += "FinishRecording,";
4436  if (kFlagErrored & f)
4437  msg += "Errored,";
4438  if (kFlagCancelNextRecording & f)
4439  msg += "CancelNextRecording,";
4440 
4441  // Tuning flags
4442  if ((kFlagRec & f) == kFlagRec)
4443  msg += "REC,";
4444  else
4445  {
4446  if (kFlagLiveTV & f)
4447  msg += "LiveTV,";
4448  if (kFlagRecording & f)
4449  msg += "Recording,";
4450  }
4451  if ((kFlagNoRec & f) == kFlagNoRec)
4452  msg += "NOREC,";
4453  else
4454  {
4455  if (kFlagEITScan & f)
4456  msg += "EITScan,";
4457  if (kFlagCloseRec & f)
4458  msg += "CloseRec,";
4459  if (kFlagKillRec & f)
4460  msg += "KillRec,";
4461  if (kFlagAntennaAdjust & f)
4462  msg += "AntennaAdjust,";
4463  }
4465  msg += "PENDINGACTIONS,";
4466  else
4467  {
4468  if (kFlagWaitingForRecPause & f)
4469  msg += "WaitingForRecPause,";
4470  if (kFlagWaitingForSignal & f)
4471  msg += "WaitingForSignal,";
4472  if (kFlagNeedToStartRecorder & f)
4473  msg += "NeedToStartRecorder,";
4474  if (kFlagKillRingBuffer & f)
4475  msg += "KillRingBuffer,";
4476  }
4477  if ((kFlagAnyRunning & f) == kFlagAnyRunning)
4478  msg += "ANYRUNNING,";
4479  else
4480  {
4481  if (kFlagSignalMonitorRunning & f)
4482  msg += "SignalMonitorRunning,";
4483  if (kFlagEITScannerRunning & f)
4484  msg += "EITScannerRunning,";
4486  msg += "ANYRECRUNNING,";
4487  else
4488  {
4489  if (kFlagDummyRecorderRunning & f)
4490  msg += "DummyRecorderRunning,";
4491  if (kFlagRecorderRunning & f)
4492  msg += "RecorderRunning,";
4493  }
4494  }
4495  if (kFlagRingBufferReady & f)
4496  msg += "RingBufferReady,";
4497 
4498  if (msg.isEmpty())
4499  msg = QString("0x%1").arg(f,0,16);
4500 
4501  return msg;
4502 }
4503 
4505 {
4506  QMutexLocker lock(&m_nextLiveTVDirLock);
4507 
4508  bool found = !m_nextLiveTVDir.isEmpty();
4509  if (!found && m_triggerLiveTVDir.wait(&m_nextLiveTVDirLock, 500))
4510  {
4511  found = !m_nextLiveTVDir.isEmpty();
4512  }
4513 
4514  return found;
4515 }
4516 
4517 void TVRec::SetNextLiveTVDir(QString dir)
4518 {
4519  QMutexLocker lock(&m_nextLiveTVDirLock);
4520 
4521  m_nextLiveTVDir = std::move(dir);
4522  m_triggerLiveTVDir.wakeAll();
4523 }
4524 
4526  RingBuffer **rb,
4527  const QString & channum)
4528 {
4529  LOG(VB_RECORD, LOG_INFO, LOC + "GetProgramRingBufferForLiveTV()");
4530  if (!m_channel || !m_tvChain || !pginfo || !rb)
4531  return false;
4532 
4533  m_nextLiveTVDirLock.lock();
4534  m_nextLiveTVDir.clear();
4535  m_nextLiveTVDirLock.unlock();
4536 
4537  // Dispatch this early, the response can take a while.
4538  MythEvent me(QString("QUERY_NEXT_LIVETV_DIR %1").arg(m_inputid));
4539  gCoreContext->dispatch(me);
4540 
4541  uint sourceid = m_channel->GetSourceID();
4542  int chanid = ChannelUtil::GetChanID(sourceid, channum);
4543 
4544  if (chanid < 0)
4545  {
4546  // Test setups might have zero channels
4547  if (m_genOpt.inputtype == "IMPORT" || m_genOpt.inputtype == "DEMO")
4548  chanid = 9999;
4549  else
4550  {
4551  LOG(VB_GENERAL, LOG_ERR, LOC +
4552  QString("Channel: \'%1\' was not found in the database.\n"
4553  "\t\tMost likely, the 'starting channel' for this "
4554  "Input Connection is invalid.\n"
4555  "\t\tCould not start livetv.").arg(channum));
4556  return false;
4557  }
4558  }
4559 
4560  int hoursMax = gCoreContext->GetNumSetting("MaxHoursPerLiveTVRecording", 8);
4561  if (hoursMax <= 0)
4562  hoursMax = 8;
4563 
4564  RecordingInfo *prog = nullptr;
4567  else
4568  {
4569  prog = new RecordingInfo(
4570  chanid, MythDate::current(true), true, hoursMax);
4571  }
4572 
4573  prog->SetInputID(m_inputid);
4574 
4575  if (prog->GetRecordingStartTime() == prog->GetRecordingEndTime())
4576  {
4577  LOG(VB_GENERAL, LOG_ERR, LOC + "GetProgramRingBufferForLiveTV()"
4578  "\n\t\t\tProgramInfo is invalid."
4579  "\n" + prog->toString());
4580  prog->SetScheduledEndTime(prog->GetRecordingStartTime().addSecs(3600));
4582 
4583  prog->SetChanID(chanid);
4584  }
4585 
4588 
4589  prog->SetStorageGroup("LiveTV");
4590 
4591  if (WaitForNextLiveTVDir())
4592  {
4593  QMutexLocker lock(&m_nextLiveTVDirLock);
4595  }
4596  else
4597  {
4598  StorageGroup sgroup("LiveTV", gCoreContext->GetHostName());
4599  prog->SetPathname(sgroup.FindNextDirMostFree());
4600  }
4601 
4603  prog->SetRecordingGroup("LiveTV");
4604 
4605  StartedRecording(prog);
4606 
4607  *rb = RingBuffer::Create(prog->GetPathname(), true);
4608  if (!(*rb) || !(*rb)->IsOpen())
4609  {
4610  LOG(VB_GENERAL, LOG_ERR, LOC + QString("RingBuffer '%1' not open...")
4611  .arg(prog->GetPathname()));
4612 
4613  delete *rb;
4614  delete prog;
4615 
4616  return false;
4617  }
4618 
4619  *pginfo = prog;
4620  return true;
4621 }
4622 
4623 bool TVRec::CreateLiveTVRingBuffer(const QString & channum)
4624 {
4625  LOG(VB_RECORD, LOG_INFO, LOC + QString("CreateLiveTVRingBuffer(%1)")
4626  .arg(channum));
4627 
4628  RecordingInfo *pginfo = nullptr;
4629  RingBuffer *rb = nullptr;
4630  QString inputName;
4631 
4632  if (!m_channel ||
4633  !m_channel->CheckChannel(channum))
4634  {
4636  return false;
4637  }
4638 
4639  if (!GetProgramRingBufferForLiveTV(&pginfo, &rb, channum))
4640  {
4641  ClearFlags(kFlagPendingActions, __FILE__, __LINE__);
4643  LOG(VB_GENERAL, LOG_ERR, LOC +
4644  QString("CreateLiveTVRingBuffer(%1) failed").arg(channum));
4645  return false;
4646  }
4647 
4648  SetRingBuffer(rb);
4649 
4653 
4654  bool discont = (m_tvChain->TotalSize() > 0);
4656  m_channel->GetInputName(), discont);
4657 
4658  if (m_curRecording)
4659  {
4661  delete m_curRecording;
4662  }
4663 
4664  m_curRecording = pginfo;
4666 
4667  return true;
4668 }
4669 
4670 bool TVRec::SwitchLiveTVRingBuffer(const QString & channum,
4671  bool discont, bool set_rec)
4672 {
4673  QString msg;
4674  if (m_curRecording)
4675  {
4676  msg = QString(" curRec(%1) curRec.size(%2)")
4677  .arg(m_curRecording->MakeUniqueKey())
4678  .arg(m_curRecording->GetFilesize());
4679  }
4680  LOG(VB_RECORD, LOG_INFO, LOC +
4681  QString("SwitchLiveTVRingBuffer(discont %1, set_next_rec %2)")
4682  .arg(discont).arg(set_rec) + msg);
4683 
4684  RecordingInfo *pginfo = nullptr;
4685  RingBuffer *rb = nullptr;
4686  QString inputName;
4687 
4688  if (!m_channel ||
4689  !m_channel->CheckChannel(channum))
4690  {
4692  return false;
4693  }
4694 
4695  if (!GetProgramRingBufferForLiveTV(&pginfo, &rb, channum))
4696  {
4698  return false;
4699  }
4700 
4701  QString oldinputtype = m_tvChain->GetInputType(-1);
4702 
4703  pginfo->MarkAsInUse(true, kRecorderInUseID);
4708  m_channel->GetInputName(), discont);
4709 
4710  if (set_rec && m_recorder)
4711  {
4712  m_recorder->SetNextRecording(pginfo, rb);
4713  if (discont)
4715  delete pginfo;
4716  SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4717  }
4718  else if (!set_rec)
4719  {
4720  // dummy recordings are finished before this
4721  // is called and other recordings must be finished..
4722  if (m_curRecording && oldinputtype != "DUMMY")
4723  {
4726  delete m_curRecording;
4727  }
4728  m_curRecording = pginfo;
4729  SetRingBuffer(rb);
4730  }
4731  else
4732  {
4733  delete rb;
4734  }
4735 
4736  return true;
4737 }
4738 
4740 {
4741  LOG(VB_RECORD, LOG_INFO, LOC + "SwitchRecordingRingBuffer()");
4742 
4743  if (m_switchingBuffer)
4744  {
4745  LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4746  "already switching.");
4747  return nullptr;
4748  }
4749 
4750  if (!m_recorder)
4751  {
4752  LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4753  "invalid recorder.");
4754  return nullptr;
4755  }
4756 
4757  if (!m_curRecording)
4758  {
4759  LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4760  "invalid recording.");
4761  return nullptr;
4762  }
4763 
4764  if (rcinfo.GetChanID() != m_curRecording->GetChanID())
4765  {
4766  LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer -> "
4767  "Not the same channel.");
4768  return nullptr;
4769  }
4770 
4771  RecordingInfo *ri = new RecordingInfo(rcinfo);
4773 
4774  QString pn = LoadProfile(nullptr, ri, profile);
4775 
4776  if (pn != m_recProfileName)
4777  {
4778  LOG(VB_RECORD, LOG_ERR, LOC +
4779  QString("SwitchRecordingRingBuffer() -> "
4780  "cannot switch profile '%1' to '%2'")
4781  .arg(m_recProfileName).arg(pn));
4782  return nullptr;
4783  }
4784 
4786 
4787  ri->MarkAsInUse(true, kRecorderInUseID);
4788  StartedRecording(ri);
4789 
4790  bool write = m_genOpt.inputtype != "IMPORT";
4792  if (!rb || !rb->IsOpen())
4793  {
4794  delete rb;
4796  FinishedRecording(ri, nullptr);
4797  ri->MarkAsInUse(false, kRecorderInUseID);
4798  delete ri;
4799  LOG(VB_RECORD, LOG_ERR, LOC + "SwitchRecordingRingBuffer() -> "
4800  "Failed to create new RB.");
4801  return nullptr;
4802  }
4803 
4804  m_recorder->SetNextRecording(ri, rb);
4805  SetFlags(kFlagRingBufferReady, __FILE__, __LINE__);
4807  m_switchingBuffer = true;
4809  LOG(VB_RECORD, LOG_INFO, LOC + "SwitchRecordingRingBuffer -> done");
4810  return ri;
4811 }
4812 
4814 {
4815  QMap<uint,TVRec*>::const_iterator it = s_inputs.find(inputid);
4816  if (it == s_inputs.end())
4817  return nullptr;
4818  return *it;
4819 }
4820 
4821 QString TuningRequest::toString(void) const
4822 {
4823  return QString("Program(%1) channel(%2) input(%3) flags(%4)")
4824  .arg((program == nullptr) ? QString("NULL") : program->toString())
4825  .arg(channel).arg(input)
4826  .arg(TVRec::FlagToString(flags));
4827 }
4828 
4829 #ifdef USING_DVB
4830 #include "dvbchannel.h"
4832 {
4833  // Some DVB devices munge the PMT and/or PAT so the CRC check fails.
4834  // We need to tell the stream data class to not check the CRC on
4835  // these devices. This can cause segfaults.
4836  if (dynamic_cast<DVBChannel*>(c))
4837  s->SetIgnoreCRC(dynamic_cast<DVBChannel*>(c)->HasCRCBug());
4838 }
4839 #else
4841 #endif // USING_DVB
4842 
4843 /* vim: set expandtab tabstop=4 shiftwidth=4: */
uint flags
Definition: tv_rec.h:123
uint dvb_tuning_delay
Definition: tv_rec.h:91
DTVSignalMonitor * GetDTVSignalMonitor(void)
Definition: tv_rec.cpp:2160
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:782
QMutex m_pendingRecLock
Definition: tv_rec.h:390
void SetStorageGroup(const QString &group)
Definition: programinfo.h:515
bool GetKeyframeDurations(int64_t start, int64_t end, frm_pos_map_t &) const
Definition: tv_rec.cpp:2618
QString GetID(void) const
Definition: livetvchain.h:53
QWaitCondition m_triggerLiveTVDir
Definition: tv_rec.h:422
bool GetProgramRingBufferForLiveTV(RecordingInfo **pginfo, RingBuffer **rb, const QString &channum)
Definition: tv_rec.cpp:4525
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
virtual int ChangePictureAttribute(PictureAdjustType, PictureAttribute, bool)
Definition: channelbase.h:95
void AddListener(SignalMonitorListener *listener)
def write(text, progress=True)
Definition: mythburn.py:279
int audiosamplerate
Definition: tv_rec.h:79
virtual void SetRotorTarget(float)
Sets rotor target pos from 0.0 to 1.0.
QString TuningGetChanNum(const TuningRequest &, QString &input) const
Definition: tv_rec.cpp:3390
ProgramInfo * info
Definition: tv_rec.h:140
Error State, if we ever try to enter this state errored is set.
Definition: tv.h:54
virtual int GetChanID(void) const
void SetVideoStreamsRequired(uint num)
virtual void Reset(void)
Fetch information on previous channel.
Definition: tv.h:41
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:863
uint GetInputID(void) const
Definition: programinfo.h:455
static void GetPreviewImage(const ProgramInfo &pginfo, const QString &token)
Submit a request for the generation of a preview image.
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
#define LOC
Definition: tv_rec.cpp:46
This is a specialization of RecorderBase used to handle MPEG-2, MPEG-4, MPEG-4 AVC,...
Definition: dtvrecorder.h:28
long long GetKeyframePosition(long long desired) const
Returns closest keyframe position before the desired frame.
int TotalSize(void) const
static QString GetInputName(uint inputid)
Definition: cardutil.cpp:1683
virtual void Close(void)=0
Closes the channel changing hardware to use.
RecordingInfo * m_curRecording
Definition: tv_rec.h:409
void StopRecording(bool killFile=false)
Changes from a recording state to kState_None.
Definition: tv_rec.cpp:702
RingBuffer * m_ringBuffer
Definition: tv_rec.h:429
Watching LiveTV is the state for when we are watching a recording and the user has control over the c...
Definition: tv.h:63
uint GetInputId(void)
Returns the inputid.
Definition: tv_rec.h:241
int ChangePictureAttribute(PictureAdjustType type, PictureAttribute attr, bool direction)
Returns current value [0,100] if it succeeds, -1 otherwise.
Definition: tv_rec.cpp:3006
static RecorderBase * CreateRecorder(TVRec *tvrec, ChannelBase *channel, const RecordingProfile &profile, const GeneralDBOptions &genOpt)
bool m_runJobOnHostOnly
Definition: tv_rec.h:367
virtual void ReturnCachedTable(const PSIPTable *psip) const
void SavePositionMap(bool force=false, bool finished=false)
Save the seektable to the DB.
AutoRunInitType
Definition: tv_rec.h:332
long long GetFramesWritten(void)
Returns number of frames written to disk by recorder.
Definition: tv_rec.cpp:2559
int m_eitCrawlIdleStart
Definition: tv_rec.h:368
QString model
Definition: tv_rec.h:102
QString GetInput(void) const
Returns current input.
Definition: tv_rec.cpp:3022
static const uint kFlagNeedToStartRecorder
Definition: tv_rec.h:470
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:90
static const uint kFlagRecorderRunning
Definition: tv_rec.h:478
static SignalMonitor * Init(const QString &cardtype, int db_cardnum, ChannelBase *channel, bool release_stream)
void SetPathname(const QString &) const
uint GetTransportID(void) const
Returns DVB transport_stream_id, 0 if unknown.
Definition: dtvchannel.h:107
void RecordPending(const ProgramInfo *rcinfo, int secsleft, bool hasLater)
Tells TVRec "rcinfo" is the next pending recording.
Definition: tv_rec.cpp:271
RecStatus::Type m_recStatus
Definition: tv_rec.h:406
bool m_reachedPreFail
Definition: tv_rec.h:356
int GetProgramNumber(void) const
Returns program number in PAT, -1 if unknown.
Definition: dtvchannel.h:91
QString m_recProfileName
Definition: tv_rec.h:385
int m_audioSampleRateDB
Definition: tv_rec.h:370
static vector< uint > GetConflictingInputs(uint inputid)
Definition: cardutil.cpp:2013
const char * kRecorderInUseID
void RingBufferChanged(RingBuffer *, RecordingInfo *, RecordingQuality *)
Definition: tv_rec.cpp:3368
uint minorChan
Definition: tv_rec.h:128
static const uint kFlagRecording
final result desired is a timed recording
Definition: tv_rec.h:451
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
void FinishedRecording(RecordingInfo *, RecordingQuality *)
If not a premature stop, adds program to history of recorded programs.
Definition: tv_rec.cpp:811
uint GetSourceID(void) const
Definition: programinfo.h:454
bool ShouldSwitchToAnotherInput(const QString &chanid)
Checks if named channel exists on current tuner, or another tuner.
Definition: tv_rec.cpp:2176
virtual void Initialize(void)=0
This is called between SetOptionsFromProfile() and run() to initialize any devices,...
QMutex m_stateChangeLock
Definition: tv_rec.h:389
bool TuningOnSameMultiplex(TuningRequest &request)
Definition: tv_rec.cpp:3429
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)
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:4739
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
uint channel_timeout
Definition: tv_rec.h:82
void ToStringList(QStringList &list) const
Serializes ProgramInfo into a QStringList which can be passed over a socket.
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:311
void TuningNewRecorder(MPEGStreamData *)
Creates a recorder instance.
Definition: tv_rec.cpp:4154
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
QString vbidev
Definition: tv_rec.h:76
QString GetTitle(void) const
Definition: programinfo.h:353
bool GetKeyframePositions(int64_t start, int64_t end, frm_pos_map_t &) const
Returns byte position in RingBuffer of a keyframes according to recorder.
Definition: tv_rec.cpp:2607
static const uint kFlagEITScannerRunning
Definition: tv_rec.h:475
static const uint kFlagDummyRecorderRunning
Definition: tv_rec.h:477
void SetInputType(const QString &type)
Definition: livetvchain.cpp:53
void SetRecordingRuleID(uint id)
Definition: programinfo.h:523
vector< pid_cache_item_t > pid_cache_t
Definition: channelutil.h:43
static const uint kFlagRingBufferReady
Definition: tv_rec.h:483
int size(void) const
Definition: mythdbcon.h:203
bool isConnected(void)
Only updated once during object creation.
Definition: mythdbcon.h:135
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:34
DTVChannel * GetDTVChannel(void)
Definition: tv_rec.cpp:1173
MThread * m_eventThread
Event processing thread, runs TVRec::run().
Definition: tv_rec.h:360
bool dvb_eitscan
Definition: tv_rec.h:92
QString toString(Verbosity v=kLongDescription, const QString &sep=":", const QString &grp="\"") const
static const uint kFlagWaitingForSignal
Definition: tv_rec.h:469
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:2476
QString channel
Definition: tv_rec.h:125
static const uint kFlagRec
Definition: tv_rec.h:454
Fetch browse information on current channel and time.
Definition: tv.h:40
uint m_inputid
unique key in DB for this input
Definition: inputinfo.h:71
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
static int GetProgramNumber(uint sourceid, const QString &channum)
Definition: channelutil.h:181
QString SetInput(QString input)
Changes to the specified input.
Definition: tv_rec.cpp:3047
uint GetSourceID(void) const
Returns current source id.
Definition: tv_rec.cpp:3032
bool IsCommercialFree(void) const
Definition: programinfo.h:470
void SetRecordingStatus(RecStatus::Type new_status, int line, bool have_lock=false)
Definition: tv_rec.cpp:672
QString m_overRecordCategory
Definition: tv_rec.h:373
unsigned int uint
Definition: compat.h:140
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static RingBuffer * Create(const QString &xfilename, bool write, bool usereadahead=true, int timeout_ms=kDefaultOpenTimeout, bool stream_only=false)
Creates a RingBuffer instance.
Definition: ringbuffer.cpp:104
uint signal_timeout
Definition: tv_rec.h:81
Watching Pre-recorded is a TV only state for when we are watching a pre-existing recording.
Definition: tv.h:67
virtual void SetVideoFilters(QString &filters)=0
Tells recorder which filters to use.
RecordingType m_type
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:1100
QMutex m_triggerEventSleepLock
Definition: tv_rec.h:402
virtual bool Open(void)=0
Opens the channel changing hardware for use.
static const uint kFlagCancelNextRecording
Definition: tv_rec.h:445
void UpdateInUseMark(bool force=false)
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...
bool GetKeyframeDurations(long long start, long long end, frm_pos_map_t &) const
static void apply_broken_dvb_driver_crc_hack(ChannelBase *, MPEGStreamData *)
Definition: tv_rec.cpp:4831
RecordingFile * GetRecordingFile() const
uint m_mplexid
mplexid restriction if applicable
Definition: inputinfo.h:72
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:410
bool IsOnSameMultiplex(void) const
Definition: tv_rec.h:120
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:749
~TVRec(void)
Stops the event and scanning threads and deletes any ChannelBase, RingBuffer, and RecorderBase instan...
Definition: tv_rec.cpp:176
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:2264
void StartedRecording(RecordingInfo *)
Inserts a "curRec" into the database.
Definition: tv_rec.cpp:785
PendingMap m_pendingRecordings
Definition: tv_rec.h:416
static const uint kFlagSignalMonitorRunning
Definition: tv_rec.h:474
uint RemoteGetState(uint inputid)
void SetPseudoLiveTVRecording(RecordingInfo *)
Sets the pseudo LiveTV RecordingInfo.
Definition: tv_rec.cpp:324
static const uint kFlagEITScan
final result desired is an EIT Scan
Definition: tv_rec.h:458
void LoadRecordingFile()
static const uint kFlagCloseRec
close recorder, keep recording
Definition: tv_rec.h:460
virtual int GetPictureAttribute(PictureAttribute) const
Definition: channelbase.h:94
static const uint kFlagDetect
Definition: tv_rec.h:484
#define LOC2
Definition: tv_rec.cpp:47
static bool GetATSCChannel(uint sourceid, const QString &channum, uint &major, uint &minor)
bool m_ispip
Definition: tv_rec.h:378
bool IsErrored(void) const
Definition: signalmonitor.h:80
bool IsSameProgramWeakCheck(const ProgramInfo &other) const
Checks for duplicate using only title, chanid and startts.
virtual QString getValue(void) const
static bool IsEncoder(const QString &rawtype)
Definition: cardutil.h:121
bool dvb_on_demand
Definition: tv_rec.h:90
int GetBackendServerPort(void)
Returns the locally defined backend control port.
void ChangeState(TVState nextState)
Puts a state change on the nextState queue.
Definition: tv_rec.cpp:1078
virtual void SetStreamData(MPEGStreamData *data)
void FinishedRecording(bool allowReRecord)
If not a premature stop, adds program to history of recorded programs.
bool wait_for_seqstart
Definition: tv_rec.h:83
uint m_inputid
Definition: tv_rec.h:376
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:462
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
Fetch information on next channel.
Definition: tv.h:42
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:382
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:1799
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:331
bool m_triggerEventLoopSignal
Definition: tv_rec.h:401
bool Save(bool sendSig=true)
RecordingInfo * program
Definition: tv_rec.h:124
MarkTypes
Definition: programtypes.h:48
long long GetMaxBitrate(void) const
Returns the maximum bits per second this recorder can produce.
Definition: tv_rec.cpp:2634
enum BrowseDirections BrowseDirection
Used to request ProgramInfo for channel browsing.
void ToggleChannelFavorite(const QString &)
Toggles whether the current channel should be on our favorites list.
Definition: tv_rec.cpp:2938
Holds information on recordings and videos.
Definition: programinfo.h:66
uint QueryAverageHeight(void) const
If present in recording this loads average height of the main video stream from database's stream mar...
friend class TuningRequest
Definition: tv_rec.h:154
virtual int GetVideoFd(void)=0
Returns file descriptor of recorder device.
EITScanner * m_scanner
Definition: tv_rec.h:346
static bool GetDevices(uint inputid, uint &parentid, GeneralDBOptions &gen_opts, DVBDBOptions &dvb_opts, FireWireDBOptions &firewire_opts)
Definition: tv_rec.cpp:1626
static QString FlagToString(uint)
Definition: tv_rec.cpp:4423
virtual long long GetFramesWritten(void)=0
Returns number of frames written to disk.
QDateTime m_eitScanStartTime
Definition: tv_rec.h:398
void ReloadAll(const QStringList &data=QStringList())
void SetScheduledEndTime(const QDateTime &dt)
Definition: programinfo.h:509
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:388
LiveTVChain * m_tvChain
Definition: tv_rec.h:426
static QReadWriteLock s_inputsLock
Definition: tv_rec.h:433
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:479
int progNum
Definition: tv_rec.h:129
static const uint kFlagLiveTV
final result desired is LiveTV recording
Definition: tv_rec.h:449
static const uint kFlagPendingActions
Definition: tv_rec.h:471
bool skip_btaudio
Definition: tv_rec.h:80
bool QueueEITChannelChange(const QString &name)
Queues up a channel change for the EITScanner.
Definition: tv_rec.cpp:3122
uint m_parentid
Definition: tv_rec.h:377
QString m_nextLiveTVDir
Definition: tv_rec.h:420
QHash< QString, int > m_autoRunJobs
Definition: tv_rec.h:412
QWaitCondition m_triggerEventSleepWait
Definition: tv_rec.h:403
Class providing a generic interface to digital tuning hardware.
Definition: dtvchannel.h:34
QString GetChainID(void)
Get the chainid of the livetv instance.
Definition: tv_rec.cpp:2684
static int GetChanID(int db_mplexid, int service_transport_id, int major_channel, int minor_channel, int program_number)
void CloseChannel(void)
Definition: tv_rec.cpp:1160
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:150
void SaveVideoProperties(uint mask, uint video_property_flags)
bool hasLaterShowing
Definition: tv_rec.h:142
static bool QueueRecordingJobs(const RecordingInfo &, int jobTypes=JOB_NONE)
Definition: jobqueue.cpp:496
TVState m_desiredNextState
Definition: tv_rec.h:392
bool m_pauseNotify
Definition: tv_rec.h:394
void SpawnLiveTV(LiveTVChain *newchain, bool pip, QString startchan)
Tells TVRec to spawn a "Live TV" recorder.
Definition: tv_rec.cpp:2654
long long GetFilePosition(void)
Returns total number of bytes written by RingBuffer.
Definition: tv_rec.cpp:2574
ProgramInfo * GetRecording(void)
Allocates and returns a ProgramInfo for the current recording.
Definition: tv_rec.cpp:239
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:1260
deque< TuningRequest >::iterator iterator
Definition: mythdeque.h:44
uint GetMinorChannel(void) const
Returns minor channel, 0 if unknown.
Definition: dtvchannel.h:99
static int num_inputs(void)
Definition: tv_rec.cpp:1227
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:183
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
T dequeue()
Removes item from front of list and returns a copy. O(1).
Definition: mythdeque.h:32
QString GetSetting(const QString &key, const QString &defaultval="")
void SetDesiredProgram(int p)
static const uint kFlagRunMainLoop
Definition: tv_rec.h:441
void NotifySchedulerOfRecording(RecordingInfo *)
Tell scheduler about the recording.
Definition: tv_rec.cpp:2733
unsigned char t
Definition: ParseText.cpp:329
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:345
QString LoadProfile(void *, RecordingInfo *, RecordingProfile &)
Definition: tv_rec.cpp:4108
void SetDuration(int duration)
contains a duration during which the notification will be displayed for.
static QString toString(Type, uint id)
Converts "recstatus" into a short (unreadable) string.
Definition: recStatus.cpp:39
QString GetFormat(void)
Definition: dtvchannel.h:47
int SetSignalMonitoringRate(int rate, int notifyFrontend=1)
Sets the signal monitoring rate.
Definition: tv_rec.cpp:2122
#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:355
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:2030
bool IsLocal(void) const
Definition: programinfo.h:343
void RecorderPaused(void)
This is a callback, called by the "recorder" instance when it has actually paused.
Definition: tv_rec.cpp:2929
QMutex m_nextLiveTVDirLock
Definition: tv_rec.h:421
bool ask
Definition: tv_rec.h:144
void GetCachedPids(pid_cache_t &pid_cache) const
Returns cached MPEG PIDs for last tuned channel.
Definition: dtvchannel.cpp:96
void AppendNewProgram(ProgramInfo *pginfo, const QString &channum, const QString &inputname, bool discont)
Definition: livetvchain.cpp:64
QDateTime m_signalEventCmdTimeout
Definition: tv_rec.h:348
virtual bool InitPictureAttributes(void)
Definition: channelbase.h:93
float GetFramerate(void)
Returns recordering frame rate from the recorder.
Definition: tv_rec.cpp:2544
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:411
static const uint kSignalMonitoringRate
How many milliseconds the signal monitor should wait between checks.
Definition: tv_rec.h:437
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:2910
uint GetRecordingID(void) const
Definition: programinfo.h:438
bool HasChannel(uint major, uint minor) const
virtual bool IsOpen(void) const =0
Returns true if open for either reading or writing.
int m_overRecordSecCat
Definition: tv_rec.h:372
PictureAttribute
Definition: videoouttypes.h:89
void SendMythSystemRecEvent(const QString &msg, const RecordingInfo *pginfo)
V4LChannel * GetV4LChannel(void)
Definition: tv_rec.cpp:1178
void SetRecordingStatus(RecStatus::Type status)
Definition: programinfo.h:565
int m_overRecordSecNrml
Definition: tv_rec.h:371
static const uint kFlagKillRingBuffer
Definition: tv_rec.h:465
bool RemoteRecordPending(uint inputid, const ProgramInfo *pginfo, int secsleft, bool hasLater)
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
QString QueryRecordingGroup(void) const
Query recgroup from recorded.
#define TRANSITION(ASTATE, BSTATE)
Definition: tv_rec.cpp:982
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:389
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:4340
void UpdateRecordingEnd(void)
Update information in the recorded table when the end-time of a recording is changed.
static bool JobIsNotInMask(int job, int mask)
Definition: jobqueue.h:198
bool CheckChannel(const QString &channum) const
MPEGStreamData * GetStreamData(void) const
Definition: dtvrecorder.h:60
const char * name
Definition: ParseText.cpp:328
virtual void StoreInputChannels(void)
Saves current channel as the default channel for the current input.
RecorderBase * m_recorder
Definition: tv_rec.h:343
Implements tuning for TV cards using the V4L driver API, both versions 1 and 2.
Definition: v4lchannel.h:32
virtual void Unpause(void)
Unpause tells recorder to unpause.
void SetDesiredChannel(int major, int minor)
RecordingInfo * m_pseudoLiveTVRecording
Definition: tv_rec.h:419
QString m_liveTVStartChannel
Definition: tv_rec.h:423
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:1511
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:2590
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:563
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:430
#define SET_NEXT()
Definition: tv_rec.cpp:984
void HandleStateChange(void)
Changes the internalState to the desiredNextState if possible.
Definition: tv_rec.cpp:994
TVState m_internalState
Definition: tv_rec.h:391
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:2084
TVState GetState(void) const
Returns the TVState of the recorder.
Definition: tv_rec.cpp:225
bool WaitForNextLiveTVDir(void)
Definition: tv_rec.cpp:4504
QDateTime m_signalMonitorDeadline
Definition: tv_rec.h:352
bool m_signalEventCmdSent
Definition: tv_rec.h:349
void StartPassiveScan(ChannelBase *, EITSource *)
Start inserting Event Information Tables from the multiplex we happen to be tuned to into the databas...
Definition: eitscanner.cpp:199
const MasterGuideTable * GetCachedMGT(bool current=true) const
int elapsed(void) const
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:90
virtual void AddListeningPID(uint pid, PIDPriority priority=kPIDPriorityNormal)
void StopReads(void)
????
Definition: ringbuffer.cpp:711
static int eit_start_rand(int eitTransportTimeout)
Definition: tv_rec.cpp:1247
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:381
void SetHostPrefix(const QString &prefix)
Definition: livetvchain.cpp:48
PictureAttribute next(PictureAttributeSupported supported, PictureAttribute attribute)
ATSCStreamData * GetATSCStreamData()
Returns the ATSC stream data if it exists.
void SetChanID(uint _chanid)
Definition: programinfo.h:507
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
QString audiodev
Definition: tv_rec.h:77
uint m_chanid
chanid restriction if applicable
Definition: inputinfo.h:73
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:807
void SetInputID(uint id)
Definition: programinfo.h:525
bool SetupDTVSignalMonitor(bool EITscan)
Tells DTVSignalMonitor what channel to look for.
Definition: tv_rec.cpp:1849
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:252
TVRec(int _inputid)
Performs instance initialization not requiring access to database.
Definition: tv_rec.cpp:85
int m_eitTransportTimeout
Definition: tv_rec.h:369
virtual bool IsRecording(void)
Tells whether the StartRecorder() loop is running.
QDateTime GetRecordEndTime(const ProgramInfo *) const
Returns recording end time with proper post-roll.
Definition: tv_rec.cpp:334
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void SetCaching(bool cacheTables)
Abstract class providing a generic interface to tuning hardware.
Definition: channelbase.h:31
RecStatus::Type GetRecordingStatus(void) const
Definition: programinfo.h:439
int GetPictureAttribute(PictureAttribute attr)
Definition: tv_rec.cpp:2988
PictureAdjustType
Definition: tv.h:120
virtual uint GetNextChannel(uint chanid, ChannelChangeDirection direction) const
TVState RemovePlaying(TVState state)
Returns TVState that would remove the playing, but potentially keep recording if we are watching an i...
Definition: tv_rec.cpp:765
bool GetBoolSetting(const QString &key, bool defaultval=false)
This table tells the decoder on which PIDs to find other tables, and their sizes and each table's cur...
Definition: atsctables.h:74
void InitAutoRunJobs(RecordingInfo *, AutoRunInitType, RecordingProfile *, int line)
Definition: tv_rec.cpp:2779
FireWireDBOptions m_fwOpt
Definition: tv_rec.h:383
void Reset(void) override
Definition: dvbstreamdata.h:35
bool SetVideoFiltersForChannel(uint sourceid, const QString &channum)
Definition: tv_rec.cpp:2445
static const uint kFlagWaitingForRecPause
Definition: tv_rec.h:468
bool HasFlags(uint f) const
Definition: tv_rec.h:294
void FinishedRecording(ProgramInfo *pginfo)
Fetch information on current channel in the past.
Definition: tv.h:43
uint m_sourceid
associated channel listings source
Definition: inputinfo.h:70
QString inputtype
Definition: tv_rec.h:78
static void Init()
Initializes the some static constants needed by SignalMonitorValue.
void SetNextLiveTVDir(QString dir)
Definition: tv_rec.cpp:4517
RecordingType GetRecordingRuleType(void) const
Definition: programinfo.h:443
bool SwitchLiveTVRingBuffer(const QString &channum, bool discont, bool set_rec)
Definition: tv_rec.cpp:4670
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:3312
void SetRecording(const RecordingInfo *pginfo)
Changes the Recording from the one set initially with SetOptionsFromProfile().
bool m_transcodeFirst
Definition: tv_rec.h:365
QDateTime recordingStart
Definition: tv_rec.h:141
RecStatus::Type GetRecordingStatus(void) const
Definition: tv_rec.cpp:666
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:364
static bool is_dishnet_eit(uint inputid)
Definition: tv_rec.cpp:1207
static const uint kFlagFrontendReady
Definition: tv_rec.h:440
virtual RecordingQuality * GetRecordingQuality(const RecordingInfo *) const
Returns a report about the current recordings quality.
QWaitCondition m_triggerEventLoopWait
Definition: tv_rec.h:400
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:245
void StartActiveScan(TVRec *, uint max_seconds_per_source)
Definition: eitscanner.cpp:234
TuningRequest m_lastTuningRequest
Definition: tv_rec.h:397
void HandlePendingRecordings(void)
Definition: tv_rec.cpp:1548
MThread * m_recorderThread
Recorder thread, runs RecorderBase::run().
Definition: tv_rec.h:362
vector< uint > possibleConflicts
Definition: tv_rec.h:146
void SetRecordingRuleType(RecordingType type)
Definition: programinfo.h:566
QString m_recProfile
virtual void SetFd(int fd)
Sets file descriptor.
Definition: channelbase.h:55
static const uint64_t kDTVSigMon_WaitForSDT
uint GetRecordingRuleID(void) const
Definition: programinfo.h:441
volatile bool m_switchingBuffer
Definition: tv_rec.h:405
QString toStringXML(void) const
static bool IsOnSameMultiplex(uint srcid, const QString &new_channum, const QString &old_channum)
Fetch information on current channel in the future.
Definition: tv.h:44
QString input
Definition: tv_rec.h:126
bool m_triggerEventSleepSignal
Definition: tv_rec.h:404
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:3148
static const uint kFlagAnyRunning
Definition: tv_rec.h:480
void SetDishNetEIT(bool)
static QString GetStartChannel(uint inputid)
Definition: tv_rec.cpp:1713
GoomState states[STATES_NB]
Definition: goom_core.c:52
void SetRecordingStartTime(const QDateTime &dt)
Definition: programinfo.h:510
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:1816
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:396
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:4813
void SetRecordingEndTime(const QDateTime &dt)
Definition: programinfo.h:511
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:404
uint TableType(uint i) const
Definition: atsctables.h:120
void SetNextRecording(const RecordingInfo *, RingBuffer *)
Sets next recording info, to be applied as soon as practical.
AutoExpireType GetAutoExpire(void) const
Definition: recordingrule.h:63
void TuningShutdowns(const TuningRequest &)
This shuts down anything that needs to be shut down before handling the passed in tuning request.
Definition: tv_rec.cpp:3552
virtual bool SetChannelByString(const QString &chan)=0
uint QueryMplexID(void) const
Queries multiplex any recording would be made on, zero if unknown.
static uint GetSourceID(uint inputid)
Definition: cardutil.cpp:1731
void CheckForRecGroupChange(void)
Check if frontend changed the recording group.
Definition: tv_rec.cpp:2699
static bool IsSupported(const QString &cardtype)
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
void SetRingBuffer(RingBuffer *)
Sets "ringBuffer", deleting any existing RingBuffer.
Definition: tv_rec.cpp:3351
bool StateIsPlaying(TVState state)
Returns true if we are in any state associated with a player.
Definition: tv_rec.cpp:739
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:4623
bool m_reachedRecordingDeadline
Definition: tv_rec.h:354
static long int random(void)
Definition: compat.h:149