MythTV master
eitscanner.cpp
Go to the documentation of this file.
1// -*- Mode: c++ -*-
2
3// POSIX headers
4#include <sys/time.h>
6
7// C++
8#include <cstdlib>
9
10// MythTV
11#include "libmythbase/iso639.h"
12#include "libmythbase/mthread.h"
14#include "libmythbase/mythdb.h"
18
19#include "cardutil.h"
20#include "channelutil.h"
21#include "eithelper.h"
22#include "eitscanner.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 m_sourceid(CardUtil::GetSourceID(cardnum))
40{
41 QStringList langPref = iso639_get_language_list();
43
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
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
253void 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.
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
350 m_activeScanCond.wait(&m_lock, 100);
351
352 m_rec = nullptr;
353}
Collection of helper utilities for input DB use.
Definition: cardutil.h:42
Abstract class providing a generic interface to tuning hardware.
Definition: channelbase.h:32
virtual QString GetChannelName(void) const
Definition: channelbase.h:64
virtual int GetChanID(void) const
static uint GetMplexID(uint sourceid, const QString &channum)
static int GetChanID(int db_mplexid, int service_transport_id, int major_channel, int minor_channel, int program_number)
void SetSourceID(uint sourceid)
Definition: eithelper.cpp:156
static void PruneEITCache(uint timestamp)
Definition: eithelper.cpp:780
uint GetListSize(void) const
Definition: eithelper.cpp:66
uint ProcessEvents(void)
Get events from queue and insert into DB after processing.
Definition: eithelper.cpp:87
void SetLanguagePreferences(const QStringList &langPref)
Definition: eithelper.cpp:139
void RescheduleRecordings(void)
Tells scheduler about programming changes.
Definition: eithelper.cpp:1365
static void WriteEITCache(void)
Definition: eithelper.cpp:785
void SetChannelID(uint channelid)
Definition: eithelper.cpp:162
ChannelBase * m_channel
Definition: eitscanner.h:48
MThread * m_eventThread
Definition: eitscanner.h:52
uint m_eitCount
Definition: eitscanner.h:68
EITScanner(uint cardnum)
Definition: eitscanner.cpp:35
void TeardownAll(void)
Stop active scan, delete thread and delete eithelper.
Definition: eitscanner.cpp:55
QMutex m_lock
Definition: eitscanner.h:47
uint m_sourceid
Definition: eitscanner.h:66
EITHelper * m_eitHelper
Definition: eitscanner.h:51
void StopEITEventProcessing(void)
Stops inserting Event Information Tables into DB.
Definition: eitscanner.cpp:221
void run(void) override
This runs the event loop for EITScanner until 'm_exitThread' is true.
Definition: eitscanner.cpp:79
QWaitCondition m_activeScanCond
Definition: eitscanner.h:59
QDateTime m_activeScanNextTrig
Definition: eitscanner.h:60
TVRec * m_rec
Definition: eitscanner.h:56
void RescheduleRecordings(void)
Tells scheduler about programming changes.
Definition: eitscanner.cpp:179
QWaitCondition m_exitThreadCond
Definition: eitscanner.h:54
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
void StartActiveScan(TVRec *rec, std::chrono::seconds max_seconds_per_multiplex)
Start active EIT scan.
Definition: eitscanner.cpp:253
volatile bool m_activeScan
Definition: eitscanner.h:57
std::chrono::seconds m_activeScanTrigTime
Definition: eitscanner.h:61
QStringList::iterator m_activeScanNextChan
Definition: eitscanner.h:63
QStringList m_activeScanChannels
Definition: eitscanner.h:62
volatile bool m_exitThread
Definition: eitscanner.h:53
EITSource * m_eitSource
Definition: eitscanner.h:49
volatile bool m_activeScanStopped
Definition: eitscanner.h:58
QString m_sourceName
Definition: eitscanner.h:67
uint m_mplexid
Definition: eitscanner.h:69
void StopActiveScan(void)
Stop active EIT scan.
Definition: eitscanner.cpp:333
virtual void SetEITHelper(EITHelper *)=0
virtual void SetEITRate(float rate)=0
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
QVariant value(int i) const
Definition: mythdbcon.h:204
bool isActive(void) const
Definition: mythdbcon.h:215
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:49
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:14
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:91
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
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
static QString GetSourceName(uint sourceid)
Definition: sourceutil.cpp:47
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:143
bool QueueEITChannelChange(const QString &name)
Queues up a channel change for the EITScanner.
Definition: tv_rec.cpp:3188
uint GetInputId(void) const
Returns the inputid.
Definition: tv_rec.h:234
uint GetSourceID(void) const
Returns current source id.
Definition: tv_rec.cpp:3098
#define LOC
Definition: eitscanner.cpp:28
unsigned int uint
Definition: freesurround.h:24
QStringList iso639_get_language_list(void)
Returns list of three character ISO-639 language descriptors, starting with the most preferred.
Definition: iso639.cpp:33
ISO 639-1 and ISO 639-2 support functions.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
Convenience inline random number generator functions.
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
uint32_t MythRandom()
generate 32 random bits
Definition: mythrandom.h:20