vdr  1.7.27
pat.c
Go to the documentation of this file.
00001 /*
00002  * pat.c: PAT section filter
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: pat.c 2.17 2012/03/02 10:56:45 kls Exp $
00008  */
00009 
00010 #include "pat.h"
00011 #include <malloc.h>
00012 #include "channels.h"
00013 #include "libsi/section.h"
00014 #include "libsi/descriptor.h"
00015 #include "thread.h"
00016 #include "vdrttxtsubshooks.h"
00017 
00018 #define PMT_SCAN_TIMEOUT  10 // seconds
00019 
00020 // --- cCaDescriptor ---------------------------------------------------------
00021 
00022 class cCaDescriptor : public cListObject {
00023 private:
00024   int caSystem;
00025   int esPid;
00026   int length;
00027   uchar *data;
00028 public:
00029   cCaDescriptor(int CaSystem, int CaPid, int EsPid, int Length, const uchar *Data);
00030   virtual ~cCaDescriptor();
00031   bool operator== (const cCaDescriptor &arg) const;
00032   int CaSystem(void) { return caSystem; }
00033   int EsPid(void) { return esPid; }
00034   int Length(void) const { return length; }
00035   const uchar *Data(void) const { return data; }
00036   };
00037 
00038 cCaDescriptor::cCaDescriptor(int CaSystem, int CaPid, int EsPid, int Length, const uchar *Data)
00039 {
00040   caSystem = CaSystem;
00041   esPid = EsPid;
00042   length = Length + 6;
00043   data = MALLOC(uchar, length);
00044   data[0] = SI::CaDescriptorTag;
00045   data[1] = length - 2;
00046   data[2] = (caSystem >> 8) & 0xFF;
00047   data[3] =  caSystem       & 0xFF;
00048   data[4] = ((CaPid   >> 8) & 0x1F) | 0xE0;
00049   data[5] =   CaPid         & 0xFF;
00050   if (Length)
00051      memcpy(&data[6], Data, Length);
00052 }
00053 
00054 cCaDescriptor::~cCaDescriptor()
00055 {
00056   free(data);
00057 }
00058 
00059 bool cCaDescriptor::operator== (const cCaDescriptor &arg) const
00060 {
00061   return esPid == arg.esPid && length == arg.length && memcmp(data, arg.data, length) == 0;
00062 }
00063 
00064 // --- cCaDescriptors --------------------------------------------------------
00065 
00066 class cCaDescriptors : public cListObject {
00067 private:
00068   int source;
00069   int transponder;
00070   int serviceId;
00071   int numCaIds;
00072   int caIds[MAXCAIDS + 1];
00073   cList<cCaDescriptor> caDescriptors;
00074   void AddCaId(int CaId);
00075 public:
00076   cCaDescriptors(int Source, int Transponder, int ServiceId);
00077   bool operator== (const cCaDescriptors &arg) const;
00078   bool Is(int Source, int Transponder, int ServiceId);
00079   bool Is(cCaDescriptors * CaDescriptors);
00080   bool Empty(void) { return caDescriptors.Count() == 0; }
00081   void AddCaDescriptor(SI::CaDescriptor *d, int EsPid);
00082   int GetCaDescriptors(const int *CaSystemIds, int BufSize, uchar *Data, int EsPid);
00083   const int *CaIds(void) { return caIds; }
00084   };
00085 
00086 cCaDescriptors::cCaDescriptors(int Source, int Transponder, int ServiceId)
00087 {
00088   source = Source;
00089   transponder = Transponder;
00090   serviceId = ServiceId;
00091   numCaIds = 0;
00092   caIds[0] = 0;
00093 }
00094 
00095 bool cCaDescriptors::operator== (const cCaDescriptors &arg) const
00096 {
00097   cCaDescriptor *ca1 = caDescriptors.First();
00098   cCaDescriptor *ca2 = arg.caDescriptors.First();
00099   while (ca1 && ca2) {
00100         if (!(*ca1 == *ca2))
00101            return false;
00102         ca1 = caDescriptors.Next(ca1);
00103         ca2 = arg.caDescriptors.Next(ca2);
00104         }
00105   return !ca1 && !ca2;
00106 }
00107 
00108 bool cCaDescriptors::Is(int Source, int Transponder, int ServiceId)
00109 {
00110   return source == Source && transponder == Transponder && serviceId == ServiceId;
00111 }
00112 
00113 bool cCaDescriptors::Is(cCaDescriptors *CaDescriptors)
00114 {
00115   return Is(CaDescriptors->source, CaDescriptors->transponder, CaDescriptors->serviceId);
00116 }
00117 
00118 void cCaDescriptors::AddCaId(int CaId)
00119 {
00120   if (numCaIds < MAXCAIDS) {
00121      for (int i = 0; i < numCaIds; i++) {
00122          if (caIds[i] == CaId)
00123             return;
00124          }
00125      caIds[numCaIds++] = CaId;
00126      caIds[numCaIds] = 0;
00127      }
00128 }
00129 
00130 void cCaDescriptors::AddCaDescriptor(SI::CaDescriptor *d, int EsPid)
00131 {
00132   cCaDescriptor *nca = new cCaDescriptor(d->getCaType(), d->getCaPid(), EsPid, d->privateData.getLength(), d->privateData.getData());
00133   for (cCaDescriptor *ca = caDescriptors.First(); ca; ca = caDescriptors.Next(ca)) {
00134       if (*ca == *nca) {
00135          delete nca;
00136          return;
00137          }
00138       }
00139   AddCaId(nca->CaSystem());
00140   caDescriptors.Add(nca);
00141 //#define DEBUG_CA_DESCRIPTORS 1
00142 #ifdef DEBUG_CA_DESCRIPTORS
00143   char buffer[1024];
00144   char *q = buffer;
00145   q += sprintf(q, "CAM: %04X %5d %5d %04X %04X -", source, transponder, serviceId, d->getCaType(), EsPid);
00146   for (int i = 0; i < nca->Length(); i++)
00147       q += sprintf(q, " %02X", nca->Data()[i]);
00148   dsyslog("%s", buffer);
00149 #endif
00150 }
00151 
00152 // EsPid is to select the "type" of CaDescriptor to be returned
00153 // >0 - CaDescriptor for the particular esPid
00154 // =0 - common CaDescriptor
00155 // <0 - all CaDescriptors regardless of type (old default)
00156 
00157 int cCaDescriptors::GetCaDescriptors(const int *CaSystemIds, int BufSize, uchar *Data, int EsPid)
00158 {
00159   if (!CaSystemIds || !*CaSystemIds)
00160      return 0;
00161   if (BufSize > 0 && Data) {
00162      int length = 0;
00163      for (cCaDescriptor *d = caDescriptors.First(); d; d = caDescriptors.Next(d)) {
00164          if (EsPid < 0 || d->EsPid() == EsPid) {
00165             const int *caids = CaSystemIds;
00166             do {
00167                if (d->CaSystem() == *caids) {
00168                   if (length + d->Length() <= BufSize) {
00169                      memcpy(Data + length, d->Data(), d->Length());
00170                      length += d->Length();
00171                      }
00172                   else
00173                      return -1;
00174                   }
00175                } while (*++caids);
00176             }
00177          }
00178      return length;
00179      }
00180   return -1;
00181 }
00182 
00183 // --- cCaDescriptorHandler --------------------------------------------------
00184 
00185 class cCaDescriptorHandler : public cList<cCaDescriptors> {
00186 private:
00187   cMutex mutex;
00188 public:
00189   int AddCaDescriptors(cCaDescriptors *CaDescriptors);
00190       // Returns 0 if this is an already known descriptor,
00191       // 1 if it is an all new descriptor with actual contents,
00192       // and 2 if an existing descriptor was changed.
00193   int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, int EsPid);
00194   };
00195 
00196 int cCaDescriptorHandler::AddCaDescriptors(cCaDescriptors *CaDescriptors)
00197 {
00198   cMutexLock MutexLock(&mutex);
00199   for (cCaDescriptors *ca = First(); ca; ca = Next(ca)) {
00200       if (ca->Is(CaDescriptors)) {
00201          if (*ca == *CaDescriptors) {
00202             delete CaDescriptors;
00203             return 0;
00204             }
00205          Del(ca);
00206          Add(CaDescriptors);
00207          return 2;
00208          }
00209       }
00210   Add(CaDescriptors);
00211   return CaDescriptors->Empty() ? 0 : 1;
00212 }
00213 
00214 int cCaDescriptorHandler::GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, int EsPid)
00215 {
00216   cMutexLock MutexLock(&mutex);
00217   for (cCaDescriptors *ca = First(); ca; ca = Next(ca)) {
00218       if (ca->Is(Source, Transponder, ServiceId))
00219          return ca->GetCaDescriptors(CaSystemIds, BufSize, Data, EsPid);
00220       }
00221   return 0;
00222 }
00223 
00224 cCaDescriptorHandler CaDescriptorHandler;
00225 
00226 int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, int EsPid)
00227 {
00228   return CaDescriptorHandler.GetCaDescriptors(Source, Transponder, ServiceId, CaSystemIds, BufSize, Data, EsPid);
00229 }
00230 
00231 // --- cPatFilter ------------------------------------------------------------
00232 
00233 cPatFilter::cPatFilter(void)
00234 {
00235   pmtIndex = 0;
00236   pmtPid = 0;
00237   pmtSid = 0;
00238   lastPmtScan = 0;
00239   numPmtEntries = 0;
00240   Set(0x00, 0x00);  // PAT
00241 }
00242 
00243 void cPatFilter::SetStatus(bool On)
00244 {
00245   cFilter::SetStatus(On);
00246   pmtIndex = 0;
00247   pmtPid = 0;
00248   pmtSid = 0;
00249   lastPmtScan = 0;
00250   numPmtEntries = 0;
00251 }
00252 
00253 void cPatFilter::Trigger(void)
00254 {
00255   numPmtEntries = 0;
00256 }
00257 
00258 bool cPatFilter::PmtVersionChanged(int PmtPid, int Sid, int Version)
00259 {
00260   uint64_t v = Version;
00261   v <<= 32;
00262   uint64_t id = (PmtPid | (Sid << 16)) & 0x00000000FFFFFFFFLL;
00263   for (int i = 0; i < numPmtEntries; i++) {
00264       if ((pmtVersion[i] & 0x00000000FFFFFFFFLL) == id) {
00265          bool Changed = (pmtVersion[i] & 0x000000FF00000000LL) != v;
00266          if (Changed)
00267             pmtVersion[i] = id | v;
00268          return Changed;
00269          }
00270       }
00271   if (numPmtEntries < MAXPMTENTRIES)
00272      pmtVersion[numPmtEntries++] = id | v;
00273   return true;
00274 }
00275 
00276 void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
00277 {
00278   if (Pid == 0x00) {
00279      if (Tid == 0x00) {
00280         if (pmtPid && time(NULL) - lastPmtScan > PMT_SCAN_TIMEOUT) {
00281            Del(pmtPid, 0x02);
00282            pmtPid = 0;
00283            pmtIndex++;
00284            lastPmtScan = time(NULL);
00285            }
00286         if (!pmtPid) {
00287            SI::PAT pat(Data, false);
00288            if (!pat.CheckCRCAndParse())
00289               return;
00290            SI::PAT::Association assoc;
00291            int Index = 0;
00292            for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) {
00293                if (!assoc.isNITPid()) {
00294                   if (Index++ >= pmtIndex && Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId())) {
00295                      pmtPid = assoc.getPid();
00296                      pmtSid = assoc.getServiceId();
00297                      Add(pmtPid, 0x02);
00298                      break;
00299                      }
00300                   }
00301                }
00302            if (!pmtPid)
00303               pmtIndex = 0;
00304            }
00305         }
00306      }
00307   else if (Pid == pmtPid && Tid == SI::TableIdPMT && Source() && Transponder()) {
00308      SI::PMT pmt(Data, false);
00309      if (!pmt.CheckCRCAndParse())
00310         return;
00311      if (pmt.getServiceId() != pmtSid)
00312         return; // skip broken PMT records
00313      if (!PmtVersionChanged(pmtPid, pmt.getTableIdExtension(), pmt.getVersionNumber())) {
00314         lastPmtScan = 0; // this triggers the next scan
00315         return;
00316         }
00317      if (!Channels.Lock(true, 10)) {
00318         numPmtEntries = 0; // to make sure we try again
00319         return;
00320         }
00321      cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), pmt.getServiceId());
00322      if (Channel) {
00323         SI::CaDescriptor *d;
00324         cCaDescriptors *CaDescriptors = new cCaDescriptors(Channel->Source(), Channel->Transponder(), Channel->Sid());
00325         // Scan the common loop:
00326         for (SI::Loop::Iterator it; (d = (SI::CaDescriptor*)pmt.commonDescriptors.getNext(it, SI::CaDescriptorTag)); ) {
00327             CaDescriptors->AddCaDescriptor(d, 0);
00328             delete d;
00329             }
00330         // Scan the stream-specific loop:
00331         SI::PMT::Stream stream;
00332         int Vpid = 0;
00333         int Ppid = 0;
00334         int Vtype = 0;
00335         int Apids[MAXAPIDS + 1] = { 0 }; // these lists are zero-terminated
00336         int Atypes[MAXAPIDS + 1] = { 0 };
00337         int Dpids[MAXDPIDS + 1] = { 0 };
00338         int Dtypes[MAXDPIDS + 1] = { 0 };
00339         int Spids[MAXSPIDS + 1] = { 0 };
00340         uchar SubtitlingTypes[MAXSPIDS + 1] = { 0 };
00341         uint16_t CompositionPageIds[MAXSPIDS + 1] = { 0 };
00342         uint16_t AncillaryPageIds[MAXSPIDS + 1] = { 0 };
00343         char ALangs[MAXAPIDS][MAXLANGCODE2] = { "" };
00344         char DLangs[MAXDPIDS][MAXLANGCODE2] = { "" };
00345         char SLangs[MAXSPIDS][MAXLANGCODE2] = { "" };
00346         int Tpid = 0;
00347         tTeletextSubtitlePage TeletextSubtitlePages[MAXTXTPAGES];
00348         int NumTPages = 0;
00349         int NumApids = 0;
00350         int NumDpids = 0;
00351         int NumSpids = 0;
00352         for (SI::Loop::Iterator it; pmt.streamLoop.getNext(stream, it); ) {
00353             bool ProcessCaDescriptors = false;
00354             int esPid = stream.getPid();
00355             switch (stream.getStreamType()) {
00356               case 1: // STREAMTYPE_11172_VIDEO
00357               case 2: // STREAMTYPE_13818_VIDEO
00358               case 0x1B: // MPEG4
00359                       Vpid = esPid;
00360                       Ppid = pmt.getPCRPid();
00361                       Vtype = stream.getStreamType();
00362                       ProcessCaDescriptors = true;
00363                       break;
00364               case 3: // STREAMTYPE_11172_AUDIO
00365               case 4: // STREAMTYPE_13818_AUDIO
00366               case 0x0F: // ISO/IEC 13818-7 Audio with ADTS transport syntax
00367               case 0x11: // ISO/IEC 14496-3 Audio with LATM transport syntax
00368                       {
00369                       if (NumApids < MAXAPIDS) {
00370                          Apids[NumApids] = esPid;
00371                          Atypes[NumApids] = stream.getStreamType();
00372                          SI::Descriptor *d;
00373                          for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
00374                              switch (d->getDescriptorTag()) {
00375                                case SI::ISO639LanguageDescriptorTag: {
00376                                     SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
00377                                     SI::ISO639LanguageDescriptor::Language l;
00378                                     char *s = ALangs[NumApids];
00379                                     int n = 0;
00380                                     for (SI::Loop::Iterator it; ld->languageLoop.getNext(l, it); ) {
00381                                         if (*ld->languageCode != '-') { // some use "---" to indicate "none"
00382                                            if (n > 0)
00383                                               *s++ = '+';
00384                                            strn0cpy(s, I18nNormalizeLanguageCode(l.languageCode), MAXLANGCODE1);
00385                                            s += strlen(s);
00386                                            if (n++ > 1)
00387                                               break;
00388                                            }
00389                                         }
00390                                     }
00391                                     break;
00392                                default: ;
00393                                }
00394                              delete d;
00395                              }
00396                          NumApids++;
00397                          }
00398                       ProcessCaDescriptors = true;
00399                       }
00400                       break;
00401               case 5: // STREAMTYPE_13818_PRIVATE
00402               case 6: // STREAMTYPE_13818_PES_PRIVATE
00403               //XXX case 8: // STREAMTYPE_13818_DSMCC
00404                       {
00405                       int dpid = 0;
00406                       int dtype = 0;
00407                       char lang[MAXLANGCODE1] = { 0 };
00408                       SI::Descriptor *d;
00409                       for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
00410                           switch (d->getDescriptorTag()) {
00411                             case SI::AC3DescriptorTag:
00412                             case SI::EnhancedAC3DescriptorTag:
00413                                  dpid = esPid;
00414                                  dtype = d->getDescriptorTag();
00415                                  ProcessCaDescriptors = true;
00416                                  break;
00417                             case SI::SubtitlingDescriptorTag:
00418                                  if (NumSpids < MAXSPIDS) {
00419                                     Spids[NumSpids] = esPid;
00420                                     SI::SubtitlingDescriptor *sd = (SI::SubtitlingDescriptor *)d;
00421                                     SI::SubtitlingDescriptor::Subtitling sub;
00422                                     char *s = SLangs[NumSpids];
00423                                     int n = 0;
00424                                     for (SI::Loop::Iterator it; sd->subtitlingLoop.getNext(sub, it); ) {
00425                                         if (sub.languageCode[0]) {
00426                                            SubtitlingTypes[NumSpids] = sub.getSubtitlingType();
00427                                            CompositionPageIds[NumSpids] = sub.getCompositionPageId();
00428                                            AncillaryPageIds[NumSpids] = sub.getAncillaryPageId();
00429                                            if (n > 0)
00430                                               *s++ = '+';
00431                                            strn0cpy(s, I18nNormalizeLanguageCode(sub.languageCode), MAXLANGCODE1);
00432                                            s += strlen(s);
00433                                            if (n++ > 1)
00434                                               break;
00435                                            }
00436                                         }
00437                                     NumSpids++;
00438                                     }
00439                                  break;
00440                             case SI::TeletextDescriptorTag: {
00441                                  Tpid = esPid;
00442                                  SI::TeletextDescriptor *sd = (SI::TeletextDescriptor *)d;
00443                                  SI::TeletextDescriptor::Teletext ttxt;
00444                                  for (SI::Loop::Iterator it; sd->teletextLoop.getNext(ttxt, it); ) {
00445                                      bool isSubtitlePage = (ttxt.getTeletextType() == 0x02) || (ttxt.getTeletextType() == 0x05);
00446                                      if ((NumTPages < MAXTXTPAGES) && ttxt.languageCode[0] && isSubtitlePage) {
00447                                         strn0cpy(TeletextSubtitlePages[NumTPages].ttxtLanguage, I18nNormalizeLanguageCode(ttxt.languageCode), MAXLANGCODE1);
00448                                         TeletextSubtitlePages[NumTPages].ttxtPage = ttxt.getTeletextPageNumber();
00449                                         TeletextSubtitlePages[NumTPages].ttxtMagazine = ttxt.getTeletextMagazineNumber();
00450                                         TeletextSubtitlePages[NumTPages].ttxtType = ttxt.getTeletextType();
00451                                         NumTPages++;
00452                                         }
00453                                      }
00454                                  }
00455                                  break;
00456                             case SI::ISO639LanguageDescriptorTag: {
00457                                  SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
00458                                  strn0cpy(lang, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1);
00459                                  }
00460                                  break;
00461                             default: ;
00462                             }
00463                           delete d;
00464                           }
00465                       if (dpid) {
00466                          if (NumDpids < MAXDPIDS) {
00467                             Dpids[NumDpids] = dpid;
00468                             Dtypes[NumDpids] = dtype;
00469                             strn0cpy(DLangs[NumDpids], lang, MAXLANGCODE1);
00470                             NumDpids++;
00471                             }
00472                          }
00473                       }
00474                       break;
00475               case 0x80: // STREAMTYPE_USER_PRIVATE - DigiCipher II VIDEO (ANSI/SCTE 57)
00476                       Vpid = esPid;
00477                       Ppid = pmt.getPCRPid();
00478                       Vtype = 0x02; // compression based upon MPEG-2
00479                       ProcessCaDescriptors = true;
00480                       break;
00481               case 0x81: // STREAMTYPE_USER_PRIVATE - ATSC A/53 AUDIO (ANSI/SCTE 57)
00482                       {
00483                       char lang[MAXLANGCODE1] = { 0 };
00484                       SI::Descriptor *d;
00485                       for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
00486                           switch (d->getDescriptorTag()) {
00487                             case SI::ISO639LanguageDescriptorTag: {
00488                                  SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
00489                                  strn0cpy(lang, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1);
00490                                  }
00491                                  break;
00492                             default: ;
00493                             }
00494                          delete d;
00495                          }
00496                       if (NumDpids < MAXDPIDS) {
00497                          Dpids[NumDpids] = esPid;
00498                          Dtypes[NumDpids] = SI::AC3DescriptorTag;
00499                          strn0cpy(DLangs[NumDpids], lang, MAXLANGCODE1);
00500                          NumDpids++;
00501                          }
00502                       ProcessCaDescriptors = true;
00503                       }
00504                       break;
00505               case 0x82 ... 0xFF: // STREAMTYPE_USER_PRIVATE
00506                       {
00507                       char lang[MAXLANGCODE1] = { 0 };
00508                       bool IsAc3 = false;
00509                       SI::Descriptor *d;
00510                       for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
00511                           switch (d->getDescriptorTag()) {
00512                             case SI::RegistrationDescriptorTag: {
00513                                  SI::RegistrationDescriptor *rd = (SI::RegistrationDescriptor *)d;
00514                                  // http://www.smpte-ra.org/mpegreg/mpegreg.html
00515                                  switch (rd->getFormatIdentifier()) {
00516                                    case 0x41432D33: // 'AC-3'
00517                                         IsAc3 = true;
00518                                         break;
00519                                    default:
00520                                         //printf("Format identifier: 0x%08X (pid: %d)\n", rd->getFormatIdentifier(), esPid);
00521                                         break;
00522                                    }
00523                                  }
00524                                  break;
00525                             case SI::ISO639LanguageDescriptorTag: {
00526                                  SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
00527                                  strn0cpy(lang, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1);
00528                                  }
00529                                  break;
00530                             default: ;
00531                             }
00532                          delete d;
00533                          }
00534                       if (IsAc3) {
00535                          if (NumDpids < MAXDPIDS) {
00536                             Dpids[NumDpids] = esPid;
00537                             Dtypes[NumDpids] = SI::AC3DescriptorTag;
00538                             strn0cpy(DLangs[NumDpids], lang, MAXLANGCODE1);
00539                             NumDpids++;
00540                             }
00541                          ProcessCaDescriptors = true;
00542                          }
00543                       }
00544                       break;
00545               default: ;//printf("PID: %5d %5d %2d %3d %3d\n", pmt.getServiceId(), stream.getPid(), stream.getStreamType(), pmt.getVersionNumber(), Channel->Number());
00546               }
00547             if (ProcessCaDescriptors) {
00548                for (SI::Loop::Iterator it; (d = (SI::CaDescriptor*)stream.streamDescriptors.getNext(it, SI::CaDescriptorTag)); ) {
00549                    CaDescriptors->AddCaDescriptor(d, esPid);
00550                    delete d;
00551                    }
00552                }
00553             }
00554         if (Setup.UpdateChannels >= 2) {
00555            Channel->SetPids(Vpid, Ppid, Vtype, Apids, Atypes, ALangs, Dpids, Dtypes, DLangs, Spids, SLangs, Tpid);
00556            if (NumTPages < MAXTXTPAGES) {
00557               int manualPageNumber = cVDRTtxtsubsHookListener::Hook()->ManualPageNumber(Channel);
00558               if (manualPageNumber)
00559                  TeletextSubtitlePages[NumTPages++] = tTeletextSubtitlePage(manualPageNumber);
00560               }
00561            Channel->SetTeletextSubtitlePages(TeletextSubtitlePages, NumTPages);
00562            Channel->SetCaIds(CaDescriptors->CaIds());
00563            Channel->SetSubtitlingDescriptors(SubtitlingTypes, CompositionPageIds, AncillaryPageIds);
00564            }
00565         Channel->SetCaDescriptors(CaDescriptorHandler.AddCaDescriptors(CaDescriptors));
00566         }
00567      lastPmtScan = 0; // this triggers the next scan
00568      Channels.Unlock();
00569      }
00570 }