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 "channelutil.h"
20 #include "eithelper.h"
21 #include "eitscanner.h"
22 #include "recorders/channelbase.h"
23 #include "scheduledrecording.h"
24 #include "tv_rec.h"
25 
26 #define LOC QString("EITScanner: ")
27 #define LOC_ID QString("EITScanner[%1]: ").arg(m_cardnum)
28 
36  : m_eitHelper(new EITHelper(cardnum)),
37  m_eventThread(new MThread("EIT", this)),
38  m_activeScanNextChanIndex(MythRandom()),
39  m_cardnum(cardnum)
40 {
41  QStringList langPref = iso639_get_language_list();
43 
44  LOG(VB_EIT, LOG_INFO, LOC_ID + "Start EIT scanner thread");
45  m_eventThread->start(QThread::IdlePriority);
46 }
47 
49 {
51  if (!m_exitThread)
52  {
53  m_lock.lock();
54  m_exitThread = true;
55  m_exitThreadCond.wakeAll();
56  m_lock.unlock();
57  }
59  delete m_eventThread;
60  m_eventThread = nullptr;
61 
62  if (m_eitHelper)
63  {
64  delete m_eitHelper;
65  m_eitHelper = nullptr;
66  }
67 }
68 
72 void EITScanner::run(void)
73 {
74  m_lock.lock();
75 
76  MythTimer t;
77  uint eitCount = 0;
78 
79  while (!m_exitThread)
80  {
81  m_lock.unlock();
82  uint list_size = m_eitHelper->GetListSize();
83 
84  m_lock.lock();
85  if (m_eitSource)
86  m_eitSource->SetEITRate(1.0F);
87  m_lock.unlock();
88 
89  if (list_size)
90  {
91  eitCount += m_eitHelper->ProcessEvents();
92  t.start();
93  }
94 
95  // Tell the scheduler to run if we are in passive scan
96  // and there have been updated events since the last scheduler run
97  // but not in the last 60 seconds
98  if (!m_activeScan && eitCount && (t.elapsed() > 60s))
99  {
100  LOG(VB_EIT, LOG_INFO,
101  LOC_ID + QString("Added %1 EIT events in passive scan").arg(eitCount));
102  eitCount = 0;
104  }
105 
106  // Is it time to move to the next transport in active scan?
108  {
109  // If there have been any new events, tell scheduler to run.
110  if (eitCount)
111  {
112  LOG(VB_EIT, LOG_INFO,
113  LOC_ID + QString("Added %1 EIT events in active scan").arg(eitCount));
114  eitCount = 0;
116  }
117 
119  {
122  }
123 
124  if (!(*m_activeScanNextChan).isEmpty())
125  {
128  {
131  LOG(VB_EIT, LOG_INFO, LOC_ID +
132  QString("Next looking for EIT data on multiplex of channel %1 of source %2")
133  .arg(*m_activeScanNextChan).arg(m_rec->GetSourceID()));
134  }
135  }
136 
138  .addSecs(m_activeScanTrigTime.count());
139  if (!m_activeScanChannels.empty())
140  {
144  }
145 
146  // 24 hours ago
147  EITHelper::PruneEITCache(m_activeScanNextTrig.toSecsSinceEpoch() - 86400);
148  }
149 
150  m_lock.lock();
152  m_exitThreadCond.wait(&m_lock, 400); // sleep up to 400 ms.
153 
155  {
156  m_activeScanStopped = true;
157  m_activeScanCond.wakeAll();
158  }
159  }
160 
161  if (eitCount) /* some events have been handled since the last schedule request */
162  {
164  }
165 
166  m_activeScanStopped = true;
167  m_activeScanCond.wakeAll();
168  m_lock.unlock();
169 }
170 
175 {
177 }
178 
184  EITSource *eitSource)
185 {
186  QMutexLocker locker(&m_lock);
187 
188  m_eitSource = eitSource;
189  m_channel = channel;
190 
192  m_eitSource->SetEITRate(1.0F);
193  int chanid = m_channel->GetChanID();
194  if (chanid > 0)
195  {
196  m_eitHelper->SetChannelID(chanid);
198  LOG(VB_EIT, LOG_INFO, LOC_ID +
199  QString("Start processing EIT events in %1 scan for channel %2 chanid %3")
200  .arg(m_activeScan ? "active" : "passive",
202  QString::number(chanid)));
203  }
204  else
205  {
206  LOG(VB_EIT, LOG_INFO, LOC_ID +
207  QString("Failed to start processing EIT events, invalid chanid:%1")
208  .arg(chanid));
209  }
210 }
211 
216 {
217  QMutexLocker locker(&m_lock);
218 
219  if (m_eitSource)
220  {
221  m_eitSource->SetEITHelper(nullptr);
222  m_eitSource = nullptr;
223  }
224  m_channel = nullptr;
225 
229  LOG(VB_EIT, LOG_INFO, LOC_ID +
230  QString("Stop processing EIT events in %1 scan")
231  .arg(m_activeScanStopped ? "passive" : "active"));
232 }
233 
234 void EITScanner::StartActiveScan(TVRec *rec, std::chrono::seconds max_seconds_per_source)
235 {
236  m_rec = rec;
237 
238  if (m_activeScanChannels.isEmpty())
239  {
240  // TODO get input name and use it in crawl.
241  MSqlQuery query(MSqlQuery::InitCon());
242  query.prepare(
243  "SELECT channum, MIN(chanid) "
244  "FROM channel, capturecard, videosource "
245  "WHERE deleted IS NULL AND "
246  " capturecard.sourceid = channel.sourceid AND "
247  " videosource.sourceid = channel.sourceid AND "
248  " channel.mplexid IS NOT NULL AND "
249  " visible > 0 AND "
250  " useonairguide = 1 AND "
251  " useeit = 1 AND "
252  " channum != '' AND "
253  " capturecard.cardid = :CARDID "
254  "GROUP BY mplexid "
255  "ORDER BY capturecard.sourceid, mplexid, "
256  " atsc_major_chan, atsc_minor_chan ");
257  query.bindValue(":CARDID", m_rec->GetInputId());
258 
259  if (!query.exec() || !query.isActive())
260  {
261  MythDB::DBError("EITScanner::StartActiveScan", query);
262  return;
263  }
264 
265  while (query.next())
266  m_activeScanChannels.push_back(query.value(0).toString());
267 
269  }
270 
271  LOG(VB_EIT, LOG_INFO, LOC_ID +
272  QString("StartActiveScan called with %1 multiplexes")
273  .arg(m_activeScanChannels.size()));
274 
275  // Start at a random channel. This is so that multiple cards with
276  // the same source don't all scan the same channels in the same
277  // order when the backend is first started up.
278  if (!m_activeScanChannels.empty())
279  {
280  // The start channel is random. From now on, start on the
281  // next channel. This makes sure the immediately following
282  // channels get scanned in a timely manner if we keep erroring
283  // out on the previous channel.
288 
290  m_activeScanTrigTime = max_seconds_per_source;
291  // Add a little randomness to trigger time so multiple
292  // cards will have a staggered channel changing time.
293  m_activeScanTrigTime += std::chrono::seconds(MythRandom(0, 28));
294 
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();
310  locker.relock();
311 
312  while (!m_activeScan && !m_activeScanStopped)
313  m_activeScanCond.wait(&m_lock, 100);
314 
315  m_rec = nullptr;
316 }
MSqlQuery::isActive
bool isActive(void) const
Definition: mythdbcon.h:216
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:807
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
EITHelper::WriteEITCache
static void WriteEITCache(void)
Definition: eithelper.cpp:763
EITScanner::RescheduleRecordings
void RescheduleRecordings(void)
Tells scheduler about programming changes.
Definition: eitscanner.cpp:174
MThread::start
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:286
EITScanner::StopEITEventProcessing
void StopEITEventProcessing(void)
Stops inserting Event Information Tables into DB.
Definition: eitscanner.cpp:215
mythdb.h
mythrandom.h
EITScanner::m_exitThread
volatile bool m_exitThread
Definition: eitscanner.h:54
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:140
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:303
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:183
EITScanner::m_activeScanNextTrig
QDateTime m_activeScanNextTrig
Definition: eitscanner.h:61
EITScanner::m_activeScanCond
QWaitCondition m_activeScanCond
Definition: eitscanner.h:60
EITScanner::m_eitSource
EITSource * m_eitSource
Definition: eitscanner.h:50
TVRec::GetSourceID
uint GetSourceID(void) const
Returns current source id.
Definition: tv_rec.cpp:2987
EITScanner::EITScanner
EITScanner(uint cardnum)
Definition: eitscanner.cpp:35
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:205
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:608
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
channelbase.h
ChannelBase::GetChannelName
virtual QString GetChannelName(void) const
Definition: channelbase.h:64
EITScanner::m_rec
TVRec * m_rec
Definition: eitscanner.h:57
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:52
EITSource::SetEITRate
virtual void SetEITRate(float rate)=0
EITScanner::m_activeScanNextChanIndex
uint m_activeScanNextChanIndex
Definition: eitscanner.h:65
EITScanner::StopActiveScan
void StopActiveScan(void)
Definition: eitscanner.cpp:300
EITHelper::RescheduleRecordings
void RescheduleRecordings(void)
Tells scheduler about programming changes.
Definition: eithelper.cpp:1408
TVRec::GetInputId
uint GetInputId(void) const
Returns the inputid.
Definition: tv_rec.h:236
mythdate.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:1309
mythlogging.h
ChannelBase
Abstract class providing a generic interface to tuning hardware.
Definition: channelbase.h:31
hardwareprofile.i18n.t
t
Definition: i18n.py:36
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:540
compat.h
eithelper.h
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:227
EITScanner::m_eventThread
MThread * m_eventThread
Definition: eitscanner.h:53
EITHelper::SetChannelID
void SetChannelID(uint channelid)
Definition: eithelper.cpp:146
EITSource::SetEITHelper
virtual void SetEITHelper(EITHelper *)=0
scheduledrecording.h
EITScanner::m_exitThreadCond
QWaitCondition m_exitThreadCond
Definition: eitscanner.h:55
EITScanner::m_lock
QMutex m_lock
Definition: eitscanner.h:48
uint
unsigned int uint
Definition: compat.h:79
EITHelper
Definition: eithelper.h:93
EITScanner::m_activeScanTrigTime
std::chrono::seconds m_activeScanTrigTime
Definition: eitscanner.h:62
TVRec::QueueEITChannelChange
bool QueueEITChannelChange(const QString &name)
Queues up a channel change for the EITScanner.
Definition: tv_rec.cpp:3077
EITScanner::m_activeScan
volatile bool m_activeScan
Definition: eitscanner.h:58
channelutil.h
EITHelper::SetLanguagePreferences
void SetLanguagePreferences(const QStringList &langPref)
Definition: eithelper.cpp:123
EITScanner::m_activeScanNextChan
QStringList::iterator m_activeScanNextChan
Definition: eitscanner.h:64
EITScanner::m_channel
ChannelBase * m_channel
Definition: eitscanner.h:49
EITHelper::ProcessEvents
uint ProcessEvents(void)
Get events from queue and insert into DB after processing.
Definition: eithelper.cpp:74
LOC_ID
#define LOC_ID
Definition: eitscanner.cpp:27
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:883
ChannelUtil::GetSourceIDForChannel
static uint GetSourceIDForChannel(uint chanid)
Definition: channelutil.cpp:805
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 'exitThread' is true.
Definition: eitscanner.cpp:72
TVRec
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:144
MThread
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:48
mthread.h
tv_rec.h
mythtimer.h
iso639.h
ISO 639-1 and ISO 639-2 support functions.
EITHelper::GetListSize
uint GetListSize(void) const
Definition: eithelper.cpp:53
EITHelper::PruneEITCache
static void PruneEITCache(uint timestamp)
Definition: eithelper.cpp:758
EITScanner::m_activeScanChannels
QStringList m_activeScanChannels
Definition: eitscanner.h:63
EITScanner::m_activeScanStopped
volatile bool m_activeScanStopped
Definition: eitscanner.h:59
MythRandomStd::MythRandom
uint32_t MythRandom()
generate 32 random bits
Definition: mythrandom.h:20
EITScanner::StartActiveScan
void StartActiveScan(TVRec *rec, std::chrono::seconds max_seconds_per_source)
Definition: eitscanner.cpp:234
EITScanner::TeardownAll
void TeardownAll(void)
Definition: eitscanner.cpp:48
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:832
ChannelBase::GetChanID
virtual int GetChanID(void) const
Definition: channelbase.cpp:496