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