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 <cstdio>
37 #include <cstdlib>
38 #include <iostream>
39 #include <map>
40 #include <utility>
41 #include <vector>
42 
43 using namespace std;
44 
45 #include <fcntl.h>
46 #include <sys/ioctl.h>
47 #include <sys/poll.h>
48 #include <linux/dvb/ca.h>
49 
50 #include <dvbci.h>
51 
52 #include "recorderbase.h"
53 
54 #include "cardutil.h"
55 
56 #include "dvbcam.h"
57 #include "mthread.h"
58 #include "dvbchannel.h"
59 #include "dvbrecorder.h"
60 #include "mythlogging.h"
61 
62 #define LOC QString("DVB#%1 CA: ").arg(m_device)
63 
64 DVBCam::DVBCam(QString aDevice)
65  : m_device(std::move(aDevice))
66 {
67  QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_CA, m_device);
68  QByteArray dev = dvbdev.toLatin1();
69  int cafd = open(dev.constData(), O_RDWR);
70  if (cafd >= 0)
71  {
72  ca_caps_t caps;
73  // slot_num will be uninitialised if ioctl fails
74  if (ioctl(cafd, CA_GET_CAP, &caps) >= 0)
75  m_numslots = caps.slot_num;
76  else
77  LOG(VB_GENERAL, LOG_ERR, "ioctl CA_GET_CAP failed: " + ENO);
78 
79  close(cafd);
80  }
81 }
82 
84 {
85  Stop();
86 }
87 
88 bool DVBCam::Start(void)
89 {
90  if (m_numslots == 0)
91  return false;
92 
93  m_havePmt = false;
94  m_pmtSent = false;
95  m_pmtUpdated = false;
96  m_pmtAdded = false;
97 
98  QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_CA, m_device);
99  QByteArray dev = dvbdev.toLatin1();
100  m_ciHandler = cCiHandler::CreateCiHandler(dev.constData());
101  if (!m_ciHandler)
102  {
103  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialize CI handler");
104  return false;
105  }
106 
107  QMutexLocker locker(&m_ciHandlerLock);
108  m_ciHandlerDoRun = true;
109  m_ciHandlerThread = new MThread("DVBCam", this);
112  m_ciHandlerWait.wait(locker.mutex(), 1000);
113 
114  if (m_ciHandlerRunning)
115  LOG(VB_DVBCAM, LOG_INFO, LOC + "CI handler successfully initialized!");
116 
117  return m_ciHandlerRunning;
118 }
119 
120 bool DVBCam::Stop(void)
121 {
122  {
123  QMutexLocker locker(&m_ciHandlerLock);
124  if (m_ciHandlerRunning)
125  {
126  m_ciHandlerDoRun = false;
127  locker.unlock();
129  locker.relock();
130  delete m_ciHandlerThread;
131  m_ciHandlerThread = nullptr;
132  }
133 
134  if (m_ciHandler)
135  {
136  delete m_ciHandler;
137  m_ciHandler = nullptr;
138  }
139  }
140 
141  QMutexLocker locker(&m_pmtLock);
142  pmt_list_t::iterator it;
143 
144  for (it = m_pmtList.begin(); it != m_pmtList.end(); ++it)
145  delete *it;
146  m_pmtList.clear();
147 
148  for (it = m_pmtAddList.begin(); it != m_pmtAddList.end(); ++it)
149  delete *it;
150  m_pmtAddList.clear();
151 
152  return true;
153 }
154 
156 {
158  if (enq != nullptr)
159  {
160  if (enq->Text() != nullptr)
161  LOG(VB_DVBCAM, LOG_INFO, LOC + QString("CAM: Received message: %1")
162  .arg(enq->Text()));
163  delete enq;
164  }
165 
167  if (menu != nullptr)
168  {
169  if (menu->TitleText() != nullptr)
170  LOG(VB_DVBCAM, LOG_INFO, LOC + QString("CAM: Menu Title: %1")
171  .arg(menu->TitleText()));
172  if (menu->SubTitleText() != nullptr)
173  LOG(VB_DVBCAM, LOG_INFO, LOC + QString("CAM: Menu SubTitle: %1")
174  .arg(menu->SubTitleText()));
175  if (menu->BottomText() != nullptr)
176  LOG(VB_DVBCAM, LOG_INFO, LOC + QString("CAM: Menu BottomText: %1")
177  .arg(menu->BottomText()));
178 
179  for (int i=0; i<menu->NumEntries(); i++)
180  {
181  if (menu->Entry(i) != nullptr)
182  LOG(VB_DVBCAM, LOG_INFO, LOC + QString("CAM: Menu Entry: %1")
183  .arg(menu->Entry(i)));
184  }
185 
186  if (menu->Selectable())
187  {
188  LOG(VB_CHANNEL, LOG_INFO, LOC + "CAM: Menu is selectable");
189  }
190 
191  if (menu->NumEntries() > 0)
192  {
193  LOG(VB_DVBCAM, LOG_INFO, LOC + "CAM: Selecting first entry");
194  menu->Select(0);
195  }
196  else
197  {
198  LOG(VB_DVBCAM, LOG_INFO, LOC + "CAM: Cancelling menu");
199  }
200 
201  delete menu;
202  }
203 }
204 
206 {
207  LOG(VB_DVBCAM, LOG_INFO, LOC + "CiHandler needs CA_PMT");
208  QMutexLocker locker(&m_pmtLock);
209 
210  if (m_pmtSent && m_pmtAdded && !m_pmtUpdated)
211  {
212  // Send added PMT
213  while (!m_pmtAddList.empty())
214  {
215  pmt_list_t::iterator it = m_pmtAddList.begin();
216  const ChannelBase *chan = it.key();
217  ProgramMapTable *pmt = (*it);
218  m_pmtList[chan] = pmt;
219  m_pmtAddList.erase(it);
220  SendPMT(*pmt, CPLM_ADD);
221  }
222 
223  m_pmtUpdated = false;
224  m_pmtAdded = false;
225  return;
226  }
227 
228  // Grab any added PMT
229  while (!m_pmtAddList.empty())
230  {
231  pmt_list_t::iterator it = m_pmtAddList.begin();
232  const ChannelBase *chan = it.key();
233  ProgramMapTable *pmt = (*it);
234  m_pmtList[chan] = pmt;
235  m_pmtAddList.erase(it);
236  }
237 
238  uint length = m_pmtList.size();
239  uint count = 0;
240 
241  pmt_list_t::const_iterator pmtit;
242  for (pmtit = m_pmtList.begin(); pmtit != m_pmtList.end(); ++pmtit)
243  {
244  uint cplm = (count == 0) ? CPLM_FIRST : CPLM_MORE;
245  cplm = (count + 1 == length) ? CPLM_LAST : cplm;
246  cplm = (length == 1) ? CPLM_ONLY : cplm;
247 
248  SendPMT(**pmtit, cplm);
249 
250  count++;
251  }
252 
253  m_pmtSent = true;
254  m_pmtUpdated = false;
255  m_pmtAdded = false;
256 }
257 
258 void DVBCam::run(void)
259 {
260  LOG(VB_DVBCAM, LOG_INFO, LOC + "CI handler thread running");
261 
262  QMutexLocker locker(&m_ciHandlerLock);
263  m_ciHandlerRunning = true;
264 
265  while (m_ciHandlerDoRun)
266  {
267  locker.unlock();
268  if (m_ciHandler->Process())
269  {
270  if (m_ciHandler->HasUserIO())
271  HandleUserIO();
272 
273  bool handle_pmt = m_pmtSent && (m_pmtUpdated || m_pmtAdded);
274  handle_pmt |= m_havePmt && m_ciHandler->NeedCaPmt();
275 
276  if (handle_pmt)
277  HandlePMT();
278  }
279  locker.relock();
280  m_ciHandlerWait.wait(locker.mutex(), 10);
281  }
282 
283  m_ciHandlerRunning = false;
284  LOG(VB_DVBCAM, LOG_INFO, LOC + "CiHandler thread stopped");
285 }
286 
287 void DVBCam::SetPMT(const ChannelBase *chan, const ProgramMapTable *pmt)
288 {
289  QMutexLocker locker(&m_pmtLock);
290 
291  pmt_list_t::iterator it = m_pmtList.find(chan);
292  pmt_list_t::iterator it2 = m_pmtAddList.find(chan);
293  if (!pmt && (it != m_pmtList.end()))
294  {
295  delete *it;
296  m_pmtList.erase(it);
297  m_pmtUpdated = true;
298  }
299  else if (!pmt && (it2 != m_pmtAddList.end()))
300  {
301  delete *it2;
302  m_pmtAddList.erase(it2);
303  m_pmtAdded = !m_pmtAddList.empty();
304  }
305  else if (pmt && (m_pmtList.empty() || (it != m_pmtList.end())))
306  {
307  if (it != m_pmtList.end())
308  delete *it;
309  m_pmtList[chan] = new ProgramMapTable(*pmt);
310  m_havePmt = true;
311  m_pmtUpdated = true;
312  }
313  else if (pmt && (it == m_pmtList.end()))
314  {
315  m_pmtAddList[chan] = new ProgramMapTable(*pmt);
316  m_pmtAdded = true;
317  }
318 }
319 
320 void DVBCam::SetTimeOffset(double offset_in_seconds)
321 {
322  QMutexLocker locker(&m_ciHandlerLock);
323  if (m_ciHandler)
324  m_ciHandler->SetTimeOffset(offset_in_seconds);
325 }
326 
327 static const char *cplm_info[] =
328 {
329  "CPLM_MORE",
330  "CPLM_FIRST",
331  "CPLM_LAST",
332  "CPLM_ONLY",
333  "CPLM_ADD",
334  "CPLM_UPDATE"
335 };
336 
337 cCiCaPmt CreateCAPMT(const ProgramMapTable& /*pmt*/, const unsigned short* /*casids*/, uint /*cplm*/);
338 
339 /*
340  * Send a CA_PMT object to the CAM (see EN50221, section 8.4.3.4)
341  */
342 void DVBCam::SendPMT(const ProgramMapTable &pmt, uint cplm)
343 {
344  bool success = false;
345 
346  for (uint s = 0; s < (uint)m_ciHandler->NumSlots(); s++)
347  {
348  const unsigned short *casids = m_ciHandler->GetCaSystemIds(s);
349 
350  if (!casids)
351  {
352  LOG(success ? VB_DVBCAM : VB_GENERAL, LOG_ERR,
353  LOC + "GetCaSystemIds returned NULL! " +
354  QString("(Slot #%1)").arg(s));
355  continue;
356  }
357 
358  if (!casids[0])
359  {
360  LOG(success ? VB_DVBCAM : VB_GENERAL, LOG_ERR,
361  LOC + "CAM supports no CA systems! " +
362  QString("(Slot #%1)").arg(s));
363  continue;
364  }
365 
366  LOG(VB_DVBCAM, LOG_INFO, LOC +
367  QString("Creating CA_PMT, ServiceID = %1")
368  .arg(pmt.ProgramNumber()));
369 
370  cCiCaPmt capmt = CreateCAPMT(pmt, casids, cplm);
371 
372  LOG(VB_DVBCAM, LOG_INFO, LOC +
373  QString("Sending CA_PMT with %1 to CI slot #%2")
374  .arg(cplm_info[cplm]).arg(s));
375 
376  if (!m_ciHandler->SetCaPmt(capmt, s))
377  {
378  LOG(success ? VB_DVBCAM : VB_GENERAL, LOG_ERR,
379  LOC + "CA_PMT send failed!");
380  }
381  else
382  {
383  success = true;
384  }
385  }
386 }
387 
388 static void process_desc(cCiCaPmt &capmt,
389  const unsigned short *casids,
390  const desc_list_t &desc)
391 {
392  desc_list_t::const_iterator it;
393  for (it = desc.begin(); it != desc.end(); ++it)
394  {
396  for (uint q = 0; casids[q]; q++)
397  {
398  if (!cad.IsValid() || cad.SystemID() != casids[q])
399  continue;
400 
401  LOG(VB_DVBCAM, LOG_INFO, QString("DVBCam: Adding CA descriptor: "
402  "CASID(0x%2), ECM PID(0x%3)")
403  .arg(cad.SystemID(),0,16).arg(cad.PID(),0,16));
404 
405  capmt.AddCaDescriptor(cad.SystemID(), cad.PID(),
406  cad.DataSize(), cad.Data());
407  }
408  }
409 
410 }
411 
413  const unsigned short *casids,
414  uint cplm)
415 {
416  cCiCaPmt capmt(pmt.ProgramNumber(), cplm);
417 
418  // Add CA descriptors for the service
420  pmt.ProgramInfo(), pmt.ProgramInfoLength(),
422 
423  process_desc(capmt, casids, gdesc);
424 
425  // Add elementary streams + CA descriptors
426  for (uint i = 0; i < pmt.StreamCount(); i++)
427  {
428  LOG(VB_DVBCAM, LOG_INFO,
429  QString("DVBCam: Adding elementary stream: %1, pid(0x%2)")
430  .arg(pmt.StreamDescription(i, "dvb"))
431  .arg(pmt.StreamPID(i),0,16));
432 
433  capmt.AddElementaryStream(pmt.StreamType(i), pmt.StreamPID(i));
434 
436  pmt.StreamInfo(i), pmt.StreamInfoLength(i),
438 
439  process_desc(capmt, casids, desc);
440  }
441  return capmt;
442 }
ProgramMapTable::ProgramInfo
const unsigned char * ProgramInfo(void) const
Definition: mpegtables.h:709
dvbci.h
DVBCam::m_numslots
int m_numslots
Definition: dvbcam.h:48
MThread::start
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:292
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:72
cCiCaPmt::AddCaDescriptor
void AddCaDescriptor(int ca_system_id, int ca_pid, int data_len, const uint8_t *data)
Definition: dvbci.cpp:1498
dvbcam.h
CardUtil::GetDeviceName
static QString GetDeviceName(dvb_dev_type_t type, const QString &device)
ProgramMapTable::StreamCount
uint StreamCount(void) const
Definition: mpegtables.h:724
recorderbase.h
cCiHandler::SetCaPmt
virtual bool SetCaPmt(cCiCaPmt &CaPmt, int Slot)=0
ProgramMapTable::StreamInfo
const unsigned char * StreamInfo(uint i) const
Definition: mpegtables.h:721
cplm_info
static const char * cplm_info[]
Definition: dvbcam.cpp:327
ProgramMapTable
A PMT table maps a program described in the ProgramAssociationTable to various PID's which describe t...
Definition: mpegtables.h:666
cCiHandler::GetEnquiry
virtual cCiEnquiry * GetEnquiry(void)=0
DescriptorID::conditional_access
@ conditional_access
Definition: mpegdescriptors.h:33
cCiEnquiry::Text
const char * Text(void)
Definition: dvbci.h:107
DVBCam::Stop
bool Stop(void)
Definition: dvbcam.cpp:120
arg
arg(title).arg(filename).arg(doDelete))
DVBCam::m_pmtList
pmt_list_t m_pmtList
Definition: dvbcam.h:58
desc_list_t
vector< const unsigned char * > desc_list_t
Definition: mpegdescriptors.h:18
DVBCam::HandleUserIO
void HandleUserIO(void)
Definition: dvbcam.cpp:155
cCiCaPmt
Definition: dvbci.h:123
ProgramMapTable::ProgramNumber
uint ProgramNumber(void) const
Definition: mpegtables.h:703
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
DVBCam::m_pmtUpdated
bool m_pmtUpdated
Definition: dvbcam.h:62
DVB_DEV_CA
@ DVB_DEV_CA
Definition: cardutil.h:32
cCiHandler::Process
virtual bool Process(void)=0
ConditionalAccessDescriptor::DataSize
uint DataSize(void) const
Definition: mpegdescriptors.h:390
menu
static MythThemedMenu * menu
Definition: mythtv/programs/mythtv-setup/main.cpp:55
dvbchannel.h
close
#define close
Definition: compat.h:16
CPLM_FIRST
#define CPLM_FIRST
Definition: dvbci.h:117
DVBCam::SetTimeOffset
void SetTimeOffset(double offset_in_seconds)
Definition: dvbcam.cpp:320
dvbrecorder.h
MPEGDescriptor::IsValid
bool IsValid(void) const
Definition: mpegdescriptors.h:315
cCiHandler::SetTimeOffset
virtual void SetTimeOffset(double)
Definition: dvbci.h:156
DVBCam::~DVBCam
~DVBCam() override
Definition: dvbcam.cpp:83
ProgramMapTable::StreamDescription
QString StreamDescription(uint i, const QString &sistandard) const
Returns a better (and more expensive) string representation of type at stream index i than StreamType...
Definition: mpegtables.cpp:1160
ConditionalAccessDescriptor::Data
const unsigned char * Data(void) const
Definition: mpegdescriptors.h:391
DVBCam::m_pmtAddList
pmt_list_t m_pmtAddList
Definition: dvbcam.h:59
mythlogging.h
ChannelBase
Abstract class providing a generic interface to tuning hardware.
Definition: channelbase.h:31
CreateCAPMT
cCiCaPmt CreateCAPMT(const ProgramMapTable &, const unsigned short *, uint)
Definition: dvbcam.cpp:412
CPLM_MORE
#define CPLM_MORE
Definition: dvbci.h:116
cCiHandler::NumSlots
virtual int NumSlots(void)=0
ConditionalAccessDescriptor::SystemID
uint SystemID(void) const
Definition: mpegdescriptors.h:388
cCiHandler::NeedCaPmt
virtual bool NeedCaPmt(void)=0
ProgramMapTable::StreamType
uint StreamType(uint i) const
Definition: mpegtables.h:712
DVBCam::run
void run(void) override
Definition: dvbcam.cpp:258
DVBCam::Start
bool Start(void)
Definition: dvbcam.cpp:88
DVBCam::m_havePmt
bool m_havePmt
Definition: dvbcam.h:60
ConditionalAccessDescriptor
Definition: mpegdescriptors.h:382
DVBCam::m_ciHandlerLock
QMutex m_ciHandlerLock
Definition: dvbcam.h:50
MPEGDescriptor::ParseOnlyInclude
static desc_list_t ParseOnlyInclude(const unsigned char *data, uint len, int excluded_descid)
Definition: mpegdescriptors.cpp:57
DVBCam::m_ciHandlerWait
QWaitCondition m_ciHandlerWait
Definition: dvbcam.h:51
cCiHandler::HasUserIO
virtual bool HasUserIO(void)=0
LOC
#define LOC
Definition: dvbcam.cpp:62
DVBCam::m_pmtSent
bool m_pmtSent
Definition: dvbcam.h:61
uint
unsigned int uint
Definition: compat.h:140
DVBCam::m_device
QString m_device
Definition: dvbcam.h:47
ProgramMapTable::ProgramInfoLength
uint ProgramInfoLength(void) const
Definition: mpegtables.h:706
cCiHandler::GetCaSystemIds
virtual const unsigned short * GetCaSystemIds(int Slot)=0
DVBCam::m_ciHandlerDoRun
bool m_ciHandlerDoRun
Definition: dvbcam.h:52
DVBCam::SendPMT
void SendPMT(const ProgramMapTable &pmt, uint cplm)
Definition: dvbcam.cpp:342
cardutil.h
DVBCam::m_ciHandlerThread
MThread * m_ciHandlerThread
Definition: dvbcam.h:55
MThread
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:48
DVBCam::SetPMT
void SetPMT(const ChannelBase *chan, const ProgramMapTable *pmt)
Definition: dvbcam.cpp:287
CPLM_ADD
#define CPLM_ADD
Definition: dvbci.h:120
mthread.h
cCiHandler::CreateCiHandler
static cCiHandler * CreateCiHandler(const char *FileName)
Definition: dvbci.cpp:1555
CPLM_ONLY
#define CPLM_ONLY
Definition: dvbci.h:119
cCiMenu
Definition: dvbci.h:72
cCiHandler::GetMenu
virtual cCiMenu * GetMenu(void)=0
cCiEnquiry
Definition: dvbci.h:97
DVBCam::HandlePMT
void HandlePMT(void)
Definition: dvbcam.cpp:205
DVBCam::DVBCam
DVBCam(QString device)
Definition: dvbcam.cpp:64
MThread::wait
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:309
DVBCam::m_pmtAdded
bool m_pmtAdded
Definition: dvbcam.h:63
ProgramMapTable::StreamInfoLength
uint StreamInfoLength(uint i) const
Definition: mpegtables.h:718
ProgramMapTable::StreamPID
uint StreamPID(uint i) const
Definition: mpegtables.h:715
CPLM_LAST
#define CPLM_LAST
Definition: dvbci.h:118
process_desc
static void process_desc(cCiCaPmt &capmt, const unsigned short *casids, const desc_list_t &desc)
Definition: dvbcam.cpp:388
DVBCam::m_ciHandlerRunning
bool m_ciHandlerRunning
Definition: dvbcam.h:53
ConditionalAccessDescriptor::PID
uint PID(void) const
Definition: mpegdescriptors.h:389
DVBCam::m_pmtLock
QMutex m_pmtLock
Definition: dvbcam.h:57
cCiCaPmt::AddElementaryStream
void AddElementaryStream(int type, int pid)
Definition: dvbci.cpp:1463
DVBCam::m_ciHandler
cCiHandler * m_ciHandler
Definition: dvbcam.h:54