MythTV  master
dvbcam.cpp
Go to the documentation of this file.
1 /*
2  * Class DVBCam
3  *
4  * Original Project
5  * MythTV http://www.mythtv.org
6  *
7  * Author(s):
8  * Jesper Sorensen
9  * - Changed to work with Taylor Jacob's DVB rewrite
10  * Kenneth Aafloy
11  * - General Implementation
12  *
13  * Description:
14  * This Class has been developed from bits n' pieces of other
15  * projects.
16  *
17  *
18  *
19  * This program is free software; you can redistribute it and/or
20  * modify it under the terms of the GNU General Public License
21  * as published by the Free Software Foundation; either version 2
22  * of the License, or (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License
30  * along with this program; if not, write to the Free Software
31  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
32  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
33  *
34  */
35 
36 #include <iostream>
37 #include <vector>
38 #include <map>
39 #include <cstdlib>
40 #include <cstdio>
41 using namespace std;
42 
43 #include <fcntl.h>
44 #include <sys/ioctl.h>
45 #include <sys/poll.h>
46 #include <linux/dvb/ca.h>
47 
48 #include <dvbci.h>
49 
50 #include "recorderbase.h"
51 
52 #include "cardutil.h"
53 
54 #include "dvbcam.h"
55 #include "mthread.h"
56 #include "dvbchannel.h"
57 #include "dvbrecorder.h"
58 #include "mythlogging.h"
59 
60 #define LOC QString("DVB#%1 CA: ").arg(m_device)
61 
62 DVBCam::DVBCam(const QString &aDevice)
63  : m_device(aDevice)
64 {
65  QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_CA, m_device);
66  QByteArray dev = dvbdev.toLatin1();
67  int cafd = open(dev.constData(), O_RDWR);
68  if (cafd >= 0)
69  {
70  ca_caps_t caps;
71  // slot_num will be uninitialised if ioctl fails
72  if (ioctl(cafd, CA_GET_CAP, &caps) >= 0)
73  m_numslots = caps.slot_num;
74  else
75  LOG(VB_GENERAL, LOG_ERR, "ioctl CA_GET_CAP failed: " + ENO);
76 
77  close(cafd);
78  }
79 }
80 
82 {
83  Stop();
84 }
85 
86 bool DVBCam::Start(void)
87 {
88  if (m_numslots == 0)
89  return false;
90 
91  m_havePmt = false;
92  m_pmtSent = false;
93  m_pmtUpdated = false;
94  m_pmtAdded = false;
95 
96  QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_CA, m_device);
97  QByteArray dev = dvbdev.toLatin1();
98  m_ciHandler = cCiHandler::CreateCiHandler(dev.constData());
99  if (!m_ciHandler)
100  {
101  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialize CI handler");
102  return false;
103  }
104 
105  QMutexLocker locker(&m_ciHandlerLock);
106  m_ciHandlerDoRun = true;
107  m_ciHandlerThread = new MThread("DVBCam", this);
110  m_ciHandlerWait.wait(locker.mutex(), 1000);
111 
112  if (m_ciHandlerRunning)
113  LOG(VB_DVBCAM, LOG_INFO, LOC + "CI handler successfully initialized!");
114 
115  return m_ciHandlerRunning;
116 }
117 
118 bool DVBCam::Stop(void)
119 {
120  {
121  QMutexLocker locker(&m_ciHandlerLock);
122  if (m_ciHandlerRunning)
123  {
124  m_ciHandlerDoRun = false;
125  locker.unlock();
127  locker.relock();
128  delete m_ciHandlerThread;
129  m_ciHandlerThread = nullptr;
130  }
131 
132  if (m_ciHandler)
133  {
134  delete m_ciHandler;
135  m_ciHandler = nullptr;
136  }
137  }
138 
139  QMutexLocker locker(&m_pmtLock);
140  pmt_list_t::iterator it;
141 
142  for (it = m_pmtList.begin(); it != m_pmtList.end(); ++it)
143  delete *it;
144  m_pmtList.clear();
145 
146  for (it = m_pmtAddList.begin(); it != m_pmtAddList.end(); ++it)
147  delete *it;
148  m_pmtAddList.clear();
149 
150  return true;
151 }
152 
154 {
156  if (enq != nullptr)
157  {
158  if (enq->Text() != nullptr)
159  LOG(VB_DVBCAM, LOG_INFO, LOC + QString("CAM: Received message: %1")
160  .arg(enq->Text()));
161  delete enq;
162  }
163 
165  if (menu != nullptr)
166  {
167  if (menu->TitleText() != nullptr)
168  LOG(VB_DVBCAM, LOG_INFO, LOC + QString("CAM: Menu Title: %1")
169  .arg(menu->TitleText()));
170  if (menu->SubTitleText() != nullptr)
171  LOG(VB_DVBCAM, LOG_INFO, LOC + QString("CAM: Menu SubTitle: %1")
172  .arg(menu->SubTitleText()));
173  if (menu->BottomText() != nullptr)
174  LOG(VB_DVBCAM, LOG_INFO, LOC + QString("CAM: Menu BottomText: %1")
175  .arg(menu->BottomText()));
176 
177  for (int i=0; i<menu->NumEntries(); i++)
178  if (menu->Entry(i) != nullptr)
179  LOG(VB_DVBCAM, LOG_INFO, LOC + QString("CAM: Menu Entry: %1")
180  .arg(menu->Entry(i)));
181 
182  if (menu->Selectable())
183  {
184  LOG(VB_CHANNEL, LOG_INFO, LOC + "CAM: Menu is selectable");
185  }
186 
187  if (menu->NumEntries() > 0)
188  {
189  LOG(VB_DVBCAM, LOG_INFO, LOC + "CAM: Selecting first entry");
190  menu->Select(0);
191  }
192  else
193  {
194  LOG(VB_DVBCAM, LOG_INFO, LOC + "CAM: Cancelling menu");
195  }
196 
197  delete menu;
198  }
199 }
200 
202 {
203  LOG(VB_DVBCAM, LOG_INFO, LOC + "CiHandler needs CA_PMT");
204  QMutexLocker locker(&m_pmtLock);
205 
206  if (m_pmtSent && m_pmtAdded && !m_pmtUpdated)
207  {
208  // Send added PMT
209  while (!m_pmtAddList.empty())
210  {
211  pmt_list_t::iterator it = m_pmtAddList.begin();
212  const ChannelBase *chan = it.key();
213  ProgramMapTable *pmt = (*it);
214  m_pmtList[chan] = pmt;
215  m_pmtAddList.erase(it);
216  SendPMT(*pmt, CPLM_ADD);
217  }
218 
219  m_pmtUpdated = false;
220  m_pmtAdded = false;
221  return;
222  }
223 
224  // Grab any added PMT
225  while (!m_pmtAddList.empty())
226  {
227  pmt_list_t::iterator it = m_pmtAddList.begin();
228  const ChannelBase *chan = it.key();
229  ProgramMapTable *pmt = (*it);
230  m_pmtList[chan] = pmt;
231  m_pmtAddList.erase(it);
232  }
233 
234  uint length = m_pmtList.size();
235  uint count = 0;
236 
237  pmt_list_t::const_iterator pmtit;
238  for (pmtit = m_pmtList.begin(); pmtit != m_pmtList.end(); ++pmtit)
239  {
240  uint cplm = (count == 0) ? CPLM_FIRST : CPLM_MORE;
241  cplm = (count + 1 == length) ? CPLM_LAST : cplm;
242  cplm = (length == 1) ? CPLM_ONLY : cplm;
243 
244  SendPMT(**pmtit, cplm);
245 
246  count++;
247  }
248 
249  m_pmtSent = true;
250  m_pmtUpdated = false;
251  m_pmtAdded = false;
252 }
253 
254 void DVBCam::run(void)
255 {
256  LOG(VB_DVBCAM, LOG_INFO, LOC + "CI handler thread running");
257 
258  QMutexLocker locker(&m_ciHandlerLock);
259  m_ciHandlerRunning = true;
260 
261  while (m_ciHandlerDoRun)
262  {
263  locker.unlock();
264  if (m_ciHandler->Process())
265  {
266  if (m_ciHandler->HasUserIO())
267  HandleUserIO();
268 
269  bool handle_pmt = m_pmtSent && (m_pmtUpdated || m_pmtAdded);
270  handle_pmt |= m_havePmt && m_ciHandler->NeedCaPmt();
271 
272  if (handle_pmt)
273  HandlePMT();
274  }
275  locker.relock();
276  m_ciHandlerWait.wait(locker.mutex(), 10);
277  }
278 
279  m_ciHandlerRunning = false;
280  LOG(VB_DVBCAM, LOG_INFO, LOC + "CiHandler thread stopped");
281 }
282 
283 void DVBCam::SetPMT(const ChannelBase *chan, const ProgramMapTable *pmt)
284 {
285  QMutexLocker locker(&m_pmtLock);
286 
287  pmt_list_t::iterator it = m_pmtList.find(chan);
288  pmt_list_t::iterator it2 = m_pmtAddList.find(chan);
289  if (!pmt && (it != m_pmtList.end()))
290  {
291  delete *it;
292  m_pmtList.erase(it);
293  m_pmtUpdated = true;
294  }
295  else if (!pmt && (it2 != m_pmtAddList.end()))
296  {
297  delete *it2;
298  m_pmtAddList.erase(it2);
299  m_pmtAdded = !m_pmtAddList.empty();
300  }
301  else if (pmt && (m_pmtList.empty() || (it != m_pmtList.end())))
302  {
303  if (it != m_pmtList.end())
304  delete *it;
305  m_pmtList[chan] = new ProgramMapTable(*pmt);
306  m_havePmt = true;
307  m_pmtUpdated = true;
308  }
309  else if (pmt && (it == m_pmtList.end()))
310  {
311  m_pmtAddList[chan] = new ProgramMapTable(*pmt);
312  m_pmtAdded = true;
313  }
314 }
315 
316 void DVBCam::SetTimeOffset(double offset_in_seconds)
317 {
318  QMutexLocker locker(&m_ciHandlerLock);
319  if (m_ciHandler)
320  m_ciHandler->SetTimeOffset(offset_in_seconds);
321 }
322 
323 static const char *cplm_info[] =
324 {
325  "CPLM_MORE",
326  "CPLM_FIRST",
327  "CPLM_LAST",
328  "CPLM_ONLY",
329  "CPLM_ADD",
330  "CPLM_UPDATE"
331 };
332 
333 cCiCaPmt CreateCAPMT(const ProgramMapTable& /*pmt*/, const unsigned short* /*casids*/, uint /*cplm*/);
334 
335 /*
336  * Send a CA_PMT object to the CAM (see EN50221, section 8.4.3.4)
337  */
338 void DVBCam::SendPMT(const ProgramMapTable &pmt, uint cplm)
339 {
340  bool success = false;
341 
342  for (uint s = 0; s < (uint)m_ciHandler->NumSlots(); s++)
343  {
344  const unsigned short *casids = m_ciHandler->GetCaSystemIds(s);
345 
346  if (!casids)
347  {
348  LOG(success ? VB_DVBCAM : VB_GENERAL, LOG_ERR,
349  LOC + "GetCaSystemIds returned NULL! " +
350  QString("(Slot #%1)").arg(s));
351  continue;
352  }
353 
354  if (!casids[0])
355  {
356  LOG(success ? VB_DVBCAM : VB_GENERAL, LOG_ERR,
357  LOC + "CAM supports no CA systems! " +
358  QString("(Slot #%1)").arg(s));
359  continue;
360  }
361 
362  LOG(VB_DVBCAM, LOG_INFO, LOC +
363  QString("Creating CA_PMT, ServiceID = %1")
364  .arg(pmt.ProgramNumber()));
365 
366  cCiCaPmt capmt = CreateCAPMT(pmt, casids, cplm);
367 
368  LOG(VB_DVBCAM, LOG_INFO, LOC +
369  QString("Sending CA_PMT with %1 to CI slot #%2")
370  .arg(cplm_info[cplm]).arg(s));
371 
372  if (!m_ciHandler->SetCaPmt(capmt, s))
373  LOG(success ? VB_DVBCAM : VB_GENERAL, LOG_ERR,
374  LOC + "CA_PMT send failed!");
375  else
376  success = true;
377  }
378 }
379 
380 static void process_desc(cCiCaPmt &capmt,
381  const unsigned short *casids,
382  const desc_list_t &desc)
383 {
384  desc_list_t::const_iterator it;
385  for (it = desc.begin(); it != desc.end(); ++it)
386  {
388  for (uint q = 0; casids[q]; q++)
389  {
390  if (!cad.IsValid() || cad.SystemID() != casids[q])
391  continue;
392 
393  LOG(VB_DVBCAM, LOG_INFO, QString("DVBCam: Adding CA descriptor: "
394  "CASID(0x%2), ECM PID(0x%3)")
395  .arg(cad.SystemID(),0,16).arg(cad.PID(),0,16));
396 
397  capmt.AddCaDescriptor(cad.SystemID(), cad.PID(),
398  cad.DataSize(), cad.Data());
399  }
400  }
401 
402 }
403 
405  const unsigned short *casids,
406  uint cplm)
407 {
408  cCiCaPmt capmt(pmt.ProgramNumber(), cplm);
409 
410  // Add CA descriptors for the service
412  pmt.ProgramInfo(), pmt.ProgramInfoLength(),
414 
415  process_desc(capmt, casids, gdesc);
416 
417  // Add elementary streams + CA descriptors
418  for (uint i = 0; i < pmt.StreamCount(); i++)
419  {
420  LOG(VB_DVBCAM, LOG_INFO,
421  QString("DVBCam: Adding elementary stream: %1, pid(0x%2)")
422  .arg(pmt.StreamDescription(i, "dvb"))
423  .arg(pmt.StreamPID(i),0,16));
424 
425  capmt.AddElementaryStream(pmt.StreamType(i), pmt.StreamPID(i));
426 
428  pmt.StreamInfo(i), pmt.StreamInfoLength(i),
430 
431  process_desc(capmt, casids, desc);
432  }
433  return capmt;
434 }
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
#define LOC
Definition: dvbcam.cpp:60
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
virtual int NumSlots(void)=0
#define CPLM_LAST
Definition: dvbci.h:118
bool m_pmtAdded
Definition: dvbcam.h:63
static cCiHandler * CreateCiHandler(const char *FileName)
Definition: dvbci.cpp:1564
QMutex m_pmtLock
Definition: dvbcam.h:57
cCiCaPmt CreateCAPMT(const ProgramMapTable &, const unsigned short *, uint)
Definition: dvbcam.cpp:404
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:311
int m_numslots
Definition: dvbcam.h:48
virtual bool NeedCaPmt(void)=0
cCiHandler * m_ciHandler
Definition: dvbcam.h:54
unsigned int uint
Definition: compat.h:140
bool Start(void)
Definition: dvbcam.cpp:86
vector< const unsigned char * > desc_list_t
static MythThemedMenu * menu
virtual bool HasUserIO(void)=0
static QString GetDeviceName(dvb_dev_type_t, const QString &device)
bool m_havePmt
Definition: dvbcam.h:60
bool Stop(void)
Definition: dvbcam.cpp:118
pmt_list_t m_pmtList
Definition: dvbcam.h:58
static const char * cplm_info[]
Definition: dvbcam.cpp:323
void AddCaDescriptor(int ca_system_id, int ca_pid, int data_len, const uint8_t *data)
Definition: dvbci.cpp:1504
~DVBCam()
Definition: dvbcam.cpp:81
uint StreamInfoLength(uint i) const
Definition: mpegtables.h:708
uint StreamType(uint i) const
Definition: mpegtables.h:702
static void process_desc(cCiCaPmt &capmt, const unsigned short *casids, const desc_list_t &desc)
Definition: dvbcam.cpp:380
void HandleUserIO(void)
Definition: dvbcam.cpp:153
uint StreamPID(uint i) const
Definition: mpegtables.h:705
virtual cCiEnquiry * GetEnquiry(void)=0
#define close
Definition: compat.h:16
QString m_device
Definition: dvbcam.h:47
#define CPLM_FIRST
Definition: dvbci.h:117
uint StreamCount(void) const
Definition: mpegtables.h:714
virtual bool SetCaPmt(cCiCaPmt &CaPmt, int Slot)=0
const unsigned char * Data(void) const
bool m_pmtUpdated
Definition: dvbcam.h:62
const unsigned char * StreamInfo(uint i) const
Definition: mpegtables.h:711
virtual const unsigned short * GetCaSystemIds(int Slot)=0
const char * Text(void)
Definition: dvbci.h:107
virtual cCiMenu * GetMenu(void)=0
bool m_ciHandlerDoRun
Definition: dvbcam.h:52
#define CPLM_ADD
Definition: dvbci.h:120
pmt_list_t m_pmtAddList
Definition: dvbcam.h:59
void SetTimeOffset(double offset_in_seconds)
Definition: dvbcam.cpp:316
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
virtual void SetTimeOffset(double)
Definition: dvbci.h:156
void run(void) override
Definition: dvbcam.cpp:254
void HandlePMT(void)
Definition: dvbcam.cpp:201
#define CPLM_MORE
Definition: dvbci.h:116
#define CPLM_ONLY
Definition: dvbci.h:119
QMutex m_ciHandlerLock
Definition: dvbcam.h:50
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QString StreamDescription(uint i, const QString &sistandard) const
Returns a better (and more expensive) string representation of type at stream index i than StreamType...
Abstract class providing a generic interface to tuning hardware.
Definition: channelbase.h:31
QWaitCondition m_ciHandlerWait
Definition: dvbcam.h:51
uint ProgramNumber(void) const
Definition: mpegtables.h:693
Definition: dvbci.h:72
bool m_pmtSent
Definition: dvbcam.h:61
uint ProgramInfoLength(void) const
Definition: mpegtables.h:696
bool m_ciHandlerRunning
Definition: dvbcam.h:53
bool IsValid(void) const
virtual bool Process(void)=0
A PMT table maps a program described in the ProgramAssociationTable to various PID's which describe t...
Definition: mpegtables.h:656
void SendPMT(const ProgramMapTable &pmt, uint cplm)
Definition: dvbcam.cpp:338
MThread * m_ciHandlerThread
Definition: dvbcam.h:55
const unsigned char * ProgramInfo(void) const
Definition: mpegtables.h:699
static desc_list_t ParseOnlyInclude(const unsigned char *data, uint len, int excluded_descid)
DVBCam(const QString &device)
Definition: dvbcam.cpp:62
void SetPMT(const ChannelBase *chan, const ProgramMapTable *pmt)
Definition: dvbcam.cpp:283