MythTV  master
eitscanner.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 
3 // POSIX headers
4 #include <sys/time.h>
5 #include "compat.h"
6 
7 #include <cstdlib>
8 
9 #include "scheduledrecording.h"
10 #include "channelbase.h"
11 #include "channelutil.h"
12 #include "mythlogging.h"
13 #include "eitscanner.h"
14 #include "eithelper.h"
15 #include "mythtimer.h"
16 #include "mythdate.h"
17 #include "mthread.h"
18 #include "iso639.h"
19 #include "mythdb.h"
20 #include "tv_rec.h"
21 
22 #define LOC QString("EITScanner: ")
23 #define LOC_ID QString("EITScanner[%1]: ").arg(m_cardnum)
24 
33  : m_eitHelper(new EITHelper()), m_eventThread(new MThread("EIT", this)),
34  m_cardnum(cardnum)
35 {
36  QStringList langPref = iso639_get_language_list();
38 
39  m_eventThread->start(QThread::IdlePriority);
40 }
41 
43 {
45  if (!m_exitThread)
46  {
47  m_lock.lock();
48  m_exitThread = true;
49  m_exitThreadCond.wakeAll();
50  m_lock.unlock();
51  }
53  delete m_eventThread;
54  m_eventThread = nullptr;
55 
56  if (m_eitHelper)
57  {
58  delete m_eitHelper;
59  m_eitHelper = nullptr;
60  }
61 }
62 
66 void EITScanner::run(void)
67 {
68  static constexpr uint kSz[] = { 2000, 1800, 1600, 1400, 1200, };
69  static constexpr float kRt[] = { 0.0F, 0.2F, 0.4F, 0.6F, 0.8F, };
70 
71  m_lock.lock();
72 
73  MythTimer t;
74  uint eitCount = 0;
75 
76  while (!m_exitThread)
77  {
78  m_lock.unlock();
79  uint list_size = m_eitHelper->GetListSize();
80 
81  float rate = 1.0F;
82  for (uint i = 0; i < 5; i++)
83  {
84  if (list_size >= kSz[i])
85  {
86  rate = kRt[i];
87  break;
88  }
89  }
90 
91  m_lock.lock();
92  if (m_eitSource)
93  m_eitSource->SetEITRate(rate);
94  m_lock.unlock();
95 
96  if (list_size)
97  {
98  eitCount += m_eitHelper->ProcessEvents();
99  t.start();
100  }
101 
102  // Tell the scheduler to run if
103  // we are in passive scan
104  // and there have been updated events since the last scheduler run
105  // but not in the last 60 seconds
106  if (!m_activeScan && eitCount && (t.elapsed() > 60 * 1000))
107  {
108  LOG(VB_EIT, LOG_INFO,
109  LOC_ID + QString("Added %1 EIT Events").arg(eitCount));
110  eitCount = 0;
112  }
113 
114  // Is it time to move to the next transport in active scan?
116  {
117  // if there have been any new events, tell scheduler to run.
118  if (eitCount)
119  {
120  LOG(VB_EIT, LOG_INFO,
121  LOC_ID + QString("Added %1 EIT Events").arg(eitCount));
122  eitCount = 0;
124  }
125 
127  {
130  }
131 
132  if (!(*m_activeScanNextChan).isEmpty())
133  {
136  {
139  LOG(VB_EIT, LOG_INFO,
140  LOC_ID + QString("Now looking for EIT data on "
141  "multiplex of channel %1")
142  .arg(*m_activeScanNextChan));
143  }
144  }
145 
147  .addSecs(m_activeScanTrigTime);
148  if (!m_activeScanChannels.empty())
149  {
153  }
154 
155  // 24 hours ago
156 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
158 #else
159  EITHelper::PruneEITCache(m_activeScanNextTrig.toSecsSinceEpoch() - 86400);
160 #endif
161  }
162 
163  m_lock.lock();
165  m_exitThreadCond.wait(&m_lock, 400); // sleep up to 400 ms.
166 
168  {
169  m_activeScanStopped = true;
170  m_activeScanCond.wakeAll();
171  }
172  }
173 
174  if (eitCount) /* some events have been handled since the last schedule request */
175  {
177  }
178 
179  m_activeScanStopped = true;
180  m_activeScanCond.wakeAll();
181  m_lock.unlock();
182 }
183 
191 {
193 }
194 
200  EITSource *eitSource)
201 {
202  QMutexLocker locker(&m_lock);
203 
204  m_eitSource = eitSource;
205  m_channel = channel;
206 
208  m_eitSource->SetEITRate(1.0F);
211 
212  LOG(VB_EIT, LOG_INFO, LOC_ID + "Started passive scan.");
213 }
214 
219 {
220  QMutexLocker locker(&m_lock);
221 
222  if (m_eitSource)
223  {
224  m_eitSource->SetEITHelper(nullptr);
225  m_eitSource = nullptr;
226  }
227  m_channel = nullptr;
228 
232  LOG(VB_EIT, LOG_INFO, LOC_ID + "Stopped passive scan.");
233 }
234 
235 void EITScanner::StartActiveScan(TVRec *_rec, uint max_seconds_per_source)
236 {
237  m_rec = _rec;
238 
239  if (m_activeScanChannels.isEmpty())
240  {
241  // TODO get input name and use it in crawl.
242  MSqlQuery query(MSqlQuery::InitCon());
243  query.prepare(
244  "SELECT channum, MIN(chanid) "
245  "FROM channel, capturecard, videosource "
246  "WHERE deleted IS NULL AND "
247  " capturecard.sourceid = channel.sourceid AND "
248  " videosource.sourceid = channel.sourceid AND "
249  " channel.mplexid IS NOT NULL AND "
250  " visible > 0 AND "
251  " useonairguide = 1 AND "
252  " useeit = 1 AND "
253  " channum != '' AND "
254  " capturecard.cardid = :CARDID "
255  "GROUP BY mplexid "
256  "ORDER BY capturecard.sourceid, mplexid, "
257  " atsc_major_chan, atsc_minor_chan ");
258  query.bindValue(":CARDID", m_rec->GetInputId());
259 
260  if (!query.exec() || !query.isActive())
261  {
262  MythDB::DBError("EITScanner::StartActiveScan", query);
263  return;
264  }
265 
266  while (query.next())
267  m_activeScanChannels.push_back(query.value(0).toString());
268 
270  }
271 
272  LOG(VB_EIT, LOG_INFO, LOC_ID +
273  QString("StartActiveScan called with %1 multiplexes")
274  .arg(m_activeScanChannels.size()));
275 
276  // Start at a random channel. This is so that multiple cards with
277  // the same source don't all scan the same channels in the same
278  // order when the backend is first started up.
279  if (!m_activeScanChannels.empty())
280  {
281  // The start channel is random. From now on, start on the
282  // next channel. This makes sure the immediately following
283  // channels get scanned in a timely manner if we keep erroring
284  // out on the previous channel.
289 
291  m_activeScanTrigTime = max_seconds_per_source;
292  // Add a little randomness to trigger time so multiple
293  // cards will have a staggered channel changing time.
294  m_activeScanTrigTime += random() % 29;
295  m_activeScanStopped = false;
296  m_activeScan = true;
297  }
298 }
299 
301 {
302  QMutexLocker locker(&m_lock);
303 
304  m_activeScanStopped = false;
305  m_activeScan = false;
306  m_exitThreadCond.wakeAll();
307 
308  locker.unlock();
309  StopPassiveScan();
310  locker.relock();
311 
312  LOG(VB_EIT, LOG_INFO, LOC_ID + "Stopped active scan.");
313 
314  while (!m_activeScan && !m_activeScanStopped)
315  m_activeScanCond.wait(&m_lock, 100);
316 
317  m_rec = nullptr;
318 }
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
ChannelBase * m_channel
Definition: eitscanner.h:52
virtual int GetChanID(void) const
ISO 639-1 and ISO 639-2 support functions.
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:864
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
uint GetInputId(void)
Returns the inputid.
Definition: tv_rec.h:234
uint m_activeScanNextChanIndex
Definition: eitscanner.h:68
void run(void) override
This runs the event loop for EITScanner until 'exitThread' is true.
Definition: eitscanner.cpp:66
virtual void SetEITHelper(EITHelper *)=0
QStringList m_activeScanChannels
Definition: eitscanner.h:66
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:311
MThread * m_eventThread
Definition: eitscanner.h:56
void TeardownAll(void)
Definition: eitscanner.cpp:42
QMutex m_lock
Definition: eitscanner.h:51
uint GetSourceID(void) const
Returns current source id.
Definition: tv_rec.cpp:3044
#define LOC_ID
Definition: eitscanner.cpp:23
QWaitCondition m_exitThreadCond
Definition: eitscanner.h:58
void SetLanguagePreferences(const QStringList &langPref)
Definition: eithelper.cpp:112
QStringList iso639_get_language_list(void)
Returns list of three character ISO-639 language descriptors, starting with the most preferred.
Definition: iso639.cpp:35
uint GetListSize(void) const
Definition: eithelper.cpp:52
void StopPassiveScan(void)
Stops inserting Event Information Tables into DB.
Definition: eitscanner.cpp:218
QVariant value(int i) const
Definition: mythdbcon.h:198
void RescheduleRecordings(void)
Tells scheduler about programming changes.
Definition: eitscanner.cpp:190
static void WriteEITCache(void)
Definition: eithelper.cpp:764
QDateTime m_activeScanNextTrig
Definition: eitscanner.h:64
QStringList::iterator m_activeScanNextChan
Definition: eitscanner.h:67
bool QueueEITChannelChange(const QString &name)
Queues up a channel change for the EITScanner.
Definition: tv_rec.cpp:3134
static int GetChanID(int db_mplexid, int service_transport_id, int major_channel, int minor_channel, int program_number)
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:142
void StartPassiveScan(ChannelBase *channel, EITSource *eitSource)
Start inserting Event Information Tables from the multiplex we happen to be tuned to into the databas...
Definition: eitscanner.cpp:199
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
bool isActive(void) const
Definition: mythdbcon.h:204
volatile bool m_activeScanStopped
Definition: eitscanner.h:62
TVRec * m_rec
Definition: eitscanner.h:60
unsigned int uint
Definition: compat.h:140
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
void SetSourceID(uint _sourceid)
Definition: eithelper.cpp:129
EITHelper * m_eitHelper
Definition: eitscanner.h:55
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808
virtual void SetEITRate(float rate)=0
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static uint GetSourceIDForChannel(uint chanid)
Abstract class providing a generic interface to tuning hardware.
Definition: channelbase.h:31
static void PruneEITCache(uint timestamp)
Definition: eithelper.cpp:759
volatile bool m_exitThread
Definition: eitscanner.h:57
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
QWaitCondition m_activeScanCond
Definition: eitscanner.h:63
void SetChannelID(uint _channelid)
Definition: eithelper.cpp:135
void StartActiveScan(TVRec *_rec, uint max_seconds_per_source)
Definition: eitscanner.cpp:235
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
static long int random(void)
Definition: compat.h:149
uint ProcessEvents(void)
Inserts events in EIT list.
Definition: eithelper.cpp:63
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
volatile bool m_activeScan
Definition: eitscanner.h:61
EITSource * m_eitSource
Definition: eitscanner.h:53
EITScanner(uint cardnum)
Definition: eitscanner.cpp:32
void RescheduleRecordings(void)
Tells scheduler about programming changes.
Definition: eithelper.cpp:1407
void StopActiveScan(void)
Definition: eitscanner.cpp:300
uint m_activeScanTrigTime
Definition: eitscanner.h:65