vdr  1.7.27
eit.c
Go to the documentation of this file.
00001 /*
00002  * eit.c: EIT section filter
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * Original version (as used in VDR before 1.3.0) written by
00008  * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
00009  * Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg <marcel.wiesweg@gmx.de>.
00010  *
00011  * $Id: eit.c 2.16 2012/03/14 10:11:15 kls Exp $
00012  */
00013 
00014 #include "eit.h"
00015 #include "epg.h"
00016 #include "i18n.h"
00017 #include "libsi/section.h"
00018 #include "libsi/descriptor.h"
00019 
00020 #define VALID_TIME (31536000 * 2) // two years
00021 
00022 // --- cEIT ------------------------------------------------------------------
00023 
00024 class cEIT : public SI::EIT {
00025 public:
00026   cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bool OnlyRunningStatus = false);
00027   };
00028 
00029 cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bool OnlyRunningStatus)
00030 :SI::EIT(Data, false)
00031 {
00032   if (!CheckCRCAndParse())
00033      return;
00034 
00035   time_t Now = time(NULL);
00036   if (Now < VALID_TIME)
00037      return; // we need the current time for handling PDC descriptors
00038 
00039   if (!Channels.Lock(false, 10))
00040      return;
00041   tChannelID channelID(Source, getOriginalNetworkId(), getTransportStreamId(), getServiceId());
00042   cChannel *channel = Channels.GetByChannelID(channelID, true);
00043   if (!channel || EpgHandlers.IgnoreChannel(channel)) {
00044      Channels.Unlock();
00045      return;
00046      }
00047 
00048   cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channel, true);
00049 
00050   bool Empty = true;
00051   bool Modified = false;
00052   time_t SegmentStart = 0;
00053   time_t SegmentEnd = 0;
00054   struct tm tm_r;
00055   struct tm t = *localtime_r(&Now, &tm_r); // this initializes the time zone in 't'
00056 
00057   SI::EIT::Event SiEitEvent;
00058   for (SI::Loop::Iterator it; eventLoop.getNext(SiEitEvent, it); ) {
00059       if (EpgHandlers.HandleEitEvent(pSchedule, &SiEitEvent, Tid, getVersionNumber()))
00060          continue; // an EPG handler has done all of the processing
00061       time_t StartTime = SiEitEvent.getStartTime();
00062       int Duration = SiEitEvent.getDuration();
00063       // Drop bogus events - but keep NVOD reference events, where all bits of the start time field are set to 1, resulting in a negative number.
00064       if (StartTime == 0 || StartTime > 0 && Duration == 0)
00065          continue;
00066       Empty = false;
00067       if (!SegmentStart)
00068          SegmentStart = StartTime;
00069       SegmentEnd = StartTime + Duration;
00070       cEvent *newEvent = NULL;
00071       cEvent *rEvent = NULL;
00072       cEvent *pEvent = (cEvent *)pSchedule->GetEvent(SiEitEvent.getEventId(), StartTime);
00073       if (!pEvent) {
00074          if (OnlyRunningStatus)
00075             continue;
00076          // If we don't have that event yet, we create a new one.
00077          // Otherwise we copy the information into the existing event anyway, because the data might have changed.
00078          pEvent = newEvent = new cEvent(SiEitEvent.getEventId());
00079          newEvent->SetStartTime(StartTime);
00080          newEvent->SetDuration(Duration);
00081          pSchedule->AddEvent(newEvent);
00082          }
00083       else {
00084          // We have found an existing event, either through its event ID or its start time.
00085          pEvent->SetSeen();
00086          uchar TableID = max(pEvent->TableID(), uchar(0x4E)); // for backwards compatibility, table ids less than 0x4E are treated as if they were "present"
00087          // If the new event has a higher table ID, let's skip it.
00088          // The lower the table ID, the more "current" the information.
00089          if (Tid > TableID)
00090             continue;
00091          // If the new event comes from the same table and has the same version number
00092          // as the existing one, let's skip it to avoid unnecessary work.
00093          // Unfortunately some stations (like, e.g. "Premiere") broadcast their EPG data on several transponders (like
00094          // the actual Premiere transponder and the Sat.1/Pro7 transponder), but use different version numbers on
00095          // each of them :-( So if one DVB card is tuned to the Premiere transponder, while an other one is tuned
00096          // to the Sat.1/Pro7 transponder, events will keep toggling because of the bogus version numbers.
00097          else if (Tid == TableID && pEvent->Version() == getVersionNumber())
00098             continue;
00099          EpgHandlers.SetEventID(pEvent, SiEitEvent.getEventId()); // unfortunately some stations use different event ids for the same event in different tables :-(
00100          EpgHandlers.SetStartTime(pEvent, StartTime);
00101          EpgHandlers.SetDuration(pEvent, Duration);
00102          }
00103       if (pEvent->TableID() > 0x4E) // for backwards compatibility, table ids less than 0x4E are never overwritten
00104          pEvent->SetTableID(Tid);
00105       if (Tid == 0x4E) { // we trust only the present/following info on the actual TS
00106          if (SiEitEvent.getRunningStatus() >= SI::RunningStatusNotRunning)
00107             pSchedule->SetRunningStatus(pEvent, SiEitEvent.getRunningStatus(), channel);
00108          }
00109       if (OnlyRunningStatus) {
00110          pEvent->SetVersion(0xFF); // we have already changed the table id above, so set the version to an invalid value to make sure the next full run will be executed
00111          continue; // do this before setting the version, so that the full update can be done later
00112          }
00113       pEvent->SetVersion(getVersionNumber());
00114 
00115       int LanguagePreferenceShort = -1;
00116       int LanguagePreferenceExt = -1;
00117       bool UseExtendedEventDescriptor = false;
00118       SI::Descriptor *d;
00119       SI::ExtendedEventDescriptors *ExtendedEventDescriptors = NULL;
00120       SI::ShortEventDescriptor *ShortEventDescriptor = NULL;
00121       cLinkChannels *LinkChannels = NULL;
00122       cComponents *Components = NULL;
00123       for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext(it2)); ) {
00124           switch (d->getDescriptorTag()) {
00125             case SI::ExtendedEventDescriptorTag: {
00126                  SI::ExtendedEventDescriptor *eed = (SI::ExtendedEventDescriptor *)d;
00127                  if (I18nIsPreferredLanguage(Setup.EPGLanguages, eed->languageCode, LanguagePreferenceExt) || !ExtendedEventDescriptors) {
00128                     delete ExtendedEventDescriptors;
00129                     ExtendedEventDescriptors = new SI::ExtendedEventDescriptors;
00130                     UseExtendedEventDescriptor = true;
00131                     }
00132                  if (UseExtendedEventDescriptor) {
00133                     ExtendedEventDescriptors->Add(eed);
00134                     d = NULL; // so that it is not deleted
00135                     }
00136                  if (eed->getDescriptorNumber() == eed->getLastDescriptorNumber())
00137                     UseExtendedEventDescriptor = false;
00138                  }
00139                  break;
00140             case SI::ShortEventDescriptorTag: {
00141                  SI::ShortEventDescriptor *sed = (SI::ShortEventDescriptor *)d;
00142                  if (I18nIsPreferredLanguage(Setup.EPGLanguages, sed->languageCode, LanguagePreferenceShort) || !ShortEventDescriptor) {
00143                     delete ShortEventDescriptor;
00144                     ShortEventDescriptor = sed;
00145                     d = NULL; // so that it is not deleted
00146                     }
00147                  }
00148                  break;
00149             case SI::ContentDescriptorTag: {
00150                  SI::ContentDescriptor *cd = (SI::ContentDescriptor *)d;
00151                  SI::ContentDescriptor::Nibble Nibble;
00152                  int NumContents = 0;
00153                  uchar Contents[MaxEventContents] = { 0 };
00154                  for (SI::Loop::Iterator it3; cd->nibbleLoop.getNext(Nibble, it3); ) {
00155                      if (NumContents < MaxEventContents) {
00156                         Contents[NumContents] = ((Nibble.getContentNibbleLevel1() & 0xF) << 4) | (Nibble.getContentNibbleLevel2() & 0xF);
00157                         NumContents++;
00158                         }
00159                      }
00160                  EpgHandlers.SetContents(pEvent, Contents);
00161                  }
00162                  break;
00163             case SI::ParentalRatingDescriptorTag: {
00164                  int LanguagePreferenceRating = -1;
00165                  SI::ParentalRatingDescriptor *prd = (SI::ParentalRatingDescriptor *)d;
00166                  SI::ParentalRatingDescriptor::Rating Rating;
00167                  for (SI::Loop::Iterator it3; prd->ratingLoop.getNext(Rating, it3); ) {
00168                      if (I18nIsPreferredLanguage(Setup.EPGLanguages, Rating.languageCode, LanguagePreferenceRating)) {
00169                         int ParentalRating = (Rating.getRating() & 0xFF);
00170                         switch (ParentalRating) {
00171                           // values defined by the DVB standard (minimum age = rating + 3 years):
00172                           case 0x01 ... 0x0F: ParentalRating += 3; break;
00173                           // values defined by broadcaster CSAT (now why didn't they just use 0x07, 0x09 and 0x0D?):
00174                           case 0x11:          ParentalRating = 10; break;
00175                           case 0x12:          ParentalRating = 12; break;
00176                           case 0x13:          ParentalRating = 16; break;
00177                           default:            ParentalRating = 0;
00178                           }
00179                         EpgHandlers.SetParentalRating(pEvent, ParentalRating);
00180                         }
00181                      }
00182                  }
00183                  break;
00184             case SI::PDCDescriptorTag: {
00185                  SI::PDCDescriptor *pd = (SI::PDCDescriptor *)d;
00186                  t.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
00187                  int month = t.tm_mon;
00188                  t.tm_mon = pd->getMonth() - 1;
00189                  t.tm_mday = pd->getDay();
00190                  t.tm_hour = pd->getHour();
00191                  t.tm_min = pd->getMinute();
00192                  t.tm_sec = 0;
00193                  if (month == 11 && t.tm_mon == 0) // current month is dec, but event is in jan
00194                     t.tm_year++;
00195                  else if (month == 0 && t.tm_mon == 11) // current month is jan, but event is in dec
00196                     t.tm_year--;
00197                  time_t vps = mktime(&t);
00198                  EpgHandlers.SetVps(pEvent, vps);
00199                  }
00200                  break;
00201             case SI::TimeShiftedEventDescriptorTag: {
00202                  SI::TimeShiftedEventDescriptor *tsed = (SI::TimeShiftedEventDescriptor *)d;
00203                  cSchedule *rSchedule = (cSchedule *)Schedules->GetSchedule(tChannelID(Source, channel->Nid(), channel->Tid(), tsed->getReferenceServiceId()));
00204                  if (!rSchedule)
00205                     break;
00206                  rEvent = (cEvent *)rSchedule->GetEvent(tsed->getReferenceEventId());
00207                  if (!rEvent)
00208                     break;
00209                  EpgHandlers.SetTitle(pEvent, rEvent->Title());
00210                  EpgHandlers.SetShortText(pEvent, rEvent->ShortText());
00211                  EpgHandlers.SetDescription(pEvent, rEvent->Description());
00212                  }
00213                  break;
00214             case SI::LinkageDescriptorTag: {
00215                  SI::LinkageDescriptor *ld = (SI::LinkageDescriptor *)d;
00216                  tChannelID linkID(Source, ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId());
00217                  if (ld->getLinkageType() == 0xB0) { // Premiere World
00218                     bool hit = StartTime <= Now && Now < StartTime + Duration;
00219                     if (hit) {
00220                        char linkName[ld->privateData.getLength() + 1];
00221                        strn0cpy(linkName, (const char *)ld->privateData.getData(), sizeof(linkName));
00222                        // TODO is there a standard way to determine the character set of this string?
00223                        cChannel *link = Channels.GetByChannelID(linkID);
00224                        if (link != channel) { // only link to other channels, not the same one
00225                           //fprintf(stderr, "Linkage %s %4d %4d %5d %5d %5d %5d  %02X  '%s'\n", hit ? "*" : "", channel->Number(), link ? link->Number() : -1, SiEitEvent.getEventId(), ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId(), ld->getLinkageType(), linkName);//XXX
00226                           if (link) {
00227                              if (Setup.UpdateChannels == 1 || Setup.UpdateChannels >= 3)
00228                                 link->SetName(linkName, "", "");
00229                              }
00230                           else if (Setup.UpdateChannels >= 4) {
00231                              cChannel *transponder = channel;
00232                              if (channel->Tid() != ld->getTransportStreamId())
00233                                 transponder = Channels.GetByTransponderID(linkID);
00234                              link = Channels.NewChannel(transponder, linkName, "", "", ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId());
00235                              //XXX patFilter->Trigger();
00236                              }
00237                           if (link) {
00238                              if (!LinkChannels)
00239                                 LinkChannels = new cLinkChannels;
00240                              LinkChannels->Add(new cLinkChannel(link));
00241                              }
00242                           }
00243                        else
00244                           channel->SetPortalName(linkName);
00245                        }
00246                     }
00247                  }
00248                  break;
00249             case SI::ComponentDescriptorTag: {
00250                  SI::ComponentDescriptor *cd = (SI::ComponentDescriptor *)d;
00251                  uchar Stream = cd->getStreamContent();
00252                  uchar Type = cd->getComponentType();
00253                  if (1 <= Stream && Stream <= 6 && Type != 0) { // 1=MPEG2-video, 2=MPEG1-audio, 3=subtitles, 4=AC3-audio, 5=H.264-video, 6=HEAAC-audio
00254                     if (!Components)
00255                        Components = new cComponents;
00256                     char buffer[Utf8BufSize(256)];
00257                     Components->SetComponent(Components->NumComponents(), Stream, Type, I18nNormalizeLanguageCode(cd->languageCode), cd->description.getText(buffer, sizeof(buffer)));
00258                     }
00259                  }
00260                  break;
00261             default: ;
00262             }
00263           delete d;
00264           }
00265 
00266       if (!rEvent) {
00267          if (ShortEventDescriptor) {
00268             char buffer[Utf8BufSize(256)];
00269             EpgHandlers.SetTitle(pEvent, ShortEventDescriptor->name.getText(buffer, sizeof(buffer)));
00270             EpgHandlers.SetShortText(pEvent, ShortEventDescriptor->text.getText(buffer, sizeof(buffer)));
00271             }
00272          else {
00273             EpgHandlers.SetTitle(pEvent, NULL);
00274             EpgHandlers.SetShortText(pEvent, NULL);
00275             }
00276          if (ExtendedEventDescriptors) {
00277             char buffer[Utf8BufSize(ExtendedEventDescriptors->getMaximumTextLength(": ")) + 1];
00278             EpgHandlers.SetDescription(pEvent, ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": "));
00279             }
00280          else
00281             EpgHandlers.SetDescription(pEvent, NULL);
00282          }
00283       delete ExtendedEventDescriptors;
00284       delete ShortEventDescriptor;
00285 
00286       pEvent->SetComponents(Components);
00287 
00288       EpgHandlers.FixEpgBugs(pEvent);
00289       if (LinkChannels)
00290          channel->SetLinkChannels(LinkChannels);
00291       Modified = true;
00292       EpgHandlers.HandleEvent(pEvent);
00293       }
00294   if (Tid == 0x4E) {
00295      if (Empty && getSectionNumber() == 0)
00296         // ETR 211: an empty entry in section 0 of table 0x4E means there is currently no event running
00297         pSchedule->ClrRunningStatus(channel);
00298      pSchedule->SetPresentSeen();
00299      }
00300   if (Modified && !OnlyRunningStatus) {
00301      EpgHandlers.SortSchedule(pSchedule);
00302      EpgHandlers.DropOutdated(pSchedule, SegmentStart, SegmentEnd, Tid, getVersionNumber());
00303      Schedules->SetModified(pSchedule);
00304      }
00305   Channels.Unlock();
00306 }
00307 
00308 // --- cTDT ------------------------------------------------------------------
00309 
00310 class cTDT : public SI::TDT {
00311 private:
00312   static cMutex mutex;
00313   static int lastDiff;
00314 public:
00315   cTDT(const u_char *Data);
00316   };
00317 
00318 cMutex cTDT::mutex;
00319 int cTDT::lastDiff = 0;
00320 
00321 cTDT::cTDT(const u_char *Data)
00322 :SI::TDT(Data, false)
00323 {
00324   CheckParse();
00325 
00326   time_t sattim = getTime();
00327   time_t loctim = time(NULL);
00328 
00329   int diff = abs(sattim - loctim);
00330   if (diff > 2) {
00331      mutex.Lock();
00332      if (abs(diff - lastDiff) < 3) {
00333         if (stime(&sattim) == 0)
00334            isyslog("system time changed from %s (%ld) to %s (%ld)", *TimeToString(loctim), loctim, *TimeToString(sattim), sattim);
00335         else
00336            esyslog("ERROR while setting system time: %m");
00337         }
00338      lastDiff = diff;
00339      mutex.Unlock();
00340      }
00341 }
00342 
00343 // --- cEitFilter ------------------------------------------------------------
00344 
00345 time_t cEitFilter::disableUntil = 0;
00346 
00347 cEitFilter::cEitFilter(void)
00348 {
00349   Set(0x12, 0x40, 0xC0);  // event info now&next actual/other TS (0x4E/0x4F), future actual/other TS (0x5X/0x6X)
00350   if (Setup.SetSystemTime && Setup.TimeTransponder)
00351      Set(0x14, 0x70);     // TDT
00352 }
00353 
00354 void cEitFilter::SetDisableUntil(time_t Time)
00355 {
00356   disableUntil = Time;
00357 }
00358 
00359 void cEitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
00360 {
00361   if (disableUntil) {
00362      if (time(NULL) > disableUntil)
00363         disableUntil = 0;
00364      else
00365         return;
00366      }
00367   switch (Pid) {
00368     case 0x12: {
00369          if (Tid >= 0x4E && Tid <= 0x6F) {
00370             cSchedulesLock SchedulesLock(true, 10);
00371             cSchedules *Schedules = (cSchedules *)cSchedules::Schedules(SchedulesLock);
00372             if (Schedules)
00373                cEIT EIT(Schedules, Source(), Tid, Data);
00374             else {
00375                // If we don't get a write lock, let's at least get a read lock, so
00376                // that we can set the running status and 'seen' timestamp (well, actually
00377                // with a read lock we shouldn't be doing that, but it's only integers that
00378                // get changed, so it should be ok)
00379                cSchedulesLock SchedulesLock;
00380                cSchedules *Schedules = (cSchedules *)cSchedules::Schedules(SchedulesLock);
00381                if (Schedules)
00382                   cEIT EIT(Schedules, Source(), Tid, Data, true);
00383                }
00384             }
00385          }
00386          break;
00387     case 0x14: {
00388          if (Setup.SetSystemTime && Setup.TimeTransponder && ISTRANSPONDER(Transponder(), Setup.TimeTransponder))
00389             cTDT TDT(Data);
00390          }
00391          break;
00392     default: ;
00393     }
00394 }