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