00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include <iostream>
00037 #include <vector>
00038 #include <map>
00039 #include <cstdlib>
00040 #include <cstdio>
00041 using namespace std;
00042
00043 #include <fcntl.h>
00044 #include <sys/ioctl.h>
00045 #include <sys/poll.h>
00046 #include <linux/dvb/ca.h>
00047
00048 #include <dvbci.h>
00049
00050 #include "recorderbase.h"
00051
00052 #include "cardutil.h"
00053
00054 #include "dvbcam.h"
00055 #include "mthread.h"
00056 #include "dvbchannel.h"
00057 #include "dvbrecorder.h"
00058 #include "mythlogging.h"
00059
00060 #define LOC QString("DVB#%1 CA: ").arg(device)
00061
00062 DVBCam::DVBCam(const QString &aDevice)
00063 : device(aDevice), numslots(0),
00064 ciHandlerDoRun(false), ciHandlerRunning(false),
00065 ciHandler(NULL), ciHandlerThread(NULL),
00066 have_pmt(false), pmt_sent(false),
00067 pmt_updated(false), pmt_added(false)
00068 {
00069 QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_CA, device);
00070 QByteArray dev = dvbdev.toAscii();
00071 int cafd = open(dev.constData(), O_RDWR);
00072 if (cafd >= 0)
00073 {
00074 ca_caps_t caps;
00075 ioctl(cafd, CA_GET_CAP, &caps);
00076 numslots = caps.slot_num;
00077 close(cafd);
00078 }
00079 }
00080
00081 DVBCam::~DVBCam()
00082 {
00083 Stop();
00084 }
00085
00086 bool DVBCam::Start(void)
00087 {
00088 if (numslots == 0)
00089 return false;
00090
00091 have_pmt = false;
00092 pmt_sent = false;
00093 pmt_updated = false;
00094 pmt_added = false;
00095
00096 QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_CA, device);
00097 QByteArray dev = dvbdev.toAscii();
00098 ciHandler = cCiHandler::CreateCiHandler(dev.constData());
00099 if (!ciHandler)
00100 {
00101 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialize CI handler");
00102 return false;
00103 }
00104
00105 QMutexLocker locker(&ciHandlerLock);
00106 ciHandlerDoRun = true;
00107 ciHandlerThread = new MThread("DVBCam", this);
00108 ciHandlerThread->start();
00109 while (ciHandlerDoRun && !ciHandlerRunning)
00110 ciHandlerWait.wait(locker.mutex(), 1000);
00111
00112 if (ciHandlerRunning)
00113 LOG(VB_DVBCAM, LOG_INFO, LOC + "CI handler successfully initialized!");
00114
00115 return ciHandlerRunning;
00116 }
00117
00118 bool DVBCam::Stop(void)
00119 {
00120 {
00121 QMutexLocker locker(&ciHandlerLock);
00122 if (ciHandlerRunning)
00123 {
00124 ciHandlerDoRun = false;
00125 locker.unlock();
00126 ciHandlerThread->wait();
00127 locker.relock();
00128 delete ciHandlerThread;
00129 ciHandlerThread = NULL;
00130 }
00131
00132 if (ciHandler)
00133 {
00134 delete ciHandler;
00135 ciHandler = NULL;
00136 }
00137 }
00138
00139 QMutexLocker locker(&pmt_lock);
00140 pmt_list_t::iterator it;
00141
00142 for (it = PMTList.begin(); it != PMTList.end(); ++it)
00143 delete *it;
00144 PMTList.clear();
00145
00146 for (it = PMTAddList.begin(); it != PMTAddList.end(); ++it)
00147 delete *it;
00148 PMTAddList.clear();
00149
00150 return true;
00151 }
00152
00153 void DVBCam::HandleUserIO(void)
00154 {
00155 cCiEnquiry* enq = ciHandler->GetEnquiry();
00156 if (enq != NULL)
00157 {
00158 if (enq->Text() != NULL)
00159 LOG(VB_DVBCAM, LOG_INFO, LOC + QString("CAM: Received message: %1")
00160 .arg(enq->Text()));
00161 delete enq;
00162 }
00163
00164 cCiMenu* menu = ciHandler->GetMenu();
00165 if (menu != NULL)
00166 {
00167 if (menu->TitleText() != NULL)
00168 LOG(VB_DVBCAM, LOG_INFO, LOC + QString("CAM: Menu Title: %1")
00169 .arg(menu->TitleText()));
00170 if (menu->SubTitleText() != NULL)
00171 LOG(VB_DVBCAM, LOG_INFO, LOC + QString("CAM: Menu SubTitle: %1")
00172 .arg(menu->SubTitleText()));
00173 if (menu->BottomText() != NULL)
00174 LOG(VB_DVBCAM, LOG_INFO, LOC + QString("CAM: Menu BottomText: %1")
00175 .arg(menu->BottomText()));
00176
00177 for (int i=0; i<menu->NumEntries(); i++)
00178 if (menu->Entry(i) != NULL)
00179 LOG(VB_DVBCAM, LOG_INFO, LOC + QString("CAM: Menu Entry: %1")
00180 .arg(menu->Entry(i)));
00181
00182 if (menu->Selectable())
00183 {
00184 LOG(VB_CHANNEL, LOG_INFO, LOC + "CAM: Menu is selectable");
00185 }
00186
00187 if (menu->NumEntries() > 0)
00188 {
00189 LOG(VB_DVBCAM, LOG_INFO, LOC + "CAM: Selecting first entry");
00190 menu->Select(0);
00191 }
00192 else
00193 {
00194 LOG(VB_DVBCAM, LOG_INFO, LOC + "CAM: Cancelling menu");
00195 }
00196
00197 delete menu;
00198 }
00199 }
00200
00201 void DVBCam::HandlePMT(void)
00202 {
00203 LOG(VB_DVBCAM, LOG_INFO, LOC + "CiHandler needs CA_PMT");
00204 QMutexLocker locker(&pmt_lock);
00205
00206 if (pmt_sent && pmt_added && !pmt_updated)
00207 {
00208
00209 while (PMTAddList.size() > 0)
00210 {
00211 pmt_list_t::iterator it = PMTAddList.begin();
00212 const ChannelBase *chan = it.key();
00213 ProgramMapTable *pmt = (*it);
00214 PMTList[chan] = pmt;
00215 PMTAddList.erase(it);
00216 SendPMT(*pmt, CPLM_ADD);
00217 }
00218
00219 pmt_updated = false;
00220 pmt_added = false;
00221 return;
00222 }
00223
00224
00225 while (PMTAddList.size() > 0)
00226 {
00227 pmt_list_t::iterator it = PMTAddList.begin();
00228 const ChannelBase *chan = it.key();
00229 ProgramMapTable *pmt = (*it);
00230 PMTList[chan] = pmt;
00231 PMTAddList.erase(it);
00232 }
00233
00234 uint length = PMTList.size();
00235 uint count = 0;
00236
00237 pmt_list_t::const_iterator pmtit;
00238 for (pmtit = PMTList.begin(); pmtit != PMTList.end(); ++pmtit)
00239 {
00240 uint cplm = (count == 0) ? CPLM_FIRST : CPLM_MORE;
00241 cplm = (count + 1 == length) ? CPLM_LAST : cplm;
00242 cplm = (length == 1) ? CPLM_ONLY : cplm;
00243
00244 SendPMT(**pmtit, cplm);
00245
00246 count++;
00247 }
00248
00249 pmt_sent = true;
00250 pmt_updated = false;
00251 pmt_added = false;
00252 }
00253
00254 void DVBCam::run(void)
00255 {
00256 LOG(VB_DVBCAM, LOG_INFO, LOC + "CI handler thread running");
00257
00258 QMutexLocker locker(&ciHandlerLock);
00259 ciHandlerRunning = true;
00260
00261 while (ciHandlerDoRun)
00262 {
00263 locker.unlock();
00264 if (ciHandler->Process())
00265 {
00266 if (ciHandler->HasUserIO())
00267 HandleUserIO();
00268
00269 bool handle_pmt = pmt_sent && (pmt_updated || pmt_added);
00270 handle_pmt |= have_pmt && ciHandler->NeedCaPmt();
00271
00272 if (handle_pmt)
00273 HandlePMT();
00274 }
00275 locker.relock();
00276 ciHandlerWait.wait(locker.mutex(), 10);
00277 }
00278
00279 ciHandlerRunning = false;
00280 LOG(VB_DVBCAM, LOG_INFO, LOC + "CiHandler thread stopped");
00281 }
00282
00283 void DVBCam::SetPMT(const ChannelBase *chan, const ProgramMapTable *pmt)
00284 {
00285 QMutexLocker locker(&pmt_lock);
00286
00287 pmt_list_t::iterator it = PMTList.find(chan);
00288 pmt_list_t::iterator it2 = PMTAddList.find(chan);
00289 if (!pmt && (it != PMTList.end()))
00290 {
00291 delete *it;
00292 PMTList.erase(it);
00293 pmt_updated = true;
00294 }
00295 else if (!pmt && (it2 != PMTAddList.end()))
00296 {
00297 delete *it2;
00298 PMTAddList.erase(it2);
00299 pmt_added = !PMTAddList.empty();
00300 }
00301 else if (pmt && (PMTList.empty() || (it != PMTList.end())))
00302 {
00303 if (it != PMTList.end())
00304 delete *it;
00305 PMTList[chan] = new ProgramMapTable(*pmt);
00306 have_pmt = true;
00307 pmt_updated = true;
00308 }
00309 else if (pmt && (it == PMTList.end()))
00310 {
00311 PMTAddList[chan] = new ProgramMapTable(*pmt);
00312 pmt_added = true;
00313 }
00314 }
00315
00316 void DVBCam::SetTimeOffset(double offset_in_seconds)
00317 {
00318 QMutexLocker locker(&ciHandlerLock);
00319 if (ciHandler)
00320 ciHandler->SetTimeOffset(offset_in_seconds);
00321 }
00322
00323 static const char *cplm_info[] =
00324 {
00325 "CPLM_MORE",
00326 "CPLM_FIRST",
00327 "CPLM_LAST",
00328 "CPLM_ONLY",
00329 "CPLM_ADD",
00330 "CPLM_UPDATE"
00331 };
00332
00333 cCiCaPmt CreateCAPMT(const ProgramMapTable&, const unsigned short*, uint);
00334
00335
00336
00337
00338 void DVBCam::SendPMT(const ProgramMapTable &pmt, uint cplm)
00339 {
00340 bool success = false;
00341
00342 for (uint s = 0; s < (uint)ciHandler->NumSlots(); s++)
00343 {
00344 const unsigned short *casids = ciHandler->GetCaSystemIds(s);
00345
00346 if (!casids)
00347 {
00348 LOG(success ? VB_DVBCAM : VB_GENERAL, LOG_ERR,
00349 LOC + "GetCaSystemIds returned NULL! " +
00350 QString("(Slot #%1)").arg(s));
00351 continue;
00352 }
00353
00354 if (!casids[0])
00355 {
00356 LOG(success ? VB_DVBCAM : VB_GENERAL, LOG_ERR,
00357 LOC + "CAM supports no CA systems! " +
00358 QString("(Slot #%1)").arg(s));
00359 continue;
00360 }
00361
00362 LOG(VB_DVBCAM, LOG_INFO, LOC +
00363 QString("Creating CA_PMT, ServiceID = %1")
00364 .arg(pmt.ProgramNumber()));
00365
00366 cCiCaPmt capmt = CreateCAPMT(pmt, casids, cplm);
00367
00368 LOG(VB_DVBCAM, LOG_INFO, LOC +
00369 QString("Sending CA_PMT with %1 to CI slot #%2")
00370 .arg(cplm_info[cplm]).arg(s));
00371
00372 if (!ciHandler->SetCaPmt(capmt, s))
00373 LOG(success ? VB_DVBCAM : VB_GENERAL, LOG_ERR,
00374 LOC + "CA_PMT send failed!");
00375 else
00376 success = true;
00377 }
00378 }
00379
00380 static void process_desc(cCiCaPmt &capmt,
00381 const unsigned short *casids,
00382 const desc_list_t &desc)
00383 {
00384 desc_list_t::const_iterator it;
00385 for (it = desc.begin(); it != desc.end(); ++it)
00386 {
00387 ConditionalAccessDescriptor cad(*it);
00388 for (uint q = 0; casids[q]; q++)
00389 {
00390 if (cad.SystemID() != casids[q])
00391 continue;
00392
00393 LOG(VB_DVBCAM, LOG_INFO, QString("DVBCam: Adding CA descriptor: "
00394 "CASID(0x%2), ECM PID(0x%3)")
00395 .arg(cad.SystemID(),0,16).arg(cad.PID(),0,16));
00396
00397 capmt.AddCaDescriptor(cad.SystemID(), cad.PID(),
00398 cad.DataSize(), cad.Data());
00399 }
00400 }
00401
00402 }
00403
00404 cCiCaPmt CreateCAPMT(const ProgramMapTable &pmt,
00405 const unsigned short *casids,
00406 uint cplm)
00407 {
00408 cCiCaPmt capmt(pmt.ProgramNumber(), cplm);
00409
00410
00411 desc_list_t gdesc = MPEGDescriptor::ParseOnlyInclude(
00412 pmt.ProgramInfo(), pmt.ProgramInfoLength(),
00413 DescriptorID::conditional_access);
00414
00415 process_desc(capmt, casids, gdesc);
00416
00417
00418 for (uint i = 0; i < pmt.StreamCount(); i++)
00419 {
00420 LOG(VB_DVBCAM, LOG_INFO,
00421 QString("DVBCam: Adding elementary stream: %1, pid(0x%2)")
00422 .arg(pmt.StreamDescription(i, "dvb"))
00423 .arg(pmt.StreamPID(i),0,16));
00424
00425 capmt.AddElementaryStream(pmt.StreamType(i), pmt.StreamPID(i));
00426
00427 desc_list_t desc = MPEGDescriptor::ParseOnlyInclude(
00428 pmt.StreamInfo(i), pmt.StreamInfoLength(i),
00429 DescriptorID::conditional_access);
00430
00431 process_desc(capmt, casids, desc);
00432 }
00433 return capmt;
00434 }