vdr
1.7.27
|
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 }