/* * eit.c: EIT section filter * * See the main source file 'vdr.c' for copyright information and * how to reach the author. * * Original version (as used in VDR before 1.3.0) written by * Robert Schneider and Rolf Hakenes . * Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg . * * $Id: eit.c 1.85 2004/01/09 15:44:43 kls Exp $ */ #include "eit.h" #include "epg.h" #include "i18n.h" #include "libsi/section.h" #include "libsi/descriptor.h" // --- cEIT ------------------------------------------------------------------ class cEIT : public SI::EIT { public: cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data); }; cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data) :SI::EIT(Data, false) { if (!CheckCRCAndParse()) return; tChannelID channelID(Source, getOriginalNetworkId(), getTransportStreamId(), getServiceId()); cChannel *channel = Channels.GetByChannelID(channelID, true); if (!channel) return; // only collect data for known channels cEvent *rEvent = NULL; cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channelID); if (!pSchedule) { pSchedule = new cSchedule(channelID); Schedules->Add(pSchedule); } SI::EIT::Event SiEitEvent; for (SI::Loop::Iterator it; eventLoop.hasNext(it); ) { SiEitEvent = eventLoop.getNext(it); cEvent *pEvent = (cEvent *)pSchedule->GetEvent(SiEitEvent.getEventId(), SiEitEvent.getStartTime()); if (!pEvent) { // If we don't have that event ID yet, we create a new one. // Otherwise we copy the information into the existing event anyway, because the data might have changed. pEvent = pSchedule->AddEvent(new cEvent(channelID, SiEitEvent.getEventId())); if (!pEvent) continue; pEvent->SetTableID(Tid); } else { // We have found an existing event, either through its event ID or its start time. // If the existing event has a zero table ID it was defined externally and shall // not be overwritten. if (pEvent->TableID() == 0x00) continue; // If the new event comes from a table that belongs to an "other TS" and the existing // one comes from an "actual TS" table, let's skip it. #define ISACTUALTS(tid) (tid == 0x4E || (tid & 0x50) == 0x50) if (!ISACTUALTS(Tid) && ISACTUALTS(pEvent->TableID())) continue; // If the new event comes from a "schedule" table and the existing one comes from // a "present/following" table, let's skip it (the p/f table usually contains more // information, like e.g. a description). if ((Tid & 0x50) == 0x50 && pEvent->TableID() == 0x4E || (Tid & 0x60) == 0x60 && pEvent->TableID() == 0x4F) continue; // If both events come from the same "schedule" table and the new event's table id is larger than the // existing one's, let's skip it (higher tids mean "farther in the future" and usually have less information). if (((Tid & 0x50) == 0x50 || (Tid & 0x60) == 0x60) && (pEvent->TableID() & 0xF0) == (Tid & 0xF0) && (Tid > pEvent->TableID())) continue; // If the new event comes from the same table and has the same version number // as the existing one, let's skip it to avoid unnecessary work. // Unfortunately some stations (like, e.g. "Premiere") broadcast their EPG data on several transponders (like // the actual Premiere transponder and the Sat.1/Pro7 transponder), but use different version numbers on // each of them :-( So if one DVB card is tuned to the Premiere transponder, while an other one is tuned // to the Sat.1/Pro7 transponder, events will keep toggling because of the bogus version numbers. if (Tid == pEvent->TableID() && pEvent->Version() == getVersionNumber()) continue; } pEvent->SetVersion(getVersionNumber()); pEvent->SetTableID(Tid); pEvent->SetEventID(SiEitEvent.getEventId()); // unfortunately some stations use different event ids for the same event in different tables :-( int LanguagePreferenceShort = -1; int LanguagePreferenceExt = -1; bool UseExtendedEventDescriptor = false; SI::Descriptor *d; SI::ExtendedEventDescriptors *ExtendedEventDescriptors = NULL; SI::ShortEventDescriptor *ShortEventDescriptor = NULL; for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext(it2)); ) { switch (d->getDescriptorTag()) { case SI::ExtendedEventDescriptorTag: { SI::ExtendedEventDescriptor *eed = (SI::ExtendedEventDescriptor *)d; if (I18nIsPreferredLanguage(Setup.EPGLanguages, I18nLanguageIndex(eed->languageCode), LanguagePreferenceExt) || !ExtendedEventDescriptors) { delete ExtendedEventDescriptors; ExtendedEventDescriptors = new SI::ExtendedEventDescriptors; UseExtendedEventDescriptor = true; } if (UseExtendedEventDescriptor) { ExtendedEventDescriptors->Add(eed); d = NULL; // so that it is not deleted } if (eed->getDescriptorNumber() == eed->getLastDescriptorNumber()) UseExtendedEventDescriptor = false; } break; case SI::ShortEventDescriptorTag: { SI::ShortEventDescriptor *sed = (SI::ShortEventDescriptor *)d; if (I18nIsPreferredLanguage(Setup.EPGLanguages, I18nLanguageIndex(sed->languageCode), LanguagePreferenceShort) || !ShortEventDescriptor) { delete ShortEventDescriptor; ShortEventDescriptor = sed; d = NULL; // so that it is not deleted } } break; case SI::ContentDescriptorTag: break; case SI::ParentalRatingDescriptorTag: break; case SI::TimeShiftedEventDescriptorTag: { SI::TimeShiftedEventDescriptor *tsed = (SI::TimeShiftedEventDescriptor *)d; cSchedule *rSchedule = (cSchedule *)Schedules->GetSchedule(tChannelID(Source, 0, 0, tsed->getReferenceServiceId())); if (!rSchedule) break; rEvent = (cEvent *)rSchedule->GetEvent(tsed->getReferenceEventId()); if (!rEvent) break; pEvent->SetTitle(rEvent->Title()); pEvent->SetShortText(rEvent->ShortText()); pEvent->SetDescription(rEvent->Description()); } break; default: ; } delete d; } if (!rEvent) { if (ShortEventDescriptor) { char buffer[256]; pEvent->SetTitle(ShortEventDescriptor->name.getText(buffer)); pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer)); } if (ExtendedEventDescriptors) { char buffer[ExtendedEventDescriptors->getMaximumTextLength()]; pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer)); } } delete ExtendedEventDescriptors; delete ShortEventDescriptor; pEvent->SetStartTime(SiEitEvent.getStartTime()); pEvent->SetDuration(SiEitEvent.getDuration()); pEvent->FixEpgBugs(); if (isPresentFollowing()) { if (SiEitEvent.getRunningStatus() == SI::RunningStatusPausing || SiEitEvent.getRunningStatus() == SI::RunningStatusRunning) pSchedule->SetPresentEvent(pEvent); else if (SiEitEvent.getRunningStatus() == SI::RunningStatusStartsInAFewSeconds) pSchedule->SetFollowingEvent(pEvent); } } } // --- cTDT ------------------------------------------------------------------ class cTDT : public SI::TDT { private: static cMutex mutex; public: cTDT(const u_char *Data); }; cMutex cTDT::mutex; cTDT::cTDT(const u_char *Data) :SI::TDT(Data, false) { CheckParse(); time_t sattim = getTime(); time_t loctim = time(NULL); if (abs(sattim - loctim) > 2) { mutex.Lock(); isyslog("System Time = %s (%ld)\n", ctime(&loctim), loctim); isyslog("Local Time = %s (%ld)\n", ctime(&sattim), sattim); if (stime(&sattim) < 0) esyslog("ERROR while setting system time: %m"); mutex.Unlock(); } } // --- cEitFilter ------------------------------------------------------------ cEitFilter::cEitFilter(void) { Set(0x12, 0x4E, 0xFE); // event info, actual(0x4E)/other(0x4F) TS, present/following Set(0x12, 0x50, 0xF0); // event info, actual TS, schedule(0x50)/schedule for future days(0x5X) Set(0x12, 0x60, 0xF0); // event info, other TS, schedule(0x60)/schedule for future days(0x6X) Set(0x14, 0x70); // TDT } void cEitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length) { switch (Pid) { case 0x12: { cSchedulesLock SchedulesLock(true, 10); cSchedules *Schedules = (cSchedules *)cSchedules::Schedules(SchedulesLock); if (Schedules) cEIT EIT(Schedules, Source(), Tid, Data); } break; case 0x14: { if (Setup.SetSystemTime && Setup.TimeTransponder && ISTRANSPONDER(Transponder(), Setup.TimeTransponder)) cTDT TDT(Data); } break; } }