MythTV  master
signalmonitor.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 // Copyright (c) 2005, Daniel Thor Kristjansson
3 
4 // C headers
5 #include <csignal>
6 #include <sys/types.h>
7 #include <unistd.h>
8 
9 // MythTV headers
10 #include "scriptsignalmonitor.h"
11 #include "signalmonitor.h"
12 #include "mythcontext.h"
13 #include "compat.h"
14 #include "mythlogging.h"
15 #include "tv_rec.h"
16 
17 extern "C" {
18 #include "libavcodec/avcodec.h"
19 }
20 #include "mythdate.h"
21 
22 #ifdef USING_DVB
23 # include "dvbsignalmonitor.h"
24 # include "dvbchannel.h"
25 #endif
26 
27 #ifdef USING_V4L2
28 // Old
29 # include "analogsignalmonitor.h"
30 // New
31 # include "v4l2encsignalmonitor.h"
32 # include "v4lchannel.h"
33 #endif
34 
35 #ifdef USING_HDHOMERUN
36 # include "hdhrsignalmonitor.h"
37 # include "hdhrchannel.h"
38 #endif
39 
40 #ifdef USING_IPTV
41 # include "iptvsignalmonitor.h"
42 # include "iptvchannel.h"
43 #endif
44 
45 #ifdef USING_FIREWIRE
46 # include "firewiresignalmonitor.h"
47 # include "firewirechannel.h"
48 #endif
49 
50 #ifdef USING_ASI
51 # include "asisignalmonitor.h"
52 # include "asichannel.h"
53 #endif
54 
55 #ifdef USING_CETON
56 # include "cetonsignalmonitor.h"
57 # include "cetonchannel.h"
58 #endif
59 
60 #include "ExternalSignalMonitor.h"
61 #include "ExternalChannel.h"
62 
63 #undef DBG_SM
64 #define DBG_SM(FUNC, MSG) LOG(VB_CHANNEL, LOG_DEBUG, \
65  QString("SigMon[%1](%2)::%3: %4").arg(m_inputid) \
66  .arg(m_channel->GetDevice()).arg(FUNC).arg(MSG))
67 
85 SignalMonitor *SignalMonitor::Init(const QString& cardtype, int db_cardnum,
86  ChannelBase *channel,
87  bool release_stream)
88 {
89  (void) cardtype;
90  (void) db_cardnum;
91  (void) channel;
92 
93  SignalMonitor *signalMonitor = nullptr;
94 
95  if (cardtype == "GuaranteedToFail")
96  {
97  // This lets all the conditionally compiled tests be set up as
98  // 'else if' statements
99  }
100 #ifdef USING_DVB
101  else if (CardUtil::IsDVBInputType(cardtype))
102  {
103  DVBChannel *dvbc = dynamic_cast<DVBChannel*>(channel);
104  if (dvbc)
105  signalMonitor = new DVBSignalMonitor(db_cardnum, dvbc,
106  release_stream);
107  }
108 #endif
109 
110 #ifdef USING_V4L2
111  else if ((cardtype.toUpper() == "HDPVR"))
112  {
113  V4LChannel *chan = dynamic_cast<V4LChannel*>(channel);
114  if (chan)
115  signalMonitor = new AnalogSignalMonitor(db_cardnum, chan,
116  release_stream);
117  }
118  else if (cardtype.toUpper() == "V4L2ENC")
119  {
120  V4LChannel *chan = dynamic_cast<V4LChannel*>(channel);
121  if (chan)
122  signalMonitor = new V4L2encSignalMonitor(db_cardnum, chan,
123  release_stream);
124  }
125 #endif
126 
127 #ifdef USING_HDHOMERUN
128  else if (cardtype.toUpper() == "HDHOMERUN")
129  {
130  HDHRChannel *hdhrc = dynamic_cast<HDHRChannel*>(channel);
131  if (hdhrc)
132  signalMonitor = new HDHRSignalMonitor(db_cardnum, hdhrc,
133  release_stream);
134  }
135 #endif
136 
137 #ifdef USING_CETON
138  else if (cardtype.toUpper() == "CETON")
139  {
140  CetonChannel *cetonchan = dynamic_cast<CetonChannel*>(channel);
141  if (cetonchan)
142  signalMonitor = new CetonSignalMonitor(db_cardnum, cetonchan,
143  release_stream);
144  }
145 #endif
146 
147 #ifdef USING_IPTV
148  else if (cardtype.toUpper() == "FREEBOX")
149  {
150  IPTVChannel *fbc = dynamic_cast<IPTVChannel*>(channel);
151  if (fbc)
152  signalMonitor = new IPTVSignalMonitor(db_cardnum, fbc,
153  release_stream);
154  }
155 #endif
156 
157 #ifdef USING_VBOX
158  else if (cardtype.toUpper() == "VBOX")
159  {
160  IPTVChannel *fbc = dynamic_cast<IPTVChannel*>(channel);
161  if (fbc)
162  signalMonitor = new IPTVSignalMonitor(db_cardnum, fbc,
163  release_stream);
164  }
165 #endif
166 
167 #ifdef USING_FIREWIRE
168  else if (cardtype.toUpper() == "FIREWIRE")
169  {
170  FirewireChannel *fc = dynamic_cast<FirewireChannel*>(channel);
171  if (fc)
172  signalMonitor = new FirewireSignalMonitor(db_cardnum, fc,
173  release_stream);
174  }
175 #endif
176 
177 #ifdef USING_ASI
178  else if (cardtype.toUpper() == "ASI")
179  {
180  ASIChannel *fc = dynamic_cast<ASIChannel*>(channel);
181  if (fc)
182  signalMonitor = new ASISignalMonitor(db_cardnum, fc,
183  release_stream);
184  }
185 #endif
186 
187  else if (cardtype.toUpper() == "EXTERNAL")
188  {
189  ExternalChannel *fc = dynamic_cast<ExternalChannel*>(channel);
190  if (fc)
191  signalMonitor = new ExternalSignalMonitor(db_cardnum, fc,
192  release_stream);
193  }
194 
195  if (!signalMonitor && channel)
196  {
197  signalMonitor = new ScriptSignalMonitor(db_cardnum, channel,
198  release_stream);
199  }
200 
201  if (!signalMonitor)
202  {
203  LOG(VB_GENERAL, LOG_ERR,
204  QString("Failed to create signal monitor in Init(%1, %2, 0x%3)")
205  .arg(cardtype).arg(db_cardnum).arg((long)channel,0,16));
206  }
207 
208  return signalMonitor;
209 }
210 
224  bool _release_stream, uint64_t wait_for_mask)
225  : MThread("SignalMonitor"),
226  m_channel(_channel),
227  m_inputid(_inputid), m_flags(wait_for_mask),
228  m_release_stream(_release_stream),
229  m_signalLock (QCoreApplication::translate("(Common)", "Signal Lock"),
230  "slock", 1, true, 0, 1, 0),
231  m_signalStrength(QCoreApplication::translate("(Common)", "Signal Power"),
232  "signal", 0, true, 0, 100, 0),
233  m_scriptStatus (QCoreApplication::translate("(Common)", "Script Status"),
234  "script", 3, true, 0, 3, 0)
235 {
237  {
239  }
240 }
241 
246 {
248  wait();
249 }
250 
251 void SignalMonitor::AddFlags(uint64_t _flags)
252 {
253  DBG_SM("AddFlags", sm_flags_to_string(_flags));
254  m_flags |= _flags;
255 }
256 
257 void SignalMonitor::RemoveFlags(uint64_t _flags)
258 {
259  DBG_SM("RemoveFlags", sm_flags_to_string(_flags));
260  m_flags &= ~_flags;
261 }
262 
263 bool SignalMonitor::HasFlags(uint64_t _flags) const
264 {
265  return (m_flags & _flags) == _flags;
266 }
267 
268 bool SignalMonitor::HasAnyFlag(uint64_t _flags) const
269 {
270  return (m_flags & _flags) != 0U;
271 }
272 
277 {
278  DBG_SM("Start", "begin");
279  {
280  QMutexLocker locker(&m_startStopLock);
281  m_exit = false;
282  start();
283  while (!m_running)
284  m_startStopWait.wait(locker.mutex());
285  }
286  DBG_SM("Start", "end");
287 }
288 
293 {
294  DBG_SM("Stop", "begin");
295 
296  QMutexLocker locker(&m_startStopLock);
297  m_exit = true;
298  if (m_running)
299  {
300  locker.unlock();
301  wait();
302  }
303 
304  DBG_SM("Stop", "end");
305 }
306 
316 QStringList SignalMonitor::GetStatusList(void) const
317 {
318  QStringList list;
319  m_statusLock.lock();
324  m_statusLock.unlock();
325 
326  return list;
327 }
328 
331 {
332  RunProlog();
333 
334  QMutexLocker locker(&m_startStopLock);
335  m_running = true;
336  m_startStopWait.wakeAll();
337 
338  while (!m_exit)
339  {
340  locker.unlock();
341 
342  UpdateValues();
343 
344  if (m_notify_frontend && m_inputid>=0)
345  {
346  QStringList slist = GetStatusList();
347  MythEvent me(QString("SIGNAL %1").arg(m_inputid), slist);
348  gCoreContext->dispatch(me);
349  }
350 
351  locker.relock();
352  m_startStopWait.wait(locker.mutex(), m_update_rate);
353  }
354 
355  // We need to send a last informational message because a
356  // signal update may have come in while we were sleeping
357  // if we are using the multithreaded dtvsignalmonitor.
358  locker.unlock();
359  if (m_notify_frontend && m_inputid>=0)
360  {
361  QStringList slist = GetStatusList();
362  MythEvent me(QString("SIGNAL %1").arg(m_inputid), slist);
363  gCoreContext->dispatch(me);
364  }
365  locker.relock();
366 
367  m_running = false;
368  m_startStopWait.wakeAll();
369 
370  RunEpilog();
371 }
372 
374 {
375  QMutexLocker locker(&m_listenerLock);
376  for (size_t i = 0; i < m_listeners.size(); i++)
377  {
378  if (m_listeners[i] == listener)
379  return;
380  }
381  m_listeners.push_back(listener);
382 }
383 
385 {
386  QMutexLocker locker(&m_listenerLock);
387 
388  vector<SignalMonitorListener*> new_listeners;
389  for (size_t i = 0; i < m_listeners.size(); i++)
390  {
391  if (m_listeners[i] != listener)
392  new_listeners.push_back(m_listeners[i]);
393  }
394 
395  m_listeners = new_listeners;
396 }
397 
400 {
401  m_statusLock.lock();
402  const SignalMonitorValue& val = value;
403  m_statusLock.unlock();
404 
405  QMutexLocker locker(&m_listenerLock);
406  for (size_t i = 0; i < m_listeners.size(); i++)
407  {
408  SignalMonitorListener *listener = m_listeners[i];
409  DVBSignalMonitorListener *dvblistener =
410  dynamic_cast<DVBSignalMonitorListener*>(listener);
411 
412  switch (type)
413  {
414  case kStatusSignalLock:
415  listener->StatusSignalLock(val);
416  break;
417  case kAllGood:
418  listener->AllGood();
419  break;
421  listener->StatusSignalStrength(val);
422  break;
423  case kStatusChannelTuned:
424  listener->StatusChannelTuned(val);
425  break;
427  if (dvblistener)
428  dvblistener->StatusSignalToNoise(val);
429  break;
430  case kStatusBitErrorRate:
431  if (dvblistener)
432  dvblistener->StatusBitErrorRate(val);
433  break;
435  if (dvblistener)
436  dvblistener->StatusUncorrectedBlocks(val);
437  break;
439  if (dvblistener)
440  dvblistener->StatusRotorPosition(val);
441  break;
442  }
443  }
444 }
445 
447 {
448  QMutexLocker locker(&m_statusLock);
449  if (m_scriptStatus.GetValue() < 2)
450  {
452  }
453 }
454 
456 {
457  QMutexLocker locker(&m_listenerLock);
458  for (size_t i = 0; i < m_listeners.size(); i++)
459  m_listeners[i]->AllGood();
460 }
461 
463 {
468 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
QString GetStatus() const
Returns a signal monitor value as one long string.
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
void AddListener(SignalMonitorListener *listener)
SignalMonitorValue m_scriptStatus
QMutex m_statusLock
virtual void StatusUncorrectedBlocks(const SignalMonitorValue &)=0
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
SignalMonitor(int _inputid, ChannelBase *_channel, bool _release_stream, uint64_t wait_for_mask)
Initializes signal lock and signal values.
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:91
virtual void Start()
Start signal monitoring thread.
static SignalMonitor * Init(const QString &cardtype, int db_cardnum, ChannelBase *channel, bool release_stream)
QString GetName(void) const
Returns the long name of this value.
int GetValue() const
Returns the value.
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:311
bool HasAnyFlag(uint64_t _flags) const
#define DBG_SM(FUNC, MSG)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Provides interface to the tuning hardware when using DVB drivers.
Definition: dvbchannel.h:29
virtual void StatusSignalLock(const SignalMonitorValue &)=0
Signal to be sent as true when it is safe to begin or continue recording, and false if it may not be ...
void SendMessage(SignalMonitorMessageType type, const SignalMonitorValue &val)
QString sm_flags_to_string(uint64_t)
void SetValue(int _value)
virtual bool IsExternalChannelChangeInUse(void)
virtual void Stop()
Stop signal monitoring thread.
virtual void StatusSignalToNoise(const SignalMonitorValue &)=0
This class is used as a container for messages.
Definition: mythevent.h:16
void RemoveListener(SignalMonitorListener *listener)
static const uint64_t kSigMon_WaitForSig
ChannelBase * m_channel
virtual void StatusSignalStrength(const SignalMonitorValue &)=0
Signal to be sent with an actual signal value.
QWaitCondition m_startStopWait
volatile bool m_exit
uint GetScriptStatus(bool holding_lock=false)
FirewireChannel Copyright (c) 2005 by Jim Westfall and Dave Abrahams Distributed as part of MythTV un...
virtual ~SignalMonitor()
Stops monitoring thread.
virtual void StatusBitErrorRate(const SignalMonitorValue &)=0
virtual QStringList GetStatusList(void) const
Returns QStringList containing all signals and their current values.
void SendMessageAllGood(void)
virtual void StatusChannelTuned(const SignalMonitorValue &)=0
Signal to be sent with change change status.
volatile bool m_running
Implements tuning for TV cards using the V4L driver API, both versions 1 and 2.
Definition: v4lchannel.h:32
void dispatch(const MythEvent &event)
QMutex m_startStopLock
Signal monitoring base class.
Definition: signalmonitor.h:32
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
Abstract class providing a generic interface to tuning hardware.
Definition: channelbase.h:31
SignalMonitorValue m_signalStrength
volatile uint64_t m_flags
void run(void) override
Basic signal monitoring loop.
SignalMonitorMessageType
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:202
virtual void AllGood(void)=0
Signal to be sent when you have a lock on all values.
virtual void AddFlags(uint64_t _flags)
vector< SignalMonitorListener * > m_listeners
virtual void StatusRotorPosition(const SignalMonitorValue &)=0
virtual void RemoveFlags(uint64_t _flags)
static bool IsDVBInputType(const QString &inputType)
Returns true iff the input_type is one of the DVB types.
Definition: cardutil.cpp:950
SignalMonitorValue m_signalLock
virtual void EmitStatus(void)
QMutex m_listenerLock
bool m_notify_frontend
virtual void UpdateValues(void)
This should be overridden to actually do signal monitoring.
bool HasFlags(uint64_t _flags) const