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 "libmythbase/compat.h"
6 
7 // C++
8 #include <cstdlib>
9 
10 // MythTV
11 #include "libmythbase/iso639.h"
12 #include "libmythbase/mthread.h"
13 #include "libmythbase/mythdate.h"
14 #include "libmythbase/mythdb.h"
16 #include "libmythbase/mythrandom.h"
17 #include "libmythbase/mythtimer.h"
18 
19 #include "cardutil.h"
20 #include "channelutil.h"
21 #include "eithelper.h"
22 #include "eitscanner.h"
23 #include "recorders/channelbase.h"
24 #include "scheduledrecording.h"
25 #include "sourceutil.h"
26 #include "tv_rec.h"
27 
28 #define LOC QString("EITScanner[%1]: ").arg(m_cardnum)
29 
36  : m_eitHelper(new EITHelper(cardnum)),
37  m_eventThread(new MThread("EIT", this)),
38  m_cardnum(cardnum)
39 {
40  QStringList langPref = iso639_get_language_list();
42 
45  LOG(VB_EIT, LOG_INFO, LOC +
46  QString("Start EIT scanner thread for source %1 '%2'")
47  .arg(m_sourceid).arg(m_sourceName));
48 
49  m_eventThread->start(QThread::IdlePriority);
50 }
51 
56 {
58  if (!m_exitThread)
59  {
60  m_lock.lock();
61  m_exitThread = true;
62  m_exitThreadCond.wakeAll();
63  m_lock.unlock();
64  }
66  delete m_eventThread;
67  m_eventThread = nullptr;
68 
69  if (m_eitHelper)
70  {
71  delete m_eitHelper;
72  m_eitHelper = nullptr;
73  }
74 }
75 
79 void EITScanner::run(void)
80 {
81  m_lock.lock();
82 
83  MythTimer tsle; // Time since last event or since start of active scan
84  MythTimer tsrr; // Time since RescheduleRecordings in passive scan
85  tsrr.start();
86  m_eitCount = 0; // Number of new events processed
87 
88  while (!m_exitThread)
89  {
90  m_lock.unlock();
91 
92  uint list_size = m_eitHelper->GetListSize();
93  if (list_size)
94  {
96  tsle.start();
97  }
98 
99  // Run the scheduler every 5 minutes if we are in passive scan
100  // and there have been new events.
101  if (!m_activeScan && m_eitCount && (tsrr.elapsed() > 5min))
102  {
103  LOG(VB_EIT, LOG_INFO, LOC +
104  QString("Added %1 EIT events in passive scan ").arg(m_eitCount) +
105  QString("for source %1 '%2'").arg(m_sourceid).arg(m_sourceName));
106  m_eitCount = 0;
108  tsrr.start();
109  }
110 
111  // Move to the next transport in active scan when no events
112  // have been received for 60 seconds or when the scan time is up.
113  const std::chrono::seconds eventTimeout = 60s;
114  bool noEvents = tsle.isRunning() && (tsle.elapsed() > eventTimeout);
115  bool scanTimeUp = MythDate::current() > m_activeScanNextTrig;
116  if (m_activeScan && (noEvents || scanTimeUp))
117  {
118  if (noEvents && !scanTimeUp)
119  {
120  LOG(VB_EIT, LOG_INFO, LOC +
121  QString("No new EIT events received in last %1 seconds ").arg(eventTimeout.count()) +
122  QString("for source %2 '%3' ").arg(m_sourceid).arg(m_sourceName) +
123  QString("on multiplex %4, move to next multiplex").arg(m_mplexid));
124  }
125 
127  {
129  }
130 
131  if (!(*m_activeScanNextChan).isEmpty())
132  {
135  {
137  m_eitHelper->SetChannelID(chanid);
139  LOG(VB_EIT, LOG_DEBUG, LOC +
140  QString("Next EIT active scan source %1 '%2' multiplex %3 chanid %4 channel %5")
141  .arg(m_sourceid).arg(m_sourceName).arg(m_mplexid).arg(chanid).arg(*m_activeScanNextChan));
142  }
143  }
144 
147 
148  tsle.start();
149 
150  // Remove all EIT cache entries that are more than 24 hours old
151  EITHelper::PruneEITCache(m_activeScanNextTrig.toSecsSinceEpoch() - 86400);
152  }
153 
154  m_lock.lock();
156  m_exitThreadCond.wait(&m_lock, 400); // sleep up to 400 ms.
157 
159  {
160  m_activeScanStopped = true;
161  m_activeScanCond.wakeAll();
162  }
163  }
164 
165  // Some events have been handled since the last schedule request
166  if (m_eitCount)
167  {
169  }
170 
171  m_activeScanStopped = true;
172  m_activeScanCond.wakeAll();
173  m_lock.unlock();
174 }
175 
180 {
182 }
183 
189  EITSource *eitSource)
190 {
191  QMutexLocker locker(&m_lock);
192 
193  m_eitSource = eitSource;
194  m_channel = channel;
195 
197  m_eitSource->SetEITRate(1.0F);
198  int chanid = m_channel->GetChanID();
199  QString channum = m_channel->GetChannelName();
200  if (chanid > 0)
201  {
203  m_eitHelper->SetChannelID(chanid);
205  LOG(VB_EIT, LOG_INFO, LOC +
206  QString("Start EIT %1 scan source %2 '%3' multiplex %4 chanid %5 channel %6")
207  .arg(m_activeScan ? "active" : "passive").arg(m_sourceid).arg(m_sourceName)
208  .arg(m_mplexid).arg(chanid).arg(channum));
209  }
210  else
211  {
212  LOG(VB_EIT, LOG_INFO, LOC +
213  QString("Failed to start EIT scan, invalid chanid %1 channum %2")
214  .arg(chanid).arg(channum));
215  }
216 }
217 
222 {
223  QMutexLocker locker(&m_lock);
224 
225  if (!m_eitSource)
226  return;
227 
228  LOG(VB_EIT, LOG_INFO, LOC +
229  QString("Stop EIT scan source %1 '%2', added %3 EIT events")
230  .arg(m_sourceid).arg(m_sourceName).arg(m_eitCount));
231 
232  if (m_eitCount)
233  {
234  m_eitCount = 0;
236  }
237 
238  if (m_eitSource)
239  {
240  m_eitSource->SetEITHelper(nullptr);
241  m_eitSource = nullptr;
242  }
243  m_channel = nullptr;
244 
248 }
249 
253 void EITScanner::StartActiveScan(TVRec *rec, std::chrono::seconds max_seconds_per_multiplex)
254 {
255  QMutexLocker locker(&m_lock);
256 
257  m_rec = rec;
258 
259  if (m_activeScanChannels.isEmpty())
260  {
261  // Create a channel list with one channel from each multiplex
262  // from the video source connected to this input.
263  // This list is then used in the EIT crawl so that all
264  // available multiplexes are visited in turn.
265  MSqlQuery query(MSqlQuery::InitCon());
266  query.prepare(
267  "SELECT channum, MIN(chanid) "
268  "FROM channel, capturecard, videosource "
269  "WHERE capturecard.sourceid = channel.sourceid AND "
270  " videosource.sourceid = channel.sourceid AND "
271  " channel.deleted IS NULL AND "
272  " channel.mplexid IS NOT NULL AND "
273  " channel.visible > 0 AND "
274  " channel.useonairguide = 1 AND "
275  " channel.channum != '' AND "
276  " videosource.useeit = 1 AND "
277  " capturecard.cardid = :CARDID "
278  "GROUP BY mplexid "
279  "ORDER BY capturecard.sourceid, mplexid, "
280  " atsc_major_chan, atsc_minor_chan ");
281  query.bindValue(":CARDID", m_rec->GetInputId());
282 
283  if (!query.exec() || !query.isActive())
284  {
285  MythDB::DBError("EITScanner::StartActiveScan", query);
286  return;
287  }
288 
289  while (query.next())
290  m_activeScanChannels.push_back(query.value(0).toString());
291 
292  // Start at a random channel. This is so that multiple cards with
293  // the same source don't all scan the same channels in the same
294  // order when the backend is first started up.
295  // Also, from now on, always start on the next channel.
296  // This makes sure the immediately following channels get scanned
297  // in a timely manner if we keep erroring out on the previous channel.
298 
299  if (!m_activeScanChannels.empty())
300  {
301  auto nextChanIndex = MythRandom() % m_activeScanChannels.size();
302  m_activeScanNextChan = m_activeScanChannels.begin() + nextChanIndex;
303  }
304  uint sourceid = m_rec->GetSourceID();
305  QString sourcename = SourceUtil::GetSourceName(sourceid);
306  LOG(VB_EIT, LOG_INFO, LOC +
307  QString("EIT scan channel table for source %1 '%2' with %3 multiplexes")
308  .arg(sourceid).arg(sourcename).arg(m_activeScanChannels.size()));
309  }
310 
311  if (!m_activeScanChannels.empty())
312  {
313  // Start scan now
315 
316  // Add a little randomness to trigger time so multiple
317  // cards will have a staggered channel changing time.
318  m_activeScanTrigTime = max_seconds_per_multiplex;
319  m_activeScanTrigTime += std::chrono::seconds(MythRandom(0, 28));
320 
321  m_activeScanStopped = false;
322  m_activeScan = true;
323 
324  LOG(VB_EIT, LOG_INFO, LOC +
325  QString("Start EIT active scan on source %1 '%2'")
326  .arg(m_sourceid).arg(m_sourceName));
327  }
328 }
329 
334 {
335  QMutexLocker locker(&m_lock);
336 
337  m_activeScanStopped = false;
338  m_activeScan = false;
339  m_exitThreadCond.wakeAll();
340 
341  locker.unlock();
343  locker.relock();
344 
345  LOG(VB_EIT, LOG_INFO, LOC +
346  QString("Stop EIT active scan on source %1 '%2'")
347  .arg(m_sourceid).arg(m_sourceName));
348 
349  while (!m_activeScan && !m_activeScanStopped)
350  m_activeScanCond.wait(&m_lock, 100);
351 
352  m_rec = nullptr;
353 }
MSqlQuery::isActive
bool isActive(void) const
Definition: mythdbcon.h:215
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:127
MythTimer::elapsed
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:91
EITHelper::WriteEITCache
static void WriteEITCache(void)
Definition: eithelper.cpp:778
EITScanner::RescheduleRecordings
void RescheduleRecordings(void)
Tells scheduler about programming changes.
Definition: eitscanner.cpp:179
MThread::start
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
EITScanner::StopEITEventProcessing
void StopEITEventProcessing(void)
Stops inserting Event Information Tables into DB.
Definition: eitscanner.cpp:221
EITScanner::m_cardnum
uint m_cardnum
Definition: eitscanner.h:65
mythdb.h
SourceUtil::GetSourceName
static QString GetSourceName(uint sourceid)
Definition: sourceutil.cpp:47
mythrandom.h
EITScanner::m_exitThread
volatile bool m_exitThread
Definition: eitscanner.h:53
MythTimer
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:13
EITHelper::SetSourceID
void SetSourceID(uint sourceid)
Definition: eithelper.cpp:155
MThread::wait
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
eitscanner.h
EITScanner::StartEITEventProcessing
void StartEITEventProcessing(ChannelBase *channel, EITSource *eitSource)
Start inserting Event Information Tables from the multiplex we happen to be tuned to into the databas...
Definition: eitscanner.cpp:188
EITScanner::m_activeScanNextTrig
QDateTime m_activeScanNextTrig
Definition: eitscanner.h:60
EITScanner::m_activeScanCond
QWaitCondition m_activeScanCond
Definition: eitscanner.h:59
EITScanner::m_eitSource
EITSource * m_eitSource
Definition: eitscanner.h:49
EITScanner::m_sourceid
uint m_sourceid
Definition: eitscanner.h:66
TVRec::GetSourceID
uint GetSourceID(void) const
Returns current source id.
Definition: tv_rec.cpp:3086
EITScanner::EITScanner
EITScanner(uint cardnum)
Definition: eitscanner.cpp:35
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:204
MythTimer::isRunning
bool isRunning(void) const
Returns true if start() or restart() has been called at least once since construction and since any c...
Definition: mythtimer.cpp:135
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
EITScanner::m_sourceName
QString m_sourceName
Definition: eitscanner.h:67
MythTimer::start
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
channelbase.h
ChannelUtil::GetMplexID
static uint GetMplexID(uint sourceid, const QString &channum)
Definition: channelutil.cpp:462
ChannelBase::GetChannelName
virtual QString GetChannelName(void) const
Definition: channelbase.h:64
EITScanner::m_rec
TVRec * m_rec
Definition: eitscanner.h:56
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:14
EITScanner::m_eitHelper
EITHelper * m_eitHelper
Definition: eitscanner.h:51
EITSource::SetEITRate
virtual void SetEITRate(float rate)=0
EITScanner::StopActiveScan
void StopActiveScan(void)
Stop active EIT scan.
Definition: eitscanner.cpp:333
EITHelper::RescheduleRecordings
void RescheduleRecordings(void)
Tells scheduler about programming changes.
Definition: eithelper.cpp:1426
TVRec::GetInputId
uint GetInputId(void) const
Returns the inputid.
Definition: tv_rec.h:234
mythdate.h
sourceutil.h
ChannelUtil::GetChanID
static int GetChanID(int db_mplexid, int service_transport_id, int major_channel, int minor_channel, int program_number)
Definition: channelutil.cpp:1312
mythlogging.h
ChannelBase
Abstract class providing a generic interface to tuning hardware.
Definition: channelbase.h:31
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
compat.h
eithelper.h
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
EITScanner::m_eventThread
MThread * m_eventThread
Definition: eitscanner.h:52
EITHelper::SetChannelID
void SetChannelID(uint channelid)
Definition: eithelper.cpp:161
EITSource::SetEITHelper
virtual void SetEITHelper(EITHelper *)=0
EITScanner::StartActiveScan
void StartActiveScan(TVRec *rec, std::chrono::seconds max_seconds_per_multiplex)
Start active EIT scan.
Definition: eitscanner.cpp:253
scheduledrecording.h
EITScanner::m_exitThreadCond
QWaitCondition m_exitThreadCond
Definition: eitscanner.h:54
EITScanner::m_lock
QMutex m_lock
Definition: eitscanner.h:47
uint
unsigned int uint
Definition: compat.h:81
EITHelper
Definition: eithelper.h:93
EITScanner::m_activeScanTrigTime
std::chrono::seconds m_activeScanTrigTime
Definition: eitscanner.h:61
TVRec::QueueEITChannelChange
bool QueueEITChannelChange(const QString &name)
Queues up a channel change for the EITScanner.
Definition: tv_rec.cpp:3176
EITScanner::m_activeScan
volatile bool m_activeScan
Definition: eitscanner.h:57
LOC
#define LOC
Definition: eitscanner.cpp:28
channelutil.h
EITHelper::SetLanguagePreferences
void SetLanguagePreferences(const QStringList &langPref)
Definition: eithelper.cpp:138
EITScanner::m_activeScanNextChan
QStringList::iterator m_activeScanNextChan
Definition: eitscanner.h:63
EITScanner::m_channel
ChannelBase * m_channel
Definition: eitscanner.h:48
EITHelper::ProcessEvents
uint ProcessEvents(void)
Get events from queue and insert into DB after processing.
Definition: eithelper.cpp:86
cardutil.h
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
iso639_get_language_list
QStringList iso639_get_language_list(void)
Returns list of three character ISO-639 language descriptors, starting with the most preferred.
Definition: iso639.cpp:33
EITScanner::run
void run(void) override
This runs the event loop for EITScanner until 'm_exitThread' is true.
Definition: eitscanner.cpp:79
TVRec
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:142
MThread
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:48
CardUtil::GetSourceID
static uint GetSourceID(uint inputid)
Definition: cardutil.cpp:1944
mthread.h
tv_rec.h
EITScanner::m_mplexid
uint m_mplexid
Definition: eitscanner.h:69
mythtimer.h
iso639.h
ISO 639-1 and ISO 639-2 support functions.
EITHelper::GetListSize
uint GetListSize(void) const
Definition: eithelper.cpp:65
EITHelper::PruneEITCache
static void PruneEITCache(uint timestamp)
Definition: eithelper.cpp:773
EITScanner::m_activeScanChannels
QStringList m_activeScanChannels
Definition: eitscanner.h:62
EITScanner::m_activeScanStopped
volatile bool m_activeScanStopped
Definition: eitscanner.h:58
MythRandomStd::MythRandom
uint32_t MythRandom()
generate 32 random bits
Definition: mythrandom.h:20
EITScanner::TeardownAll
void TeardownAll(void)
Stop active scan, delete thread and delete eithelper.
Definition: eitscanner.cpp:55
EITSource
Definition: eitscanner.h:19
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838
EITScanner::m_eitCount
uint m_eitCount
Definition: eitscanner.h:68
ChannelBase::GetChanID
virtual int GetChanID(void) const
Definition: channelbase.cpp:494