MythTV master
asistreamhandler.cpp
Go to the documentation of this file.
1// -*- Mode: c++ -*-
2
3#include <QtGlobal>
4#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
5#include <QtSystemDetection>
6#endif
7
8// POSIX headers
9#include <fcntl.h>
10#include <unistd.h>
11#ifndef Q_OS_WINDOWS
12#include <sys/select.h>
13#include <sys/ioctl.h>
14#endif
15
16// Qt headers
17#include <QString>
18#include <QFile>
19
20// MythTV headers
22
23#include "asichannel.h"
24#include "asistreamhandler.h"
25#include "cardutil.h"
26#include "dtvsignalmonitor.h"
27#include "mpeg/mpegstreamdata.h"
29
30// DVEO ASI headers
31#include <dveo/asi.h>
32#include <dveo/master.h>
33
34#define LOC QString("ASISH[%1](%2): ").arg(m_inputId).arg(m_device)
35
36QMap<QString,ASIStreamHandler*> ASIStreamHandler::s_handlers;
37QMap<QString,uint> ASIStreamHandler::s_handlersRefCnt;
39
41 int inputid)
42{
43 QMutexLocker locker(&s_handlersLock);
44
45 const QString& devkey = devname;
46
47 QMap<QString,ASIStreamHandler*>::iterator it = s_handlers.find(devkey);
48
49 if (it == s_handlers.end())
50 {
51 auto *newhandler = new ASIStreamHandler(devname, inputid);
52 newhandler->Open();
53 s_handlers[devkey] = newhandler;
54 s_handlersRefCnt[devkey] = 1;
55
56 LOG(VB_RECORD, LOG_INFO,
57 QString("ASISH[%1]: Creating new stream handler %2 for %3")
58 .arg(QString::number(inputid), devkey, devname));
59 }
60 else
61 {
62 s_handlersRefCnt[devkey]++;
63 uint rcount = s_handlersRefCnt[devkey];
64 LOG(VB_RECORD, LOG_INFO,
65 QString("ASISH[%1]: Using existing stream handler %2 for %3")
66 .arg(QString::number(inputid), devkey, devname) +
67 QString(" (%1 in use)").arg(rcount));
68 }
69
70 return s_handlers[devkey];
71}
72
74{
75 QMutexLocker locker(&s_handlersLock);
76
77 QString devname = ref->m_device;
78
79 QMap<QString,uint>::iterator rit = s_handlersRefCnt.find(devname);
80 if (rit == s_handlersRefCnt.end())
81 return;
82
83 QMap<QString,ASIStreamHandler*>::iterator it = s_handlers.find(devname);
84
85 if (*rit > 1)
86 {
87 ref = nullptr;
88 (*rit)--;
89 return;
90 }
91
92 if ((it != s_handlers.end()) && (*it == ref))
93 {
94 LOG(VB_RECORD, LOG_INFO, QString("ASISH[%1]: Closing handler for %2")
95 .arg(inputid).arg(devname));
96 ref->Close();
97 delete *it;
98 s_handlers.erase(it);
99 }
100 else
101 {
102 LOG(VB_GENERAL, LOG_ERR,
103 QString("ASISH[%1] Error: Couldn't find handler for %2")
104 .arg(inputid).arg(devname));
105 }
106
107 s_handlersRefCnt.erase(rit);
108 ref = nullptr;
109}
110
111ASIStreamHandler::ASIStreamHandler(const QString &device, int inputid)
112 : StreamHandler(device, inputid)
113{
114 setObjectName("ASISH");
115}
116
118{
119 m_clockSource = cs;
120 // TODO we should make it possible to set this immediately
121 // not wait for the next open
122}
123
125{
126 m_rxMode = m;
127 // TODO we should make it possible to set this immediately
128 // not wait for the next open
129}
130
132{
133 if (m_drb && m_runningDesired && !desired)
134 m_drb->Stop();
136}
137
139{
140 RunProlog();
141
142 LOG(VB_RECORD, LOG_INFO, LOC + "run(): begin");
143
144 if (!Open())
145 {
146 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open device %1 : %2")
147 .arg(m_device, strerror(errno)));
148 m_bError = true;
149 return;
150 }
151
152 auto *drb = new DeviceReadBuffer(this, true, false);
153 bool ok = drb->Setup(m_device, m_fd, m_packetSize, m_bufSize,
154 m_numBuffers / 4);
155 if (!ok)
156 {
157 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to allocate DRB buffer");
158 delete drb;
159 drb = nullptr;
160 Close();
161 m_bError = true;
162 RunEpilog();
163 return;
164 }
165
166 uint buffer_size = m_packetSize * 15000;
167 auto *buffer = new unsigned char[buffer_size];
168 if (!buffer)
169 {
170 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to allocate buffer");
171 delete drb;
172 drb = nullptr;
173 Close();
174 m_bError = true;
175 RunEpilog();
176 return;
177 }
178 memset(buffer, 0, buffer_size);
179
180 SetRunning(true, true, false);
181
182 drb->Start();
183
184 {
185 QMutexLocker locker(&m_startStopLock);
186 m_drb = drb;
187 }
188
189 int remainder = 0;
190 while (m_runningDesired && !m_bError)
191 {
193
194 ssize_t len = 0;
195
196 len = drb->Read(
197 &(buffer[remainder]), buffer_size - remainder);
198
199 if (!m_runningDesired)
200 break;
201
202 // Check for DRB errors
203 if (drb->IsErrored())
204 {
205 LOG(VB_GENERAL, LOG_ERR, LOC + "Device error detected");
206 m_bError = true;
207 }
208
209 if (drb->IsEOF())
210 {
211 LOG(VB_GENERAL, LOG_ERR, LOC + "Device EOF detected");
212 m_bError = true;
213 }
214
215 len += remainder;
216
217 if (len < 10) // 10 bytes = 4 bytes TS header + 6 bytes PES header
218 {
219 remainder = len;
220 continue;
221 }
222
223 if (!m_listenerLock.tryLock())
224 {
225 remainder = len;
226 continue;
227 }
228
229 if (m_streamDataList.empty())
230 {
231 m_listenerLock.unlock();
232 continue;
233 }
234
235 for (auto sit = m_streamDataList.cbegin();
236 sit != m_streamDataList.cend(); ++sit)
237 remainder = sit.key()->ProcessData(buffer, len);
238
239 WriteMPTS(buffer, len - remainder);
240
241 m_listenerLock.unlock();
242
243 if (remainder > 0 && (len > remainder)) // leftover bytes
244 memmove(buffer, &(buffer[len - remainder]), remainder);
245 }
246 LOG(VB_RECORD, LOG_INFO, LOC + "run(): " + "shutdown");
247
249
250 {
251 QMutexLocker locker(&m_startStopLock);
252 m_drb = nullptr;
253 }
254
255 if (drb->IsRunning())
256 drb->Stop();
257
258 delete drb;
259 delete[] buffer;
260 Close();
261
262 LOG(VB_RECORD, LOG_INFO, LOC + "run(): " + "end");
263
264 SetRunning(false, true, false);
265 RunEpilog();
266}
267
269{
270 if (m_fd >= 0)
271 return true;
272
273 QString error;
275 if (m_deviceNum < 0)
276 {
277 LOG(VB_GENERAL, LOG_ERR, LOC + error);
278 return false;
279 }
280
282 if (m_bufSize <= 0)
283 {
284 LOG(VB_GENERAL, LOG_ERR, LOC + error);
285 return false;
286 }
287
289 if (m_numBuffers <= 0)
290 {
291 LOG(VB_GENERAL, LOG_ERR, LOC + error);
292 return false;
293 }
294
296 {
297 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to set RX Mode: " + error);
298 return false;
299 }
300
301 // actually open the device
302 m_fd = open(m_device.toLocal8Bit().constData(), O_RDONLY, 0);
303 if (m_fd < 0)
304 {
305 LOG(VB_GENERAL, LOG_ERR, LOC +
306 QString("Failed to open '%1'").arg(m_device) + ENO);
307 return false;
308 }
309
310 // get the rx capabilities
311 unsigned int cap = 0;
312 if (ioctl(m_fd, ASI_IOC_RXGETCAP, &cap) < 0)
313 {
314 LOG(VB_GENERAL, LOG_ERR, LOC +
315 QString("Failed to query capabilities '%1'").arg(m_device) + ENO);
316 Close();
317 return false;
318 }
319 // TODO? do stuff with capabilities..
320
321 // we need to handle 188 & 204 byte packets..
322 switch (m_rxMode)
323 {
324 case kASIRXRawMode:
327 break;
328 case kASIRXSyncOn204:
330 break;
331 case kASIRXSyncOn188:
335 break;
336 }
337
338 // pid counter?
339
340 return m_fd >= 0;
341}
342
344{
345 if (m_fd >= 0)
346 {
347 close(m_fd);
348 m_fd = -1;
349 }
350}
351
353{
354 int val = 0;
355 if(ioctl(fd, ASI_IOC_RXGETEVENTS, &val) < 0)
356 {
357 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open device %1: ")
358 .arg(m_device) + ENO);
359 //TODO: Handle error
360 return;
361 }
362 if(val & ASI_EVENT_RX_BUFFER)
363 {
364 LOG(VB_RECORD, LOG_ERR, LOC +
365 QString("Driver receive buffer queue overrun detected %1")
366 .arg(m_device));
367 }
368 if(val & ASI_EVENT_RX_FIFO)
369 {
370 LOG(VB_RECORD, LOG_ERR, LOC +
371 QString("Driver receive FIFO overrun detected %1")
372 .arg(m_device));
373 }
374 if(val & ASI_EVENT_RX_CARRIER)
375 {
376 LOG(VB_RECORD, LOG_NOTICE, LOC +
377 QString("Carrier Status change detected %1")
378 .arg(m_device));
379 }
380 if(val & ASI_EVENT_RX_LOS)
381 {
382 LOG(VB_RECORD, LOG_ERR, LOC +
383 QString("Loss of Packet Sync detected %1")
384 .arg(m_device));
385 }
386 if(val & ASI_EVENT_RX_AOS)
387 {
388 LOG(VB_RECORD, LOG_NOTICE, LOC +
389 QString("Acquisition of Sync detected %1")
390 .arg(m_device));
391 }
392 if(val & ASI_EVENT_RX_DATA)
393 {
394 LOG(VB_RECORD, LOG_NOTICE, LOC +
395 QString("Receive data status change detected %1")
396 .arg(m_device));
397 }
398}
#define LOC
ASIRXMode
@ kASIRXSyncOnActualSize
@ kASIRXSyncOn188
@ kASIRXSyncOn204
@ kASIRXSyncOnActualConvertTo188
@ kASIRXSyncOn204ConvertTo188
@ kASIRXRawMode
ASIClockSource
static ASIStreamHandler * Get(const QString &devname, int inputid)
static QMutex s_handlersLock
static QMap< QString, ASIStreamHandler * > s_handlers
DeviceReadBuffer * m_drb
ASIClockSource m_clockSource
void SetRunningDesired(bool desired) override
At minimum this sets _running_desired, this may also send signals to anything that might be blocking ...
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
ASIStreamHandler(const QString &device, int inputid)
void SetRXMode(ASIRXMode m)
void PriorityEvent(int fd) override
void SetClockSource(ASIClockSource cs)
static void Return(ASIStreamHandler *&ref, int inputid)
static QMap< QString, uint > s_handlersRefCnt
static int GetASIDeviceNumber(const QString &device, QString *error=nullptr)
Definition: cardutil.cpp:3280
static uint GetASINumBuffers(uint device_num, QString *error=nullptr)
Definition: cardutil.cpp:3359
static uint GetASIBufferSize(uint device_num, QString *error=nullptr)
Definition: cardutil.cpp:3335
static bool SetASIMode(uint device_num, uint mode, QString *error=nullptr)
Definition: cardutil.cpp:3406
Buffers reads from device files.
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:180
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:193
void setObjectName(const QString &name)
Definition: mthread.cpp:222
StreamDataList m_streamDataList
void WriteMPTS(const unsigned char *buffer, uint len)
Write out a copy of the raw MPTS.
virtual void SetRunningDesired(bool desired)
At minimum this sets _running_desired, this may also send signals to anything that might be blocking ...
QString m_device
volatile bool m_runningDesired
volatile bool m_bError
bool RemoveAllPIDFilters(void)
void SetRunning(bool running, bool using_buffering, bool using_section_reader)
QMutex m_startStopLock
bool UpdateFiltersFromStreamData(void)
QRecursiveMutex m_listenerLock
static constexpr unsigned int kDVBEmissionSize
Definition: tspacket.h:263
static constexpr unsigned int kSize
Definition: tspacket.h:261
unsigned int uint
Definition: compat.h:60
#define close
Definition: compat.h:28
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:74
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
def error(message)
Definition: smolt.py:409