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 "asistreamhandler.h"
17 #include "asichannel.h"
18 #include "dtvsignalmonitor.h"
19 #include "streamlisteners.h"
20 #include "mpegstreamdata.h"
21 #include "cardutil.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_handlers_refcnt;
32 
33 ASIStreamHandler *ASIStreamHandler::Get(const QString &devname,
34  int inputid)
35 {
36  QMutexLocker locker(&s_handlers_lock);
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  ASIStreamHandler *newhandler = new ASIStreamHandler(devname, inputid);
45  newhandler->Open();
46  s_handlers[devkey] = newhandler;
47  s_handlers_refcnt[devkey] = 1;
48 
49  LOG(VB_RECORD, LOG_INFO,
50  QString("ASISH[%1]: Creating new stream handler %2 for %3")
51  .arg(inputid).arg(devkey).arg(devname));
52  }
53  else
54  {
55  s_handlers_refcnt[devkey]++;
56  uint rcount = s_handlers_refcnt[devkey];
57  LOG(VB_RECORD, LOG_INFO,
58  QString("ASISH[%1]: Using existing stream handler %2 for %3")
59  .arg(inputid).arg(devkey)
60  .arg(devname) + 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_handlers_lock);
69 
70  QString devname = ref->m_device;
71 
72  QMap<QString,uint>::iterator rit = s_handlers_refcnt.find(devname);
73  if (rit == s_handlers_refcnt.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_handlers_refcnt.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_clock_source = cs;
113  // TODO we should make it possible to set this immediately
114  // not wait for the next open
115 }
116 
118 {
119  m_rx_mode = 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_running_desired && !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).arg(strerror(errno)));
141  m_bError = true;
142  return;
143  }
144 
145  DeviceReadBuffer *drb = new DeviceReadBuffer(this, true, false);
146  bool ok = drb->Setup(m_device, m_fd, m_packet_size, m_buf_size,
147  m_num_buffers / 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_packet_size * 15000;
160  unsigned char *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_start_stop_lock);
179  m_drb = drb;
180  }
181 
182  int remainder = 0;
183  while (m_running_desired && !m_bError)
184  {
186 
187  ssize_t len = 0;
188 
189  len = drb->Read(
190  &(buffer[remainder]), buffer_size - remainder);
191 
192  if (!m_running_desired)
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_listener_lock.tryLock())
217  {
218  remainder = len;
219  continue;
220  }
221 
222  if (m_stream_data_list.empty())
223  {
224  m_listener_lock.unlock();
225  continue;
226  }
227 
228  StreamDataList::const_iterator sit = m_stream_data_list.begin();
229  for (; sit != m_stream_data_list.end(); ++sit)
230  remainder = sit.key()->ProcessData(buffer, len);
231 
232  WriteMPTS(buffer, len - remainder);
233 
234  m_listener_lock.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_start_stop_lock);
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_device_num < 0)
269  {
270  LOG(VB_GENERAL, LOG_ERR, LOC + error);
271  return false;
272  }
273 
275  if (m_buf_size <= 0)
276  {
277  LOG(VB_GENERAL, LOG_ERR, LOC + error);
278  return false;
279  }
280 
282  if (m_num_buffers <= 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;
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_rx_mode)
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;
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  LOG(VB_RECORD, LOG_ERR, LOC +
357  QString("Driver receive buffer queue overrun detected %1")
358  .arg(m_device));
359  if(val & ASI_EVENT_RX_FIFO)
360  LOG(VB_RECORD, LOG_ERR, LOC +
361  QString("Driver receive FIFO overrun detected %1")
362  .arg(m_device));
363  if(val & ASI_EVENT_RX_CARRIER)
364  LOG(VB_RECORD, LOG_NOTICE, LOC +
365  QString("Carrier Status change detected %1")
366  .arg(m_device));
367  if(val & ASI_EVENT_RX_LOS)
368  LOG(VB_RECORD, LOG_ERR, LOC +
369  QString("Loss of Packet Sync detected %1")
370  .arg(m_device));
371  if(val & ASI_EVENT_RX_AOS)
372  LOG(VB_RECORD, LOG_NOTICE, LOC +
373  QString("Acquisition of Sync detected %1")
374  .arg(m_device));
375  if(val & ASI_EVENT_RX_DATA)
376  LOG(VB_RECORD, LOG_NOTICE, LOC +
377  QString("Receive data status change detected %1")
378  .arg(m_device));
379 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
bool IsRunning(void) const
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
static uint GetASIBufferSize(uint device_num, QString *error=nullptr)
bool IsEOF(void) const
volatile bool m_running_desired
static void error(const char *str,...)
Definition: vbi.c:42
static ASIStreamHandler * Get(const QString &devname, int inputid)
bool UpdateFiltersFromStreamData(void)
QString m_device
void SetClockSource(ASIClockSource cs)
DeviceReadBuffer * m_drb
unsigned int uint
Definition: compat.h:140
QMutex m_listener_lock
static QMutex s_handlers_lock
static uint GetASINumBuffers(uint device_num, QString *error=nullptr)
void setObjectName(const QString &name)
Definition: mthread.cpp:249
bool IsErrored(void) const
static QMap< QString, uint > s_handlers_refcnt
uint Read(unsigned char *buf, uint count)
Try to Read count bytes from into buffer.
void SetRunning(bool running, bool using_buffering, bool using_section_reader)
#define close
Definition: compat.h:16
void PriorityEvent(int fd) override
ASIRXMode
void SetRXMode(ASIRXMode m)
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static const unsigned int kSize
Definition: tspacket.h:220
static int GetASIDeviceNumber(const QString &device, QString *error=nullptr)
StreamDataList m_stream_data_list
bool Setup(const QString &streamName, int streamfd, uint readQuanta=sizeof(TSPacket), uint deviceBufferSize=0, uint deviceBufferCount=1)
ASIStreamHandler(const QString &, int inputid)
virtual void SetRunningDesired(bool desired)
At minimum this sets _running_desired, this may also send signals to anything that might be blocking ...
#define LOC
static bool SetASIMode(uint device_num, uint mode, QString *error=nullptr)
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:202
volatile bool m_bError
Buffers reads from device files.
QMutex m_start_stop_lock
void WriteMPTS(unsigned char *buffer, uint len)
Write out a copy of the raw MPTS.
ASIClockSource
static QMap< QString, ASIStreamHandler * > s_handlers
static void Return(ASIStreamHandler *&ref, int inputid)
ASIClockSource m_clock_source
static const unsigned int kDVBEmissionSize
Definition: tspacket.h:222
void SetRunningDesired(bool desired) override
At minimum this sets _running_desired, this may also send signals to anything that might be blocking ...
bool RemoveAllPIDFilters(void)