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