Ticket #12932: firewiresignalmonitor.cpp

File firewiresignalmonitor.cpp, 8.4 KB (added by Roger James <roger@…>, 7 years ago)
Line 
1// -*- Mode: c++ -*-
2// Copyright (c) 2006, Daniel Thor Kristjansson
3
4#include <fcntl.h>
5#include <unistd.h>
6#include <sys/select.h>
7
8#include "mythdbcon.h"
9#include "atscstreamdata.h"
10#include "mpegtables.h"
11#include "atsctables.h"
12#include "firewirechannel.h"
13#include "firewiresignalmonitor.h"
14#include "mythlogging.h"
15
16#define LOC QString("FireSigMon[%1](%2): ") \
17            .arg(capturecardnum).arg(channel->GetDevice())
18
19void FirewireTableMonitorThread::run(void)
20{
21    RunProlog();
22    m_parent->RunTableMonitor();
23    RunEpilog();
24}
25
26const uint FirewireSignalMonitor::kPowerTimeout  = 3000; /* ms */
27const uint FirewireSignalMonitor::kBufferTimeout = 5000; /* ms */
28
29QMap<void*,uint> FirewireSignalMonitor::pat_keys;
30QMutex           FirewireSignalMonitor::pat_keys_lock;
31
32/** \fn FirewireSignalMonitor::FirewireSignalMonitor(int,FirewireChannel*,uint64_t)
33 *  \brief Initializes signal lock and signal values.
34 *
35 *   Start() must be called to actually begin continuous
36 *   signal monitoring. The timeout is set to 3 seconds,
37 *   and the signal threshold is initialized to 0%.
38 *
39 *  \param db_cardnum Recorder number to monitor,
40 *                    if this is less than 0, SIGNAL events will not be
41 *                    sent to the frontend even if SetNotifyFrontend(true)
42 *                    is called.
43 *  \param _channel FirewireChannel for card
44 *  \param _flags   Flags to start with
45 */
46FirewireSignalMonitor::FirewireSignalMonitor(
47    int db_cardnum,
48    FirewireChannel *_channel,
49    uint64_t _flags) :
50    DTVSignalMonitor(db_cardnum, _channel, _flags),
51    dtvMonitorRunning(false),
52    tableMonitorThread(NULL),
53    stb_needs_retune(true),
54    stb_needs_to_wait_for_pat(false),
55    stb_needs_to_wait_for_power(false)
56{
57    LOG(VB_CHANNEL, LOG_INFO, LOC + "ctor");
58
59    signalStrength.SetThreshold(65);
60
61    AddFlags(kSigMon_WaitForSig);
62
63    stb_needs_retune =
64        (FirewireDevice::kAVCPowerOff == _channel->GetPowerState());
65}
66
67/** \fn FirewireSignalMonitor::~FirewireSignalMonitor()
68 *  \brief Stops signal monitoring and table monitoring threads.
69 */
70FirewireSignalMonitor::~FirewireSignalMonitor()
71{
72    LOG(VB_CHANNEL, LOG_INFO, LOC + "dtor");
73    Stop();
74}
75
76/** \fn FirewireSignalMonitor::Stop(void)
77 *  \brief Stop signal monitoring and table monitoring threads.
78 */
79void FirewireSignalMonitor::Stop(void)
80{
81    LOG(VB_CHANNEL, LOG_INFO, LOC + "Stop() -- begin");
82    SignalMonitor::Stop();
83    if (tableMonitorThread)
84    {
85        dtvMonitorRunning = false;
86        tableMonitorThread->wait();
87        delete tableMonitorThread;
88        tableMonitorThread = NULL;
89    }
90    LOG(VB_CHANNEL, LOG_INFO, LOC + "Stop() -- end");
91}
92
93void FirewireSignalMonitor::HandlePAT(const ProgramAssociationTable *pat)
94{
95    AddFlags(kDTVSigMon_PATSeen);
96
97    FirewireChannel *fwchan = dynamic_cast<FirewireChannel*>(channel);
98    if (!fwchan)
99        return;
100
101    bool crc_bogus = !fwchan->GetFirewireDevice()->IsSTBBufferCleared();
102    if (crc_bogus && stb_needs_to_wait_for_pat &&
103        (stb_wait_for_pat_timer.elapsed() < (int)kBufferTimeout))
104    {
105        LOG(VB_CHANNEL, LOG_INFO, LOC + "HandlePAT() ignoring PAT");
106        uint tsid = pat->TransportStreamID();
107        GetStreamData()->SetVersionPAT(tsid, -1,0);
108        return;
109    }
110
111    if (crc_bogus && stb_needs_to_wait_for_pat)
112    {
113        LOG(VB_GENERAL, LOG_WARNING, LOC + "Wait for valid PAT timed out");
114        stb_needs_to_wait_for_pat = false;
115    }
116
117    DTVSignalMonitor::HandlePAT(pat);
118}
119
120void FirewireSignalMonitor::HandlePMT(uint pnum, const ProgramMapTable *pmt)
121{
122    LOG(VB_CHANNEL, LOG_INFO, LOC + "HandlePMT()");
123
124    AddFlags(kDTVSigMon_PMTSeen);
125
126    if (!HasFlags(kDTVSigMon_PATMatch))
127    {
128        GetStreamData()->SetVersionPMT(pnum, -1, 0);
129        LOG(VB_CHANNEL, LOG_INFO, LOC + "HandlePMT() ignoring PMT");
130        return;
131    }
132
133    DTVSignalMonitor::HandlePMT(pnum, pmt);
134}
135
136void FirewireSignalMonitor::RunTableMonitor(void)
137{
138    stb_needs_to_wait_for_pat = true;
139    stb_wait_for_pat_timer.start();
140    dtvMonitorRunning = true;
141
142    LOG(VB_CHANNEL, LOG_INFO, LOC + "RunTableMonitor(): -- begin");
143
144    FirewireChannel *lchan = dynamic_cast<FirewireChannel*>(channel);
145    if (!lchan)
146    {
147        LOG(VB_CHANNEL, LOG_INFO, LOC + "RunTableMonitor(): -- err");
148        while (dtvMonitorRunning)
149            usleep(10000);
150        LOG(VB_CHANNEL, LOG_INFO, LOC + "RunTableMonitor(): -- err end");
151        return;
152    }
153
154    FirewireDevice *dev = lchan->GetFirewireDevice();
155
156    dev->OpenPort();
157    dev->AddListener(this);
158
159    while (dtvMonitorRunning && GetStreamData())
160        usleep(10000);
161
162    LOG(VB_CHANNEL, LOG_INFO, LOC + "RunTableMonitor(): -- shutdown ");
163
164    dev->RemoveListener(this);
165    dev->ClosePort();
166
167    while (dtvMonitorRunning)
168        usleep(10000);
169
170    LOG(VB_CHANNEL, LOG_INFO, LOC + "RunTableMonitor(): -- end");
171}
172
173void FirewireSignalMonitor::AddData(const unsigned char *data, uint len)
174{
175    if (!dtvMonitorRunning)
176        return;
177
178    if (GetStreamData())
179        GetStreamData()->ProcessData((unsigned char *)data, len);
180}
181
182/** \fn FirewireSignalMonitor::UpdateValues(void)
183 *  \brief Fills in frontend stats and emits status Qt signals.
184 *
185 *   This function uses five ioctl's FE_READ_SNR, FE_READ_SIGNAL_STRENGTH
186 *   FE_READ_BER, FE_READ_UNCORRECTED_BLOCKS, and FE_READ_STATUS to obtain
187 *   statistics from the frontend.
188 *
189 *   This is automatically called by run(), after Start()
190 *   has been used to start the signal monitoring thread.
191 */
192void FirewireSignalMonitor::UpdateValues(void)
193{
194    if (!running || exit)
195        return;
196
197    if (dtvMonitorRunning)
198    {
199        EmitStatus();
200        if (IsAllGood())
201            SendMessageAllGood();
202        // TODO dtv signals...
203
204        update_done = true;
205        return;
206    }
207
208    if (stb_needs_to_wait_for_power &&
209        (stb_wait_for_power_timer.elapsed() < (int)kPowerTimeout))
210    {
211        return;
212    }
213    stb_needs_to_wait_for_power = false;
214
215    FirewireChannel *fwchan = dynamic_cast<FirewireChannel*>(channel);
216    if (!fwchan)
217        return;
218
219    if (HasFlags(kFWSigMon_WaitForPower) && !HasFlags(kFWSigMon_PowerMatch))
220    {
221        bool retried = false;
222        while (true)
223        {
224            FirewireDevice::PowerState power = fwchan->GetPowerState();
225            if (FirewireDevice::kAVCPowerOn == power)
226            {
227                AddFlags(kFWSigMon_PowerSeen | kFWSigMon_PowerMatch);
228            }
229            else if (FirewireDevice::kAVCPowerOff == power)
230            {
231                AddFlags(kFWSigMon_PowerSeen);
232                fwchan->SetPowerState(true);
233                stb_wait_for_power_timer.start();
234                stb_needs_to_wait_for_power = true;
235            }
236            else
237            {
238                bool qfailed = (FirewireDevice::kAVCPowerQueryFailed == power);
239                if (qfailed && !retried)
240                {
241                    retried = true;
242                    continue;
243                }
244
245                LOG(VB_RECORD, LOG_WARNING,
246                    "Can't determine if STB is power on, assuming it is...");
247                AddFlags(kFWSigMon_PowerSeen | kFWSigMon_PowerMatch);
248            }
249            break;
250        }
251    }
252
253    bool isLocked = !HasFlags(kFWSigMon_WaitForPower) ||
254        HasFlags(kFWSigMon_WaitForPower | kFWSigMon_PowerMatch);
255
256    if (isLocked && stb_needs_retune)
257    {
258        fwchan->Retune();
259        isLocked = stb_needs_retune = false;
260    }
261
262    SignalMonitor::UpdateValues();
263
264    {
265        QMutexLocker locker(&statusLock);
266        if (!scriptStatus.IsGood())
267            return;
268    }
269
270    // Set SignalMonitorValues from info from card.
271    {
272        QMutexLocker locker(&statusLock);
273        signalStrength.SetValue(isLocked ? 100 : 0);
274        signalLock.SetValue(isLocked ? 1 : 0);
275    }
276
277    EmitStatus();
278    if (IsAllGood())
279        SendMessageAllGood();
280
281    // Start table monitoring if we are waiting on any table
282    // and we have a lock.
283    if (isLocked && GetStreamData() &&
284        HasAnyFlag(kDTVSigMon_WaitForPAT | kDTVSigMon_WaitForPMT |
285                   kDTVSigMon_WaitForMGT | kDTVSigMon_WaitForVCT |
286                   kDTVSigMon_WaitForNIT | kDTVSigMon_WaitForSDT))
287    {
288        tableMonitorThread = new FirewireTableMonitorThread(this);
289
290        LOG(VB_CHANNEL, LOG_INFO, LOC + "UpdateValues() -- "
291                "Waiting for table monitor to start");
292
293        while (!dtvMonitorRunning)
294            usleep(5000);
295
296        LOG(VB_CHANNEL, LOG_INFO, LOC + "UpdateValues() -- "
297                "Table monitor started");
298    }
299
300    update_done = true;
301}