diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | eepg.c | 645 | ||||
-rw-r--r-- | eit2.c | 640 | ||||
-rw-r--r-- | eit2.h | 49 | ||||
-rw-r--r-- | epghandler.c | 7 | ||||
-rw-r--r-- | epghandler.h | 2 | ||||
-rw-r--r-- | util.h | 3 |
7 files changed, 702 insertions, 646 deletions
@@ -65,7 +65,7 @@ DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' ### The object files (add further files here): -OBJS = $(PLUGIN).o dish.o epghandler.o setupeepg.o equivhandler.o util.o +OBJS = $(PLUGIN).o dish.o epghandler.o setupeepg.o equivhandler.o util.o eit2.o ifdef DBG CXXFLAGS += -g @@ -150,7 +150,7 @@ void cMenuSetupPremiereEpg::Store (void) #endif } -#define Asprintf(a, b, c...) void( asprintf(a, b, c) < 0 ? esyslog("memory allocation error - %s", b) : void() ) +//#define Asprintf(a, b, c...) void( asprintf(a, b, c) < 0 ? esyslog("memory allocation error - %s", b) : void() ) // --- CRC16 ------------------------------------------------------------------- @@ -173,27 +173,6 @@ unsigned int crc16 (unsigned int crc, unsigned char const *p, int len) static int LastVersionNagra = -1; //currently only used for Nagra, should be stored per transponder, per system - -#ifdef USE_NOEPG -bool allowedEPG (tChannelID kanalID) -{ - bool rc; - - if (Setup.noEPGMode == 1) { - rc = false; - if (strstr (::Setup.noEPGList, kanalID.ToString ()) != NULL) - rc = true; - } else { - rc = true; - if (strstr (::Setup.noEPGList, kanalID.ToString ()) != NULL) - rc = false; - } - - return rc; -} -#endif /* NOEPG */ - - class cFilterEEPG:public cFilter { private: @@ -2662,628 +2641,6 @@ void cFilterEEPG::AddFilter (u_short Pid, u_char Tid, unsigned char Mask) } } -namespace SI -{ - enum DescriptorTagExt { - DishRatingDescriptorTag = 0x89, - DishShortEventDescriptorTag = 0x91, - DishExtendedEventDescriptorTag = 0x92, - DishSeriesDescriptorTag = 0x96, - }; - -// typedef InheritEnum< DescriptorTagExt, SI::DescriptorTag > ExtendedDescriptorTag; - -/*extern const char *getCharacterTable(const unsigned char *&buffer, int &length, bool *isSingleByte = NULL); -extern bool convertCharacterTable(const char *from, size_t fromLength, char *to, size_t toLength, const char *fromCode); -extern bool SystemCharacterTableIsSingleByte;*/ -class cEIT2:public SI::EIT -{ -public: - cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Data, bool isEITPid = false, bool OnlyRunningStatus = false); -//protected: -// void updateEquivalent(cSchedules * Schedules, tChannelID channelID, cEvent *pEvent); -}; - - -cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Data, bool isEITPid, bool OnlyRunningStatus) -: SI::EIT (Data, false) -{ - //LogD(2, prep("cEIT2::cEIT2")); - if (Tid > 0 && (Format == DISH_BEV || (SetupPE->ProcessEIT && isEITPid))) Tid--; - - if (!CheckCRCAndParse ()) { - LogD(2, prep("!CheckCRCAndParse ()")); - return; - } - - bool searchOtherSatPositions = Format == DISH_BEV; - - tChannelID channelID (Source, getOriginalNetworkId (), getTransportStreamId (), getServiceId ()); - cChannel *channel = GetChannelByID (channelID, searchOtherSatPositions); - if (!channel) { - LogD(3, prep("!channel channelID: %s"), *channelID.ToString()); - return; // only collect data for known channels - } - - //LogD(5, prep("channelID: %s format:%d"), *channel->GetChannelID().ToString(), Format); - -#ifdef USE_NOEPG - // only use epg from channels not blocked by noEPG-patch - tChannelID kanalID; - kanalID = channel->GetChannelID (); - if (!allowedEPG (kanalID)) - return; -#endif /* NOEPG */ - - cSchedule *pSchedule = (cSchedule *) Schedules->GetSchedule (channel, true); - - bool Empty = true; - bool Modified = false; - bool HasExternalData = false; - time_t SegmentStart = 0; - time_t SegmentEnd = 0; - - SI::EIT::Event SiEitEvent; - for (SI::Loop::Iterator it; eventLoop.getNext (SiEitEvent, it);) { - bool ExternalData = false; - // 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. - if (SiEitEvent.getStartTime () == 0 || (SiEitEvent.getStartTime () > 0 && SiEitEvent.getDuration () == 0)) - continue; - Empty = false; - if (!SegmentStart) - SegmentStart = SiEitEvent.getStartTime (); - SegmentEnd = SiEitEvent.getStartTime () + SiEitEvent.getDuration (); - int versionNumber = getVersionNumber(); - - cEvent *newEvent = NULL; - cEvent *rEvent = NULL; - cEvent *pEvent = (cEvent *) pSchedule->GetEvent (SiEitEvent.getEventId (), SiEitEvent.getStartTime ()); - if (!pEvent) { - if (OnlyRunningStatus) - continue; - // If we don't have that event yet, we create a new one. - // Otherwise we copy the information into the existing event anyway, because the data might have changed. - pEvent = newEvent = new cEvent (SiEitEvent.getEventId ()); - if (!pEvent) - continue; - } else { - //LogD(3, prep("existing event channelID: %s Title: %s TableID 0x%02X new TID 0x%02X Version %i, new version %i"), *channel->GetChannelID().ToString(), pEvent->Title(), pEvent->TableID(), Tid, pEvent->Version(), versionNumber); - // We have found an existing event, either through its event ID or its start time. - pEvent->SetSeen (); - - // If the existing event has a zero table ID it was defined externally and shall - // not be overwritten. - if (pEvent->TableID () == 0x00) { -#ifdef USE_DDEPGENTRY - if (pEvent->Version () == getVersionNumber ()) { - if (Setup.MixEpgAction == 0) - continue; - //printf("in"); - //printf("%s", pEvent->GetTimeString()); - // to use the info of the original epg, update the extern one, - // if it has less info - SI::Descriptor * d; - SI::ExtendedEventDescriptors * ExtendedEventDescriptors = NULL; - //SI::ExtendedEventDescriptor *eed = NULL; - SI::ShortEventDescriptor * ShortEventDescriptor = NULL; - //SI::ShortEventDescriptor *sed = NULL; - //SI::TimeShiftedEventDescriptor *tsed = NULL; - //cLinkChannels *LinkChannels = NULL; - for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext (it2));) { - if (d->getDescriptorTag () == SI::ShortEventDescriptorTag) { - int LanguagePreferenceShort = -1; - SI::ShortEventDescriptor * sed = (SI::ShortEventDescriptor *) d; - if (I18nIsPreferredLanguage (Setup.EPGLanguages, sed->languageCode, LanguagePreferenceShort) - || !ShortEventDescriptor) { - delete ShortEventDescriptor; - ShortEventDescriptor = sed; - d = NULL; // so that it is not deleted - } - } else if (d->getDescriptorTag () == SI::ExtendedEventDescriptorTag) { - int LanguagePreferenceExt = -1; - bool UseExtendedEventDescriptor = false; - SI::ExtendedEventDescriptor * eed = (SI::ExtendedEventDescriptor *) d; - if (I18nIsPreferredLanguage (Setup.EPGLanguages, 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; - } - delete d; - } - if (pEvent) { - if (ShortEventDescriptor) { - char buffer[256]; - if (ShortEventDescriptor->text.getText (buffer, sizeof (buffer)) && pEvent->ShortText () - && (strlen (ShortEventDescriptor->text.getText (buffer, sizeof (buffer))) > - strlen (pEvent->ShortText ()))) { - pEvent->SetShortText (ShortEventDescriptor->text.getText (buffer, sizeof (buffer))); - pEvent->FixEpgBugs (); - } - } - if (ExtendedEventDescriptors) { - char buffer[ExtendedEventDescriptors->getMaximumTextLength (": ") + 1]; - //pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": ")); - if (ExtendedEventDescriptors->getText (buffer, sizeof (buffer), ": ") - && pEvent->Description () - && (strlen (ExtendedEventDescriptors->getText (buffer, sizeof (buffer), ": ")) > - strlen (pEvent->Description ()))) { - pEvent->SetDescription (ExtendedEventDescriptors->getText (buffer, sizeof (buffer), ": ")); - pEvent->FixEpgBugs (); - } - } - } - delete ExtendedEventDescriptors; - delete ShortEventDescriptor; - continue; - } -#else - if (pEvent->Version () == versionNumber) - continue; -#endif /* DDEPGENTRY */ - HasExternalData = ExternalData = true; - } - // If the new event has a higher table ID, let's skip it. - // The lower the table ID, the more "current" the information. - else if (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. - else if (Tid == pEvent->TableID() && pEvent->Version() == versionNumber) - continue; - } - if (!ExternalData) { - pEvent->SetEventID (SiEitEvent.getEventId ()); // unfortunately some stations use different event ids for the same event in different tables :-( - pEvent->SetTableID (Tid); - pEvent->SetStartTime (SiEitEvent.getStartTime ()); - pEvent->SetDuration (SiEitEvent.getDuration ()); - } - if (newEvent) - pSchedule->AddEvent (newEvent); - if (Tid == 0x4E) { // we trust only the present/following info on the actual TS -#ifdef USE_DDEPGENTRY - if (Setup.DisableVPS == 0 && SiEitEvent.getRunningStatus () >= SI::RunningStatusNotRunning) -#else - if (SiEitEvent.getRunningStatus () >= SI::RunningStatusNotRunning) -#endif /* DDEPGENTRY */ - pSchedule->SetRunningStatus (pEvent, SiEitEvent.getRunningStatus (), channel); - } - if (OnlyRunningStatus) - continue; // do this before setting the version, so that the full update can be done later - pEvent->SetVersion (versionNumber); - - int LanguagePreferenceShort = -1; - int LanguagePreferenceExt = -1; - unsigned char nDescriptorTag; - bool UseExtendedEventDescriptor = false; - SI::Descriptor * d; - SI::ExtendedEventDescriptors * ExtendedEventDescriptors = NULL; - SI::ShortEventDescriptor * ShortEventDescriptor = NULL; - //SI::DishDescriptor *DishExtendedEventDescriptor = NULL; - SI::DishDescriptor *DishEventDescriptor = NULL; - //uchar DishTheme = 0, DishCategory = 0; - - - cLinkChannels *LinkChannels = NULL; - cComponents *Components = NULL; - for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext (it2));) { - if (ExternalData && d->getDescriptorTag () != SI::ComponentDescriptorTag) { - delete d; - LogD(2, prep("continue:d->getDescriptorTAG():%x)"), d->getDescriptorTag ()); - continue; - } - - //LogD(2, prep("EEPGDEBUG:d->getDescriptorTAG():%x)"), d->getDescriptorTag ()); - nDescriptorTag = d->getDescriptorTag (); - switch (nDescriptorTag) { - case SI::ExtendedEventDescriptorTag: { - SI::ExtendedEventDescriptor * eed = (SI::ExtendedEventDescriptor *) d; - if (I18nIsPreferredLanguage (Setup.EPGLanguages, 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, sed->languageCode, LanguagePreferenceShort) - || !ShortEventDescriptor) { - delete ShortEventDescriptor; - ShortEventDescriptor = sed; - d = NULL; // so that it is not deleted - } - } - break; -#if APIVERSNUM > 10711 - case SI::ContentDescriptorTag: { - SI::ContentDescriptor *cd = (SI::ContentDescriptor *)d; - SI::ContentDescriptor::Nibble Nibble; - int NumContents = 0; - uchar Contents[MaxEventContents] = { 0 }; - for (SI::Loop::Iterator it3; cd->nibbleLoop.getNext(Nibble, it3); ) { - if (NumContents < MaxEventContents) { - Contents[NumContents] = ((Nibble.getContentNibbleLevel1() & 0xF) << 4) | (Nibble.getContentNibbleLevel2() & 0xF); - NumContents++; - } - if (DishEventDescriptor && NumContents == 1) { - DishEventDescriptor->setContent(Nibble); - } -// LogD(2, prep("EEPGDEBUG:Nibble:%x-%x-%x-%x)"), Nibble.getContentNibbleLevel1(),Nibble.getContentNibbleLevel2() -// , Nibble.getUserNibble1(), Nibble.getUserNibble2()); - - } - pEvent->SetContents(Contents); - } - break; -#endif - case SI::ParentalRatingDescriptorTag: { - int LanguagePreferenceRating = -1; - SI::ParentalRatingDescriptor *prd = (SI::ParentalRatingDescriptor *)d; - SI::ParentalRatingDescriptor::Rating Rating; - for (SI::Loop::Iterator it3; prd->ratingLoop.getNext(Rating, it3); ) { - if (I18nIsPreferredLanguage(Setup.EPGLanguages, Rating.languageCode, LanguagePreferenceRating)) { - int ParentalRating = (Rating.getRating() & 0xFF); - switch (ParentalRating) { - // values defined by the DVB standard (minimum age = rating + 3 years): - case 0x01 ... 0x0F: - ParentalRating += 3; - break; - // values defined by broadcaster CSAT (now why didn't they just use 0x07, 0x09 and 0x0D?): - case 0x11: - ParentalRating = 10; - break; - case 0x12: - ParentalRating = 12; - break; - case 0x13: - ParentalRating = 16; - break; - default: - ParentalRating = 0; - } - pEvent->SetParentalRating(ParentalRating); - } - } - } - break; - case SI::PDCDescriptorTag: { - SI::PDCDescriptor * pd = (SI::PDCDescriptor *) d; - time_t now = time (NULL); - struct tm tm_r; - struct tm t = *localtime_r (&now, &tm_r); // this initializes the time zone in 't' - t.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting - int month = t.tm_mon; - t.tm_mon = pd->getMonth () - 1; - t.tm_mday = pd->getDay (); - t.tm_hour = pd->getHour (); - t.tm_min = pd->getMinute (); - t.tm_sec = 0; - if (month == 11 && t.tm_mon == 0) // current month is dec, but event is in jan - t.tm_year++; - else if (month == 0 && t.tm_mon == 11) // current month is jan, but event is in dec - t.tm_year--; - time_t vps = mktime (&t); - pEvent->SetVps (vps); - } - break; - case SI::TimeShiftedEventDescriptorTag: { - SI::TimeShiftedEventDescriptor * tsed = (SI::TimeShiftedEventDescriptor *) d; - cSchedule *rSchedule = - (cSchedule *) Schedules-> - GetSchedule (tChannelID (Source, channel->Nid (), channel->Tid (), 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; - case SI::LinkageDescriptorTag: { - SI::LinkageDescriptor * ld = (SI::LinkageDescriptor *) d; - tChannelID linkID (Source, ld->getOriginalNetworkId (), ld->getTransportStreamId (), ld->getServiceId ()); - if (ld->getLinkageType () == 0xB0) { // Premiere World - time_t now = time (NULL); - bool hit = SiEitEvent.getStartTime () <= now - && now < SiEitEvent.getStartTime () + SiEitEvent.getDuration (); - if (hit) { - char linkName[ld->privateData.getLength () + 1]; - strn0cpy (linkName, (const char *) ld->privateData.getData (), sizeof (linkName)); - // TODO is there a standard way to determine the character set of this string? - cChannel *link = Channels.GetByChannelID (linkID); - if (link != channel) { // only link to other channels, not the same one - //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 - if (link) { - if (Setup.UpdateChannels == 1 || Setup.UpdateChannels >= 3) - link->SetName (linkName, "", ""); - } else if (Setup.UpdateChannels >= 4) { - cChannel *transponder = channel; - if (channel->Tid () != ld->getTransportStreamId ()) - transponder = Channels.GetByTransponderID (linkID); - link = - Channels.NewChannel (transponder, linkName, "", "", ld->getOriginalNetworkId (), - ld->getTransportStreamId (), ld->getServiceId ()); - } - if (link) { - if (!LinkChannels) - LinkChannels = new cLinkChannels; - LinkChannels->Add (new cLinkChannel (link)); - } - } else - channel->SetPortalName (linkName); - } - } - } - break; - case SI::ComponentDescriptorTag: { - SI::ComponentDescriptor * cd = (SI::ComponentDescriptor *) d; - uchar Stream = cd->getStreamContent (); - uchar Type = cd->getComponentType (); - //if (1 <= Stream && Stream <= 3 && Type != 0) { // 1=video, 2=audio, 3=subtitles - 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 - if (!Components) - Components = new cComponents; - char buffer[Utf8BufSize (256)]; - Components->SetComponent (Components->NumComponents (), Stream, Type, - I18nNormalizeLanguageCode (cd->languageCode), - cd->description.getText (buffer, sizeof (buffer))); - } - } - break; - case SI::DishExtendedEventDescriptorTag: { - SI::UnimplementedDescriptor *deed = (SI::UnimplementedDescriptor *)d; - if (!DishEventDescriptor) { - DishEventDescriptor = new SI::DishDescriptor(); - } - DishEventDescriptor->setExtendedtData(Tid+1, deed->getData()); - HasExternalData = true; - } - break; - case SI::DishShortEventDescriptorTag: { - SI::UnimplementedDescriptor *dsed = (SI::UnimplementedDescriptor *)d; - if (!DishEventDescriptor) { - DishEventDescriptor = new SI::DishDescriptor(); - } - DishEventDescriptor->setShortData(Tid+1, dsed->getData()); - HasExternalData = true; - } - break; - case SI::DishRatingDescriptorTag: { - if (d->getLength() == 4) { - if (!DishEventDescriptor) { - DishEventDescriptor = new SI::DishDescriptor(); - } - uint16_t rating = d->getData().TwoBytes(2); - DishEventDescriptor->setRating(rating); - } - } - break; - case SI::DishSeriesDescriptorTag: { - if (d->getLength() == 10) { - //LogD(2, prep("DishSeriesDescriptorTag: %s)"), (const char*) d->getData().getData()); - if (!DishEventDescriptor) { - DishEventDescriptor = new SI::DishDescriptor(); - } - DishEventDescriptor->setEpisodeInfo(d->getData()); - } -// else { -// LogD(2, prep("DishSeriesDescriptorTag length: %d)"), d->getLength()); -// } - } - break; - default: - break; - } - delete d; - } - - if (!rEvent) { - if (ShortEventDescriptor) { - char buffer[Utf8BufSize (256)]; - unsigned char *f; - int l = ShortEventDescriptor->name.getLength(); - f = (unsigned char *) ShortEventDescriptor->name.getData().getData(); - decodeText2 (f, l, buffer, sizeof (buffer)); - //ShortEventDescriptor->name.getText(buffer, sizeof(buffer)); - LogD(2, prep("Title: %s Decoded: %s"), f, buffer); - pEvent->SetTitle (buffer); - LogD(3, prep("channelID: %s Title: %s"), *channel->GetChannelID().ToString(), pEvent->Title()); - l = ShortEventDescriptor->text.getLength(); - if (l > 0) { //Set the Short Text only if there is data so that we do not overwrite valid data - f = (unsigned char *) ShortEventDescriptor->text.getData().getData(); - decodeText2 (f, l, buffer, sizeof (buffer)); - //ShortEventDescriptor->text.getText(buffer, sizeof(buffer)); - pEvent->SetShortText (buffer); - } - LogD(3, prep("ShortText: %s"), pEvent->ShortText()); - LogD(2, prep("ShortText: %s Decoded: %s"), f, buffer); - } else if (!HasExternalData) { - pEvent->SetTitle (NULL); - pEvent->SetShortText (NULL); - LogD(3, prep("SetTitle (NULL)")); - } - if (ExtendedEventDescriptors) { - char buffer[Utf8BufSize (ExtendedEventDescriptors->getMaximumTextLength (": ")) + 1]; - pEvent->SetDescription (ExtendedEventDescriptors->getText (buffer, sizeof (buffer), ": ")); - LogD(3, prep("Description: %s"), pEvent->Description()); - } else if (!HasExternalData) - pEvent->SetDescription (NULL); - - if (DishEventDescriptor) { - if (DishEventDescriptor->getName()) - pEvent->SetTitle(DishEventDescriptor->getName()); - //LogD(2, prep("channelID: %s DishTitle: %s"), *channel->GetChannelID().ToString(), DishEventDescriptor->getName()); - pEvent->SetShortText(DishEventDescriptor->getShortText()); - char *tmp; - string fmt; - - const char * description = DishEventDescriptor->getDescription(); - //BEV sets the description previously with ExtendedEventDescriptor - if (0 == strcmp(DishEventDescriptor->getDescription(),"") && pEvent->Description()) - description = pEvent->Description(); - - - fmt = "%s"; - if (DishEventDescriptor->hasTheme()) { - fmt += "\nTheme: "; - } - fmt += "%s"; - if (DishEventDescriptor->hasCategory()) { - fmt += "\nCategory: "; - } - fmt += "%s"; - - if ((0 != strcmp(DishEventDescriptor->getRating(),"") - || 0 != strcmp(DishEventDescriptor->getStarRating(),""))) { - fmt += "\n\nRating: "; - } - fmt += "%s %s"; - if (0 != strcmp(DishEventDescriptor->getProgramId(),"")) { - fmt += "\n\nProgram ID: "; - } - fmt += "%s %s%s"; - time_t orgAirDate = DishEventDescriptor->getOriginalAirDate(); - char datestr [80]; - bool dateok = false; - if (orgAirDate == 0) { - dateok = strftime (datestr,80," Original Air Date: %a %b %d %Y",gmtime(&orgAirDate)) > 0; - } - - Asprintf (&tmp, fmt.c_str(), description - , DishEventDescriptor->getTheme(), DishEventDescriptor->getCategory() - , DishEventDescriptor->getRating(), DishEventDescriptor->getStarRating() - , DishEventDescriptor->getProgramId(), DishEventDescriptor->getSeriesId() - , orgAirDate == 0 || !dateok ? "" : datestr); - pEvent->SetDescription(tmp); - free(tmp); - - - //LogD(2, prep("DishDescription: %s"), DishEventDescriptor->getDescription()); - //LogD(2, prep("DishShortText: %s"), DishEventDescriptor->getShortText()); - } - - } - delete ExtendedEventDescriptors; - delete ShortEventDescriptor; - delete DishEventDescriptor; - - pEvent->SetComponents (Components); - -// LogD(2, prep("channelID: %s Title: %s"), *channel->GetChannelID().ToString(), pEvent->Title()); - -// if (pEvent->ChannelID() == tChannelID::FromString("S119.0W-4100-6-110-110")) { -// LogD(2, prep("ID: %d Title: %s Time: %d Tid: 0x%x"), pEvent->EventID(), pEvent->Title(), pEvent->StartTime(), pEvent->TableID()); -// } - - if (!HasExternalData) - pEvent->FixEpgBugs (); - if (LinkChannels) - channel->SetLinkChannels (LinkChannels); - Modified = true; -#ifdef USE_DDEPGENTRY - //to avoid double epg-entrys from ext and int epg sources :EW - if (pEvent && pEvent->TableID () != 0x00) { - cEvent *pPreviousEvent = (cEvent *) pSchedule->GetPreviousEvent (pEvent); - if (pPreviousEvent) { - if (Setup.DoubleEpgAction == 0) { - pPreviousEvent->SetStartTime (pEvent->StartTime ()); - pPreviousEvent->SetDuration (pEvent->Duration ()); - if (Setup.DisableVPS == 0) { - if (channel) - pPreviousEvent->SetRunningStatus (pEvent->RunningStatus (), channel); - else - pPreviousEvent->SetRunningStatus (pEvent->RunningStatus ()); - } - // to use the info of the original epg, update the extern one, - // if it has less info - char buffer_short_intern[256]; - char buffer_short_extern[256]; - int len_short_intern = 0; - int len_short_extern = 0; - if (pEvent->ShortText ()) - len_short_intern = - snprintf (buffer_short_intern, sizeof (buffer_short_intern) - 1, "%s", pEvent->ShortText ()); - if (pPreviousEvent->ShortText ()) - len_short_extern = - snprintf (buffer_short_extern, sizeof (buffer_short_extern) - 1, "%s", pPreviousEvent->ShortText ()); - if (len_short_intern > 0) { - if (len_short_extern < 1) - pPreviousEvent->SetShortText (buffer_short_intern); - else if (len_short_intern > len_short_extern) - pPreviousEvent->SetShortText (buffer_short_intern); - } - if (pEvent->Description ()) { - char buffer_title_intern[4096]; - char buffer_title_extern[4096]; - int len_title_intern = 0; - int len_title_extern = 0; - if (pEvent->Description ()) - len_title_intern = - snprintf (buffer_title_intern, sizeof (buffer_title_intern) - 1, "%s", pEvent->Description ()); - if (pPreviousEvent->Description ()) - len_title_extern = - snprintf (buffer_title_extern, sizeof (buffer_title_extern) - 1, "%s", - pPreviousEvent->Description ()); - if (len_title_intern > 0) { - if (len_title_extern < 1) - pPreviousEvent->SetDescription (buffer_title_intern); - else if (len_title_intern > len_title_extern) - pPreviousEvent->SetDescription (buffer_title_intern); - } - } - if (pPreviousEvent->Vps () == 0 && pEvent->Vps () != 0) - pPreviousEvent->SetVps (pEvent->Vps ()); - pSchedule->DelEvent (pEvent); - pPreviousEvent->FixEpgBugs (); - } else - pSchedule->DelEvent (pPreviousEvent); - } - } -#endif /* DDEPGENTRY */ - EquivHandler->updateEquivalent(Schedules, channel->GetChannelID(), pEvent); - } - if (Empty && Tid == 0x4E && getSectionNumber () == 0) - // ETR 211: an empty entry in section 0 of table 0x4E means there is currently no event running - pSchedule->ClrRunningStatus (channel); - if (Tid == 0x4E) - pSchedule->SetPresentSeen (); - if (OnlyRunningStatus) { - LogD(4, prep("OnlyRunningStatus")); - return; - } - if (Modified) { - if (!HasExternalData) - pSchedule->DropOutdated (SegmentStart, SegmentEnd, Tid, getVersionNumber ()); - sortSchedules(Schedules, channel->GetChannelID()); - } - LogD(4, prep("end of cEIT2")); - -} -//end of cEIT2 - -} //end namespace SI - void cFilterEEPG::ProcessNextFormat (bool FirstTime = false) { /* for (int i = 0; i <= HIGHEST_FORMAT; i++) @@ -0,0 +1,640 @@ +/* + * eit2.c + * + * Created on: Oct 16, 2012 + * Author: d.petrovski + */ +#include "eit2.h" + +#include <string> +#include <vdr/config.h> +#include "log.h" +#include "util.h" + +using namespace std; +using namespace util; + +namespace SI +{ + + cEvent* cEIT2::ProcessEitEvent(cSchedule* pSchedule,const SI::EIT::Event* EitEvent, + uchar Tid, uchar versionNumber) + { + bool ExternalData = false; + // 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. + if (EitEvent->getStartTime () == 0 || (EitEvent->getStartTime () > 0 && EitEvent->getDuration () == 0)) + return NULL; + Empty = false; + if (!SegmentStart) + SegmentStart = EitEvent->getStartTime (); + SegmentEnd = EitEvent->getStartTime () + EitEvent->getDuration (); +// int versionNumber = getVersionNumber(); + + cEvent *newEvent = NULL; + cEvent *pEvent = (cEvent *) pSchedule->GetEvent (EitEvent->getEventId (), EitEvent->getStartTime ()); + if (!pEvent) { + if (OnlyRunningStatus) + return NULL; + // If we don't have that event yet, we create a new one. + // Otherwise we copy the information into the existing event anyway, because the data might have changed. + pEvent = newEvent = new cEvent (EitEvent->getEventId ()); + if (!pEvent) + return NULL; + } else { + //LogD(3, prep("existing event channelID: %s Title: %s TableID 0x%02X new TID 0x%02X Version %i, new version %i"), *channel->GetChannelID().ToString(), pEvent->Title(), pEvent->TableID(), Tid, pEvent->Version(), versionNumber); + // We have found an existing event, either through its event ID or its start time. + pEvent->SetSeen (); + + // If the existing event has a zero table ID it was defined externally and shall + // not be overwritten. + if (pEvent->TableID () == 0x00) { + if (pEvent->Version () == versionNumber) + return NULL; + /*HasExternalData = */ExternalData = true; + } + // If the new event has a higher table ID, let's skip it. + // The lower the table ID, the more "current" the information. + else if (Tid > pEvent->TableID()) + return NULL; + // 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. + else if (Tid == pEvent->TableID() && pEvent->Version() == versionNumber) + return NULL; + } + if (!ExternalData) { + pEvent->SetEventID (EitEvent->getEventId ()); // unfortunately some stations use different event ids for the same event in different tables :-( + pEvent->SetTableID (Tid); + pEvent->SetStartTime (EitEvent->getStartTime ()); + pEvent->SetDuration (EitEvent->getDuration ()); + } + if (newEvent) + pSchedule->AddEvent (newEvent); + if (Tid == 0x4E) { // we trust only the present/following info on the actual TS + if (EitEvent->getRunningStatus () >= SI::RunningStatusNotRunning) + pSchedule->SetRunningStatus (pEvent, EitEvent->getRunningStatus (), channel); + } + if (OnlyRunningStatus) + return NULL; // do this before setting the version, so that the full update can be done later + pEvent->SetVersion (versionNumber); + + ProcessEventDescriptors(ExternalData, channel->Source(), Tid, EitEvent, + pEvent, Schedules, channel); + + Modified = true; + return pEvent; + } + + void cEIT2::ProcessEventDescriptors(bool ExternalData, int Source, + u_char Tid, const SI::EIT::Event* SiEitEvent, cEvent* pEvent, + cSchedules* Schedules, cChannel* channel) + { + + cEvent *rEvent = NULL; + int LanguagePreferenceShort = -1; + int LanguagePreferenceExt = -1; + unsigned char nDescriptorTag; + bool UseExtendedEventDescriptor = false; + SI::Descriptor * d; + SI::ExtendedEventDescriptors * ExtendedEventDescriptors = NULL; + SI::ShortEventDescriptor * ShortEventDescriptor = NULL; + //SI::DishDescriptor *DishExtendedEventDescriptor = NULL; + SI::DishDescriptor *DishEventDescriptor = NULL; + //uchar DishTheme = 0, DishCategory = 0; + + + cLinkChannels *LinkChannels = NULL; + cComponents *Components = NULL; + + + + for (SI::Loop::Iterator it2; (d = SiEitEvent->eventDescriptors.getNext(it2)); ) + { + if (ExternalData && d->getDescriptorTag() != SI::ComponentDescriptorTag) + { + delete d; + LogD(2, prep("continue:d->getDescriptorTAG():%x)"), d->getDescriptorTag ()); + continue; + } + + //LogD(2, prep("EEPGDEBUG:d->getDescriptorTAG():%x)"), d->getDescriptorTag ()); + nDescriptorTag = d->getDescriptorTag(); + switch (nDescriptorTag) + { + case SI::ExtendedEventDescriptorTag: + { + SI::ExtendedEventDescriptor * eed = + (SI::ExtendedEventDescriptor *) d; + if (I18nIsPreferredLanguage(Setup.EPGLanguages, 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, sed->languageCode, + LanguagePreferenceShort) || !ShortEventDescriptor) + { + delete ShortEventDescriptor; + ShortEventDescriptor = sed; + d = NULL; // so that it is not deleted + } + } + break; +#if APIVERSNUM > 10711 + case SI::ContentDescriptorTag: + { + SI::ContentDescriptor *cd = (SI::ContentDescriptor *) d; + SI::ContentDescriptor::Nibble Nibble; + int NumContents = 0; + uchar Contents[MaxEventContents] = + { 0 }; + for (SI::Loop::Iterator it3; cd->nibbleLoop.getNext(Nibble, it3);) + { + if (NumContents < MaxEventContents) + { + Contents[NumContents] = ((Nibble.getContentNibbleLevel1() + & 0xF) << 4) | (Nibble.getContentNibbleLevel2() & 0xF); + NumContents++; + } + if (DishEventDescriptor && NumContents == 1) + { + DishEventDescriptor->setContent(Nibble); + } + // LogD(2, prep("EEPGDEBUG:Nibble:%x-%x-%x-%x)"), Nibble.getContentNibbleLevel1(),Nibble.getContentNibbleLevel2() + // , Nibble.getUserNibble1(), Nibble.getUserNibble2()); + + } + pEvent->SetContents(Contents); + } + break; +#endif + case SI::ParentalRatingDescriptorTag: + { + int LanguagePreferenceRating = -1; + SI::ParentalRatingDescriptor *prd = + (SI::ParentalRatingDescriptor *) d; + SI::ParentalRatingDescriptor::Rating Rating; + for (SI::Loop::Iterator it3; prd->ratingLoop.getNext(Rating, it3);) + { + if (I18nIsPreferredLanguage(Setup.EPGLanguages, + Rating.languageCode, LanguagePreferenceRating)) + { + int ParentalRating = (Rating.getRating() & 0xFF); + switch (ParentalRating) + { + // values defined by the DVB standard (minimum age = rating + 3 years): + case 0x01 ... 0x0F: + ParentalRating += 3; + break; + // values defined by broadcaster CSAT (now why didn't they just use 0x07, 0x09 and 0x0D?): + case 0x11: + ParentalRating = 10; + break; + case 0x12: + ParentalRating = 12; + break; + case 0x13: + ParentalRating = 16; + break; + default: + ParentalRating = 0; + } + pEvent->SetParentalRating(ParentalRating); + } + } + } + break; + case SI::PDCDescriptorTag: + { + SI::PDCDescriptor * pd = (SI::PDCDescriptor *) d; + time_t now = time(NULL); + struct tm tm_r; + struct tm t = *localtime_r(&now, &tm_r); // this initializes the time zone in 't' + t.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting + int month = t.tm_mon; + t.tm_mon = pd->getMonth() - 1; + t.tm_mday = pd->getDay(); + t.tm_hour = pd->getHour(); + t.tm_min = pd->getMinute(); + t.tm_sec = 0; + if (month == 11 && t.tm_mon == 0) // current month is dec, but event is in jan + t.tm_year++; + else if (month == 0 && t.tm_mon == 11) // current month is jan, but event is in dec + t.tm_year--; + time_t vps = mktime(&t); + pEvent->SetVps(vps); + } + break; + case SI::TimeShiftedEventDescriptorTag: + { + if (Schedules) { + SI::TimeShiftedEventDescriptor * tsed = + (SI::TimeShiftedEventDescriptor *) d; + cSchedule *rSchedule = (cSchedule *) Schedules->GetSchedule( + tChannelID(Source, channel->Nid(), channel->Tid(), + 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; + case SI::LinkageDescriptorTag: + { + SI::LinkageDescriptor * ld = (SI::LinkageDescriptor *) d; + tChannelID linkID(Source, ld->getOriginalNetworkId(), + ld->getTransportStreamId(), ld->getServiceId()); + if (ld->getLinkageType() == 0xB0) + { // Premiere World + time_t now = time(NULL); + bool hit = SiEitEvent->getStartTime() <= now + && now + < SiEitEvent->getStartTime() + SiEitEvent->getDuration(); + if (hit) + { + char linkName[ld->privateData.getLength() + 1]; + strn0cpy(linkName, (const char *) ld->privateData.getData(), + sizeof(linkName)); + // TODO is there a standard way to determine the character set of this string? + cChannel *link = Channels.GetByChannelID(linkID); + if (link != channel) + { // only link to other channels, not the same one + //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 + if (link) + { + if (Setup.UpdateChannels == 1 + || Setup.UpdateChannels >= 3) + link->SetName(linkName, "", ""); + } + else if (Setup.UpdateChannels >= 4) + { + cChannel *transponder = channel; + if (channel->Tid() != ld->getTransportStreamId()) + transponder = Channels.GetByTransponderID(linkID); + link = Channels.NewChannel(transponder, linkName, + "", "", ld->getOriginalNetworkId(), + ld->getTransportStreamId(), ld->getServiceId()); + } + if (link) + { + if (!LinkChannels) + LinkChannels = new cLinkChannels; + LinkChannels->Add(new cLinkChannel(link)); + } + } + else + channel->SetPortalName(linkName); + } + } + } + break; + case SI::ComponentDescriptorTag: + { + SI::ComponentDescriptor * cd = (SI::ComponentDescriptor *) d; + uchar Stream = cd->getStreamContent(); + uchar Type = cd->getComponentType(); + //if (1 <= Stream && Stream <= 3 && Type != 0) { // 1=video, 2=audio, 3=subtitles + 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 + if (!Components) + Components = new cComponents; + char buffer[Utf8BufSize (256)]; + Components->SetComponent(Components->NumComponents(), Stream, + Type, I18nNormalizeLanguageCode(cd->languageCode), + cd->description.getText(buffer, sizeof(buffer))); + } + } + break; + case SI::DishExtendedEventDescriptorTag: + { + SI::UnimplementedDescriptor *deed = + (SI::UnimplementedDescriptor *) d; + if (!DishEventDescriptor) + { + DishEventDescriptor = new SI::DishDescriptor(); + } + DishEventDescriptor->setExtendedtData(Tid + 1, deed->getData()); + // HasExternalData = true; + } + break; + case SI::DishShortEventDescriptorTag: + { + SI::UnimplementedDescriptor *dsed = + (SI::UnimplementedDescriptor *) d; + if (!DishEventDescriptor) + { + DishEventDescriptor = new SI::DishDescriptor(); + } + DishEventDescriptor->setShortData(Tid + 1, dsed->getData()); + // HasExternalData = true; + } + break; + case SI::DishRatingDescriptorTag: + { + if (d->getLength() == 4) + { + if (!DishEventDescriptor) + { + DishEventDescriptor = new SI::DishDescriptor(); + } + uint16_t rating = d->getData().TwoBytes(2); + DishEventDescriptor->setRating(rating); + } + } + break; + case SI::DishSeriesDescriptorTag: + { + if (d->getLength() == 10) + { + //LogD(2, prep("DishSeriesDescriptorTag: %s)"), (const char*) d->getData().getData()); + if (!DishEventDescriptor) + { + DishEventDescriptor = new SI::DishDescriptor(); + } + DishEventDescriptor->setEpisodeInfo(d->getData()); + } + // else { + // LogD(2, prep("DishSeriesDescriptorTag length: %d)"), d->getLength()); + // } + } + break; + default: + break; + } + delete d; + } + if (!rEvent) { + if (ShortEventDescriptor) { + char buffer[Utf8BufSize (256)]; + unsigned char *f; + int l = ShortEventDescriptor->name.getLength(); + f = (unsigned char *) ShortEventDescriptor->name.getData().getData(); + decodeText2 (f, l, buffer, sizeof (buffer)); + //ShortEventDescriptor->name.getText(buffer, sizeof(buffer)); + LogD(2, prep("Title: %s Decoded: %s"), f, buffer); + pEvent->SetTitle (buffer); + LogD(3, prep("channelID: %s Title: %s"), *channel->GetChannelID().ToString(), pEvent->Title()); + l = ShortEventDescriptor->text.getLength(); + if (l > 0) { //Set the Short Text only if there is data so that we do not overwrite valid data + f = (unsigned char *) ShortEventDescriptor->text.getData().getData(); + decodeText2 (f, l, buffer, sizeof (buffer)); + //ShortEventDescriptor->text.getText(buffer, sizeof(buffer)); + pEvent->SetShortText (buffer); + } + LogD(3, prep("ShortText: %s"), pEvent->ShortText()); + LogD(2, prep("ShortText: %s Decoded: %s"), f, buffer); + } else if (/*!HasExternalData*/!DishEventDescriptor) { + pEvent->SetTitle (NULL); + pEvent->SetShortText (NULL); + LogD(3, prep("SetTitle (NULL)")); + } + if (ExtendedEventDescriptors) { + char buffer[Utf8BufSize (ExtendedEventDescriptors->getMaximumTextLength (": ")) + 1]; + pEvent->SetDescription (ExtendedEventDescriptors->getText (buffer, sizeof (buffer), ": ")); + LogD(3, prep("Description: %s"), pEvent->Description()); + } else if (!/*HasExternalData*/DishEventDescriptor) + pEvent->SetDescription (NULL); + + if (DishEventDescriptor) { + if (DishEventDescriptor->getName()) + pEvent->SetTitle(DishEventDescriptor->getName()); + //LogD(2, prep("channelID: %s DishTitle: %s"), *channel->GetChannelID().ToString(), DishEventDescriptor->getName()); + pEvent->SetShortText(DishEventDescriptor->getShortText()); + char *tmp; + string fmt; + + const char * description = DishEventDescriptor->getDescription(); + //BEV sets the description previously with ExtendedEventDescriptor + if (0 == strcmp(DishEventDescriptor->getDescription(),"") && pEvent->Description()) + description = pEvent->Description(); + + + fmt = "%s"; + if (DishEventDescriptor->hasTheme()) { + fmt += "\nTheme: "; + } + fmt += "%s"; + if (DishEventDescriptor->hasCategory()) { + fmt += "\nCategory: "; + } + fmt += "%s"; + + if ((0 != strcmp(DishEventDescriptor->getRating(),"") + || 0 != strcmp(DishEventDescriptor->getStarRating(),""))) { + fmt += "\n\nRating: "; + } + fmt += "%s %s"; + if (0 != strcmp(DishEventDescriptor->getProgramId(),"")) { + fmt += "\n\nProgram ID: "; + } + fmt += "%s %s%s"; + time_t orgAirDate = DishEventDescriptor->getOriginalAirDate(); + char datestr [80]; + bool dateok = false; + if (orgAirDate == 0) { + dateok = strftime (datestr,80," Original Air Date: %a %b %d %Y",gmtime(&orgAirDate)) > 0; + } + + Asprintf (&tmp, fmt.c_str(), description + , DishEventDescriptor->getTheme(), DishEventDescriptor->getCategory() + , DishEventDescriptor->getRating(), DishEventDescriptor->getStarRating() + , DishEventDescriptor->getProgramId(), DishEventDescriptor->getSeriesId() + , orgAirDate == 0 || !dateok ? "" : datestr); + pEvent->SetDescription(tmp); + free(tmp); + + + //LogD(2, prep("DishDescription: %s"), DishEventDescriptor->getDescription()); + //LogD(2, prep("DishShortText: %s"), DishEventDescriptor->getShortText()); + } + + } + delete ExtendedEventDescriptors; + delete ShortEventDescriptor; + delete DishEventDescriptor; + + pEvent->SetComponents (Components); + +// LogD(2, prep("channelID: %s Title: %s"), *channel->GetChannelID().ToString(), pEvent->Title()); + +// if (pEvent->ChannelID() == tChannelID::FromString("S119.0W-4100-6-110-110")) { +// LogD(2, prep("ID: %d Title: %s Time: %d Tid: 0x%x"), pEvent->EventID(), pEvent->Title(), pEvent->StartTime(), pEvent->TableID()); +// } + +// if (!HasExternalData) + pEvent->FixEpgBugs (); + if (LinkChannels) + channel->SetLinkChannels (LinkChannels); + } + +cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Data, bool isEITPid, bool OnlyRunningStatus) +: SI::EIT (Data, false) +, OnlyRunningStatus(OnlyRunningStatus) +, Schedules(Schedules) +{ + //LogD(2, prep("cEIT2::cEIT2")); + if (Tid > 0 && (Format == DISH_BEV || (SetupPE->ProcessEIT && isEITPid))) Tid--; + + if (!CheckCRCAndParse ()) { + LogD(2, prep("!CheckCRCAndParse ()")); + return; + } + + bool searchOtherSatPositions = Format == DISH_BEV; + + tChannelID channelID (Source, getOriginalNetworkId (), getTransportStreamId (), getServiceId ()); + channel = GetChannelByID (channelID, searchOtherSatPositions); + if (!channel) { + LogD(3, prep("!channel channelID: %s"), *channelID.ToString()); + return; // only collect data for known channels + } + + //LogD(5, prep("channelID: %s format:%d"), *channel->GetChannelID().ToString(), Format); + + cSchedule *pSchedule = (cSchedule *) Schedules->GetSchedule (channel, true); + + Empty = true; + Modified = false; +// bool HasExternalData = false; + SegmentStart = 0; + SegmentEnd = 0; + + SI::EIT::Event SiEitEvent; + for (SI::Loop::Iterator it; eventLoop.getNext (SiEitEvent, it);) { + int versionNumber = getVersionNumber(); +// bool ExternalData = false; +// // 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. +// if (SiEitEvent.getStartTime () == 0 || (SiEitEvent.getStartTime () > 0 && SiEitEvent.getDuration () == 0)) +// continue; +// Empty = false; +// if (!SegmentStart) +// SegmentStart = SiEitEvent.getStartTime (); +// SegmentEnd = SiEitEvent.getStartTime () + SiEitEvent.getDuration (); +// int versionNumber = getVersionNumber(); +// +// cEvent *newEvent = NULL; +// cEvent *pEvent = (cEvent *) pSchedule->GetEvent (SiEitEvent.getEventId (), SiEitEvent.getStartTime ()); +// if (!pEvent) { +// if (OnlyRunningStatus) +// continue; +// // If we don't have that event yet, we create a new one. +// // Otherwise we copy the information into the existing event anyway, because the data might have changed. +// pEvent = newEvent = new cEvent (SiEitEvent.getEventId ()); +// if (!pEvent) +// continue; +// } else { +// //LogD(3, prep("existing event channelID: %s Title: %s TableID 0x%02X new TID 0x%02X Version %i, new version %i"), *channel->GetChannelID().ToString(), pEvent->Title(), pEvent->TableID(), Tid, pEvent->Version(), versionNumber); +// // We have found an existing event, either through its event ID or its start time. +// pEvent->SetSeen (); +// +// // If the existing event has a zero table ID it was defined externally and shall +// // not be overwritten. +// if (pEvent->TableID () == 0x00) { +// if (pEvent->Version () == versionNumber) +// continue; +// /*HasExternalData = */ExternalData = true; +// } +// // If the new event has a higher table ID, let's skip it. +// // The lower the table ID, the more "current" the information. +// else if (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. +// else if (Tid == pEvent->TableID() && pEvent->Version() == versionNumber) +// continue; +// } +// if (!ExternalData) { +// pEvent->SetEventID (SiEitEvent.getEventId ()); // unfortunately some stations use different event ids for the same event in different tables :-( +// pEvent->SetTableID (Tid); +// pEvent->SetStartTime (SiEitEvent.getStartTime ()); +// pEvent->SetDuration (SiEitEvent.getDuration ()); +// } +// if (newEvent) +// pSchedule->AddEvent (newEvent); +// if (Tid == 0x4E) { // we trust only the present/following info on the actual TS +// if (SiEitEvent.getRunningStatus () >= SI::RunningStatusNotRunning) +// pSchedule->SetRunningStatus (pEvent, SiEitEvent.getRunningStatus (), channel); +// } +// if (OnlyRunningStatus) +// continue; // do this before setting the version, so that the full update can be done later +// pEvent->SetVersion (versionNumber); +// +// ProcessEventDescriptors(ExternalData, Source, Tid, SiEitEvent, +// pEvent, Schedules, channel); +// +// Modified = true; + cEvent *pEvent = ProcessEitEvent(pSchedule, &SiEitEvent, + Tid, versionNumber); + if (pEvent) + EquivHandler->updateEquivalent(Schedules, channel->GetChannelID(), pEvent); + } + + //// + + if (Empty && Tid == 0x4E && getSectionNumber () == 0) + // ETR 211: an empty entry in section 0 of table 0x4E means there is currently no event running + pSchedule->ClrRunningStatus (channel); + if (Tid == 0x4E) + pSchedule->SetPresentSeen (); + if (OnlyRunningStatus) { + LogD(4, prep("OnlyRunningStatus")); + return; + } + if (Modified) { +// if (!HasExternalData) + pSchedule->DropOutdated (SegmentStart, SegmentEnd, Tid, getVersionNumber ()); + sortSchedules(Schedules, channel->GetChannelID()); + } + LogD(4, prep("end of cEIT2")); + + } + //end of cEIT2 + +cEIT2::cEIT2 (cSchedule * Schedule) +: OnlyRunningStatus(false) +, Empty(true) +, Modified(false) +, SegmentStart(0) +, SegmentEnd(0) +, Schedules(NULL) +{ + //LogD(2, prep("cEIT2::cEIT2")); +// if (Tid > 0 && (Format == DISH_BEV || (SetupPE->ProcessEIT && isEITPid))) Tid--; + + bool searchOtherSatPositions = Format == DISH_BEV; + + tChannelID channelID (Schedule->channelID.Source(), getOriginalNetworkId (), getTransportStreamId (), getServiceId ()); + channel = GetChannelByID (channelID, searchOtherSatPositions); + if (!channel) { + LogD(3, prep("!channel channelID: %s"), *channelID.ToString()); + return; // only collect data for known channels + } +} +} //end namespace SI + @@ -0,0 +1,49 @@ +#ifndef CEIT2_H_ +#define CEIT2_H_ +#include <libsi/section.h> +#include <libsi/descriptor.h> +#include <libsi/si.h> + +namespace SI +{ + enum DescriptorTagExt { + DishRatingDescriptorTag = 0x89, + DishShortEventDescriptorTag = 0x91, + DishExtendedEventDescriptorTag = 0x92, + DishSeriesDescriptorTag = 0x96, + }; + +// typedef InheritEnum< DescriptorTagExt, SI::DescriptorTag > ExtendedDescriptorTag; + +/*extern const char *getCharacterTable(const unsigned char *&buffer, int &length, bool *isSingleByte = NULL); +extern bool convertCharacterTable(const char *from, size_t fromLength, char *to, size_t toLength, const char *fromCode); +extern bool SystemCharacterTableIsSingleByte;*/ +class cEIT2:public SI::EIT +{ +public: + cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Data, bool isEITPid = false, bool OnlyRunningStatus = false); + cEIT2 (cSchedule * Schedule); +//protected: +// void updateEquivalent(cSchedules * Schedules, tChannelID channelID, cEvent *pEvent); + cEvent* ProcessEitEvent(cSchedule *Schedule, const SI::EIT::Event *EitEvent, uchar TableID, uchar Version); + +private: + void + ProcessEventDescriptors(bool ExternalData, + int Source, u_char Tid, const SI::EIT::Event* SiEitEvent, cEvent* pEvent, + cSchedules* Schedules, cChannel* channel); + +private: + bool Empty; + bool Modified; +// bool HasExternalData = false; + time_t SegmentStart; + time_t SegmentEnd; + cSchedules* Schedules; + cChannel* channel; + + bool OnlyRunningStatus; + }; +} //end namespace SI + +#endif /* CEIT2_H_ */ diff --git a/epghandler.c b/epghandler.c index f8a6636..3aaec2e 100644 --- a/epghandler.c +++ b/epghandler.c @@ -9,7 +9,9 @@ #if APIVERSNUM > 10725 #include "log.h" #include "equivhandler.h" +#include "eit2.h" #include <vdr/sources.h> +#include <libsi/si.h> cEEpgHandler::cEEpgHandler() { LogD(4, prep("cEEpgHandler()")); @@ -27,8 +29,11 @@ bool cEEpgHandler::HandleEitEvent(cSchedule* Schedule, //DISH NID 0x1001 to 0x100B BEV 0x100 and 0x101 //TODO move the eit handling code at least for NA providers here instead of discarding. int nid = Schedule->ChannelID().Nid(); - if ((nid >= 0x1001 && nid <= 0x100B) || nid == 0x101 || nid == 0x100) + if ((nid >= 0x1001 && nid <= 0x100B) || nid == 0x101 || nid == 0x100) { + SI::cEIT2 eit2(Schedule); + eit2.ProcessEitEvent(Schedule, EitEvent, TableID, Version); return true; + } //TODO Should it be added in setup? if (EitEvent->getDurationHour() > _LONG_EVENT_HOURS) { diff --git a/epghandler.h b/epghandler.h index 3219f58..4e60e57 100644 --- a/epghandler.h +++ b/epghandler.h @@ -34,6 +34,8 @@ public: virtual bool SortSchedule(cSchedule *Schedule); virtual bool DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version); +// bool ParseEitEvent(cSchedule *Schedule, const SI::EIT::Event *EitEvent, uchar TableID, uchar Version); + private: std::string origShortText; std::string origDescription; @@ -28,5 +28,8 @@ time_t LocalTime2UTC (time_t t); time_t UTC2LocalTime (time_t t); void GetLocalTimeOffset (void); void CleanString (unsigned char *String); + +#define Asprintf(a, b, c...) void( asprintf(a, b, c) < 0 ? esyslog("memory allocation error - %s", b) : void() ) + } #endif /* UTIL_H_ */ |