From d5e15ce71cd5cb0dda3dc3b4c3a9b4c057950bf6 Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Fri, 26 Aug 2011 11:43:24 +0200 Subject: Test support for Dish Network EEPG and Bell ExpressVU EEPG --- eepg.c | 47 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 12 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 98b81f2..f9ac114 100644 --- a/eepg.c +++ b/eepg.c @@ -1145,6 +1145,7 @@ int cFilterEEPG::GetChannelsMHW (const u_char * Data, int Length, int MHW) memcpy (C->Name, &Channel->Name, 16); //MHW1 else { //MHW2 int lenName = Data[pName] & 0x0f; + LogD (1, prep("EEPGDebug: MHW2 lenName:%d"), lenName); if (lenName < 256) //TODO impossible, after & 0x0f lenName is always < 0x0f !! decodeText2(&Data[pName+1],lenName,(char*)C->Name,256); //memcpy (C->Name, &Data[pName + 1], lenName); @@ -1236,7 +1237,7 @@ int cFilterEEPG::GetThemesMHW1 (const u_char * Data, int Length) */ int cFilterEEPG::GetThemesMHW2 (const u_char * Data, int Length) { - if (!EndThemes) { //only proces if not processed + if (!EndThemes) { //only process if not processed int p1; int p2; int pThemeName = 0; @@ -1676,7 +1677,7 @@ void cFilterEEPG::GetTitlesNagra (const u_char * Data, int Length, unsigned shor } while (p < DataEnd); //end of TitleBlock } -int cFilterEEPG::GetThemesNagra (const u_char * Data, int Length, unsigned short TableIdExtension) //return code 0 = fatal error, code 1 = sucess, code 2 = last item processed +int cFilterEEPG::GetThemesNagra (const u_char * Data, int Length, unsigned short TableIdExtension) //return code 0 = fatal error, code 1 = success, code 2 = last item processed { u_char *DataStart = (u_char *) Data; u_char *p = DataStart; //TODO Language code terminated by 0 is ignored @@ -2807,6 +2808,13 @@ void cFilterEEPG::AddFilter (u_short Pid, u_char Tid, unsigned char Mask) namespace SI { + enum DescriptorTagExt { + DishRatingDescriptorTag = 0x89, + DishShortEventDescriptorTag = 0x91, + DishExtendedEventDescriptorTag = 0x92 }; + + 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;*/ @@ -3018,6 +3026,7 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat continue; } switch (d->getDescriptorTag ()) { + case SI::DishExtendedEventDescriptorTag: case SI::ExtendedEventDescriptorTag: { SI::ExtendedEventDescriptor * eed = (SI::ExtendedEventDescriptor *) d; if (I18nIsPreferredLanguage (Setup.EPGLanguages, eed->languageCode, LanguagePreferenceExt) @@ -3034,6 +3043,7 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat UseExtendedEventDescriptor = false; } break; + case SI::DishShortEventDescriptorTag: case SI::ShortEventDescriptorTag: { SI::ShortEventDescriptor * sed = (SI::ShortEventDescriptor *) d; if (I18nIsPreferredLanguage (Setup.EPGLanguages, sed->languageCode, LanguagePreferenceShort) @@ -3149,7 +3159,6 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat link = Channels.NewChannel (transponder, linkName, "", "", ld->getOriginalNetworkId (), ld->getTransportStreamId (), ld->getServiceId ()); - //XXX patFilter->Trigger(); } if (link) { if (!LinkChannels) @@ -3177,6 +3186,17 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat } } break; + case SI::DishRatingDescriptorTag: { + if (d->getLength() == 4) { + uint16_t rating = d->getData().TwoBytes(2); + uint16_t newRating = (rating >> 10) & 0x07; + if (newRating == 0) newRating = 5; + if (newRating == 6) newRating = 0; + pEvent->SetParentalRating((newRating << 10) | (rating & 0x3FF)); +// pEvent->SetStarRating((rating >> 13) & 0x07); + } + } + break; default: break; } @@ -3187,12 +3207,12 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat if (ShortEventDescriptor) { char buffer[Utf8BufSize (256)]; unsigned char *f; - int l = ShortEventDescriptor->name.getLength (); + int l = ShortEventDescriptor->name.getLength(); f = (unsigned char *) ShortEventDescriptor->name.getData().getData(); decodeText2 (f, l, buffer, sizeof (buffer)); //ShortEventDescriptor->name.getText(buffer, sizeof(buffer)); pEvent->SetTitle (buffer); - l = ShortEventDescriptor->text.getLength (); + l = ShortEventDescriptor->text.getLength(); f = (unsigned char *) ShortEventDescriptor->text.getData().getData(); decodeText2 (f, l, buffer, sizeof (buffer)); //ShortEventDescriptor->text.getText(buffer, sizeof(buffer)); @@ -3387,6 +3407,12 @@ void cFilterEEPG::ProcessNextFormat (bool FirstTime = false) AddFilter (pid, 0x60, 0xf0); //event info, other TS, schedule(0x60)/schedule for future days(0x6X) AddFilter (0x39, 0x50, 0xf0); //event info, actual TS, Viasat AddFilter (0x39, 0x60, 0xf0); //event info, other TS, Viasat + + AddFilter (0x0300, 0x50, 0xf0); // Dish Network EEPG + AddFilter (0x0300, 0x60, 0xf0); // Dish Network EEPG + AddFilter (0x0441, 0x50, 0xf0); // Bell ExpressVU EEPG + AddFilter (0x0441, 0x60, 0xf0); // Bell ExpressVU EEPG + break; case NAGRA: // isyslog ("EEPG: NagraGuide Extended EPG detected."); @@ -3441,7 +3467,6 @@ void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Len if (pmt.CheckCRCAndParse () && pmt.getServiceId () == pmtsid) { SI::PMT::Stream stream; for (SI::Loop::Iterator it; pmt.streamLoop.getNext (stream, it);) { -// if(stream.getStreamType()==0x05) { if (stream.getStreamType () == 0x05 || stream.getStreamType () == 0xc1) { //0x05 = Premiere, SKY, Freeview, Nagra 0xc1 = MHW1,MHW2 SI::CharArray data = stream.getData (); if ((data[1] & 0xE0) == 0xE0 && (data[3] & 0xF0) == 0xF0) { @@ -3451,6 +3476,7 @@ void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Len if (data[2]==0x39) {//TODO Test This prvFRV = true; usrFRV = 1; + LogD(1, prep("if (data[2]==0x39) {//TODO Test This")); } //Format = 0; // 0 = premiere, 1 = MHW1, 2 = MHW2, 3 = Sky Italy (OpenTV), 4 = Sky UK (OpenTV), 5 = Freesat (Freeview), 6 = Nagraguide SI::Descriptor * d; @@ -3470,7 +3496,6 @@ void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Len //if (d->getLength () == 3 && d->getData ().FourBytes (2) == 0xb07ea882) { if (d->getLength () == 3 && ((d->getData ().TwoBytes (2) & 0xff00) == 0xb000)) UnprocessedFormat[NAGRA] = stream.getPid (); - //int nop; //FIXME break; case 0x90: //esyslog ("usr: %d %08x\n", d->getLength (), d->getData ().FourBytes (2)); @@ -3480,9 +3505,7 @@ void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Len //if (d->getLength () == 3 && (d->getData ().TwoBytes (2) == 0xb6a5)) //SKY IT //TODO ugly! usrOTV = SKY_IT; //Format = SKY_IT; - if (d->getLength () == 3 && d->getData ().FourBytes (2) == 0xc004e288) //SKY UK - //if (d->getLength () == 3 && ((d->getData ().TwoBytes (2) & 0xff00) == 0x9d00)) //SKY UK //TODO ugly! usrOTV = SKY_UK; //Format = SKY_UK; break; @@ -3494,12 +3517,12 @@ void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Len case 0xc2: //MHW1, MHW2 if (d->getLength () == 10 && d->getData ().FourBytes (2) == 0x45504700) //MHw1 CanDigNL and CSat UnprocessedFormat[MHW1] = stream.getPid (); - //int nop;//FIXME else if (d->getLength () == 10 && d->getData ().FourBytes (2) == 0x46494348) { //MHW2 UnprocessedFormat[MHW2] = stream.getPid (); } break; case 0xd1: //Freeview + LogD(1, prep("case 0xd1: //Freeview")); if (d->getLength () == 3 && ((d->getData ().TwoBytes (2) & 0xff00) == 0x0100)) usrFRV = 0x01; //01 = EIT pid 3842 @@ -3547,7 +3570,7 @@ void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Len else if (Source ()) { int Result; switch (Tid) { - case 0xA0: + case 0xA0: //TODO DPE test this missing break but it seems a bug if ((Pid < 0x30) || (Pid > 0x37)) { SI::PremiereCIT cit (Data, false); if (cit.CheckCRCAndParse ()) { @@ -4028,7 +4051,7 @@ void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Len // PID found: 3843 (0x0f03) [SECTION: ATSC reserved] TODO find out what compressed text info is here! // PID found: 3844 (0x0f04) [SECTION: Time Offset Table (TOT)] - if (Pid == 3842 || Pid ==0x39) {//0x39 Viasat + if (Pid == 3842 || Pid == 0x39 || Pid == 0x0300 || Pid == 0x0441 ) {//0x39 Viasat, 0x0300 Dish Network EEPG, 0x0441 Bell ExpressVU EEPG cSchedulesLock SchedulesLock (true, 10); cSchedules *Schedules = (cSchedules *) cSchedules::Schedules (SchedulesLock); if (Schedules) -- cgit v1.2.3 From a15943b480f54ebf6117bffa64a58e2ed6fde9db Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Fri, 26 Aug 2011 13:23:39 +0200 Subject: increase log level for testing --- eepg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index f9ac114..ba16c66 100644 --- a/eepg.c +++ b/eepg.c @@ -42,7 +42,7 @@ #include #include -#define VERBOSE 1 +#define VERBOSE 3 /* 0 = only print errors, 1 = print channels and themes, 2 = print channels, themes, titles, summaries 3 = debug mode */ /* all is logged into /var/log/syslog */ -- cgit v1.2.3 From ce614fe2120fa2a485c32c0833c62a02f1fcd372 Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Sat, 27 Aug 2011 17:26:41 +0200 Subject: increase debug log for test reduce warnings with gcc > 4.5.0 --- eepg.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index f9ac114..5e0012c 100644 --- a/eepg.c +++ b/eepg.c @@ -1145,8 +1145,7 @@ int cFilterEEPG::GetChannelsMHW (const u_char * Data, int Length, int MHW) memcpy (C->Name, &Channel->Name, 16); //MHW1 else { //MHW2 int lenName = Data[pName] & 0x0f; - LogD (1, prep("EEPGDebug: MHW2 lenName:%d"), lenName); - if (lenName < 256) //TODO impossible, after & 0x0f lenName is always < 0x0f !! + //LogD (1, prep("EEPGDebug: MHW2 lenName:%d"), lenName); decodeText2(&Data[pName+1],lenName,(char*)C->Name,256); //memcpy (C->Name, &Data[pName + 1], lenName); //else @@ -1214,7 +1213,7 @@ int cFilterEEPG::GetThemesMHW1 (const u_char * Data, int Length) ThemeId++; } memcpy (&Themes[Offset][0], &Theme->Name, 15); - Themes[Offset][15] = NULL; //trailing null + Themes[Offset][15] = '\0'; //trailing null CleanString (Themes[Offset]); LogI(1, prep("%.15s"), Themes[Offset]); Offset++; @@ -1390,7 +1389,7 @@ char *cFilterEEPG::GetSummaryTextNagra (const u_char * DataStart, long int Offse LastTextBlock = ((ST->LastTextNr == 0) || (ST->TextNr >= ST->LastTextNr)); } while (!LastTextBlock); Text = (unsigned char *) realloc (Text, 1 + TotLength); //allocate 1 extra byte - Text[TotLength] = NULL; //terminate string by NULL char + Text[TotLength] = '\0'; //terminate string by NULL char LogD(5, prep("DEBUG: Full Text:%s."), Text); break; @@ -1751,7 +1750,7 @@ int cFilterEEPG::GetThemesNagra (const u_char * Data, int Length, unsigned short continue; } memcpy (&Themes[ThemeId], Text, Textlength); - Themes[ThemeId][Textlength] = NULL; //trailing NULL + Themes[ThemeId][Textlength] = '\0'; //trailing NULL CleanString (Themes[ThemeId]); nThemes++; LogI(1, prep("%02x %s"), ThemeId, Themes[ThemeId]); @@ -1812,7 +1811,7 @@ int cFilterEEPG::GetChannelsNagra (const u_char * Data, int Length) if (IsFound) strncpy ((char *) C->Name, VC->Name (), 64); else - C->Name[0] = NULL; //empty string + C->Name[0] = '\0'; //empty string CleanString (C->Name); LogI(1, "|% 5d | %-26.26s | %-22.22s | %-3.3s | % 6d |\n", C->ChannelId @@ -1976,7 +1975,7 @@ int cFilterEEPG::GetNagra (const u_char * Data, int Length) void cFilterEEPG::ProcessNagra () { for (int i = 0; i < MAX_THEMES; i++) //clear all themes - Themes[i][0] = NULL; + Themes[i][0] = '\0'; for (int i = 0; i < NagraCounter; i++) { //first prcoess all themes, since they all use the same codes unsigned short int TableIdExtension = NagraTIE[i]; @@ -2061,7 +2060,7 @@ int cFilterEEPG::GetTitlesMHW1 (const u_char * Data, int Length) LogE(0, prep("Titles memory allocation error.")); return 0; } - T->Text[46] = NULL; //end string with NULL character + T->Text[46] = '\0'; //end string with NULL character //memcpy (T->Text, &Title->Title, 23); decodeText2((unsigned char *)&Title->Title, 23, (char*)T->Text, 47); CleanString (T->Text); @@ -2142,7 +2141,7 @@ int cFilterEEPG::GetTitlesMHW2 (const u_char * Data, int Length) LogE(0, prep("Titles memory allocation error.")); return 0; //fatal error } - T->Text[Len] = NULL; //end string with NULL character + T->Text[Len] = '\0'; //end string with NULL character decodeText2(&Data[Pos + 8],Len,(char*)T->Text,Len+1); //memcpy (T->Text, &Data[Pos + 8], Len); CleanString (T->Text); @@ -2189,7 +2188,7 @@ int cFilterEEPG::GetSummariesMHW1 (const u_char * Data, int Length) LogE(0, prep("Summaries memory allocation error.")); return 0; } - Text[SummaryLength+1] = NULL; //end string with NULL character + Text[SummaryLength+1] = '\0'; //end string with NULL character //memcpy (Text, &Data[SummaryOffset], SummaryLength); decodeText2(&Data[SummaryOffset], SummaryLength, (char*)Text, 2*SummaryLength + 1); // CleanString (Text); @@ -2313,7 +2312,7 @@ int cFilterEEPG::GetSummariesMHW2 (const u_char * Data, int Length) } } S->Text = (unsigned char *) malloc (SummaryLength + 2); - S->Text[SummaryLength] = NULL; //end string with NULL character + S->Text[SummaryLength] = '\0'; //end string with NULL character if (S->Text == NULL) { LogE(0, prep("Summaries memory allocation error.")); return 0; //fatal error @@ -2400,7 +2399,7 @@ int cFilterEEPG::GetChannelsSKYBOX (const u_char * Data, int Length) if (IsFound) strncpy ((char *) C->Name, VC->Name (), 64); else - C->Name[0] = NULL; //empty string + C->Name[0] = '\0'; //empty string LogI(1, "|% 5d | %-26.26s | %-22.22s | %-3.3s | % 6d |\n", C->ChannelId , *channelID.ToString(), C->Name, IsFound ? "YES" : "NO", C->SkyNumber); @@ -2509,7 +2508,7 @@ int cFilterEEPG::GetTitlesSKYBOX (const u_char * Data, int Length) LogE(0, prep("Titles memory allocation error.")); return 0; } - T->Text[Len2] = NULL; //end string with NULL character + T->Text[Len2] = '\0'; //end string with NULL character memcpy (T->Text, tmp, Len2); CleanString (T->Text); T->SummaryAvailable = 1; //TODO I assume this is true? @@ -2588,7 +2587,7 @@ int cFilterEEPG::GetSummariesSKYBOX (const u_char * Data, int Length) return 0; } memcpy (S->Text, tmp, Len2); - S->Text[Len2] = NULL; //end string with NULL character + S->Text[Len2] = '\0'; //end string with NULL character CleanString (S->Text); LogI(3, prep("EventId %08x Summnr %d:%.30s."), S->EventId, nSummaries, S->Text); p += Len1; @@ -3426,7 +3425,8 @@ void cFilterEEPG::ProcessNextFormat (bool FirstTime = false) void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Length) { int now = time (0); - //LogD(5, prep("PMT pid now 0x%04x pid now 0x%04x"), Pid, Tid); + LogD(2, prep("Pid: 0x%02x Tid: %d Length: %d PMT pid: 0x%04x"), Pid, Tid, Length, pmtpid); + LogD(2, prep("Source: %d Transponder: %d"), Source () , Transponder ()); if (Pid == 0 && Tid == SI::TableIdPAT) { if (!pmtnext || now > pmtnext) { if (pmtpid) @@ -3467,6 +3467,7 @@ void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Len if (pmt.CheckCRCAndParse () && pmt.getServiceId () == pmtsid) { SI::PMT::Stream stream; for (SI::Loop::Iterator it; pmt.streamLoop.getNext (stream, it);) { + LogD(2, prep("StreamType: 0x%02x"), stream.getStreamType ()); if (stream.getStreamType () == 0x05 || stream.getStreamType () == 0xc1) { //0x05 = Premiere, SKY, Freeview, Nagra 0xc1 = MHW1,MHW2 SI::CharArray data = stream.getData (); if ((data[1] & 0xE0) == 0xE0 && (data[3] & 0xF0) == 0xF0) { @@ -3481,7 +3482,7 @@ void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Len //Format = 0; // 0 = premiere, 1 = MHW1, 2 = MHW2, 3 = Sky Italy (OpenTV), 4 = Sky UK (OpenTV), 5 = Freesat (Freeview), 6 = Nagraguide SI::Descriptor * d; for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext (it));) { - //esyslog ("EEPGDEBUG:d->getDescriptorTAG():%x,SI::PrivateTag:%x\n", d->getDescriptorTag (), SI::PrivateDataSpecifierDescriptorTag); + LogD(2, prep("EEPGDEBUG:d->getDescriptorTAG():%x,SI::PrivateTag:%x\n"), d->getDescriptorTag (), SI::PrivateDataSpecifierDescriptorTag); switch (d->getDescriptorTag ()) { case SI::PrivateDataSpecifierDescriptorTag: //esyslog ("prv: %d %08x\n", d->getLength (), d->getData ().FourBytes (2)); -- cgit v1.2.3 From 17413902bd820c9971fb3e4227e45f2eb732cdbd Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Sat, 27 Aug 2011 22:45:23 +0200 Subject: removed duplicate methods reduced warnings on gcc > 4.5.0 --- eepg.c | 75 +++++++++++++++++++++++++----------------------------------------- 1 file changed, 28 insertions(+), 47 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 2a91946..1aa3044 100644 --- a/eepg.c +++ b/eepg.c @@ -258,6 +258,26 @@ static int AvailableSources[32]; static int NumberOfAvailableSources = 0; 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: @@ -369,25 +389,6 @@ void cFilterEEPG::NextPmt (void) LogE(3, prep("PMT next\n")); } -//TODO next routine is also in cEIT2, make this simpler -#ifdef USE_NOEPG -bool cFilterEEPG::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 */ // ------------------- Freesat ------------------- @@ -666,6 +667,9 @@ void GetLocalTimeOffset (void) void CleanString (unsigned char *String) { + + LogD (1, prep("Unclean: %s"), String); + unsigned char *Src; unsigned char *Dst; int Spaces; @@ -696,10 +700,10 @@ void CleanString (unsigned char *String) } if (Spaces < 2) { *Dst = *Src; - *Dst++; + Dst++; pC++; } - *Src++; + Src++; } if (Spaces > 0) { Dst--; @@ -707,6 +711,7 @@ void CleanString (unsigned char *String) } else { *Dst = 0; } + LogD (1, prep("Clean: %s"), String); } bool cFilterEEPG::GetThemesSKYBOX (void) //TODO can't we read this from the DVB stream? @@ -1146,7 +1151,7 @@ int cFilterEEPG::GetChannelsMHW (const u_char * Data, int Length, int MHW) else { //MHW2 int lenName = Data[pName] & 0x0f; //LogD (1, prep("EEPGDebug: MHW2 lenName:%d"), lenName); - decodeText2(&Data[pName+1],lenName,(char*)C->Name,256); + decodeText2(&Data[pName+1],lenName,(char*)C->Name,256); //memcpy (C->Name, &Data[pName + 1], lenName); //else //memcpy (C->Name, &Data[pName + 1], 256); @@ -2822,32 +2827,8 @@ class cEIT2:public SI::EIT public: cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Data, bool OnlyRunningStatus = false); - -#ifdef USE_NOEPG -private: - bool allowedEPG (tChannelID kanalID); -#endif /* NOEPG */ }; -#ifdef USE_NOEPG -bool cEIT2::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 */ - cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Data, bool OnlyRunningStatus) : SI::EIT (Data, false) { @@ -3195,7 +3176,7 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat // pEvent->SetStarRating((rating >> 13) & 0x07); } } - break; + break; default: break; } -- cgit v1.2.3 From 42656ea682424e78b6f07fa43e22dc3207386ece Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Mon, 29 Aug 2011 10:40:26 +0200 Subject: add dish bev format and make sure it is always on for test --- eepg.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 1aa3044..d2278b4 100644 --- a/eepg.c +++ b/eepg.c @@ -3346,7 +3346,11 @@ void cFilterEEPG::ProcessNextFormat (bool FirstTime = false) if (UnprocessedFormat[i]) { isyslog ("EEPG: %s Extended EPG detected on pid %x.", FormatName[i], UnprocessedFormat[i]); Format = i; - } //highest format is processed first this way;; TODO make sure that CONT protocols like Premiere, Freesat are processed AFTER ONCE protocols like MHW, SKY and NAGRA + // highest format is processed first this way + // make sure that CONT protocols like Premiere, Freesat are processed + // AFTER ONCE protocols like MHW, SKY and NAGRA + break; + } if (Format == -1) { //there are no formats left to process isyslog ("EEPG: Ended all processing"); @@ -3387,16 +3391,15 @@ void cFilterEEPG::ProcessNextFormat (bool FirstTime = false) AddFilter (pid, 0x60, 0xf0); //event info, other TS, schedule(0x60)/schedule for future days(0x6X) AddFilter (0x39, 0x50, 0xf0); //event info, actual TS, Viasat AddFilter (0x39, 0x60, 0xf0); //event info, other TS, Viasat - + break; + case NAGRA: + AddFilter (pid, 0xb0); //perhaps TID is equal to first data byte? + break; + case DISH_BEV: AddFilter (0x0300, 0x50, 0xf0); // Dish Network EEPG AddFilter (0x0300, 0x60, 0xf0); // Dish Network EEPG AddFilter (0x0441, 0x50, 0xf0); // Bell ExpressVU EEPG AddFilter (0x0441, 0x60, 0xf0); // Bell ExpressVU EEPG - - break; - case NAGRA: - // isyslog ("EEPG: NagraGuide Extended EPG detected."); - AddFilter (pid, 0xb0); //perhaps TID is equal to first data byte? break; default: break; @@ -3407,7 +3410,8 @@ void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Len { int now = time (0); LogD(2, prep("Pid: 0x%02x Tid: %d Length: %d PMT pid: 0x%04x"), Pid, Tid, Length, pmtpid); - LogD(2, prep("Source: %d Transponder: %d"), Source () , Transponder ()); +// LogD(2, prep("Source: %d Transponder: %d"), Source () , Transponder ()); + if (Pid == 0 && Tid == SI::TableIdPAT) { if (!pmtnext || now > pmtnext) { if (pmtpid) @@ -3449,7 +3453,9 @@ void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Len SI::PMT::Stream stream; for (SI::Loop::Iterator it; pmt.streamLoop.getNext (stream, it);) { LogD(2, prep("StreamType: 0x%02x"), stream.getStreamType ()); - if (stream.getStreamType () == 0x05 || stream.getStreamType () == 0xc1) { //0x05 = Premiere, SKY, Freeview, Nagra 0xc1 = MHW1,MHW2 + if (stream.getStreamType () == 0x05 || stream.getStreamType () == 0xc1 + /*|| stream.getStreamType () == 0x04 || stream.getStreamType () == 0x02 + || stream.getStreamType () == 0xd1*/) { //0x05 = Premiere, SKY, Freeview, Nagra 0xc1 = MHW1,MHW2; 0x04 DISH BEV ? SI::CharArray data = stream.getData (); if ((data[1] & 0xE0) == 0xE0 && (data[3] & 0xF0) == 0xF0) { bool prvData = false, usrData = false; @@ -3531,8 +3537,12 @@ void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Len UnprocessedFormat[FREEVIEW] = stream.getPid (); if (prvData && usrData) UnprocessedFormat[PREMIERE] = stream.getPid (); - } //if data[1] && data [3] - } //if streamtype + //TODO DPE this is not good since the DISH/BEV filters are always on, but have to test somehow. + if (!UnprocessedFormat[DISH_BEV]) { + UnprocessedFormat[DISH_BEV] = stream.getPid (); + } + } //if data[1] && data [3] + } //if streamtype /*if (Format != PREMIERE) //any format found break; //break out for loop */ } //for loop that walks through streams -- cgit v1.2.3 From 1940a126b3153798d15876d83ba4c13e4341c427 Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Mon, 29 Aug 2011 10:43:37 +0200 Subject: commented test log --- eepg.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 1aa3044..d3527dd 100644 --- a/eepg.c +++ b/eepg.c @@ -668,8 +668,7 @@ void GetLocalTimeOffset (void) void CleanString (unsigned char *String) { - LogD (1, prep("Unclean: %s"), String); - +// LogD (1, prep("Unclean: %s"), String); unsigned char *Src; unsigned char *Dst; int Spaces; @@ -711,7 +710,7 @@ void CleanString (unsigned char *String) } else { *Dst = 0; } - LogD (1, prep("Clean: %s"), String); +// LogD (1, prep("Clean: %s"), String); } bool cFilterEEPG::GetThemesSKYBOX (void) //TODO can't we read this from the DVB stream? -- cgit v1.2.3 From 584131eff2c25df6fd7c321253bf133d9a6e780c Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Thu, 1 Sep 2011 10:22:55 +0200 Subject: Dish implementation with DishDescriptor --- eepg.c | 376 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 351 insertions(+), 25 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 726fcdb..9dea796 100644 --- a/eepg.c +++ b/eepg.c @@ -306,10 +306,8 @@ private: unsigned char InitialSummary[64]; void NextPmt (void); + void ProccessContinuous(u_short Pid, u_char Tid, int Length, const u_char *Data); protected: -#ifdef USE_NOEPG - virtual bool allowedEPG (tChannelID kanalID); -#endif virtual void Process (u_short Pid, u_char Tid, const u_char * Data, int Length); virtual void AddFilter (u_short Pid, u_char Tid); virtual void AddFilter (u_short Pid, u_char Tid, unsigned char Mask); @@ -2818,6 +2816,38 @@ namespace SI typedef InheritEnum< DescriptorTagExt, SI::DescriptorTag > ExtendedDescriptorTag; +#ifndef LIBSI_DISH_H +#define LIBSI_DISH_H + +#define SIZE_TABLE_128 128 +#define SIZE_TABLE_255 255 + +class DishDescriptor : public Descriptor { +public: + DishDescriptor(void); + virtual ~DishDescriptor(); + const char* getText(void) const { return text; } + const char* getShortText(void) const { return shortText; } + // Decompress the byte arrary and stores the result to a text string + void Decompress(unsigned char Tid); +protected: + virtual void Parse() {}; + + const char* text; // name or description of the event + const char* shortText; // usually the episode name + unsigned char* decompressed; + + struct HuffmanTable { + unsigned int startingAddress; + unsigned char character; + unsigned char numberOfBits; + }; + static HuffmanTable Table128[SIZE_TABLE_128]; + static HuffmanTable Table255[SIZE_TABLE_255]; +}; + +#endif //LIBSI_DISH_H + /*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;*/ @@ -2831,13 +2861,20 @@ public: cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Data, bool OnlyRunningStatus) : SI::EIT (Data, false) { - if (!CheckCRCAndParse ()) + LogD(2, prep("cEIT2::cEIT2")); + + if (!CheckCRCAndParse ()) { + LogD(2, prep("!CheckCRCAndParse ()")); return; + } tChannelID channelID (Source, getOriginalNetworkId (), getTransportStreamId (), getServiceId ()); + LogD(2, prep("channelID: %s"), channelID.ToString()); cChannel *channel = Channels.GetByChannelID (channelID, true); - if (!channel) + if (!channel) { + LogD(2, prep("!channel")); return; // only collect data for known channels + } #ifdef USE_NOEPG // only use epg from channels not blocked by noEPG-patch @@ -2997,15 +3034,22 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat SI::Descriptor * d; SI::ExtendedEventDescriptors * ExtendedEventDescriptors = NULL; SI::ShortEventDescriptor * ShortEventDescriptor = NULL; + SI::DishDescriptor *DishExtendedEventDescriptor = NULL; + SI::DishDescriptor *DishShortEventDescriptor = NULL; + + 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 ()); + switch (d->getDescriptorTag ()) { - case SI::DishExtendedEventDescriptorTag: case SI::ExtendedEventDescriptorTag: { SI::ExtendedEventDescriptor * eed = (SI::ExtendedEventDescriptor *) d; if (I18nIsPreferredLanguage (Setup.EPGLanguages, eed->languageCode, LanguagePreferenceExt) @@ -3022,7 +3066,6 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat UseExtendedEventDescriptor = false; } break; - case SI::DishShortEventDescriptorTag: case SI::ShortEventDescriptorTag: { SI::ShortEventDescriptor * sed = (SI::ShortEventDescriptor *) d; if (I18nIsPreferredLanguage (Setup.EPGLanguages, sed->languageCode, LanguagePreferenceShort) @@ -3165,6 +3208,26 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat } } break; + case SI::DishExtendedEventDescriptorTag: { + SI::DishDescriptor *deed = (SI::DishDescriptor *)d; + deed->Decompress(Tid); + if (!DishExtendedEventDescriptor) { + DishExtendedEventDescriptor = deed; + d = NULL; // so that it is not deleted + } + HasExternalData = true; + } + break; + case SI::DishShortEventDescriptorTag: { + SI::DishDescriptor *dsed = (SI::DishDescriptor *)d; + dsed->Decompress(Tid); + if (!DishShortEventDescriptor) { + DishShortEventDescriptor = dsed; + d = NULL; // so that it is not deleted + } + HasExternalData = true; + } + break; case SI::DishRatingDescriptorTag: { if (d->getLength() == 4) { uint16_t rating = d->getData().TwoBytes(2); @@ -3282,16 +3345,264 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat pSchedule->ClrRunningStatus (channel); if (Tid == 0x4E) pSchedule->SetPresentSeen (); - if (OnlyRunningStatus) + if (OnlyRunningStatus) { + LogD(2, prep("OnlyRunningStatus")); return; + } if (Modified) { pSchedule->Sort (); if (!HasExternalData) pSchedule->DropOutdated (SegmentStart, SegmentEnd, Tid, getVersionNumber ()); Schedules->SetModified (pSchedule); } + LogD(2, prep("end of cEIT2")); + } //end of cEIT2 + +/*************************************************************************** + * * + * These routines decompress Huffman coded Dish Network EIT data. * + * The implementation is based on the algorithm presentend in * + * * + * "A memory-efficient Huffman decoding algorithm" * + * Pi-Chung Wang, Yuan-Rung Yang, Chun-Liang Lee, Hung-Yi Chang * + * Proceedings of the 19th International Conference on Advanced * + * Information Networking and Applications (AINA'05) * + * * + ***************************************************************************/ + +// returns the value of a sequence of bits in the byte array +static unsigned int getBits(int bitIndex, int bitCount, const unsigned char *byteptr, int length) +{ + union { + unsigned char b[4]; + unsigned long val; + } chunk; + + int offset = bitIndex >> 3; + int bitnum = bitIndex - (offset << 3); + int rightend = 32 - bitnum - bitCount; + + chunk.b[3] = byteptr[offset]; + chunk.b[2] = (offset+1 < length) ? byteptr[offset+1] : 0; + chunk.b[1] = (offset+2 < length) ? byteptr[offset+2] : 0; + chunk.b[0] = 0; // Never need to look this far ahead. + + return (unsigned int)((chunk.val & (0xFFFFFFFF >> bitnum)) >> rightend); +} + +DishDescriptor::DishDescriptor(void) +{ + text = NULL; + shortText = NULL; + decompressed = NULL; +} + +DishDescriptor::~DishDescriptor() +{ + free(decompressed); +} + +void DishDescriptor::Decompress(unsigned char Tid) +{ + const unsigned char *str = data.getData(); + const unsigned char *cmp = NULL; // Compressed data + int length = 0; // Length of compressed data + unsigned int dLength = 0; // Length of decompressed data + + if ((str[3] & 0xFC) == 0x80) { + length = str[1] - 2; + dLength = (str[2]&0x40) ? ((str[3] << 6)&0xFF) | (str[2]&0x3F) : str[2]&0x3F; + cmp = str + 4; + } + else { + length = str[1] - 1; + dLength = str[2] & 0x7F; + cmp = str + 3; + } + + if (length <= 0 || !dLength) + return; + + decompressed = (unsigned char*)malloc(dLength+1); + + HuffmanTable *table; + unsigned int tableSize, numBits; + + if (Tid > 0x80) { + table = Table255; + tableSize = SIZE_TABLE_255; + numBits = 13; + } + else { + table = Table128; + tableSize = SIZE_TABLE_128; + numBits = 11; + } + + unsigned int bLength = length << 3; // number of bits + unsigned int currentBit = 0, count = 0; + while (currentBit < bLength - 1 && count < dLength) { + // Find the interval containing the sequence of length numBits starting + // at currentBit. The corresponding character will be the one encoded + // at the begin of the sequence. + unsigned int code = getBits(currentBit, numBits, cmp, length); + // We could use a binary search, but in practice this linear search is faster. + unsigned int index = 0; + while (table[index].startingAddress <= code && index < tableSize) { index++; } + index--; + decompressed[count++] = table[index].character; + currentBit += table[index].numberOfBits; + } + decompressed[count] = 0; + + char* split = strchr((char*)decompressed, 0x0D); // Look for carriage return + if (split) { + *split = 0; + shortText = (char*) decompressed; + text = (split[1]==0x20) ? split+2 : split+1; + } + else { + text = (char*) decompressed; + } +} + + +struct DishDescriptor::HuffmanTable DishDescriptor::Table128[SIZE_TABLE_128] = { + { 0x0000, 0x20, 0x03 }, { 0x0100, 0x65, 0x04 }, { 0x0180, 0x74, 0x04 }, + { 0x0200, 0x61, 0x04 }, { 0x0280, 0x6F, 0x04 }, { 0x0300, 0x73, 0x04 }, + { 0x0380, 0x6E, 0x04 }, { 0x0400, 0x72, 0x06 }, { 0x0420, 0x69, 0x06 }, + { 0x0440, 0x6C, 0x06 }, { 0x0460, 0x63, 0x06 }, { 0x0480, 0x68, 0x06 }, + { 0x04A0, 0x75, 0x06 }, { 0x04C0, 0x64, 0x06 }, { 0x04E0, 0x70, 0x06 }, + { 0x0500, 0x6D, 0x06 }, { 0x0520, 0x67, 0x06 }, { 0x0540, 0x79, 0x06 }, + { 0x0560, 0x76, 0x06 }, { 0x0580, 0x0A, 0x06 }, { 0x05A0, 0x2E, 0x06 }, + { 0x05C0, 0x77, 0x06 }, { 0x05E0, 0x66, 0x06 }, { 0x0600, 0x53, 0x07 }, + { 0x0610, 0x62, 0x07 }, { 0x0620, 0x54, 0x07 }, { 0x0630, 0x22, 0x07 }, + { 0x0640, 0x6B, 0x07 }, { 0x0650, 0x50, 0x07 }, { 0x0660, 0x41, 0x07 }, + { 0x0670, 0x43, 0x07 }, { 0x0680, 0x44, 0x07 }, { 0x0690, 0x4C, 0x07 }, + { 0x06A0, 0x4D, 0x07 }, { 0x06B0, 0x49, 0x07 }, { 0x06C0, 0x4E, 0x07 }, + { 0x06D0, 0x3A, 0x07 }, { 0x06E0, 0x52, 0x07 }, { 0x06F0, 0x2C, 0x07 }, + { 0x0700, 0x45, 0x08 }, { 0x0708, 0x55, 0x08 }, { 0x0710, 0x46, 0x08 }, + { 0x0718, 0x48, 0x08 }, { 0x0720, 0x59, 0x08 }, { 0x0728, 0x56, 0x08 }, + { 0x0730, 0x2D, 0x08 }, { 0x0738, 0x7A, 0x08 }, { 0x0740, 0x78, 0x08 }, + { 0x0748, 0x2F, 0x08 }, { 0x0750, 0x4F, 0x08 }, { 0x0758, 0x3F, 0x08 }, + { 0x0760, 0x57, 0x08 }, { 0x0768, 0x47, 0x08 }, { 0x0770, 0x42, 0x08 }, + { 0x0778, 0x33, 0x08 }, { 0x0780, 0x31, 0x09 }, { 0x0784, 0x71, 0x09 }, + { 0x0788, 0x30, 0x09 }, { 0x078C, 0x21, 0x09 }, { 0x0790, 0x6A, 0x09 }, + { 0x0794, 0x5A, 0x09 }, { 0x0798, 0x39, 0x09 }, { 0x079C, 0x34, 0x09 }, + { 0x07A0, 0x4B, 0x09 }, { 0x07A4, 0x2A, 0x09 }, { 0x07A8, 0x37, 0x09 }, + { 0x07AC, 0x36, 0x09 }, { 0x07B0, 0x35, 0x09 }, { 0x07B4, 0x4A, 0x09 }, + { 0x07B8, 0x38, 0x09 }, { 0x07BC, 0x29, 0x09 }, { 0x07C0, 0x28, 0x0A }, + { 0x07C2, 0x58, 0x0A }, { 0x07C4, 0x51, 0x0A }, { 0x07C6, 0x3C, 0x0A }, + { 0x07C8, 0x32, 0x0A }, { 0x07CA, 0x27, 0x0A }, { 0x07CC, 0x26, 0x0A }, + { 0x07CE, 0x7F, 0x0B }, { 0x07CF, 0x7E, 0x0B }, { 0x07D0, 0x7D, 0x0B }, + { 0x07D1, 0x7C, 0x0B }, { 0x07D2, 0x7B, 0x0B }, { 0x07D3, 0x60, 0x0B }, + { 0x07D4, 0x5F, 0x0B }, { 0x07D5, 0x5E, 0x0B }, { 0x07D6, 0x5D, 0x0B }, + { 0x07D7, 0x5C, 0x0B }, { 0x07D8, 0x5B, 0x0B }, { 0x07D9, 0x40, 0x0B }, + { 0x07DA, 0x3E, 0x0B }, { 0x07DB, 0x3D, 0x0B }, { 0x07DC, 0x3B, 0x0B }, + { 0x07DD, 0x2B, 0x0B }, { 0x07DE, 0x25, 0x0B }, { 0x07DF, 0x24, 0x0B }, + { 0x07E0, 0x23, 0x0B }, { 0x07E1, 0x1F, 0x0B }, { 0x07E2, 0x1E, 0x0B }, + { 0x07E3, 0x1D, 0x0B }, { 0x07E4, 0x1C, 0x0B }, { 0x07E5, 0x1B, 0x0B }, + { 0x07E6, 0x1A, 0x0B }, { 0x07E7, 0x19, 0x0B }, { 0x07E8, 0x18, 0x0B }, + { 0x07E9, 0x17, 0x0B }, { 0x07EA, 0x16, 0x0B }, { 0x07EB, 0x15, 0x0B }, + { 0x07EC, 0x14, 0x0B }, { 0x07ED, 0x13, 0x0B }, { 0x07EE, 0x12, 0x0B }, + { 0x07EF, 0x11, 0x0B }, { 0x07F0, 0x10, 0x0B }, { 0x07F1, 0x0F, 0x0B }, + { 0x07F2, 0x0E, 0x0B }, { 0x07F3, 0x0D, 0x0B }, { 0x07F4, 0x0C, 0x0B }, + { 0x07F5, 0x0B, 0x0B }, { 0x07F6, 0x09, 0x0B }, { 0x07F7, 0x08, 0x0B }, + { 0x07F8, 0x07, 0x0B }, { 0x07F9, 0x06, 0x0B }, { 0x07FA, 0x05, 0x0B }, + { 0x07FB, 0x04, 0x0B }, { 0x07FC, 0x03, 0x0B }, { 0x07FD, 0x02, 0x0B }, + { 0x07FE, 0x01, 0x0B }, { 0x07FF, 0x00, 0x0B } +}; + +struct DishDescriptor::HuffmanTable DishDescriptor::Table255[SIZE_TABLE_255] = { + { 0x0000, 0x20, 0x02 }, { 0x0800, 0x65, 0x04 }, { 0x0A00, 0x72, 0x04 }, + { 0x0C00, 0x6E, 0x04 }, { 0x0E00, 0x61, 0x04 }, { 0x1000, 0x74, 0x05 }, + { 0x1100, 0x6F, 0x05 }, { 0x1200, 0x73, 0x05 }, { 0x1300, 0x69, 0x05 }, + { 0x1400, 0x6C, 0x05 }, { 0x1500, 0x75, 0x05 }, { 0x1600, 0x63, 0x05 }, + { 0x1700, 0x64, 0x05 }, { 0x1800, 0x70, 0x07 }, { 0x1840, 0x6D, 0x07 }, + { 0x1880, 0x76, 0x07 }, { 0x18C0, 0x67, 0x07 }, { 0x1900, 0x68, 0x07 }, + { 0x1940, 0x2E, 0x07 }, { 0x1980, 0x66, 0x07 }, { 0x19C0, 0x0A, 0x07 }, + { 0x1A00, 0x53, 0x07 }, { 0x1A40, 0x41, 0x07 }, { 0x1A80, 0x45, 0x07 }, + { 0x1AC0, 0x43, 0x07 }, { 0x1B00, 0x27, 0x07 }, { 0x1B40, 0x7A, 0x07 }, + { 0x1B80, 0x52, 0x07 }, { 0x1BC0, 0x22, 0x07 }, { 0x1C00, 0x4C, 0x08 }, + { 0x1C20, 0x49, 0x08 }, { 0x1C40, 0x4F, 0x08 }, { 0x1C60, 0x62, 0x08 }, + { 0x1C80, 0x54, 0x08 }, { 0x1CA0, 0x4E, 0x08 }, { 0x1CC0, 0x55, 0x08 }, + { 0x1CE0, 0x79, 0x08 }, { 0x1D00, 0x44, 0x08 }, { 0x1D20, 0x50, 0x08 }, + { 0x1D40, 0x71, 0x08 }, { 0x1D60, 0x56, 0x08 }, { 0x1D80, 0x2D, 0x08 }, + { 0x1DA0, 0x3A, 0x08 }, { 0x1DC0, 0x2C, 0x08 }, { 0x1DE0, 0x48, 0x08 }, + { 0x1E00, 0x4D, 0x09 }, { 0x1E10, 0x78, 0x09 }, { 0x1E20, 0x77, 0x09 }, + { 0x1E30, 0x42, 0x09 }, { 0x1E40, 0x47, 0x09 }, { 0x1E50, 0x46, 0x09 }, + { 0x1E60, 0x30, 0x09 }, { 0x1E70, 0x3F, 0x09 }, { 0x1E80, 0x33, 0x09 }, + { 0x1E90, 0x2F, 0x09 }, { 0x1EA0, 0x39, 0x09 }, { 0x1EB0, 0x31, 0x09 }, + { 0x1EC0, 0x38, 0x09 }, { 0x1ED0, 0x6B, 0x09 }, { 0x1EE0, 0x6A, 0x09 }, + { 0x1EF0, 0x21, 0x09 }, { 0x1F00, 0x36, 0x0A }, { 0x1F08, 0x35, 0x0A }, + { 0x1F10, 0x59, 0x0A }, { 0x1F18, 0x51, 0x0A }, { 0x1F20, 0x34, 0x0B }, + { 0x1F24, 0x58, 0x0B }, { 0x1F28, 0x32, 0x0B }, { 0x1F2C, 0x2B, 0x0B }, + { 0x1F30, 0x2A, 0x0B }, { 0x1F34, 0x5A, 0x0B }, { 0x1F38, 0x4A, 0x0B }, + { 0x1F3C, 0x29, 0x0B }, { 0x1F40, 0x28, 0x0C }, { 0x1F42, 0x23, 0x0C }, + { 0x1F44, 0x57, 0x0C }, { 0x1F46, 0x4B, 0x0C }, { 0x1F48, 0x3C, 0x0C }, + { 0x1F4A, 0x37, 0x0C }, { 0x1F4C, 0x7D, 0x0C }, { 0x1F4E, 0x7B, 0x0C }, + { 0x1F50, 0x60, 0x0C }, { 0x1F52, 0x26, 0x0C }, { 0x1F54, 0xFE, 0x0D }, + { 0x1F55, 0xFD, 0x0D }, { 0x1F56, 0xFC, 0x0D }, { 0x1F57, 0xFB, 0x0D }, + { 0x1F58, 0xFA, 0x0D }, { 0x1F59, 0xF9, 0x0D }, { 0x1F5A, 0xF8, 0x0D }, + { 0x1F5B, 0xF7, 0x0D }, { 0x1F5C, 0xF6, 0x0D }, { 0x1F5D, 0xF5, 0x0D }, + { 0x1F5E, 0xF4, 0x0D }, { 0x1F5F, 0xF3, 0x0D }, { 0x1F60, 0xF2, 0x0D }, + { 0x1F61, 0xF1, 0x0D }, { 0x1F62, 0xF0, 0x0D }, { 0x1F63, 0xEF, 0x0D }, + { 0x1F64, 0xEE, 0x0D }, { 0x1F65, 0xED, 0x0D }, { 0x1F66, 0xEC, 0x0D }, + { 0x1F67, 0xEB, 0x0D }, { 0x1F68, 0xEA, 0x0D }, { 0x1F69, 0xE9, 0x0D }, + { 0x1F6A, 0xE8, 0x0D }, { 0x1F6B, 0xE7, 0x0D }, { 0x1F6C, 0xE6, 0x0D }, + { 0x1F6D, 0xE5, 0x0D }, { 0x1F6E, 0xE4, 0x0D }, { 0x1F6F, 0xE3, 0x0D }, + { 0x1F70, 0xE2, 0x0D }, { 0x1F71, 0xE1, 0x0D }, { 0x1F72, 0xE0, 0x0D }, + { 0x1F73, 0xDF, 0x0D }, { 0x1F74, 0xDE, 0x0D }, { 0x1F75, 0xDD, 0x0D }, + { 0x1F76, 0xDC, 0x0D }, { 0x1F77, 0xDB, 0x0D }, { 0x1F78, 0xDA, 0x0D }, + { 0x1F79, 0xD9, 0x0D }, { 0x1F7A, 0xD8, 0x0D }, { 0x1F7B, 0xD7, 0x0D }, + { 0x1F7C, 0xD6, 0x0D }, { 0x1F7D, 0xD5, 0x0D }, { 0x1F7E, 0xD4, 0x0D }, + { 0x1F7F, 0xD3, 0x0D }, { 0x1F80, 0xD2, 0x0D }, { 0x1F81, 0xD1, 0x0D }, + { 0x1F82, 0xD0, 0x0D }, { 0x1F83, 0xCF, 0x0D }, { 0x1F84, 0xCE, 0x0D }, + { 0x1F85, 0xCD, 0x0D }, { 0x1F86, 0xCC, 0x0D }, { 0x1F87, 0xCB, 0x0D }, + { 0x1F88, 0xCA, 0x0D }, { 0x1F89, 0xC9, 0x0D }, { 0x1F8A, 0xC8, 0x0D }, + { 0x1F8B, 0xC7, 0x0D }, { 0x1F8C, 0xC6, 0x0D }, { 0x1F8D, 0xC5, 0x0D }, + { 0x1F8E, 0xC4, 0x0D }, { 0x1F8F, 0xC3, 0x0D }, { 0x1F90, 0xC2, 0x0D }, + { 0x1F91, 0xC1, 0x0D }, { 0x1F92, 0xC0, 0x0D }, { 0x1F93, 0xBF, 0x0D }, + { 0x1F94, 0xBE, 0x0D }, { 0x1F95, 0xBD, 0x0D }, { 0x1F96, 0xBC, 0x0D }, + { 0x1F97, 0xBB, 0x0D }, { 0x1F98, 0xBA, 0x0D }, { 0x1F99, 0xB9, 0x0D }, + { 0x1F9A, 0xB8, 0x0D }, { 0x1F9B, 0xB7, 0x0D }, { 0x1F9C, 0xB6, 0x0D }, + { 0x1F9D, 0xB5, 0x0D }, { 0x1F9E, 0xB4, 0x0D }, { 0x1F9F, 0xB3, 0x0D }, + { 0x1FA0, 0xB2, 0x0D }, { 0x1FA1, 0xB1, 0x0D }, { 0x1FA2, 0xB0, 0x0D }, + { 0x1FA3, 0xAF, 0x0D }, { 0x1FA4, 0xAE, 0x0D }, { 0x1FA5, 0xAD, 0x0D }, + { 0x1FA6, 0xAC, 0x0D }, { 0x1FA7, 0xAB, 0x0D }, { 0x1FA8, 0xAA, 0x0D }, + { 0x1FA9, 0xA9, 0x0D }, { 0x1FAA, 0xA8, 0x0D }, { 0x1FAB, 0xA7, 0x0D }, + { 0x1FAC, 0xA6, 0x0D }, { 0x1FAD, 0xA5, 0x0D }, { 0x1FAE, 0xA4, 0x0D }, + { 0x1FAF, 0xA3, 0x0D }, { 0x1FB0, 0xA2, 0x0D }, { 0x1FB1, 0xA1, 0x0D }, + { 0x1FB2, 0xA0, 0x0D }, { 0x1FB3, 0x9F, 0x0D }, { 0x1FB4, 0x9E, 0x0D }, + { 0x1FB5, 0x9D, 0x0D }, { 0x1FB6, 0x9C, 0x0D }, { 0x1FB7, 0x9B, 0x0D }, + { 0x1FB8, 0x9A, 0x0D }, { 0x1FB9, 0x99, 0x0D }, { 0x1FBA, 0x98, 0x0D }, + { 0x1FBB, 0x97, 0x0D }, { 0x1FBC, 0x96, 0x0D }, { 0x1FBD, 0x95, 0x0D }, + { 0x1FBE, 0x94, 0x0D }, { 0x1FBF, 0x93, 0x0D }, { 0x1FC0, 0x92, 0x0D }, + { 0x1FC1, 0x91, 0x0D }, { 0x1FC2, 0x90, 0x0D }, { 0x1FC3, 0x8F, 0x0D }, + { 0x1FC4, 0x8E, 0x0D }, { 0x1FC5, 0x8D, 0x0D }, { 0x1FC6, 0x8C, 0x0D }, + { 0x1FC7, 0x8B, 0x0D }, { 0x1FC8, 0x8A, 0x0D }, { 0x1FC9, 0x89, 0x0D }, + { 0x1FCA, 0x88, 0x0D }, { 0x1FCB, 0x87, 0x0D }, { 0x1FCC, 0x86, 0x0D }, + { 0x1FCD, 0x85, 0x0D }, { 0x1FCE, 0x84, 0x0D }, { 0x1FCF, 0x83, 0x0D }, + { 0x1FD0, 0x82, 0x0D }, { 0x1FD1, 0x81, 0x0D }, { 0x1FD2, 0x80, 0x0D }, + { 0x1FD3, 0x7F, 0x0D }, { 0x1FD4, 0x7E, 0x0D }, { 0x1FD5, 0x7C, 0x0D }, + { 0x1FD6, 0x5F, 0x0D }, { 0x1FD7, 0x5E, 0x0D }, { 0x1FD8, 0x5D, 0x0D }, + { 0x1FD9, 0x5C, 0x0D }, { 0x1FDA, 0x5B, 0x0D }, { 0x1FDB, 0x40, 0x0D }, + { 0x1FDC, 0x3E, 0x0D }, { 0x1FDD, 0x3D, 0x0D }, { 0x1FDE, 0x3B, 0x0D }, + { 0x1FDF, 0x25, 0x0D }, { 0x1FE0, 0x24, 0x0D }, { 0x1FE1, 0x1F, 0x0D }, + { 0x1FE2, 0x1E, 0x0D }, { 0x1FE3, 0x1D, 0x0D }, { 0x1FE4, 0x1C, 0x0D }, + { 0x1FE5, 0x1B, 0x0D }, { 0x1FE6, 0x1A, 0x0D }, { 0x1FE7, 0x19, 0x0D }, + { 0x1FE8, 0x18, 0x0D }, { 0x1FE9, 0x17, 0x0D }, { 0x1FEA, 0x16, 0x0D }, + { 0x1FEB, 0x15, 0x0D }, { 0x1FEC, 0x14, 0x0D }, { 0x1FED, 0x13, 0x0D }, + { 0x1FEE, 0x12, 0x0D }, { 0x1FEF, 0x11, 0x0D }, { 0x1FF0, 0x10, 0x0D }, + { 0x1FF1, 0x0F, 0x0D }, { 0x1FF2, 0x0E, 0x0D }, { 0x1FF3, 0x0D, 0x0D }, + { 0x1FF4, 0x0C, 0x0D }, { 0x1FF5, 0x0B, 0x0D }, { 0x1FF6, 0x09, 0x0D }, + { 0x1FF7, 0x08, 0x0D }, { 0x1FF8, 0x07, 0x0D }, { 0x1FF9, 0x06, 0x0D }, + { 0x1FFA, 0x05, 0x0D }, { 0x1FFB, 0x04, 0x0D }, { 0x1FFC, 0x03, 0x0D }, + { 0x1FFD, 0x02, 0x0D }, { 0x1FFE, 0x01, 0x0D }, { 0x1FFF, 0x00, 0x0D } +}; + } //end namespace SI void cFilterEEPG::ProcessNextFormat (bool FirstTime = false) @@ -3397,6 +3708,7 @@ void cFilterEEPG::ProcessNextFormat (bool FirstTime = false) case DISH_BEV: AddFilter (0x0300, 0x50, 0xf0); // Dish Network EEPG AddFilter (0x0300, 0x60, 0xf0); // Dish Network EEPG + AddFilter (0x0300, 0x90, 0xf0); // Dish Network EEPG AddFilter (0x0441, 0x50, 0xf0); // Bell ExpressVU EEPG AddFilter (0x0441, 0x60, 0xf0); // Bell ExpressVU EEPG break; @@ -3405,6 +3717,30 @@ void cFilterEEPG::ProcessNextFormat (bool FirstTime = false) } } +void cFilterEEPG::ProccessContinuous(u_short Pid, u_char Tid, int Length, const u_char *Data) +{ + //0x39 Viasat, 0x0300 Dish Network EEPG, 0x0441 Bell ExpressVU EEPG + LogD(2, prep("Pid: 0x%02x Tid: %d Length: %d"), Pid, Tid, Length); + cSchedulesLock SchedulesLock(true, 10); + cSchedules *Schedules = (cSchedules*)(cSchedules::Schedules(SchedulesLock)); + if(Schedules) + SI::cEIT2 EIT(Schedules, Source(), Tid, Data); + + else//cEIT EIT (Schedules, Source (), Tid, Data); + { + // If we don't get a write lock, let's at least get a read lock, so + // that we can set the running status and 'seen' timestamp (well, actually + // with a read lock we shouldn't be doing that, but it's only integers that + // get changed, so it should be ok) + cSchedulesLock SchedulesLock; + cSchedules *Schedules = (cSchedules*)(cSchedules::Schedules(SchedulesLock)); + if(Schedules) + SI::cEIT2 EIT(Schedules, Source(), Tid, Data, true); + + //cEIT EIT (Schedules, Source (), Tid, Data, true); + } +} + void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Length) { int now = time (0); @@ -3559,6 +3895,11 @@ void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Len } //checkCRC } //if pmtpid else if (Source ()) { + + if ( Pid == 0x0300 || Pid == 0x0441 ) { + ProccessContinuous(Pid, Tid, Length, Data); + return; + } int Result; switch (Tid) { case 0xA0: //TODO DPE test this missing break but it seems a bug @@ -4042,23 +4383,8 @@ void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Len // PID found: 3843 (0x0f03) [SECTION: ATSC reserved] TODO find out what compressed text info is here! // PID found: 3844 (0x0f04) [SECTION: Time Offset Table (TOT)] - if (Pid == 3842 || Pid == 0x39 || Pid == 0x0300 || Pid == 0x0441 ) {//0x39 Viasat, 0x0300 Dish Network EEPG, 0x0441 Bell ExpressVU EEPG - cSchedulesLock SchedulesLock (true, 10); - cSchedules *Schedules = (cSchedules *) cSchedules::Schedules (SchedulesLock); - if (Schedules) - SI::cEIT2 EIT (Schedules, Source (), Tid, Data); - //cEIT EIT (Schedules, Source (), Tid, Data); - else { - // If we don't get a write lock, let's at least get a read lock, so - // that we can set the running status and 'seen' timestamp (well, actually - // with a read lock we shouldn't be doing that, but it's only integers that - // get changed, so it should be ok) - cSchedulesLock SchedulesLock; - cSchedules *Schedules = (cSchedules *) cSchedules::Schedules (SchedulesLock); - if (Schedules) - SI::cEIT2 EIT (Schedules, Source (), Tid, Data, true); - //cEIT EIT (Schedules, Source (), Tid, Data, true); - } + if (Pid == 3842 || Pid == 0x39) {//0x39 Viasat, 0x0300 Dish Network EEPG, 0x0441 Bell ExpressVU EEPG + ProccessContinuous(Pid, Tid, Length, Data); } break; -- cgit v1.2.3 From d50399f90ce029e449f1a2627e5b12cb6a52f620 Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Sun, 4 Sep 2011 21:15:06 +0200 Subject: implemented dish title/description changed malloc to new --- eepg.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 9dea796..597b2e1 100644 --- a/eepg.c +++ b/eepg.c @@ -2869,7 +2869,7 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat } tChannelID channelID (Source, getOriginalNetworkId (), getTransportStreamId (), getServiceId ()); - LogD(2, prep("channelID: %s"), channelID.ToString()); + LogD(2, prep("channelID: %s"), *channelID.ToString()); cChannel *channel = Channels.GetByChannelID (channelID, true); if (!channel) { LogD(2, prep("!channel")); @@ -3246,6 +3246,17 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat } if (!rEvent) { + if (DishShortEventDescriptor) { + pEvent->SetTitle(DishShortEventDescriptor->getText()); + LogD(2, prep("DishTitle: %s"), DishShortEventDescriptor->getText()); + + } + if (DishExtendedEventDescriptor) { + pEvent->SetDescription(DishExtendedEventDescriptor->getText()); + pEvent->SetShortText(DishExtendedEventDescriptor->getShortText()); + LogD(2, prep("DishDescription: %s"), DishExtendedEventDescriptor->getText()); + LogD(2, prep("DishShortText: %s"), DishExtendedEventDescriptor->getShortText()); + } if (ShortEventDescriptor) { char buffer[Utf8BufSize (256)]; unsigned char *f; @@ -3271,6 +3282,8 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat } delete ExtendedEventDescriptors; delete ShortEventDescriptor; + delete DishExtendedEventDescriptor; + delete DishShortEventDescriptor; pEvent->SetComponents (Components); @@ -3401,7 +3414,8 @@ DishDescriptor::DishDescriptor(void) DishDescriptor::~DishDescriptor() { - free(decompressed); + delete[] decompressed; + decompressed = NULL; } void DishDescriptor::Decompress(unsigned char Tid) @@ -3425,7 +3439,7 @@ void DishDescriptor::Decompress(unsigned char Tid) if (length <= 0 || !dLength) return; - decompressed = (unsigned char*)malloc(dLength+1); + decompressed = new unsigned char[2*dLength+1]; HuffmanTable *table; unsigned int tableSize, numBits; -- cgit v1.2.3 From 8e5ae27b1ba5d6c1f572c9c57419d8c2467ef377 Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Tue, 6 Sep 2011 00:30:37 +0200 Subject: modified dish descriptor class --- eepg.c | 56 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 26 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 597b2e1..8763aef 100644 --- a/eepg.c +++ b/eepg.c @@ -2814,7 +2814,7 @@ namespace SI DishShortEventDescriptorTag = 0x91, DishExtendedEventDescriptorTag = 0x92 }; - typedef InheritEnum< DescriptorTagExt, SI::DescriptorTag > ExtendedDescriptorTag; + // typedef InheritEnum< DescriptorTagExt, SI::DescriptorTag > ExtendedDescriptorTag; #ifndef LIBSI_DISH_H #define LIBSI_DISH_H @@ -2822,9 +2822,9 @@ namespace SI #define SIZE_TABLE_128 128 #define SIZE_TABLE_255 255 -class DishDescriptor : public Descriptor { +class DishDescriptor : public UnimplementedDescriptor { public: - DishDescriptor(void); + DishDescriptor(UnimplementedDescriptor*); virtual ~DishDescriptor(); const char* getText(void) const { return text; } const char* getShortText(void) const { return shortText; } @@ -2836,6 +2836,7 @@ protected: const char* text; // name or description of the event const char* shortText; // usually the episode name unsigned char* decompressed; + UnimplementedDescriptor* unimplementedDesc; struct HuffmanTable { unsigned int startingAddress; @@ -2861,7 +2862,7 @@ public: cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Data, bool OnlyRunningStatus) : SI::EIT (Data, false) { - LogD(2, prep("cEIT2::cEIT2")); + //LogD(2, prep("cEIT2::cEIT2")); if (!CheckCRCAndParse ()) { LogD(2, prep("!CheckCRCAndParse ()")); @@ -3047,7 +3048,7 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat continue; } - LogD(2, prep("EEPGDEBUG:d->getDescriptorTAG():%x)"), d->getDescriptorTag ()); + //LogD(2, prep("EEPGDEBUG:d->getDescriptorTAG():%x)"), d->getDescriptorTag ()); switch (d->getDescriptorTag ()) { case SI::ExtendedEventDescriptorTag: { @@ -3209,25 +3210,25 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat } break; case SI::DishExtendedEventDescriptorTag: { - SI::DishDescriptor *deed = (SI::DishDescriptor *)d; - deed->Decompress(Tid); - if (!DishExtendedEventDescriptor) { - DishExtendedEventDescriptor = deed; - d = NULL; // so that it is not deleted - } - HasExternalData = true; - } - break; + SI::DishDescriptor *deed = new SI::DishDescriptor((SI::UnimplementedDescriptor *)d); + deed->Decompress(Tid); + if (!DishExtendedEventDescriptor) { + DishExtendedEventDescriptor = deed; + d = NULL; // so that it is not deleted + } + HasExternalData = true; + } + break; case SI::DishShortEventDescriptorTag: { - SI::DishDescriptor *dsed = (SI::DishDescriptor *)d; - dsed->Decompress(Tid); - if (!DishShortEventDescriptor) { - DishShortEventDescriptor = dsed; - d = NULL; // so that it is not deleted - } - HasExternalData = true; - } - break; + SI::DishDescriptor *dsed = new SI::DishDescriptor((SI::UnimplementedDescriptor *)d); + dsed->Decompress(Tid); + if (!DishShortEventDescriptor) { + DishShortEventDescriptor = dsed; + d = NULL; // so that it is not deleted + } + HasExternalData = true; + } + break; case SI::DishRatingDescriptorTag: { if (d->getLength() == 4) { uint16_t rating = d->getData().TwoBytes(2); @@ -3405,17 +3406,19 @@ static unsigned int getBits(int bitIndex, int bitCount, const unsigned char *byt return (unsigned int)((chunk.val & (0xFFFFFFFF >> bitnum)) >> rightend); } -DishDescriptor::DishDescriptor(void) +DishDescriptor::DishDescriptor(UnimplementedDescriptor* unimplementedDesc) { text = NULL; shortText = NULL; decompressed = NULL; + this->unimplementedDesc = unimplementedDesc; } DishDescriptor::~DishDescriptor() { delete[] decompressed; decompressed = NULL; + delete unimplementedDesc; } void DishDescriptor::Decompress(unsigned char Tid) @@ -3439,7 +3442,7 @@ void DishDescriptor::Decompress(unsigned char Tid) if (length <= 0 || !dLength) return; - decompressed = new unsigned char[2*dLength+1]; + decompressed = new unsigned char[dLength+1]; HuffmanTable *table; unsigned int tableSize, numBits; @@ -3472,6 +3475,7 @@ void DishDescriptor::Decompress(unsigned char Tid) decompressed[count] = 0; char* split = strchr((char*)decompressed, 0x0D); // Look for carriage return + LogD(2, prep("dLength:%d, length:%d, count:%d, decompressed: %s"), dLength, length, count, decompressed); if (split) { *split = 0; shortText = (char*) decompressed; @@ -3758,7 +3762,7 @@ void cFilterEEPG::ProccessContinuous(u_short Pid, u_char Tid, int Length, const void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Length) { int now = time (0); - LogD(2, prep("Pid: 0x%02x Tid: %d Length: %d PMT pid: 0x%04x"), Pid, Tid, Length, pmtpid); +// LogD(2, prep("Pid: 0x%02x Tid: %d Length: %d PMT pid: 0x%04x"), Pid, Tid, Length, pmtpid); // LogD(2, prep("Source: %d Transponder: %d"), Source () , Transponder ()); if (Pid == 0 && Tid == SI::TableIdPAT) { -- cgit v1.2.3 From d424ff6e6edc7905e0836649364daf0e9a823030 Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Tue, 6 Sep 2011 10:16:00 +0200 Subject: re-factored duplicate code search dish channels on other positions --- eepg.c | 99 +++++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 49 insertions(+), 50 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 8763aef..91f9842 100644 --- a/eepg.c +++ b/eepg.c @@ -711,6 +711,24 @@ void CleanString (unsigned char *String) // LogD (1, prep("Clean: %s"), String); } +cChannel *GetChannelByID(tChannelID & channelID, bool searchOtherPos) +{ + cChannel *VC = Channels.GetByChannelID(channelID, true); + if(!VC && searchOtherPos){ + //look on other satpositions + for(int i = 0;i < NumberOfAvailableSources;i++){ + channelID = tChannelID(AvailableSources[i], channelID.Nid(), channelID.Tid(), channelID.Sid()); + VC = Channels.GetByChannelID(channelID, true); + if(VC){ + //found this actually on satellite nextdoor... + break; + } + } + } + + return VC; +} + bool cFilterEEPG::GetThemesSKYBOX (void) //TODO can't we read this from the DVB stream? { string FileName = ConfDir; @@ -1160,18 +1178,10 @@ int cFilterEEPG::GetChannelsMHW (const u_char * Data, int Length, int MHW) C->Tid[0] = HILO16 (Channel->TransportId); C->Sid[0] = HILO16 (Channel->ServiceId); tChannelID channelID = tChannelID (C->Src[0], C->Nid[0], C->Tid[0], C->Sid[0]); - cChannel *VC = Channels.GetByChannelID (channelID, true); + cChannel *VC = GetChannelByID(channelID, true); bool IsFound = (VC); - if (!IsFound) { //look on other satpositions - for (int i = 0; i < NumberOfAvailableSources; i++) { - channelID = tChannelID (AvailableSources[i], C->Nid[0], C->Tid[0], C->Sid[0]); - VC = Channels.GetByChannelID (channelID, true); - IsFound = (VC); - if (IsFound) { //found this actually on satellite nextdoor... - C->Src[0] = AvailableSources[i]; - break; - } - } + if(IsFound) { + C->Src[0] = VC->Source(); } CleanString (C->Name); @@ -1791,30 +1801,20 @@ int cFilterEEPG::GetChannelsNagra (const u_char * Data, int Length) ChannelSeq[C->ChannelId] = j; //fill lookup table to go from channel-id to sequence nr in table; lookuptable starts with 0 C->SkyNumber = 0; C->NumberOfEquivalences = 1; //there is always an original channel. every equivalence adds 1 - C->Src[0] = Source (); //assume all EPG channels are on same satellite, if not, manage this via equivalents!!! + C->Src[0] = Source(); //assume all EPG channels are on same satellite, if not, manage this via equivalents!!! C->Nid[0] = HILO16 (Channel->NetworkId); C->Tid[0] = HILO16 (Channel->TransportId); C->Sid[0] = HILO16 (Channel->ServiceId); - tChannelID channelID = tChannelID (C->Src[0], C->Nid[0], C->Tid[0], C->Sid[0]); - cChannel *VC = Channels.GetByChannelID (channelID, true); + tChannelID channelID = tChannelID(C->Src[0], C->Nid[0], C->Tid[0], C->Sid[0]); + cChannel *VC = GetChannelByID(channelID, true); bool IsFound = (VC); - - if (!IsFound) { //look on other satpositions - for (int i = 0; i < NumberOfAvailableSources; i++) { - channelID = tChannelID (AvailableSources[i], C->Nid[0], C->Tid[0], C->Sid[0]); - VC = Channels.GetByChannelID (channelID, true); - IsFound = (VC); - if (IsFound) { //found this actually on satellite nextdoor... - C->Src[0] = AvailableSources[i]; - break; - } - } + if(IsFound) { + strncpy((char*)(C->Name), VC->Name (), 64); + C->Src[0] = VC->Source(); + CleanString (C->Name); } - if (IsFound) - strncpy ((char *) C->Name, VC->Name (), 64); else C->Name[0] = '\0'; //empty string - CleanString (C->Name); LogI(1, "|% 5d | %-26.26s | %-22.22s | %-3.3s | % 6d |\n", C->ChannelId , *channelID.ToString(), C->Name, IsFound ? "YES" : "NO", C->SkyNumber); @@ -2822,7 +2822,7 @@ namespace SI #define SIZE_TABLE_128 128 #define SIZE_TABLE_255 255 -class DishDescriptor : public UnimplementedDescriptor { +class DishDescriptor { public: DishDescriptor(UnimplementedDescriptor*); virtual ~DishDescriptor(); @@ -2831,8 +2831,6 @@ public: // Decompress the byte arrary and stores the result to a text string void Decompress(unsigned char Tid); protected: - virtual void Parse() {}; - const char* text; // name or description of the event const char* shortText; // usually the episode name unsigned char* decompressed; @@ -2856,10 +2854,10 @@ class cEIT2:public SI::EIT { public: cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Data, - bool OnlyRunningStatus = false); + bool searchOtherSatPositions = false, bool OnlyRunningStatus = false); }; -cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Data, bool OnlyRunningStatus) +cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Data, bool searchOtherSatPositions, bool OnlyRunningStatus) : SI::EIT (Data, false) { //LogD(2, prep("cEIT2::cEIT2")); @@ -2870,10 +2868,9 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat } tChannelID channelID (Source, getOriginalNetworkId (), getTransportStreamId (), getServiceId ()); - LogD(2, prep("channelID: %s"), *channelID.ToString()); - cChannel *channel = Channels.GetByChannelID (channelID, true); + cChannel *channel = GetChannelByID (channelID, searchOtherSatPositions); if (!channel) { - LogD(2, prep("!channel")); + LogD(3, prep("!channel channelID: %s"), *channelID.ToString()); return; // only collect data for known channels } @@ -3247,17 +3244,6 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat } if (!rEvent) { - if (DishShortEventDescriptor) { - pEvent->SetTitle(DishShortEventDescriptor->getText()); - LogD(2, prep("DishTitle: %s"), DishShortEventDescriptor->getText()); - - } - if (DishExtendedEventDescriptor) { - pEvent->SetDescription(DishExtendedEventDescriptor->getText()); - pEvent->SetShortText(DishExtendedEventDescriptor->getShortText()); - LogD(2, prep("DishDescription: %s"), DishExtendedEventDescriptor->getText()); - LogD(2, prep("DishShortText: %s"), DishExtendedEventDescriptor->getShortText()); - } if (ShortEventDescriptor) { char buffer[Utf8BufSize (256)]; unsigned char *f; @@ -3280,6 +3266,17 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat pEvent->SetDescription (ExtendedEventDescriptors->getText (buffer, sizeof (buffer), ": ")); } else if (!HasExternalData) pEvent->SetDescription (NULL); + + if (DishShortEventDescriptor) { + pEvent->SetTitle(DishShortEventDescriptor->getText()); + //LogD(2, prep("DishTitle: %s"), DishShortEventDescriptor->getText()); + } + if (DishExtendedEventDescriptor) { + pEvent->SetDescription(DishExtendedEventDescriptor->getText()); + pEvent->SetShortText(DishExtendedEventDescriptor->getShortText()); + //LogD(2, prep("DishDescription: %s"), DishExtendedEventDescriptor->getText()); + //LogD(2, prep("DishShortText: %s"), DishExtendedEventDescriptor->getShortText()); + } } delete ExtendedEventDescriptors; delete ShortEventDescriptor; @@ -3423,7 +3420,7 @@ DishDescriptor::~DishDescriptor() void DishDescriptor::Decompress(unsigned char Tid) { - const unsigned char *str = data.getData(); + const unsigned char *str = unimplementedDesc->getData().getData(); const unsigned char *cmp = NULL; // Compressed data int length = 0; // Length of compressed data unsigned int dLength = 0; // Length of decompressed data @@ -3741,8 +3738,10 @@ void cFilterEEPG::ProccessContinuous(u_short Pid, u_char Tid, int Length, const LogD(2, prep("Pid: 0x%02x Tid: %d Length: %d"), Pid, Tid, Length); cSchedulesLock SchedulesLock(true, 10); cSchedules *Schedules = (cSchedules*)(cSchedules::Schedules(SchedulesLock)); + //Look for other satelite positions only if Dish/Bell ExpressVU for the moment hardcoded pid check + bool searchOtherSatPositions = Pid == 0x0300 || Pid == 0x0441; if(Schedules) - SI::cEIT2 EIT(Schedules, Source(), Tid, Data); + SI::cEIT2 EIT(Schedules, Source(), Tid, Data, searchOtherSatPositions); else//cEIT EIT (Schedules, Source (), Tid, Data); { @@ -3753,7 +3752,7 @@ void cFilterEEPG::ProccessContinuous(u_short Pid, u_char Tid, int Length, const cSchedulesLock SchedulesLock; cSchedules *Schedules = (cSchedules*)(cSchedules::Schedules(SchedulesLock)); if(Schedules) - SI::cEIT2 EIT(Schedules, Source(), Tid, Data, true); + SI::cEIT2 EIT(Schedules, Source(), Tid, Data, searchOtherSatPositions, true); //cEIT EIT (Schedules, Source (), Tid, Data, true); } -- cgit v1.2.3 From 6e12fdde5ec3e32f91a6647f9f4a06bc6d0ea673 Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Tue, 6 Sep 2011 22:35:36 +0200 Subject: dish fixes --- eepg.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 91f9842..69b9c92 100644 --- a/eepg.c +++ b/eepg.c @@ -2676,7 +2676,7 @@ void cFilterEEPG::LoadIntoSchedule (void) //esyslog("foundtitle %x for next title that has a summary:%d",foundtitle,i); if (!foundtitle) //no more titles with summaries - break; //TODO: does this work??? + break; if ((T->EventId == S->EventId) && (T->MjdTime == S->Replays[0].MjdTime) && ((T->ChannelId == S->Replays[0].ChannelId) || ((Format != SKY_IT) && (Format != SKY_UK)))) { //should always be true, titles and summaries are broadcasted in order... LogD(3, prep("T->EventId == S->EventId")); //MjdTime = 0 for all but SKY @@ -3285,6 +3285,10 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat pEvent->SetComponents (Components); + 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) @@ -3472,7 +3476,7 @@ void DishDescriptor::Decompress(unsigned char Tid) decompressed[count] = 0; char* split = strchr((char*)decompressed, 0x0D); // Look for carriage return - LogD(2, prep("dLength:%d, length:%d, count:%d, decompressed: %s"), dLength, length, count, decompressed); + //LogD(2, prep("dLength:%d, length:%d, count:%d, decompressed: %s"), dLength, length, count, decompressed); if (split) { *split = 0; shortText = (char*) decompressed; @@ -3721,11 +3725,11 @@ void cFilterEEPG::ProcessNextFormat (bool FirstTime = false) AddFilter (pid, 0xb0); //perhaps TID is equal to first data byte? break; case DISH_BEV: - AddFilter (0x0300, 0x50, 0xf0); // Dish Network EEPG - AddFilter (0x0300, 0x60, 0xf0); // Dish Network EEPG - AddFilter (0x0300, 0x90, 0xf0); // Dish Network EEPG - AddFilter (0x0441, 0x50, 0xf0); // Bell ExpressVU EEPG - AddFilter (0x0441, 0x60, 0xf0); // Bell ExpressVU EEPG + AddFilter (0x12, 0, 0); // event info, actual(0x4e)/other(0x4f) TS, present/following + AddFilter (0x0300, 0, 0); // Dish Network EEPG event info, actual(0x4e)/other(0x4f) TS, present/following + AddFilter (0x0441, 0, 0); // Dish Network EEPG event info, actual(0x4e)/other(0x4f) TS, present/following +// AddFilter (0x0441, 0x50, 0xf0); // Bell ExpressVU EEPG +// AddFilter (0x0441, 0x60, 0xf0); // Bell ExpressVU EEPG break; default: break; @@ -3805,9 +3809,7 @@ void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Len SI::PMT::Stream stream; for (SI::Loop::Iterator it; pmt.streamLoop.getNext (stream, it);) { LogD(2, prep("StreamType: 0x%02x"), stream.getStreamType ()); - if (stream.getStreamType () == 0x05 || stream.getStreamType () == 0xc1 - /*|| stream.getStreamType () == 0x04 || stream.getStreamType () == 0x02 - || stream.getStreamType () == 0xd1*/) { //0x05 = Premiere, SKY, Freeview, Nagra 0xc1 = MHW1,MHW2; 0x04 DISH BEV ? + if (stream.getStreamType () == 0x05 || stream.getStreamType () == 0xc1) { //0x05 = Premiere, SKY, Freeview, Nagra 0xc1 = MHW1,MHW2; SI::CharArray data = stream.getData (); if ((data[1] & 0xE0) == 0xE0 && (data[3] & 0xF0) == 0xF0) { bool prvData = false, usrData = false; @@ -3890,7 +3892,12 @@ void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Len if (prvData && usrData) UnprocessedFormat[PREMIERE] = stream.getPid (); //TODO DPE this is not good since the DISH/BEV filters are always on, but have to test somehow. - if (!UnprocessedFormat[DISH_BEV]) { + //EEPG:12472:H:S119.0W:20000:0:0:0:0:36862:4100:18:36862 + if (((Source() == cSource::FromString("S119.0W") + && Transponder() == cChannel::Transponder(12472,'H')) + || (Source() == cSource::FromString("S91.0W") + && Transponder() == cChannel::Transponder(12224,'R'))) + && !UnprocessedFormat[DISH_BEV]) { UnprocessedFormat[DISH_BEV] = stream.getPid (); } } //if data[1] && data [3] @@ -3913,8 +3920,9 @@ void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Len } //if pmtpid else if (Source ()) { - if ( Pid == 0x0300 || Pid == 0x0441 ) { - ProccessContinuous(Pid, Tid, Length, Data); + if ( Pid == 0x12 || Pid == 0x0300 || Pid == 0x0441 ) { + if (Tid >= 0x4E) + ProccessContinuous(Pid, Tid, Length, Data); return; } int Result; -- cgit v1.2.3 From 038d482b3d317e8369c11a693c485517b6646c33 Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Wed, 14 Sep 2011 14:06:11 +0200 Subject: moved dish descriptor to separate files added dish theme/category --- eepg.c | 341 ++++++++--------------------------------------------------------- 1 file changed, 41 insertions(+), 300 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 69b9c92..0183e12 100644 --- a/eepg.c +++ b/eepg.c @@ -37,6 +37,7 @@ #include #include #include "eepg.h" +#include "dish.h" #include #include @@ -288,7 +289,7 @@ private: bool EndChannels, EndThemes; //only used for ?? int MHWStartTime; //only used for MHW1 bool ChannelsOk; - int Format; //the format that this filter currently is processing + //int Format; //the format that this filter currently is processing std::map < int, int >ChannelSeq; // ChannelSeq[ChannelId] returns the recordnumber of the channel Summary_t *Summaries[MAX_TITLES]; @@ -2677,7 +2678,8 @@ void cFilterEEPG::LoadIntoSchedule (void) if (!foundtitle) //no more titles with summaries break; - if ((T->EventId == S->EventId) && (T->MjdTime == S->Replays[0].MjdTime) && ((T->ChannelId == S->Replays[0].ChannelId) || ((Format != SKY_IT) && (Format != SKY_UK)))) { //should always be true, titles and summaries are broadcasted in order... + if ((T->EventId == S->EventId) && (T->MjdTime == S->Replays[0].MjdTime) + && ((T->ChannelId == S->Replays[0].ChannelId) || ((Format != SKY_IT) && (Format != SKY_UK)))) { //should always be true, titles and summaries are broadcasted in order... LogD(3, prep("T->EventId == S->EventId")); //MjdTime = 0 for all but SKY //S->ChannelId must be equal to T->ChannelId only for SKY; in MHW1 S->ChannelId overrides T->ChannelId when NumReplays > 1 @@ -2816,57 +2818,28 @@ namespace SI // typedef InheritEnum< DescriptorTagExt, SI::DescriptorTag > ExtendedDescriptorTag; -#ifndef LIBSI_DISH_H -#define LIBSI_DISH_H - -#define SIZE_TABLE_128 128 -#define SIZE_TABLE_255 255 - -class DishDescriptor { -public: - DishDescriptor(UnimplementedDescriptor*); - virtual ~DishDescriptor(); - const char* getText(void) const { return text; } - const char* getShortText(void) const { return shortText; } - // Decompress the byte arrary and stores the result to a text string - void Decompress(unsigned char Tid); -protected: - const char* text; // name or description of the event - const char* shortText; // usually the episode name - unsigned char* decompressed; - UnimplementedDescriptor* unimplementedDesc; - - struct HuffmanTable { - unsigned int startingAddress; - unsigned char character; - unsigned char numberOfBits; - }; - static HuffmanTable Table128[SIZE_TABLE_128]; - static HuffmanTable Table255[SIZE_TABLE_255]; -}; - -#endif //LIBSI_DISH_H - /*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 searchOtherSatPositions = false, bool OnlyRunningStatus = false); + cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Data, bool OnlyRunningStatus = false); }; -cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Data, bool searchOtherSatPositions, bool OnlyRunningStatus) +cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Data, bool OnlyRunningStatus) : SI::EIT (Data, false) { //LogD(2, prep("cEIT2::cEIT2")); + if (Tid > 0 && Format == DISH_BEV) 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) { @@ -2874,6 +2847,8 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat return; // only collect data for known channels } + LogD(4, prep("channelID: %s"), *channel->GetChannelID().ToString()); + #ifdef USE_NOEPG // only use epg from channels not blocked by noEPG-patch tChannelID kanalID; @@ -3003,7 +2978,7 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat // 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 () == getVersionNumber ()) + else if (Tid == pEvent->TableID() && pEvent->Version() == getVersionNumber()) continue; } if (!ExternalData) { @@ -3024,7 +2999,7 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat } if (OnlyRunningStatus) continue; // do this before setting the version, so that the full update can be done later - pEvent->SetVersion (getVersionNumber ()); + pEvent->SetVersion (getVersionNumber()); int LanguagePreferenceShort = -1; int LanguagePreferenceExt = -1; @@ -3034,6 +3009,7 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat SI::ShortEventDescriptor * ShortEventDescriptor = NULL; SI::DishDescriptor *DishExtendedEventDescriptor = NULL; SI::DishDescriptor *DishShortEventDescriptor = NULL; + uchar DishTheme = -1, DishCategory = -1; cLinkChannels *LinkChannels = NULL; @@ -3085,6 +3061,10 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat Contents[NumContents] = ((Nibble.getContentNibbleLevel1() & 0xF) << 4) | (Nibble.getContentNibbleLevel2() & 0xF); NumContents++; } + if (Format == DISH_BEV && DishTheme == -1) { + DishTheme = Nibble.getContentNibbleLevel2() & 0xF; + DishCategory = ((Nibble.getUserNibble1() & 0xF) << 4) | (Nibble.getUserNibble2() & 0xF); + } } pEvent->SetContents(Contents); } @@ -3269,11 +3249,19 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat if (DishShortEventDescriptor) { pEvent->SetTitle(DishShortEventDescriptor->getText()); - //LogD(2, prep("DishTitle: %s"), DishShortEventDescriptor->getText()); + //LogD(2, prep("channelID: %s DishTitle: %s"), *channel->GetChannelID().ToString(), DishShortEventDescriptor->getText()); } if (DishExtendedEventDescriptor) { pEvent->SetDescription(DishExtendedEventDescriptor->getText()); - pEvent->SetShortText(DishExtendedEventDescriptor->getShortText()); + char *tmp; + if (DishTheme >= 0) { + Asprintf (&tmp, "%s - %s ~ %s", DishExtendedEventDescriptor->getShortText() + , DishExtendedEventDescriptor->getTheme(DishTheme) + , DishExtendedEventDescriptor->getCategory(DishCategory)); + pEvent->SetShortText(tmp); + free tmp; + } else + pEvent->SetShortText(DishExtendedEventDescriptor->getShortText()); //LogD(2, prep("DishDescription: %s"), DishExtendedEventDescriptor->getText()); //LogD(2, prep("DishShortText: %s"), DishExtendedEventDescriptor->getShortText()); } @@ -3285,9 +3273,11 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat pEvent->SetComponents (Components); - 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()); - } +// 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 (); @@ -3375,253 +3365,6 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat } //end of cEIT2 -/*************************************************************************** - * * - * These routines decompress Huffman coded Dish Network EIT data. * - * The implementation is based on the algorithm presentend in * - * * - * "A memory-efficient Huffman decoding algorithm" * - * Pi-Chung Wang, Yuan-Rung Yang, Chun-Liang Lee, Hung-Yi Chang * - * Proceedings of the 19th International Conference on Advanced * - * Information Networking and Applications (AINA'05) * - * * - ***************************************************************************/ - -// returns the value of a sequence of bits in the byte array -static unsigned int getBits(int bitIndex, int bitCount, const unsigned char *byteptr, int length) -{ - union { - unsigned char b[4]; - unsigned long val; - } chunk; - - int offset = bitIndex >> 3; - int bitnum = bitIndex - (offset << 3); - int rightend = 32 - bitnum - bitCount; - - chunk.b[3] = byteptr[offset]; - chunk.b[2] = (offset+1 < length) ? byteptr[offset+1] : 0; - chunk.b[1] = (offset+2 < length) ? byteptr[offset+2] : 0; - chunk.b[0] = 0; // Never need to look this far ahead. - - return (unsigned int)((chunk.val & (0xFFFFFFFF >> bitnum)) >> rightend); -} - -DishDescriptor::DishDescriptor(UnimplementedDescriptor* unimplementedDesc) -{ - text = NULL; - shortText = NULL; - decompressed = NULL; - this->unimplementedDesc = unimplementedDesc; -} - -DishDescriptor::~DishDescriptor() -{ - delete[] decompressed; - decompressed = NULL; - delete unimplementedDesc; -} - -void DishDescriptor::Decompress(unsigned char Tid) -{ - const unsigned char *str = unimplementedDesc->getData().getData(); - const unsigned char *cmp = NULL; // Compressed data - int length = 0; // Length of compressed data - unsigned int dLength = 0; // Length of decompressed data - - if ((str[3] & 0xFC) == 0x80) { - length = str[1] - 2; - dLength = (str[2]&0x40) ? ((str[3] << 6)&0xFF) | (str[2]&0x3F) : str[2]&0x3F; - cmp = str + 4; - } - else { - length = str[1] - 1; - dLength = str[2] & 0x7F; - cmp = str + 3; - } - - if (length <= 0 || !dLength) - return; - - decompressed = new unsigned char[dLength+1]; - - HuffmanTable *table; - unsigned int tableSize, numBits; - - if (Tid > 0x80) { - table = Table255; - tableSize = SIZE_TABLE_255; - numBits = 13; - } - else { - table = Table128; - tableSize = SIZE_TABLE_128; - numBits = 11; - } - - unsigned int bLength = length << 3; // number of bits - unsigned int currentBit = 0, count = 0; - while (currentBit < bLength - 1 && count < dLength) { - // Find the interval containing the sequence of length numBits starting - // at currentBit. The corresponding character will be the one encoded - // at the begin of the sequence. - unsigned int code = getBits(currentBit, numBits, cmp, length); - // We could use a binary search, but in practice this linear search is faster. - unsigned int index = 0; - while (table[index].startingAddress <= code && index < tableSize) { index++; } - index--; - decompressed[count++] = table[index].character; - currentBit += table[index].numberOfBits; - } - decompressed[count] = 0; - - char* split = strchr((char*)decompressed, 0x0D); // Look for carriage return - //LogD(2, prep("dLength:%d, length:%d, count:%d, decompressed: %s"), dLength, length, count, decompressed); - if (split) { - *split = 0; - shortText = (char*) decompressed; - text = (split[1]==0x20) ? split+2 : split+1; - } - else { - text = (char*) decompressed; - } -} - - -struct DishDescriptor::HuffmanTable DishDescriptor::Table128[SIZE_TABLE_128] = { - { 0x0000, 0x20, 0x03 }, { 0x0100, 0x65, 0x04 }, { 0x0180, 0x74, 0x04 }, - { 0x0200, 0x61, 0x04 }, { 0x0280, 0x6F, 0x04 }, { 0x0300, 0x73, 0x04 }, - { 0x0380, 0x6E, 0x04 }, { 0x0400, 0x72, 0x06 }, { 0x0420, 0x69, 0x06 }, - { 0x0440, 0x6C, 0x06 }, { 0x0460, 0x63, 0x06 }, { 0x0480, 0x68, 0x06 }, - { 0x04A0, 0x75, 0x06 }, { 0x04C0, 0x64, 0x06 }, { 0x04E0, 0x70, 0x06 }, - { 0x0500, 0x6D, 0x06 }, { 0x0520, 0x67, 0x06 }, { 0x0540, 0x79, 0x06 }, - { 0x0560, 0x76, 0x06 }, { 0x0580, 0x0A, 0x06 }, { 0x05A0, 0x2E, 0x06 }, - { 0x05C0, 0x77, 0x06 }, { 0x05E0, 0x66, 0x06 }, { 0x0600, 0x53, 0x07 }, - { 0x0610, 0x62, 0x07 }, { 0x0620, 0x54, 0x07 }, { 0x0630, 0x22, 0x07 }, - { 0x0640, 0x6B, 0x07 }, { 0x0650, 0x50, 0x07 }, { 0x0660, 0x41, 0x07 }, - { 0x0670, 0x43, 0x07 }, { 0x0680, 0x44, 0x07 }, { 0x0690, 0x4C, 0x07 }, - { 0x06A0, 0x4D, 0x07 }, { 0x06B0, 0x49, 0x07 }, { 0x06C0, 0x4E, 0x07 }, - { 0x06D0, 0x3A, 0x07 }, { 0x06E0, 0x52, 0x07 }, { 0x06F0, 0x2C, 0x07 }, - { 0x0700, 0x45, 0x08 }, { 0x0708, 0x55, 0x08 }, { 0x0710, 0x46, 0x08 }, - { 0x0718, 0x48, 0x08 }, { 0x0720, 0x59, 0x08 }, { 0x0728, 0x56, 0x08 }, - { 0x0730, 0x2D, 0x08 }, { 0x0738, 0x7A, 0x08 }, { 0x0740, 0x78, 0x08 }, - { 0x0748, 0x2F, 0x08 }, { 0x0750, 0x4F, 0x08 }, { 0x0758, 0x3F, 0x08 }, - { 0x0760, 0x57, 0x08 }, { 0x0768, 0x47, 0x08 }, { 0x0770, 0x42, 0x08 }, - { 0x0778, 0x33, 0x08 }, { 0x0780, 0x31, 0x09 }, { 0x0784, 0x71, 0x09 }, - { 0x0788, 0x30, 0x09 }, { 0x078C, 0x21, 0x09 }, { 0x0790, 0x6A, 0x09 }, - { 0x0794, 0x5A, 0x09 }, { 0x0798, 0x39, 0x09 }, { 0x079C, 0x34, 0x09 }, - { 0x07A0, 0x4B, 0x09 }, { 0x07A4, 0x2A, 0x09 }, { 0x07A8, 0x37, 0x09 }, - { 0x07AC, 0x36, 0x09 }, { 0x07B0, 0x35, 0x09 }, { 0x07B4, 0x4A, 0x09 }, - { 0x07B8, 0x38, 0x09 }, { 0x07BC, 0x29, 0x09 }, { 0x07C0, 0x28, 0x0A }, - { 0x07C2, 0x58, 0x0A }, { 0x07C4, 0x51, 0x0A }, { 0x07C6, 0x3C, 0x0A }, - { 0x07C8, 0x32, 0x0A }, { 0x07CA, 0x27, 0x0A }, { 0x07CC, 0x26, 0x0A }, - { 0x07CE, 0x7F, 0x0B }, { 0x07CF, 0x7E, 0x0B }, { 0x07D0, 0x7D, 0x0B }, - { 0x07D1, 0x7C, 0x0B }, { 0x07D2, 0x7B, 0x0B }, { 0x07D3, 0x60, 0x0B }, - { 0x07D4, 0x5F, 0x0B }, { 0x07D5, 0x5E, 0x0B }, { 0x07D6, 0x5D, 0x0B }, - { 0x07D7, 0x5C, 0x0B }, { 0x07D8, 0x5B, 0x0B }, { 0x07D9, 0x40, 0x0B }, - { 0x07DA, 0x3E, 0x0B }, { 0x07DB, 0x3D, 0x0B }, { 0x07DC, 0x3B, 0x0B }, - { 0x07DD, 0x2B, 0x0B }, { 0x07DE, 0x25, 0x0B }, { 0x07DF, 0x24, 0x0B }, - { 0x07E0, 0x23, 0x0B }, { 0x07E1, 0x1F, 0x0B }, { 0x07E2, 0x1E, 0x0B }, - { 0x07E3, 0x1D, 0x0B }, { 0x07E4, 0x1C, 0x0B }, { 0x07E5, 0x1B, 0x0B }, - { 0x07E6, 0x1A, 0x0B }, { 0x07E7, 0x19, 0x0B }, { 0x07E8, 0x18, 0x0B }, - { 0x07E9, 0x17, 0x0B }, { 0x07EA, 0x16, 0x0B }, { 0x07EB, 0x15, 0x0B }, - { 0x07EC, 0x14, 0x0B }, { 0x07ED, 0x13, 0x0B }, { 0x07EE, 0x12, 0x0B }, - { 0x07EF, 0x11, 0x0B }, { 0x07F0, 0x10, 0x0B }, { 0x07F1, 0x0F, 0x0B }, - { 0x07F2, 0x0E, 0x0B }, { 0x07F3, 0x0D, 0x0B }, { 0x07F4, 0x0C, 0x0B }, - { 0x07F5, 0x0B, 0x0B }, { 0x07F6, 0x09, 0x0B }, { 0x07F7, 0x08, 0x0B }, - { 0x07F8, 0x07, 0x0B }, { 0x07F9, 0x06, 0x0B }, { 0x07FA, 0x05, 0x0B }, - { 0x07FB, 0x04, 0x0B }, { 0x07FC, 0x03, 0x0B }, { 0x07FD, 0x02, 0x0B }, - { 0x07FE, 0x01, 0x0B }, { 0x07FF, 0x00, 0x0B } -}; - -struct DishDescriptor::HuffmanTable DishDescriptor::Table255[SIZE_TABLE_255] = { - { 0x0000, 0x20, 0x02 }, { 0x0800, 0x65, 0x04 }, { 0x0A00, 0x72, 0x04 }, - { 0x0C00, 0x6E, 0x04 }, { 0x0E00, 0x61, 0x04 }, { 0x1000, 0x74, 0x05 }, - { 0x1100, 0x6F, 0x05 }, { 0x1200, 0x73, 0x05 }, { 0x1300, 0x69, 0x05 }, - { 0x1400, 0x6C, 0x05 }, { 0x1500, 0x75, 0x05 }, { 0x1600, 0x63, 0x05 }, - { 0x1700, 0x64, 0x05 }, { 0x1800, 0x70, 0x07 }, { 0x1840, 0x6D, 0x07 }, - { 0x1880, 0x76, 0x07 }, { 0x18C0, 0x67, 0x07 }, { 0x1900, 0x68, 0x07 }, - { 0x1940, 0x2E, 0x07 }, { 0x1980, 0x66, 0x07 }, { 0x19C0, 0x0A, 0x07 }, - { 0x1A00, 0x53, 0x07 }, { 0x1A40, 0x41, 0x07 }, { 0x1A80, 0x45, 0x07 }, - { 0x1AC0, 0x43, 0x07 }, { 0x1B00, 0x27, 0x07 }, { 0x1B40, 0x7A, 0x07 }, - { 0x1B80, 0x52, 0x07 }, { 0x1BC0, 0x22, 0x07 }, { 0x1C00, 0x4C, 0x08 }, - { 0x1C20, 0x49, 0x08 }, { 0x1C40, 0x4F, 0x08 }, { 0x1C60, 0x62, 0x08 }, - { 0x1C80, 0x54, 0x08 }, { 0x1CA0, 0x4E, 0x08 }, { 0x1CC0, 0x55, 0x08 }, - { 0x1CE0, 0x79, 0x08 }, { 0x1D00, 0x44, 0x08 }, { 0x1D20, 0x50, 0x08 }, - { 0x1D40, 0x71, 0x08 }, { 0x1D60, 0x56, 0x08 }, { 0x1D80, 0x2D, 0x08 }, - { 0x1DA0, 0x3A, 0x08 }, { 0x1DC0, 0x2C, 0x08 }, { 0x1DE0, 0x48, 0x08 }, - { 0x1E00, 0x4D, 0x09 }, { 0x1E10, 0x78, 0x09 }, { 0x1E20, 0x77, 0x09 }, - { 0x1E30, 0x42, 0x09 }, { 0x1E40, 0x47, 0x09 }, { 0x1E50, 0x46, 0x09 }, - { 0x1E60, 0x30, 0x09 }, { 0x1E70, 0x3F, 0x09 }, { 0x1E80, 0x33, 0x09 }, - { 0x1E90, 0x2F, 0x09 }, { 0x1EA0, 0x39, 0x09 }, { 0x1EB0, 0x31, 0x09 }, - { 0x1EC0, 0x38, 0x09 }, { 0x1ED0, 0x6B, 0x09 }, { 0x1EE0, 0x6A, 0x09 }, - { 0x1EF0, 0x21, 0x09 }, { 0x1F00, 0x36, 0x0A }, { 0x1F08, 0x35, 0x0A }, - { 0x1F10, 0x59, 0x0A }, { 0x1F18, 0x51, 0x0A }, { 0x1F20, 0x34, 0x0B }, - { 0x1F24, 0x58, 0x0B }, { 0x1F28, 0x32, 0x0B }, { 0x1F2C, 0x2B, 0x0B }, - { 0x1F30, 0x2A, 0x0B }, { 0x1F34, 0x5A, 0x0B }, { 0x1F38, 0x4A, 0x0B }, - { 0x1F3C, 0x29, 0x0B }, { 0x1F40, 0x28, 0x0C }, { 0x1F42, 0x23, 0x0C }, - { 0x1F44, 0x57, 0x0C }, { 0x1F46, 0x4B, 0x0C }, { 0x1F48, 0x3C, 0x0C }, - { 0x1F4A, 0x37, 0x0C }, { 0x1F4C, 0x7D, 0x0C }, { 0x1F4E, 0x7B, 0x0C }, - { 0x1F50, 0x60, 0x0C }, { 0x1F52, 0x26, 0x0C }, { 0x1F54, 0xFE, 0x0D }, - { 0x1F55, 0xFD, 0x0D }, { 0x1F56, 0xFC, 0x0D }, { 0x1F57, 0xFB, 0x0D }, - { 0x1F58, 0xFA, 0x0D }, { 0x1F59, 0xF9, 0x0D }, { 0x1F5A, 0xF8, 0x0D }, - { 0x1F5B, 0xF7, 0x0D }, { 0x1F5C, 0xF6, 0x0D }, { 0x1F5D, 0xF5, 0x0D }, - { 0x1F5E, 0xF4, 0x0D }, { 0x1F5F, 0xF3, 0x0D }, { 0x1F60, 0xF2, 0x0D }, - { 0x1F61, 0xF1, 0x0D }, { 0x1F62, 0xF0, 0x0D }, { 0x1F63, 0xEF, 0x0D }, - { 0x1F64, 0xEE, 0x0D }, { 0x1F65, 0xED, 0x0D }, { 0x1F66, 0xEC, 0x0D }, - { 0x1F67, 0xEB, 0x0D }, { 0x1F68, 0xEA, 0x0D }, { 0x1F69, 0xE9, 0x0D }, - { 0x1F6A, 0xE8, 0x0D }, { 0x1F6B, 0xE7, 0x0D }, { 0x1F6C, 0xE6, 0x0D }, - { 0x1F6D, 0xE5, 0x0D }, { 0x1F6E, 0xE4, 0x0D }, { 0x1F6F, 0xE3, 0x0D }, - { 0x1F70, 0xE2, 0x0D }, { 0x1F71, 0xE1, 0x0D }, { 0x1F72, 0xE0, 0x0D }, - { 0x1F73, 0xDF, 0x0D }, { 0x1F74, 0xDE, 0x0D }, { 0x1F75, 0xDD, 0x0D }, - { 0x1F76, 0xDC, 0x0D }, { 0x1F77, 0xDB, 0x0D }, { 0x1F78, 0xDA, 0x0D }, - { 0x1F79, 0xD9, 0x0D }, { 0x1F7A, 0xD8, 0x0D }, { 0x1F7B, 0xD7, 0x0D }, - { 0x1F7C, 0xD6, 0x0D }, { 0x1F7D, 0xD5, 0x0D }, { 0x1F7E, 0xD4, 0x0D }, - { 0x1F7F, 0xD3, 0x0D }, { 0x1F80, 0xD2, 0x0D }, { 0x1F81, 0xD1, 0x0D }, - { 0x1F82, 0xD0, 0x0D }, { 0x1F83, 0xCF, 0x0D }, { 0x1F84, 0xCE, 0x0D }, - { 0x1F85, 0xCD, 0x0D }, { 0x1F86, 0xCC, 0x0D }, { 0x1F87, 0xCB, 0x0D }, - { 0x1F88, 0xCA, 0x0D }, { 0x1F89, 0xC9, 0x0D }, { 0x1F8A, 0xC8, 0x0D }, - { 0x1F8B, 0xC7, 0x0D }, { 0x1F8C, 0xC6, 0x0D }, { 0x1F8D, 0xC5, 0x0D }, - { 0x1F8E, 0xC4, 0x0D }, { 0x1F8F, 0xC3, 0x0D }, { 0x1F90, 0xC2, 0x0D }, - { 0x1F91, 0xC1, 0x0D }, { 0x1F92, 0xC0, 0x0D }, { 0x1F93, 0xBF, 0x0D }, - { 0x1F94, 0xBE, 0x0D }, { 0x1F95, 0xBD, 0x0D }, { 0x1F96, 0xBC, 0x0D }, - { 0x1F97, 0xBB, 0x0D }, { 0x1F98, 0xBA, 0x0D }, { 0x1F99, 0xB9, 0x0D }, - { 0x1F9A, 0xB8, 0x0D }, { 0x1F9B, 0xB7, 0x0D }, { 0x1F9C, 0xB6, 0x0D }, - { 0x1F9D, 0xB5, 0x0D }, { 0x1F9E, 0xB4, 0x0D }, { 0x1F9F, 0xB3, 0x0D }, - { 0x1FA0, 0xB2, 0x0D }, { 0x1FA1, 0xB1, 0x0D }, { 0x1FA2, 0xB0, 0x0D }, - { 0x1FA3, 0xAF, 0x0D }, { 0x1FA4, 0xAE, 0x0D }, { 0x1FA5, 0xAD, 0x0D }, - { 0x1FA6, 0xAC, 0x0D }, { 0x1FA7, 0xAB, 0x0D }, { 0x1FA8, 0xAA, 0x0D }, - { 0x1FA9, 0xA9, 0x0D }, { 0x1FAA, 0xA8, 0x0D }, { 0x1FAB, 0xA7, 0x0D }, - { 0x1FAC, 0xA6, 0x0D }, { 0x1FAD, 0xA5, 0x0D }, { 0x1FAE, 0xA4, 0x0D }, - { 0x1FAF, 0xA3, 0x0D }, { 0x1FB0, 0xA2, 0x0D }, { 0x1FB1, 0xA1, 0x0D }, - { 0x1FB2, 0xA0, 0x0D }, { 0x1FB3, 0x9F, 0x0D }, { 0x1FB4, 0x9E, 0x0D }, - { 0x1FB5, 0x9D, 0x0D }, { 0x1FB6, 0x9C, 0x0D }, { 0x1FB7, 0x9B, 0x0D }, - { 0x1FB8, 0x9A, 0x0D }, { 0x1FB9, 0x99, 0x0D }, { 0x1FBA, 0x98, 0x0D }, - { 0x1FBB, 0x97, 0x0D }, { 0x1FBC, 0x96, 0x0D }, { 0x1FBD, 0x95, 0x0D }, - { 0x1FBE, 0x94, 0x0D }, { 0x1FBF, 0x93, 0x0D }, { 0x1FC0, 0x92, 0x0D }, - { 0x1FC1, 0x91, 0x0D }, { 0x1FC2, 0x90, 0x0D }, { 0x1FC3, 0x8F, 0x0D }, - { 0x1FC4, 0x8E, 0x0D }, { 0x1FC5, 0x8D, 0x0D }, { 0x1FC6, 0x8C, 0x0D }, - { 0x1FC7, 0x8B, 0x0D }, { 0x1FC8, 0x8A, 0x0D }, { 0x1FC9, 0x89, 0x0D }, - { 0x1FCA, 0x88, 0x0D }, { 0x1FCB, 0x87, 0x0D }, { 0x1FCC, 0x86, 0x0D }, - { 0x1FCD, 0x85, 0x0D }, { 0x1FCE, 0x84, 0x0D }, { 0x1FCF, 0x83, 0x0D }, - { 0x1FD0, 0x82, 0x0D }, { 0x1FD1, 0x81, 0x0D }, { 0x1FD2, 0x80, 0x0D }, - { 0x1FD3, 0x7F, 0x0D }, { 0x1FD4, 0x7E, 0x0D }, { 0x1FD5, 0x7C, 0x0D }, - { 0x1FD6, 0x5F, 0x0D }, { 0x1FD7, 0x5E, 0x0D }, { 0x1FD8, 0x5D, 0x0D }, - { 0x1FD9, 0x5C, 0x0D }, { 0x1FDA, 0x5B, 0x0D }, { 0x1FDB, 0x40, 0x0D }, - { 0x1FDC, 0x3E, 0x0D }, { 0x1FDD, 0x3D, 0x0D }, { 0x1FDE, 0x3B, 0x0D }, - { 0x1FDF, 0x25, 0x0D }, { 0x1FE0, 0x24, 0x0D }, { 0x1FE1, 0x1F, 0x0D }, - { 0x1FE2, 0x1E, 0x0D }, { 0x1FE3, 0x1D, 0x0D }, { 0x1FE4, 0x1C, 0x0D }, - { 0x1FE5, 0x1B, 0x0D }, { 0x1FE6, 0x1A, 0x0D }, { 0x1FE7, 0x19, 0x0D }, - { 0x1FE8, 0x18, 0x0D }, { 0x1FE9, 0x17, 0x0D }, { 0x1FEA, 0x16, 0x0D }, - { 0x1FEB, 0x15, 0x0D }, { 0x1FEC, 0x14, 0x0D }, { 0x1FED, 0x13, 0x0D }, - { 0x1FEE, 0x12, 0x0D }, { 0x1FEF, 0x11, 0x0D }, { 0x1FF0, 0x10, 0x0D }, - { 0x1FF1, 0x0F, 0x0D }, { 0x1FF2, 0x0E, 0x0D }, { 0x1FF3, 0x0D, 0x0D }, - { 0x1FF4, 0x0C, 0x0D }, { 0x1FF5, 0x0B, 0x0D }, { 0x1FF6, 0x09, 0x0D }, - { 0x1FF7, 0x08, 0x0D }, { 0x1FF8, 0x07, 0x0D }, { 0x1FF9, 0x06, 0x0D }, - { 0x1FFA, 0x05, 0x0D }, { 0x1FFB, 0x04, 0x0D }, { 0x1FFC, 0x03, 0x0D }, - { 0x1FFD, 0x02, 0x0D }, { 0x1FFE, 0x01, 0x0D }, { 0x1FFF, 0x00, 0x0D } -}; - } //end namespace SI void cFilterEEPG::ProcessNextFormat (bool FirstTime = false) @@ -3670,8 +3413,7 @@ void cFilterEEPG::ProcessNextFormat (bool FirstTime = false) //now start looking for next format to process int pid; - Format = -1; //unused value - for (int i = 0; i <= HIGHEST_FORMAT; i++) //find first format that is detected + for (int i = 0; i <= HIGHEST_FORMAT; i++){ //find first format that is detected if (UnprocessedFormat[i]) { isyslog ("EEPG: %s Extended EPG detected on pid %x.", FormatName[i], UnprocessedFormat[i]); Format = i; @@ -3680,10 +3422,10 @@ void cFilterEEPG::ProcessNextFormat (bool FirstTime = false) // AFTER ONCE protocols like MHW, SKY and NAGRA break; } - - if (Format == -1) { //there are no formats left to process - isyslog ("EEPG: Ended all processing"); - return; + if (i == HIGHEST_FORMAT) { //there are no formats left to process + isyslog ("EEPG: Ended all processing"); + return; + } } pid = UnprocessedFormat[Format]; //and reinstall its pid @@ -3739,13 +3481,12 @@ void cFilterEEPG::ProcessNextFormat (bool FirstTime = false) void cFilterEEPG::ProccessContinuous(u_short Pid, u_char Tid, int Length, const u_char *Data) { //0x39 Viasat, 0x0300 Dish Network EEPG, 0x0441 Bell ExpressVU EEPG - LogD(2, prep("Pid: 0x%02x Tid: %d Length: %d"), Pid, Tid, Length); + LogD(4, prep("Pid: 0x%02x Tid: %d Length: %d"), Pid, Tid, Length); cSchedulesLock SchedulesLock(true, 10); cSchedules *Schedules = (cSchedules*)(cSchedules::Schedules(SchedulesLock)); //Look for other satelite positions only if Dish/Bell ExpressVU for the moment hardcoded pid check - bool searchOtherSatPositions = Pid == 0x0300 || Pid == 0x0441; if(Schedules) - SI::cEIT2 EIT(Schedules, Source(), Tid, Data, searchOtherSatPositions); + SI::cEIT2 EIT(Schedules, Source(), Tid, Data); else//cEIT EIT (Schedules, Source (), Tid, Data); { @@ -3756,7 +3497,7 @@ void cFilterEEPG::ProccessContinuous(u_short Pid, u_char Tid, int Length, const cSchedulesLock SchedulesLock; cSchedules *Schedules = (cSchedules*)(cSchedules::Schedules(SchedulesLock)); if(Schedules) - SI::cEIT2 EIT(Schedules, Source(), Tid, Data, searchOtherSatPositions, true); + SI::cEIT2 EIT(Schedules, Source(), Tid, Data, true); //cEIT EIT (Schedules, Source (), Tid, Data, true); } -- cgit v1.2.3 From 9c53646dc79a1447fd3201b37412108d7f94ea28 Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Wed, 14 Sep 2011 16:10:56 +0200 Subject: fix compile --- eepg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 0183e12..b9258c8 100644 --- a/eepg.c +++ b/eepg.c @@ -367,7 +367,7 @@ void cFilterEEPG::SetStatus (bool On) if (!On) { FreeSummaries (); FreeTitles (); - Format = 0; + //Format = 0; ChannelsOk = false; NumberOfTables = 0; } else { @@ -3259,7 +3259,7 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat , DishExtendedEventDescriptor->getTheme(DishTheme) , DishExtendedEventDescriptor->getCategory(DishCategory)); pEvent->SetShortText(tmp); - free tmp; + free(tmp); } else pEvent->SetShortText(DishExtendedEventDescriptor->getShortText()); //LogD(2, prep("DishDescription: %s"), DishExtendedEventDescriptor->getText()); @@ -3416,7 +3416,7 @@ void cFilterEEPG::ProcessNextFormat (bool FirstTime = false) for (int i = 0; i <= HIGHEST_FORMAT; i++){ //find first format that is detected if (UnprocessedFormat[i]) { isyslog ("EEPG: %s Extended EPG detected on pid %x.", FormatName[i], UnprocessedFormat[i]); - Format = i; + Format = (EFormat)i; // highest format is processed first this way // make sure that CONT protocols like Premiere, Freesat are processed // AFTER ONCE protocols like MHW, SKY and NAGRA -- cgit v1.2.3 From cb5620b02a6cacbb38096e23f57e929bf07b722c Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Thu, 22 Sep 2011 17:15:43 +0200 Subject: fix huffman tables --- eepg.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index b9258c8..5f5f52b 100644 --- a/eepg.c +++ b/eepg.c @@ -10,7 +10,6 @@ * -Freesat patch written by dom /at/ suborbital.org.uk * * - * This code is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. @@ -2847,7 +2846,7 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat return; // only collect data for known channels } - LogD(4, prep("channelID: %s"), *channel->GetChannelID().ToString()); + LogD(4, prep("channelID: %s format:%d"), *channel->GetChannelID().ToString(), Format); #ifdef USE_NOEPG // only use epg from channels not blocked by noEPG-patch @@ -3009,7 +3008,7 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat SI::ShortEventDescriptor * ShortEventDescriptor = NULL; SI::DishDescriptor *DishExtendedEventDescriptor = NULL; SI::DishDescriptor *DishShortEventDescriptor = NULL; - uchar DishTheme = -1, DishCategory = -1; + uchar DishTheme = 0, DishCategory = 0; cLinkChannels *LinkChannels = NULL; @@ -3061,10 +3060,14 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat Contents[NumContents] = ((Nibble.getContentNibbleLevel1() & 0xF) << 4) | (Nibble.getContentNibbleLevel2() & 0xF); NumContents++; } - if (Format == DISH_BEV && DishTheme == -1) { + if (Format == DISH_BEV && NumContents == 1) { DishTheme = Nibble.getContentNibbleLevel2() & 0xF; DishCategory = ((Nibble.getUserNibble1() & 0xF) << 4) | (Nibble.getUserNibble2() & 0xF); + //LogD(2, prep("EEPGDEBUG:DishTheme:%x-DishCategory:%x)"), DishTheme, DishCategory); } + //LogD(2, prep("EEPGDEBUG:Nibble:%x-%x-%x-%x)"), Nibble.getContentNibbleLevel1(),Nibble.getContentNibbleLevel2() + // , Nibble.getUserNibble1(), Nibble.getUserNibble2()); + } pEvent->SetContents(Contents); } @@ -3259,6 +3262,7 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat , DishExtendedEventDescriptor->getTheme(DishTheme) , DishExtendedEventDescriptor->getCategory(DishCategory)); pEvent->SetShortText(tmp); + //LogD(2, prep("EEPGDEBUG:DishTheme:%x-DishCategory:%x)"), DishTheme, DishCategory); free(tmp); } else pEvent->SetShortText(DishExtendedEventDescriptor->getShortText()); -- cgit v1.2.3 From 8bab540df87c3f38369c772fd956f85cd655cf6e Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Thu, 13 Oct 2011 20:40:14 +0200 Subject: test merge branches --- eepg.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 5f5f52b..54ed4a6 100644 --- a/eepg.c +++ b/eepg.c @@ -3190,21 +3190,17 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat } break; case SI::DishExtendedEventDescriptorTag: { - SI::DishDescriptor *deed = new SI::DishDescriptor((SI::UnimplementedDescriptor *)d); - deed->Decompress(Tid); if (!DishExtendedEventDescriptor) { - DishExtendedEventDescriptor = deed; - d = NULL; // so that it is not deleted + DishExtendedEventDescriptor = new SI::DishDescriptor(); + DishExtendedEventDescriptor->Decompress(Tid, d->getData()); } HasExternalData = true; } break; case SI::DishShortEventDescriptorTag: { - SI::DishDescriptor *dsed = new SI::DishDescriptor((SI::UnimplementedDescriptor *)d); - dsed->Decompress(Tid); if (!DishShortEventDescriptor) { - DishShortEventDescriptor = dsed; - d = NULL; // so that it is not deleted + DishShortEventDescriptor = new SI::DishDescriptor(); + DishShortEventDescriptor->Decompress(Tid, d->getData()); } HasExternalData = true; } -- cgit v1.2.3 From 5993000031faa54c50a32cf3c5857f5c87593e5a Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Sun, 23 Oct 2011 17:35:31 +0200 Subject: add ratings take 2 --- eepg.c | 83 ++++++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 35 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 54ed4a6..096c85d 100644 --- a/eepg.c +++ b/eepg.c @@ -3006,9 +3006,9 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat SI::Descriptor * d; SI::ExtendedEventDescriptors * ExtendedEventDescriptors = NULL; SI::ShortEventDescriptor * ShortEventDescriptor = NULL; - SI::DishDescriptor *DishExtendedEventDescriptor = NULL; - SI::DishDescriptor *DishShortEventDescriptor = NULL; - uchar DishTheme = 0, DishCategory = 0; + //SI::DishDescriptor *DishExtendedEventDescriptor = NULL; + SI::DishDescriptor *DishEventDescriptor = NULL; + //uchar DishTheme = 0, DishCategory = 0; cLinkChannels *LinkChannels = NULL; @@ -3060,10 +3060,8 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat Contents[NumContents] = ((Nibble.getContentNibbleLevel1() & 0xF) << 4) | (Nibble.getContentNibbleLevel2() & 0xF); NumContents++; } - if (Format == DISH_BEV && NumContents == 1) { - DishTheme = Nibble.getContentNibbleLevel2() & 0xF; - DishCategory = ((Nibble.getUserNibble1() & 0xF) << 4) | (Nibble.getUserNibble2() & 0xF); - //LogD(2, prep("EEPGDEBUG:DishTheme:%x-DishCategory:%x)"), DishTheme, DishCategory); + if (DishEventDescriptor && NumContents == 1) { + DishEventDescriptor->setContent(Nibble); } //LogD(2, prep("EEPGDEBUG:Nibble:%x-%x-%x-%x)"), Nibble.getContentNibbleLevel1(),Nibble.getContentNibbleLevel2() // , Nibble.getUserNibble1(), Nibble.getUserNibble2()); @@ -3190,29 +3188,27 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat } break; case SI::DishExtendedEventDescriptorTag: { - if (!DishExtendedEventDescriptor) { - DishExtendedEventDescriptor = new SI::DishDescriptor(); - DishExtendedEventDescriptor->Decompress(Tid, d->getData()); + SI::UnimplementedDescriptor *deed = (SI::UnimplementedDescriptor *)d; + if (!DishEventDescriptor) { + DishEventDescriptor = new SI::DishDescriptor(); } + DishEventDescriptor->setExtendedtData(Tid, deed->getData()); HasExternalData = true; } break; case SI::DishShortEventDescriptorTag: { - if (!DishShortEventDescriptor) { - DishShortEventDescriptor = new SI::DishDescriptor(); - DishShortEventDescriptor->Decompress(Tid, d->getData()); + SI::UnimplementedDescriptor *dsed = (SI::UnimplementedDescriptor *)d; + if (!DishEventDescriptor) { + DishEventDescriptor = new SI::DishDescriptor(); } + DishEventDescriptor->setShortData(Tid, dsed->getData()); HasExternalData = true; } break; case SI::DishRatingDescriptorTag: { - if (d->getLength() == 4) { + if (d->getLength() == 4 && DishEventDescriptor) { uint16_t rating = d->getData().TwoBytes(2); - uint16_t newRating = (rating >> 10) & 0x07; - if (newRating == 0) newRating = 5; - if (newRating == 6) newRating = 0; - pEvent->SetParentalRating((newRating << 10) | (rating & 0x3FF)); -// pEvent->SetStarRating((rating >> 13) & 0x07); + DishEventDescriptor->setRating(rating); } } break; @@ -3246,30 +3242,47 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat } else if (!HasExternalData) pEvent->SetDescription (NULL); - if (DishShortEventDescriptor) { - pEvent->SetTitle(DishShortEventDescriptor->getText()); + if (DishEventDescriptor) { + if (DishEventDescriptor->getName()) + pEvent->SetTitle(DishEventDescriptor->getName()); //LogD(2, prep("channelID: %s DishTitle: %s"), *channel->GetChannelID().ToString(), DishShortEventDescriptor->getText()); - } - if (DishExtendedEventDescriptor) { - pEvent->SetDescription(DishExtendedEventDescriptor->getText()); +// pEvent->SetDescription(DishExtendedEventDescriptor->getText()); char *tmp; - if (DishTheme >= 0) { - Asprintf (&tmp, "%s - %s ~ %s", DishExtendedEventDescriptor->getShortText() - , DishExtendedEventDescriptor->getTheme(DishTheme) - , DishExtendedEventDescriptor->getCategory(DishCategory)); - pEvent->SetShortText(tmp); - //LogD(2, prep("EEPGDEBUG:DishTheme:%x-DishCategory:%x)"), DishTheme, DishCategory); - free(tmp); - } else - pEvent->SetShortText(DishExtendedEventDescriptor->getShortText()); + string fmt; + fmt = "%s"; + if (0 != strcmp(DishEventDescriptor->getShortText(),"") && DishEventDescriptor->hasTheme()) { + fmt += " - "; + } + fmt += "%s"; + if (DishEventDescriptor->hasTheme() && DishEventDescriptor->hasCategory()) { + fmt += " ~ "; + } + fmt += "%s"; + + Asprintf (&tmp, fmt.c_str(), DishEventDescriptor->getShortText() + , DishEventDescriptor->getTheme() + , DishEventDescriptor->getCategory()); + pEvent->SetShortText(tmp); + //LogD(2, prep("EEPGDEBUG:DishTheme:%x-DishCategory:%x)"), DishTheme, DishCategory); + free(tmp); + + fmt = "%s"; + if (0 != strcmp(DishEventDescriptor->getDescription(),"") && (0 != strcmp(DishEventDescriptor->getRating(),"") || 0 != strcmp(DishEventDescriptor->getStarRating(),""))) { + fmt += "|"; + } + fmt += "%s %s"; + Asprintf (&tmp, fmt.c_str(), DishEventDescriptor->getDescription() + , DishEventDescriptor->getRating() + , DishEventDescriptor->getStarRating()); + pEvent->SetDescription(tmp); + free(tmp); //LogD(2, prep("DishDescription: %s"), DishExtendedEventDescriptor->getText()); //LogD(2, prep("DishShortText: %s"), DishExtendedEventDescriptor->getShortText()); } } delete ExtendedEventDescriptors; delete ShortEventDescriptor; - delete DishExtendedEventDescriptor; - delete DishShortEventDescriptor; + delete DishEventDescriptor; pEvent->SetComponents (Components); -- cgit v1.2.3 From d7ffd368b513b9fc5e03842add42eb7ded562a61 Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Tue, 25 Oct 2011 23:54:36 +0200 Subject: Add Program ID / Series ID fix ratings --- eepg.c | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 096c85d..2d91546 100644 --- a/eepg.c +++ b/eepg.c @@ -2813,7 +2813,9 @@ namespace SI enum DescriptorTagExt { DishRatingDescriptorTag = 0x89, DishShortEventDescriptorTag = 0x91, - DishExtendedEventDescriptorTag = 0x92 }; + DishExtendedEventDescriptorTag = 0x92, + DishSeriesDescriptorTag = 0x96, + }; // typedef InheritEnum< DescriptorTagExt, SI::DescriptorTag > ExtendedDescriptorTag; @@ -3192,7 +3194,7 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat if (!DishEventDescriptor) { DishEventDescriptor = new SI::DishDescriptor(); } - DishEventDescriptor->setExtendedtData(Tid, deed->getData()); + DishEventDescriptor->setExtendedtData(Tid+1, deed->getData()); HasExternalData = true; } break; @@ -3201,15 +3203,27 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat if (!DishEventDescriptor) { DishEventDescriptor = new SI::DishDescriptor(); } - DishEventDescriptor->setShortData(Tid, dsed->getData()); + DishEventDescriptor->setShortData(Tid+1, dsed->getData()); HasExternalData = true; } break; case SI::DishRatingDescriptorTag: { - if (d->getLength() == 4 && DishEventDescriptor) { - uint16_t rating = d->getData().TwoBytes(2); - DishEventDescriptor->setRating(rating); - } + 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() == 4) { + if (!DishEventDescriptor) { + DishEventDescriptor = new SI::DishDescriptor(); + } + DishEventDescriptor->setEpisodeInfo(d->getData()); + } } break; default: @@ -3268,14 +3282,24 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat fmt = "%s"; if (0 != strcmp(DishEventDescriptor->getDescription(),"") && (0 != strcmp(DishEventDescriptor->getRating(),"") || 0 != strcmp(DishEventDescriptor->getStarRating(),""))) { - fmt += "|"; + fmt += "\nRating: "; + } + fmt += "%s %s"; + if (0 != strcmp(DishEventDescriptor->getProgramId(),"")) { + fmt += "\n Program ID: "; } fmt += "%s %s"; + fmt += DishEventDescriptor->getOriginalAirDate() == 0 ? "%s" : " Original Air Date: "; Asprintf (&tmp, fmt.c_str(), DishEventDescriptor->getDescription() , DishEventDescriptor->getRating() - , DishEventDescriptor->getStarRating()); + , DishEventDescriptor->getStarRating() + , DishEventDescriptor->getProgramId() + , DishEventDescriptor->getSeriesId() + , DishEventDescriptor->getOriginalAirDate() == 0 ? "" : ctime (&DishEventDescriptor->getOriginalAirDate())); pEvent->SetDescription(tmp); free(tmp); + + //LogD(2, prep("DishDescription: %s"), DishExtendedEventDescriptor->getText()); //LogD(2, prep("DishShortText: %s"), DishExtendedEventDescriptor->getShortText()); } -- cgit v1.2.3 From dc9d631a4f9f8ebdf59bbff1729ebc18763d87fb Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Wed, 26 Oct 2011 00:03:37 +0200 Subject: fix compile and warnings --- eepg.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 2d91546..0c75106 100644 --- a/eepg.c +++ b/eepg.c @@ -3289,13 +3289,14 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat fmt += "\n Program ID: "; } fmt += "%s %s"; - fmt += DishEventDescriptor->getOriginalAirDate() == 0 ? "%s" : " Original Air Date: "; + time_t orgAirDate = DishEventDescriptor->getOriginalAirDate(); + fmt += orgAirDate == 0 ? "%s" : " Original Air Date: "; Asprintf (&tmp, fmt.c_str(), DishEventDescriptor->getDescription() , DishEventDescriptor->getRating() , DishEventDescriptor->getStarRating() , DishEventDescriptor->getProgramId() , DishEventDescriptor->getSeriesId() - , DishEventDescriptor->getOriginalAirDate() == 0 ? "" : ctime (&DishEventDescriptor->getOriginalAirDate())); + , DishEventDescriptor->getOriginalAirDate() == 0 ? "" : ctime (&orgAirDate)); pEvent->SetDescription(tmp); free(tmp); -- cgit v1.2.3 From 210a655220cb3d19ab63d57e3d0b0b75a6257010 Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Thu, 27 Oct 2011 21:38:46 +0200 Subject: corect episode and time info --- eepg.c | 8742 ++++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 4376 insertions(+), 4366 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 0c75106..f436a3c 100644 --- a/eepg.c +++ b/eepg.c @@ -1,4366 +1,4376 @@ -/* - * Extended Epg plugin to VDR (C++) - * - * (C) 2008-2009 Dingo35 - * - * This code is based on: - * -Premiere plugin (C) 2005-2007 Stefan Huelswitt - * -mhwepg program (C) 2002, 2003 Jean-Claude Repetto - * -LoadEpg plugin written by Luca De Pieri - * -Freesat patch written by dom /at/ suborbital.org.uk - * - * - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * Or, point your browser to http://www.gnu.org/copyleft/gpl.html - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "eepg.h" -#include "dish.h" - -#include -#include -#include - -#define VERBOSE 3 -/* 0 = only print errors, 1 = print channels and themes, 2 = print channels, themes, titles, summaries 3 = debug mode */ -/* all is logged into /var/log/syslog */ - -#if APIVERSNUM < 10401 -#error You need at least VDR API version 1.4.1 for this plugin -#endif -#if APIVERSNUM < 10507 -#define trNOOP(s) (s) -#endif - -//#define DEBUG -//#define DEBUG2 - -/*#ifdef DEBUG -#define d(x) { (x); } -#else -#define d(x) ; -#endif -#ifdef DEBUG2 -#define d2(x) { (x); } -#else -#define d2(x) ; -#endif*/ - -#define PMT_SCAN_TIMEOUT 10 // seconds -#define PMT_SCAN_IDLE 3600 // seconds - -static const char *VERSION = "0.0.6pre"; -static const char *DESCRIPTION = trNOOP ("Parses Extended EPG data"); - -using namespace std; - -// --- cSetupEEPG ------------------------------------------------------- - -const char *optPats[] = { - "%s", - "%s (Option %d)", - "%s (O%d)", - "#%2$d %1$s", - "[%2$d] %1$s" -}; - -#define NUM_PATS (sizeof(optPats)/sizeof(char *)) - -char *cs_hexdump (int m, const uchar * buf, int n) -{ - int i; - static char dump[1024]; - - dump[i = 0] = '\0'; - m = (m) ? 3 : 2; - if (m * n >= (int) sizeof (dump)) - n = (sizeof (dump) / m) - 1; - while (i < n) - sprintf (dump + (m * i++), "%02X%s", *buf++, (m > 2) ? " " : ""); - return (dump); -} - -class cSetupEEPG -{ -public: - int OptPat; - int OrderInfo; - int RatingInfo; - int FixEpg; - int DisplayMessage; -#ifdef DEBUG - int LogLevel; -#endif - -public: - cSetupEEPG (void); -}; - -cSetupEEPG SetupPE; - -cSetupEEPG::cSetupEEPG (void) -{ - OptPat = 1; - OrderInfo = 1; - RatingInfo = 1; - FixEpg = 0; - DisplayMessage = 1; -#ifdef DEBUG - LogLevel = 0; -#endif - -} - -// --- cMenuSetupPremiereEpg ------------------------------------------------------------ - -class cMenuSetupPremiereEpg:public cMenuSetupPage -{ -private: - cSetupEEPG data; - const char *optDisp[NUM_PATS]; - char buff[NUM_PATS][32]; -protected: - virtual void Store (void); -public: - cMenuSetupPremiereEpg (void); -}; - -cMenuSetupPremiereEpg::cMenuSetupPremiereEpg (void) -{ - data = SetupPE; - SetSection (tr ("PremiereEPG")); - optDisp[0] = tr ("off"); - for (unsigned int i = 1; i < NUM_PATS; i++) { - snprintf (buff[i], sizeof (buff[i]), optPats[i], "Event", 1); - optDisp[i] = buff[i]; - } - Add (new cMenuEditStraItem (tr ("Tag option events"), &data.OptPat, NUM_PATS, optDisp)); - Add (new cMenuEditBoolItem (tr ("Show order information"), &data.OrderInfo)); - Add (new cMenuEditBoolItem (tr ("Show rating information"), &data.RatingInfo)); - Add (new cMenuEditBoolItem (tr ("Fix EPG data"), &data.FixEpg)); - Add (new cMenuEditBoolItem (tr ("Display summary message"), &data.DisplayMessage)); -#ifdef DEBUG - Add (new cMenuEditIntItem (tr ("Level of logging verbosity"), &data.LogLevel, 0, 5)); -#endif -} - -void cMenuSetupPremiereEpg::Store (void) -{ - SetupPE = data; - SetupStore ("OptionPattern", SetupPE.OptPat); - SetupStore ("OrderInfo", SetupPE.OrderInfo); - SetupStore ("RatingInfo", SetupPE.RatingInfo); - SetupStore ("FixEpg", SetupPE.FixEpg); - SetupStore ("DisplayMessage", SetupPE.DisplayMessage); -#ifdef DEBUG - SetupStore ("LogLevel", SetupPE.LogLevel); -#endif -} - -bool CheckLevel(int level) -{ -#ifdef DEBUG - if (SetupPE.LogLevel >= level) -#else - if (VERBOSE >= level) -#endif - { - return true; - } - return false; -} - -const char* PrepareLog(string message) -{ - message = "EEPG: " + message; - return message.c_str(); -} - -#define MAXSYSLOGBUF 256 - -//void LogVsyslog(int errLevel, const char * message, ...) -void LogVsyslog(int errLevel, int const& lineNum, const char * function, const char * message, ...) -{ - va_list ap; - char fmt[MAXSYSLOGBUF]; - if (errLevel == LOG_DEBUG) { - snprintf(fmt, sizeof(fmt), "[%d] %s:%d %s", cThread::ThreadId(), function, lineNum, message); - } else { - snprintf(fmt, sizeof(fmt), "[%d] %s", cThread::ThreadId(), message); - } - va_start(ap,message); - vsyslog ( errLevel, fmt, ap ); - va_end(ap); -} - -#define LogI(a, b...) void( CheckLevel(a) ? LogVsyslog ( LOG_INFO, __LINE__, __FUNCTION__, b ) : void() ) -#define LogE(a, b...) void( CheckLevel(a) ? LogVsyslog ( LOG_ERR, __LINE__, __FUNCTION__, b ) : void() ) -#define LogD(a, b...) void( CheckLevel(a) ? LogVsyslog ( LOG_DEBUG, __LINE__, __FUNCTION__, b ) : void() ) -//#define LogE(a, b...) void( CheckLevel(a) ? esyslog ( b ) : void() ) -//#define LogD(a, b...) void( CheckLevel(a) ? dsyslog ( b ) : void() ) -#define prep(s) PrepareLog(s) -#define prep2(s) s - -//void LogF(int level, const char * message, ...) __attribute__ ((format (printf,2,3))); - -//void LogF(int level, const char * message, ...) -//{ -// if (CheckLevel(level)) { -// va_list ap; -// va_start(ap,message); -// vsyslog (LOG_ERR, PrepareLog(message), ap ); -// va_end(ap); -// } -//} - -#define Asprintf(a, b, c...) void( asprintf(a, b, c) < 0 ? esyslog("memory allocation error - %s", b) : void() ) - -// --- CRC16 ------------------------------------------------------------------- - -#define POLY 0xA001 // CRC16 - -unsigned int crc16 (unsigned int crc, unsigned char const *p, int len) -{ - while (len--) { - crc ^= *p++; - for (int i = 0; i < 8; i++) - crc = (crc & 1) ? (crc >> 1) ^ POLY : (crc >> 1); - } - return crc & 0xFFFF; -} - -// --- cFilterEEPG ------------------------------------------------------ - -#define STARTTIME_BIAS (20*60) - -static int AvailableSources[32]; -static int NumberOfAvailableSources = 0; -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: - int pmtpid, pmtsid, pmtidx, pmtnext; - int UnprocessedFormat[HIGHEST_FORMAT + 1]; //stores the pid when a format is detected on this transponder, and that are not processed yet - int nEquivChannels, nChannels, nThemes, nTitles, nSummaries, NumberOfTables, Version; - int TitleCounter, SummaryCounter, NoSummaryCounter, RejectTableId; - bool EndChannels, EndThemes; //only used for ?? - int MHWStartTime; //only used for MHW1 - bool ChannelsOk; - //int Format; //the format that this filter currently is processing - std::map < int, int >ChannelSeq; // ChannelSeq[ChannelId] returns the recordnumber of the channel - - Summary_t *Summaries[MAX_TITLES]; - Title_t *Titles[MAX_TITLES]; - sChannel sChannels[MAX_CHANNELS]; - unsigned char Themes[MAX_THEMES][64]; - - std::map < unsigned short int, unsigned char *>buffer; //buffer[Table_Extension_Id] returns the pointer to the buffer for this TEI - std::map < unsigned short int, int >bufsize; //bufsize[Table_Extension_Id] returns the buffersize of the buffer for this TEI - unsigned short int NagraTIE[64]; //at this moment a max of 31 table_ids could be used, so 64 should be enough ....stores the Table_Extension_Id's of summaries received, so they can be processed. Processing while receiving somehow drops sections, the 0x0000 marker will be missed ... - unsigned short int NagraCounter; - - unsigned char InitialChannel[8]; - unsigned char InitialTitle[64]; - unsigned char InitialSummary[64]; - - void NextPmt (void); - void ProccessContinuous(u_short Pid, u_char Tid, int Length, const u_char *Data); -protected: - virtual void Process (u_short Pid, u_char Tid, const u_char * Data, int Length); - virtual void AddFilter (u_short Pid, u_char Tid); - virtual void AddFilter (u_short Pid, u_char Tid, unsigned char Mask); - virtual void ProcessNextFormat (bool FirstTime); - virtual int GetChannelsSKYBOX (const u_char * Data, int Length); - virtual bool GetThemesSKYBOX (void); - virtual bool ReadFileDictionary (void); //Reads Huffman tables for SKY - virtual int GetTitlesSKYBOX (const u_char * Data, int Length); - virtual int GetSummariesSKYBOX (const u_char * Data, int Length); - virtual int GetChannelsMHW (const u_char * Data, int Length, int MHW); //TODO replace MHW by Format? - virtual int GetThemesMHW1 (const u_char * Data, int Length); - virtual int GetNagra (const u_char * Data, int Length); - virtual void ProcessNagra (void); - virtual void GetTitlesNagra (const u_char * Data, int Length, unsigned short TableIdExtension); - virtual char *GetSummaryTextNagra (const u_char * DataStart, long int Offset, unsigned int EventId); - virtual int GetChannelsNagra (const u_char * Data, int Length); - virtual int GetThemesNagra (const u_char * Data, int Length, unsigned short TableIdExtension); - virtual int GetTitlesMHW1 (const u_char * Data, int Length); - virtual int GetSummariesMHW1 (const u_char * Data, int Length); - virtual int GetThemesMHW2 (const u_char * Data, int Length); - virtual int GetTitlesMHW2 (const u_char * Data, int Length); - virtual int GetSummariesMHW2 (const u_char * Data, int Length); - virtual void FreeSummaries (void); - virtual void FreeTitles (void); - virtual void PrepareToWriteToSchedule (sChannel * C, cSchedules * s, cSchedule * ps[MAX_EQUIVALENCES]); //gets a channel and returns an array of schedules that WriteToSchedule can write to. Call this routine before a batch of titles with the same ChannelId will be WriteToScheduled; batchsize can be 1 - virtual void FinishWriteToSchedule (sChannel * C, cSchedules * s, cSchedule * ps[MAX_EQUIVALENCES]); - virtual void WriteToSchedule (cSchedule * ps[MAX_EQUIVALENCES], unsigned short int NumberOfEquivalences, - unsigned int EventId, unsigned int StartTime, unsigned int Duration, char *Text, - char *SummText, unsigned short int ThemeId, unsigned short int TableId, - unsigned short int Version, char Rating = 0x00); - virtual void LoadIntoSchedule (void); - virtual void LoadEquivalentChannels (void); -public: - cFilterEEPG (void); - virtual void SetStatus (bool On); - void Trigger (void); -}; - -cFilterEEPG::cFilterEEPG (void) -{ - Trigger (); - //Set (0x00, 0x00); -} - -void cFilterEEPG::Trigger (void) -{ - LogI(3, prep("trigger\n")); - pmtpid = 0; - pmtidx = 0; - pmtnext = 0; -} - -void cFilterEEPG::SetStatus (bool On) -{ - LogI(0, prep("setstatus %d\n"), On); - if (!On) { - FreeSummaries (); - FreeTitles (); - //Format = 0; - ChannelsOk = false; - NumberOfTables = 0; - } else { - //Set(0x00,0x00); - for (int i = 0; i <= HIGHEST_FORMAT; i++) - UnprocessedFormat[i] = 0; //pid 0 is assumed to be nonvalid for EEPG transfers - AddFilter (0, 0); - } - cFilter::SetStatus (On); - Trigger (); -} - -void cFilterEEPG::NextPmt (void) -{ - Del (pmtpid, 0x02); - pmtpid = 0; - pmtidx++; - LogE(3, prep("PMT next\n")); -} - - - -// ------------------- Freesat ------------------- - -/* FreeSat Huffman decoder for VDR - * - * Insert GPL licence - */ - -/* The following features can be controlled: - * - * FREEVIEW_NO_SYSLOG - Disable use of isyslog - */ - -#ifndef FREEVIEW_NO_SYSLOG -#include -/* Logging via vdr */ -#ifndef isyslog -#define isyslog(a...) void( (SysLogLevel > 1) ? syslog_with_tid(LOG_INFO, a) : void() ) -#endif -void syslog_with_tid (int priority, const char *format, ...) __attribute__ ((format (printf, 2, 3))); -#else -#define isyslog(a...) fprintf(stderr,a) -#endif - - - -struct hufftab { - unsigned int value; - short bits; - char next; -}; - -#define START '\0' -#define STOP '\0' -#define ESCAPE '\1' - - -int freesat_decode_error = 0; /* If set an error has occurred during decoding */ - -static struct hufftab *tables[2][256]; -static int table_size[2][256]; - -/** \brief Convert a textual character description into a value - * - * \param str - Encoded (in someway) string - * - * \return Raw character - */ -static unsigned char resolve_char (char *str) -{ - int val; - if (strcmp (str, "ESCAPE") == 0) { - return ESCAPE; - } else if (strcmp (str, "STOP") == 0) { - return STOP; - } else if (strcmp (str, "START") == 0) { - return START; - } else if (sscanf (str, "0x%02x", &val) == 1) { - return val; - } - return str[0]; - - -} - - -/** \brief Decode a binary string into a value - * - * \param binary - Binary string to decode - * - * \return Decoded value - */ -static unsigned long decode_binary (char *binary) -{ - unsigned long mask = 0x80000000; - unsigned long maskval = 0; - unsigned long val = 0; - size_t i; - - for (i = 0; i < strlen (binary); i++) { - if (binary[i] == '1') { - val |= mask; - } - maskval |= mask; - mask >>= 1; - } - return val; -} - -/** \brief Load an individual freesat data file - * - * \param tableid - Table id that should be loaded - * \param filename - Filename to load - */ -static void load_file (int tableid, const char *filename) -{ - char buf[1024]; - char *from, *to, *binary; - FILE *fp; - - tableid--; - if ((fp = fopen (filename, "r")) != NULL) { - LogI(0, prep("Loading table %d Filename <%s>"), tableid + 1, filename); - - while (fgets (buf, sizeof (buf), fp) != NULL) { - from = binary = to = NULL; - int elems = sscanf (buf, "%a[^:]:%a[^:]:%a[^:]:", &from, &binary, &to); - if (elems == 3) { - int bin_len = strlen (binary); - int from_char = resolve_char (from); - char to_char = resolve_char (to); - unsigned long bin = decode_binary (binary); - int i = table_size[tableid][from_char]++; - - tables[tableid][from_char] = - (struct hufftab *) realloc (tables[tableid][from_char], (i + 1) * sizeof (tables[tableid][from_char][0])); - tables[tableid][from_char][i].value = bin; - tables[tableid][from_char][i].next = to_char; - tables[tableid][from_char][i].bits = bin_len; - free (from); - free (to); - free (binary); - } - } - fclose (fp); - } else { - LogE(0, prep("Cannot load <%s> for table %d"), filename, tableid + 1); - } -} - - -/** \brief Decode an EPG string as necessary - * - * \param src - Possibly encoded string - * \param size - Size of the buffer - * - * \retval NULL - Can't decode - * \return A decoded string - */ -char *freesat_huffman_decode (const unsigned char *src, size_t size) -{ - int tableid; - freesat_decode_error = 0; - - if (src[0] == 0x1f && (src[1] == 1 || src[1] == 2)) { - int uncompressed_len = 30; - char *uncompressed = (char *) calloc (1, uncompressed_len + 1); - unsigned value = 0, byte = 2, bit = 0; - int p = 0; - int lastch = START; - - tableid = src[1] - 1; - while (byte < 6 && byte < size) { - value |= src[byte] << ((5 - byte) * 8); - byte++; - } - //freesat_table_load (); /**< Load the tables as necessary */ - - do { - int found = 0; - unsigned bitShift = 0; - if (lastch == ESCAPE) { - char nextCh = (value >> 24) & 0xff; - found = 1; - // Encoded in the next 8 bits. - // Terminated by the first ASCII character. - bitShift = 8; - if ((nextCh & 0x80) == 0) - lastch = nextCh; - if (p >= uncompressed_len) { - uncompressed_len += 10; - uncompressed = (char *) realloc (uncompressed, uncompressed_len + 1); - } - uncompressed[p++] = nextCh; - uncompressed[p] = 0; - } else { - int j; - for (j = 0; j < table_size[tableid][lastch]; j++) { - unsigned mask = 0, maskbit = 0x80000000; - short kk; - for (kk = 0; kk < tables[tableid][lastch][j].bits; kk++) { - mask |= maskbit; - maskbit >>= 1; - } - if ((value & mask) == tables[tableid][lastch][j].value) { - char nextCh = tables[tableid][lastch][j].next; - bitShift = tables[tableid][lastch][j].bits; - if (nextCh != STOP && nextCh != ESCAPE) { - if (p >= uncompressed_len) { - uncompressed_len += 10; - uncompressed = (char *) realloc (uncompressed, uncompressed_len + 1); - } - uncompressed[p++] = nextCh; - uncompressed[p] = 0; - } - found = 1; - lastch = nextCh; - break; - } - } - } - if (found) { - // Shift up by the number of bits. - unsigned b; - for (b = 0; b < bitShift; b++) { - value = (value << 1) & 0xfffffffe; - if (byte < size) - value |= (src[byte] >> (7 - bit)) & 1; - if (bit == 7) { - bit = 0; - byte++; - } else - bit++; - } - } else { - LogE (0, prep("Missing table %d entry: <%s>"), tableid + 1, uncompressed); - // Entry missing in table. - return uncompressed; - } - } while (lastch != STOP && value != 0); - - return uncompressed; - } - return NULL; -} - -//here all declarations for global variables over all devices - -char *ConfDir; - -sNodeH *nH, H; - -//unsigned char DecodeErrorText[4096]; //TODO only used for debugging? - -int Yesterday; -int YesterdayEpoch; -int YesterdayEpochUTC; - -/* - * Convert local time to UTC - */ -time_t LocalTime2UTC (time_t t) -{ - struct tm *temp; - - temp = gmtime (&t); - temp->tm_isdst = -1; - return mktime (temp); -} - -/* - * Convert UTC to local time - */ -time_t UTC2LocalTime (time_t t) -{ - return 2 * t - LocalTime2UTC (t); -} - -void GetLocalTimeOffset (void) -{ - time_t timeLocal; - struct tm *tmCurrent; - - timeLocal = time (NULL); - timeLocal -= 86400; - tmCurrent = gmtime (&timeLocal); - Yesterday = tmCurrent->tm_wday; - tmCurrent->tm_hour = 0; - tmCurrent->tm_min = 0; - tmCurrent->tm_sec = 0; - tmCurrent->tm_isdst = -1; - YesterdayEpoch = mktime (tmCurrent); - YesterdayEpochUTC = UTC2LocalTime (mktime (tmCurrent)); -} - -void CleanString (unsigned char *String) -{ - -// LogD (1, prep("Unclean: %s"), String); - unsigned char *Src; - unsigned char *Dst; - int Spaces; - int pC; - Src = String; - Dst = String; - Spaces = 0; - pC = 0; - while (*Src) { - // corrections - if (*Src == 0x8c) { // iso-8859-2 LATIN CAPITAL LETTER S WITH ACUTE - *Src = 0xa6; - } - if (*Src == 0x8f) { // iso-8859-2 LATIN CAPITAL LETTER Z WITH ACUTE - *Src = 0xac; - } - - if (*Src!=0x0A && *Src < 0x20) { //don't remove newline - *Src = 0x20; - } - if (*Src == 0x20) { - Spaces++; - if (pC == 0) { - Spaces++; - } - } else { - Spaces = 0; - } - if (Spaces < 2) { - *Dst = *Src; - Dst++; - pC++; - } - Src++; - } - if (Spaces > 0) { - Dst--; - *Dst = 0; - } else { - *Dst = 0; - } -// LogD (1, prep("Clean: %s"), String); -} - -cChannel *GetChannelByID(tChannelID & channelID, bool searchOtherPos) -{ - cChannel *VC = Channels.GetByChannelID(channelID, true); - if(!VC && searchOtherPos){ - //look on other satpositions - for(int i = 0;i < NumberOfAvailableSources;i++){ - channelID = tChannelID(AvailableSources[i], channelID.Nid(), channelID.Tid(), channelID.Sid()); - VC = Channels.GetByChannelID(channelID, true); - if(VC){ - //found this actually on satellite nextdoor... - break; - } - } - } - - return VC; -} - -bool cFilterEEPG::GetThemesSKYBOX (void) //TODO can't we read this from the DVB stream? -{ - string FileName = ConfDir; - FILE *FileThemes; - char *Line; - char Buffer[256]; - if (Format == SKY_IT) - FileName += "/sky_it.themes"; - else if (Format == SKY_UK) - FileName += "/sky_uk.themes"; - else { - LogE (0, prep("Error, wrong format detected in GetThemesSKYBOX. Format = %i."), Format); - return false; - } - //asprintf( &FileName, "%s/%s", ConfDir, ( lProviders + CurrentProvider )->Parm3 ); - FileThemes = fopen (FileName.c_str(), "r"); - if (FileThemes == NULL) { - LogE (0, prep("Error opening file '%s'. %s"), FileName.c_str(), strerror (errno)); - return false; - } else { - //int id = 0; - nThemes = 0; - char string1[256]; - char string2[256]; - //sTheme *T; - while ((Line = fgets (Buffer, sizeof (Buffer), FileThemes)) != NULL) { - memset (string1, 0, sizeof (string1)); - memset (string2, 0, sizeof (string2)); - if (!isempty (Line)) { - //T = &Themes[nThemes]; - if (sscanf (Line, "%[^=] =%[^\n] ", string1, string2) == 2) { - snprintf ((char *) Themes[nThemes], 255, "%s", string2); - } else { - Themes[nThemes][0] = '\0'; - } - //id ++; - nThemes++; - } - } - fclose (FileThemes); - } - return true; -} - -bool cFilterEEPG::ReadFileDictionary (void) -{ - string FileName = ConfDir; - FILE *FileDict; - char *Line; - char Buffer[256]; - switch (Format) { - case SKY_IT: - FileName += "/sky_it.dict"; - break; - case SKY_UK: - FileName += "/sky_uk.dict"; - break; - case FREEVIEW: - FileName += "/freesat.t1"; - load_file (1, FileName.c_str()); - FileName = ConfDir; - FileName += "/freesat.t2"; - load_file (2, FileName.c_str()); - break; - default: - LogE (0 ,prep("Error, wrong format detected in ReadFileDictionary. Format = %i."), Format); - return false; - } - if ((Format == SKY_IT) || (Format == SKY_UK)) { //SKY - FileDict = fopen (FileName.c_str(), "r"); - if (FileDict == NULL) { - LogE (0, prep("Error opening file '%s'. %s"), FileName.c_str(), strerror (errno)); - return false; - } else { - int i; - int LenPrefix; - char string1[256]; - char string2[256]; - H.Value = NULL; - H.P0 = NULL; - H.P1 = NULL; - while ((Line = fgets (Buffer, sizeof (Buffer), FileDict)) != NULL) { - if (!isempty (Line)) { - memset (string1, 0, sizeof (string1)); - memset (string2, 0, sizeof (string2)); - if (sscanf (Line, "%c=%[^\n]\n", string1, string2) == 2 - || (sscanf (Line, "%[^=]=%[^\n]\n", string1, string2) == 2)) { - nH = &H; - LenPrefix = strlen (string2); - for (i = 0; i < LenPrefix; i++) { - switch (string2[i]) { - case '0': - if (nH->P0 == NULL) { - nH->P0 = new sNodeH (); - nH = nH->P0; - nH->Value = NULL; - nH->P0 = NULL; - nH->P1 = NULL; - if ((LenPrefix - 1) == i) { - Asprintf (&nH->Value, "%s", string1); - } - } else { - nH = nH->P0; - if (nH->Value != NULL || (LenPrefix - 1) == i) { - LogE (0 ,prep("Error, huffman prefix code already exists for \"%s\"=%s with '%s'"), string1, - string2, nH->Value); - } - } - break; - case '1': - if (nH->P1 == NULL) { - nH->P1 = new sNodeH (); - nH = nH->P1; - nH->Value = NULL; - nH->P0 = NULL; - nH->P1 = NULL; - if ((LenPrefix - 1) == i) { - Asprintf (&nH->Value, "%s", string1); - } - } else { - nH = nH->P1; - if (nH->Value != NULL || (LenPrefix - 1) == i) { - LogE (0, prep("Error, huffman prefix code already exists for \"%s\"=%s with '%s'"), string1, - string2, nH->Value); - } - } - break; - default: - break; - } - } - } - } - } - fclose (FileDict); - } - - // check tree huffman nodes - FileDict = fopen (FileName.c_str(), "r"); - if (FileDict) { - int i; - int LenPrefix; - char string1[256]; - char string2[256]; - while ((Line = fgets (Buffer, sizeof (Buffer), FileDict)) != NULL) { - if (!isempty (Line)) { - memset (string1, 0, sizeof (string1)); - memset (string2, 0, sizeof (string2)); - if (sscanf (Line, "%c=%[^\n]\n", string1, string2) == 2 - || (sscanf (Line, "%[^=]=%[^\n]\n", string1, string2) == 2)) { - nH = &H; - LenPrefix = strlen (string2); - for (i = 0; i < LenPrefix; i++) { - switch (string2[i]) { - case '0': - if (nH->P0 != NULL) { - nH = nH->P0; - } - break; - case '1': - if (nH->P1 != NULL) { - nH = nH->P1; - } - break; - default: - break; - } - } - if (nH->Value != NULL) { - if (memcmp (nH->Value, string1, strlen (nH->Value)) != 0) { - LogE (0, prep("Error, huffman prefix value '%s' not equal to '%s'"), nH->Value, string1); - } - } else { - LogE (0, prep("Error, huffman prefix value is not exists for \"%s\"=%s"), string1, string2); - } - } - } - } - fclose (FileDict); - } - } //if Format == 3 || Format == 4 - return true; -} - -int DecodeHuffmanCode (const u_char * Data, int Length, unsigned char *DecodeText) -{ - int i; - int p; - int q; - bool CodeError; - bool IsFound; - unsigned char Byte; - unsigned char lastByte; - unsigned char Mask; - unsigned char lastMask; - nH = &H; - p = 0; - q = 0; - DecodeText[0] = '\0'; - //DecodeErrorText[0] = '\0'; - CodeError = false; - IsFound = false; - lastByte = 0; - lastMask = 0; - for (i = 0; i < Length; i++) { - Byte = Data[i]; - Mask = 0x80; - if (i == 0) { - Mask = 0x20; - lastByte = i; - lastMask = Mask; - } -loop1: - if (IsFound) { - lastByte = i; - lastMask = Mask; - IsFound = false; - } - if ((Byte & Mask) == 0) { - if (CodeError) { - //DecodeErrorText[q] = 0x30; - q++; - goto nextloop1; - } - if (nH->P0 != NULL) { - nH = nH->P0; - if (nH->Value != NULL) { - memcpy (&DecodeText[p], nH->Value, strlen (nH->Value)); - p += strlen (nH->Value); - nH = &H; - IsFound = true; - } - } else { - memcpy (&DecodeText[p], "<...?...>", 9); - p += 9; - i = lastByte; - Byte = Data[lastByte]; - Mask = lastMask; - CodeError = true; - goto loop1; - } - } else { - if (CodeError) { - //DecodeErrorText[q] = 0x31; - q++; - goto nextloop1; - } - if (nH->P1 != NULL) { - nH = nH->P1; - if (nH->Value != NULL) { - memcpy (&DecodeText[p], nH->Value, strlen (nH->Value)); - p += strlen (nH->Value); - nH = &H; - IsFound = true; - } - } else { - memcpy (&DecodeText[p], "<...?...>", 9); - p += 9; - i = lastByte; - Byte = Data[lastByte]; - Mask = lastMask; - CodeError = true; - goto loop1; - } - } -nextloop1: - Mask = Mask >> 1; - if (Mask > 0) { - goto loop1; - } - } - DecodeText[p] = '\0'; - //DecodeErrorText[q] = '\0'; - return p; -} - -void decodeText2 (const unsigned char *from, int len, char *buffer, int buffsize) -{ - if (from[0] == 0x1f) { - char *temp = freesat_huffman_decode (from, len); - if (temp) { - len = strlen (temp); - len = len < buffsize - 1 ? len : buffsize - 1; - strncpy (buffer, temp, len); - buffer[len] = 0; - free (temp); - return; - } - } - - SI::String convStr; - SI::CharArray charArray; - charArray.assign(from, len); - convStr.setData(charArray, len); - //LogE(5, prep("decodeText2 from %s - length %d"), from, len); - convStr.getText(buffer, buffsize); - //LogE(5, prep("decodeText2 buffer %s - buffsize %d"), buffer, buffsize); -} - -void cFilterEEPG::LoadEquivalentChannels (void) -{ - char Buffer[1024]; - char *Line; - FILE *File; - string FileName = string(ConfDir) + "/" + EEPG_FILE_EQUIV; - - File = fopen (FileName.c_str(), "r"); - if (File) { - memset (Buffer, 0, sizeof (Buffer)); - char string1[256]; - char string2[256]; - char string3[256]; - int int1; - int int2; - int int3; - int int4; - while ((Line = fgets (Buffer, sizeof (Buffer), File)) != NULL) { - Line = compactspace (skipspace (stripspace (Line))); - if (!isempty (Line)) { - if (sscanf (Line, "%[^ ] %[^ ] %[^\n]\n", string1, string2, string3) == 3) { - if (string1[0] != '#' && string1[0] != ';') { - int1 = 0; //TODO: this could be made more readable - int2 = 0; - int3 = 0; - int4 = 0; - if (sscanf (string1, "%[^-]-%i -%i -%i ", string3, &int1, &int2, &int3) == 4) - if (sscanf (string2, "%[^-]-%i -%i -%i ", string3, &int1, &int2, &int3) == 4) { - if (sscanf (string1, "%[^-]-%i -%i -%i -%i ", string3, &int1, &int2, &int3, &int4) != 5) { - int4 = 0; - } - tChannelID OriginalChID = tChannelID (cSource::FromString (string3), int1, int2, int3, int4); - bool found = false; - int i = 0; - sChannel *C = NULL; - while (i < nChannels && (!found)) { - C = &sChannels[i]; - if (C->Src[0] == (unsigned int)cSource::FromString (string3) && C->Nid[0] == int1 - && C->Tid[0] == int2 && C->Sid[0] == int3) - found = true; - else - i++; - } - if (!found) { - LogI(2, prep("Warning: in equivalence file, cannot find original channel %s. Perhaps you are tuned to another transponder right now."), - string1); - } else { - cChannel *OriginalChannel = Channels.GetByChannelID (OriginalChID, false); - if (!OriginalChannel) - LogI(2, prep("Warning, not found epg channel \'%s\' in channels.conf. Equivalency is assumed to be valid, but perhaps you should check the entry in the equivalents file"), string1); //TODO: skip this ing? - if (sscanf (string2, "%[^-]-%i -%i -%i ", string3, &int1, &int2, &int3) == 4) { - if (sscanf (string2, "%[^-]-%i -%i -%i -%i ", string3, &int1, &int2, &int3, &int4) - != 5) { - int4 = 0; - } - tChannelID EquivChID = tChannelID (cSource::FromString (string3), int1, int2, int3, int4); - cChannel *EquivChannel = Channels.GetByChannelID (EquivChID, false); //TODO use valid function? - if (EquivChannel) { - if (C->NumberOfEquivalences < MAX_EQUIVALENCES) { - C->Src[C->NumberOfEquivalences] = EquivChannel->Source (); - C->Nid[C->NumberOfEquivalences] = EquivChannel->Nid (); - C->Tid[C->NumberOfEquivalences] = EquivChannel->Tid (); - C->Sid[C->NumberOfEquivalences] = EquivChannel->Sid (); - C->NumberOfEquivalences++; - nEquivChannels++; - LogI(3, prep("Added equivalent nr %i with Channel Id %s-%i-%i-%i to channel with id %i."), - C->NumberOfEquivalences, *cSource::ToString (C->Src[C->NumberOfEquivalences - 1]), - C->Nid[C->NumberOfEquivalences - 1], C->Tid[C->NumberOfEquivalences - 1], - C->Sid[C->NumberOfEquivalences - 1], i); - } else - LogE(0, prep("Error, channel with id %i has more than %i equivalences. Increase MAX_EQUIVALENCES."), - i, MAX_EQUIVALENCES); - } else - LogI(0, prep("Warning, not found equivalent channel \'%s\' in channels.conf"), string2); - } - } //else !found - } //if scanf string1 - } //if string1 - } //if scanf - } //if isempty - } //while - fclose (File); - } //if file -} //end of loadequiv - -/** - * \brief Get MHW channels - * - * \return 0 = fatal error, code 1 = success, code 2 = last item processed - */ -int cFilterEEPG::GetChannelsMHW (const u_char * Data, int Length, int MHW) -{ - if ((MHW == 1) || (nChannels == 0)) { //prevents MHW2 from reading channels twice while waiting for themes on same filter - sChannelMHW1 *Channel; - int Size, Off; - Size = sizeof (sChannelMHW1); - Off = 4; - if (MHW == 1) { - //Channel = (sChannelMHW1 *) (Data + 4); - nChannels = (Length - Off) / sizeof (sChannelMHW1); - } - if (MHW == 2) { - if (Length > 120) - nChannels = Data[120]; - else { - LogE(0, prep("Error, channels packet too short for MHW2.")); - return 0; - } - int pName = ((nChannels * 8) + 121); - if (Length > pName) { - //Channel = (sChannelMHW1 *) (Data + 120); - Size -= 14; //MHW2 is 14 bytes shorter - Off = 121; //and offset differs - } else { - LogE(0, prep("Error, channels length does not match pname.")); - return 0; - } - } - - if (nChannels > MAX_CHANNELS) { - LogE(0, prep("EEPG: Error, %i channels found more than %i"), nChannels, MAX_CHANNELS); - return 0; - } else { - LogI(1, "| ID | %-26.26s | %-22.22s | FND | %-8.8s |\n", "Channel ID", "Channel Name", "Sky Num."); - LogI(1, "|------|-%-26.26s-|-%-22.22s-|-----|-%-8.8s-|\n", "------------------------------", - "-----------------------------", "--------------------"); - int pName = ((nChannels * 8) + 121); //TODO double ... - for (int i = 0; i < nChannels; i++) { - Channel = (sChannelMHW1 *) (Data + Off); - sChannel *C = &sChannels[i]; - C->ChannelId = i; - ChannelSeq[C->ChannelId] = i; //fill lookup table to go from channel-id to sequence nr in table - C->SkyNumber = 0; - if (MHW == 1) - memcpy (C->Name, &Channel->Name, 16); //MHW1 - else { //MHW2 - int lenName = Data[pName] & 0x0f; - //LogD (1, prep("EEPGDebug: MHW2 lenName:%d"), lenName); - decodeText2(&Data[pName+1],lenName,(char*)C->Name,256); - //memcpy (C->Name, &Data[pName + 1], lenName); - //else - //memcpy (C->Name, &Data[pName + 1], 256); - pName += (lenName + 1); - } - C->NumberOfEquivalences = 1; //there is always an original channel. every equivalence adds 1 - C->Src[0] = Source (); //assume all EPG channels are on same satellite, if not, manage this via equivalents!!! - C->Nid[0] = HILO16 (Channel->NetworkId); - C->Tid[0] = HILO16 (Channel->TransportId); - C->Sid[0] = HILO16 (Channel->ServiceId); - tChannelID channelID = tChannelID (C->Src[0], C->Nid[0], C->Tid[0], C->Sid[0]); - cChannel *VC = GetChannelByID(channelID, true); - bool IsFound = (VC); - if(IsFound) { - C->Src[0] = VC->Source(); - } - CleanString (C->Name); - - LogI(1, "|% 5d | %-26.26s | %-22.22s | %-3.3s | % 6d |\n", C->ChannelId - , *tChannelID (C->Src[0], C->Nid[0], C->Tid[0], C->Sid[0]).ToString() - , C->Name, IsFound ? "YES" : "NO", C->SkyNumber); - - Off += Size; - } //for loop - } //else nChannels > MAX_CHANNELS - LoadEquivalentChannels (); - GetLocalTimeOffset (); //reread timing variables, only used for MHW - return 2; //obviously, when you get here, channels are read succesfully, but since all channels are sent at once, you can stop now - } //if nChannels == 0 - LogE (0, prep("Warning: Trying to read Channels more than once!")); -//you will only get here when GetChannelsMHW is called, and nChannels !=0, e.g. when multiple citpids cause channels to be read multiple times. Second time, do nothing, give error so that the rest of the chain is not restarted also. - return 0; -} - -/** - * \brief Get MHW1 Themes - * - * \return 0 = fatal error, code 1 = success, code 2 = last item processed - */ -int cFilterEEPG::GetThemesMHW1 (const u_char * Data, int Length) -{ //MHW1 Themes - if (Length > 19) { - sThemeMHW1 *Theme = (sThemeMHW1 *) (Data + 19); - nThemes = (Length - 19) / 15; - if (nThemes > MAX_THEMES) { - LogE(1, prep("Error, %i themes found more than %i"), nThemes, MAX_THEMES); - return 0; - } else { - LogI(1, "-------------THEMES FOUND--------------"); - int ThemeId = 0; - int Offset = 0; - const u_char *ThemesIndex = (Data + 3); - for (int i = 0; i < nThemes; i++) { - if (ThemesIndex[ThemeId] == i) { - Offset = (Offset + 15) & 0xf0; //TODO do not understand this - ThemeId++; - } - memcpy (&Themes[Offset][0], &Theme->Name, 15); - Themes[Offset][15] = '\0'; //trailing null - CleanString (Themes[Offset]); - LogI(1, prep("%.15s"), Themes[Offset]); - Offset++; - Theme++; - } - if ((nThemes * 15) + 19 != Length) { - LogE(0, "Themes error: buffer is smaller or bigger than sum of entries."); - return 0; - } else - return 2; - } - } - return 1; -} - -/** - * \brief Get MHW2 Themes - * - * \return 0 = fatal error, code 1 = success, code 2 = last item processed - */ -int cFilterEEPG::GetThemesMHW2 (const u_char * Data, int Length) -{ - if (!EndThemes) { //only process if not processed - int p1; - int p2; - int pThemeName = 0; - int pSubThemeName = 0; - int lenThemeName = 0; - int lenSubThemeName = 0; - int pThemeId = 0; - if (Length > 4) { - LogI(1, "-------------THEMES FOUND--------------"); - for (int i = 0; i < Data[4]; i++) { - p1 = ((Data[5 + i * 2] << 8) | Data[6 + i * 2]) + 3; - if (Length > p1) { - for (int ii = 0; ii <= (Data[p1] & 0x3f); ii++) { - p2 = ((Data[p1 + 1 + ii * 2] << 8) | Data[p1 + 2 + ii * 2]) + 3; - if (Length > p2) { - if (ii == 0) { - pThemeName = p2 + 1; - lenThemeName = Data[p2] & 0x1f; - lenSubThemeName = 0; - } else { - pSubThemeName = p2 + 1; - lenSubThemeName = Data[p2] & 0x1f; - } - if (Length >= (pThemeName + lenThemeName)) { - pThemeId = ((i & 0x3f) << 6) | (ii & 0x3f); - if (pThemeId > MAX_THEMES) { - LogE(1, prep("Error, something wrong with themes id calculation MaxThemes: %i pThemeID:%d"), MAX_THEMES, pThemeId); - return 0; //fatal error - } - if ((lenThemeName + 2) < 256) { - decodeText2(&Data[pThemeName],lenThemeName,(char*)Themes[pThemeId],256); - //memcpy (Themes[pThemeId], &Data[pThemeName], lenThemeName); - if (Length >= (pSubThemeName + lenSubThemeName)) - if (lenSubThemeName > 0) - if ((lenThemeName + lenSubThemeName + 2) < 256) { - Themes[pThemeId][lenThemeName] = ' '; - decodeText2(&Data[pSubThemeName],lenSubThemeName,(char*)&Themes[pThemeId][lenThemeName + 1],256); - //memcpy (&Themes[pThemeId][lenThemeName + 1], &Data[pSubThemeName], lenSubThemeName); - } - CleanString (Themes[pThemeId]); - LogI(1, prep("%.*s"), lenThemeName + 1 + lenSubThemeName, Themes[pThemeId]); - //isyslog ("%.15s", (lThemes + pThemeId)->Name); - nThemes++; - if (nThemes > MAX_THEMES) { - LogE(1, prep("Error, %i themes found more than %i"), nThemes, MAX_THEMES); - return 0; //fatal error - } - } - } else - return 1; //I assume non fatal error or success - } else - return 1; //I assume non fatal error or success - } - } else - return 1; //I assume non fatal error or success - } //for loop - //Del (Pid, Tid); - EndThemes = true; - return 2; //all themes read - } //if length - } //if !EndThemes - return 1; //non fatal or success -} - -/** - * \brief Get Nagra Summary text - * - * \param TitleEventId EventId passed from title, to check it against the eventId of the summary - * \return pointer to reserved part of memory with summary text in it, terminated by NULL - */ -char *cFilterEEPG::GetSummaryTextNagra (const u_char * DataStart, long int Offset, unsigned int TitleEventId) -//EventId is passed from title, to check it against the eventid of the summary -{ - u_char *p = (u_char *) DataStart + Offset; - sSummaryDataNagraGuide *SD = (sSummaryDataNagraGuide *) p; - - if (TitleEventId != HILO32 (SD->EventId)) { - LogI(0, prep("ERROR, Title has EventId %08x and points to Summary with EventId %08x."), TitleEventId, - HILO32 (SD->EventId)); - return 0; //return empty string - } - - if (CheckLevel(3)) { - if (SD->AlwaysZero1 != 0) - isyslog ("EEPGDEBUG: SummAlwaysZero1 is NOT ZERO:%x.", SD->AlwaysZero1); - if (SD->AlwaysZero2 != 0) - isyslog ("EEPGDEBUG: SummAlwaysZero2 is NOT ZERO:%x.", SD->AlwaysZero2); - if (SD->AlwaysZero3 != 0) - isyslog ("EEPGDEBUG: SummAlwaysZero3 is NOT ZERO:%x.", SD->AlwaysZero3); - if (SD->AlwaysZero4 != 0) - isyslog ("EEPGDEBUG: SummAlwaysZero4 is NOT ZERO:%x.", SD->AlwaysZero4); - if (SD->AlwaysZero5 != 0) - isyslog ("EEPGDEBUG: SummAlwaysZero5 is NOT ZERO:%x.", SD->AlwaysZero5); - if (SD->AlwaysZero6 != 0) - isyslog ("EEPGDEBUG: SummAlwaysZero6 is NOT ZERO:%x.", SD->AlwaysZero6); - if (SD->AlwaysZero7 != 0) - isyslog ("EEPGDEBUG: SummAlwaysZero7 is NOT ZERO:%x.", SD->AlwaysZero7); - if (SD->AlwaysZero8 != 0) - isyslog ("EEPGDEBUG: SummAlwaysZero8 is NOT ZERO:%x.", SD->AlwaysZero8); - if (SD->AlwaysZero9 != 0) - isyslog ("EEPGDEBUG: SummAlwaysZero9 is NOT ZERO:%x.", SD->AlwaysZero9); - - if (SD->Always1 != 0x31) //1970 - isyslog ("EEPGDEBUG: SummAlways1:%02x.", SD->Always1); - if (SD->Always9 != 0x39) - isyslog ("EEPGDEBUG: SummAlways9:%02x.", SD->Always9); - if (SD->Always7 != 0x37) - isyslog ("EEPGDEBUG: SummAlways7:%02x.", SD->Always7); - if (SD->Always0 != 0x30) - isyslog ("EEPGDEBUG: SummAlways0:%02x.", SD->Always0); - - if (SD->Always0x01 != 0x01) // 0x01 byte - isyslog ("EEPGDEBUG: Summ0x01 byte:%02x.", SD->Always0x01); - } - u_char *p2 = (u_char *) DataStart + HILO32 (SD->SummTxtOffset); - /* esyslog - ("EEPGDEBUG: EventId %08x NumberOfBlocks %02x BlockId %08x SummTxtOffset %08x *p2: %02x Unkn1:%02x, Unkn2:%02x.", - HILO32 (SD->EventId), SD->NumberOfBlocks, HILO32 (SD->BlockId), HILO32 (SD->SummTxtOffset), *p2, SD->Unknown1, - SD->Unknown2); - */ - unsigned char *Text = NULL; //makes first realloc work like malloc - int TotLength = 0; //and also makes empty summaries if *p2 != 0x4e - if (SD->NumberOfBlocks > 1) { - switch (*p2) { - case 0x4e: //valid summary text follows - { - bool LastTextBlock = false; - - do { //for all text parts - sSummaryTextNagraGuide *ST = (sSummaryTextNagraGuide *) p2; - p2 += 8; //skip fixed block - if (ST->AlwaysZero1 != 0) - LogD(3, prep("DEBUG: ST AlwaysZero1 is NOT ZERO:%x."), ST->AlwaysZero1); - if (ST->Always0x4e != 0x4e) { - LogI(0, prep("DEBUG: ST Always0x4e is NOT 0x4e:%x."), ST->AlwaysZero1); - return 0; //fatal error, empty text - } - LogI(5, prep("DEBUG: Textnr %i, Lasttxt %i."), ST->TextNr, ST->LastTextNr); - int SummaryLength = ST->Textlength; - - Text = (unsigned char *) realloc (Text, SummaryLength + TotLength); - if (Text == NULL) { - LogI(0, prep("Summaries memory allocation error.")); - return 0; //empty text - } - memcpy (Text + TotLength, p2, SummaryLength); //append new textpart - TotLength += SummaryLength; - p2 += ST->Textlength; //skip text - - LastTextBlock = ((ST->LastTextNr == 0) || (ST->TextNr >= ST->LastTextNr)); - } while (!LastTextBlock); - Text = (unsigned char *) realloc (Text, 1 + TotLength); //allocate 1 extra byte - Text[TotLength] = '\0'; //terminate string by NULL char - LogD(5, prep("DEBUG: Full Text:%s."), Text); - - break; - } - case 0x8c: //"Geen uitzending" "Geen informatie beschikbaar" e.d. - { - sSummaryGBRNagraGuide *GBR = (sSummaryGBRNagraGuide *) p2; - - p2 += 16; //skip fixed part, point to byte after Nextlength - if (CheckLevel(3)) { - if (GBR->AlwaysZero1 != 0) - isyslog ("EEPGDEBUG: GBR AlwaysZero1 is NOT ZERO:%x.", GBR->AlwaysZero1); - if (GBR->AlwaysZero2 != 0) - isyslog ("EEPGDEBUG: GBR AlwaysZero2 is NOT ZERO:%x.", GBR->AlwaysZero2); - if (GBR->AlwaysZero3 != 0) - isyslog ("EEPGDEBUG: GBR AlwaysZero3 is NOT ZERO:%x.", GBR->AlwaysZero3); - if (GBR->AlwaysZero4 != 0) - isyslog ("EEPGDEBUG: GBR AlwaysZero4 is NOT ZERO:%x.", GBR->AlwaysZero4); - - isyslog ("EEPGDEBUG: Blocklength: %02x Data %02x %02x %02x %02x %02x %02x %02x %02x %02x", GBR->Blocklength, - GBR->Un1, GBR->Un2, GBR->Un3, GBR->Un4, GBR->Un5, GBR->Un6, GBR->Un7, GBR->Un8, GBR->Un9); - for (int i = 0; i < GBR->Nextlength; i += 2) - isyslog ("GBR Extradata %02x %02x.", *(p2 + i), *(p2 + i + 1)); - } - } - break; - default: - LogE(0, prep("ERROR *p2 has strange value: EventId %08x NumberOfBlocks %02x BlockId %08x SummTxtOffset %08x *p2: %02x Unkn1:%02x, Unkn2:%02x."), - HILO32 (SD->EventId), SD->NumberOfBlocks, HILO32 (SD->BlockId), HILO32 (SD->SummTxtOffset), *p2, - SD->Unknown1, SD->Unknown2); - break; - } //end of switch - } //NrOfBlocks > 1 - - if (TotLength == 0) - Text = NULL; - - p += 29; //skip fixed part of block - if (SD->NumberOfBlocks == 1) - p -= 4; //in this case there is NO summarytext AND no GBR??!! - for (int i = 1; i < (SD->NumberOfBlocks - 1); i++) { - LogD(3, prep("DEBUG: Extra Blockinfo: %02x %02x %02x %02x."), *p, *(p + 1), *(p + 2), *(p + 3)); - p += 4; //skip this extra blockinfo - } - return (char *) Text; -} - -void cFilterEEPG::PrepareToWriteToSchedule (sChannel * C, cSchedules * s, cSchedule * ps[MAX_EQUIVALENCES]) //gets a channel and returns an array of schedules that WriteToSchedule can write to. Call this routine before a batch of titles with the same ChannelId will be WriteToScheduled; batchsize can be 1 -{ - for (int eq = 0; eq < C->NumberOfEquivalences; eq++) { - tChannelID channelID = tChannelID (C->Src[eq], C->Nid[eq], C->Tid[eq], C->Sid[eq]); -#ifdef USE_NOEPG - if (allowedEPG (channelID) && (channelID.Valid ())) -#else - if (channelID.Valid ()) //only add channels that are known to vdr -#endif /* NOEPG */ - ps[eq] = s->AddSchedule (channelID); //open a a schedule for each equivalent channel - else { - ps[eq] = NULL; - LogE(5, prep("ERROR: Titleblock has invalid (equivalent) channel ID: Equivalence: %i, Source:%x, C->Nid:%x,C->Tid:%x,C->Sid:%x."), - eq, C->Src[eq], C->Nid[eq], C->Tid[eq], C->Sid[eq]); - } - } -} - -void cFilterEEPG::FinishWriteToSchedule (sChannel * C, cSchedules * s, cSchedule * ps[MAX_EQUIVALENCES]) -{ - for (int eq = 0; eq < C->NumberOfEquivalences; eq++) - if (ps[eq]) { - ps[eq]->Sort (); - s->SetModified (ps[eq]); - } -} - -/** - * \brief write event to schedule - * - * \param Duration the Duration of the event in minutes - * \param ps points to array of schedules ps[eq], where eq is equivalence number of the channel. If channelId is invalid then ps[eq]=NULL - */ -void cFilterEEPG::WriteToSchedule (cSchedule * ps[MAX_EQUIVALENCES], unsigned short int NumberOfEquivalences, unsigned int EventId, unsigned int StartTime, unsigned int Duration, char *Text, char *SummText, unsigned short int ThemeId, unsigned short int TableId, unsigned short int Version, char Rating) -{ - bool WrittenTitle = false; - bool WrittenSummary = false; - for (int eq = 0; eq < NumberOfEquivalences; eq++) { - if (ps[eq]) { - cEvent *Event = NULL; - - Event = (cEvent *) ps[eq]->GetEvent (EventId); //since Nagra uses consistent EventIds, try this first - bool TableIdMatches = false; - if (Event) - TableIdMatches = (Event->TableID() == TableId); - if (!Event || !TableIdMatches || abs(Event->StartTime() - (time_t) StartTime) > Duration * 60) //if EventId does not match, or it matched with wrong TableId, then try with StartTime - Event = (cEvent *) ps[eq]->GetEvent (EventId, StartTime); - - cEvent *newEvent = NULL; - if (!Event) { //event is new - Event = newEvent = new cEvent (EventId); - Event->SetSeen (); - } else if (Event->TableID() < TableId) { //existing table may not be overwritten - RejectTableId++; - //esyslog ("EEPGDEBUG: Rejecting Event, existing TableID:%x, new TableID:%x.", Event->TableID (), - // TableId); - Event = NULL; - } - - if (Event) { - Event->SetEventID (EventId); //otherwise the summary cannot be added later - Event->SetTableID (TableId); //TableID 0 is reserved for external epg, will not be overwritten; the lower the TableID, the more actual it is - Event->SetVersion (Version); //TODO use version and tableID to decide whether to update; TODO add language code - Event->SetStartTime (StartTime); - Event->SetDuration (Duration * 60); - if (Rating) { - Event->SetParentalRating(Rating); - } - char *tmp; - if (Text != 0x00) { - WrittenTitle = true; - CleanString ((uchar *) Text); - Event->SetTitle (Text); - } - Asprintf (&tmp, "%s - %d\'", Themes[ThemeId], Duration); - Event->SetShortText (tmp); - //strreplace(t, '|', '\n'); - if (SummText != 0x00) { - WrittenSummary = true; - CleanString ((uchar *) SummText); - Event->SetDescription (SummText); - } - free (tmp); - if (newEvent) - ps[eq]->AddEvent (newEvent); - //newEvent->FixEpgBugs (); causes segfault - } - /* else - esyslog ("EEPG: ERROR, somehow not able to add/update event.");*///at this moment only reports RejectTableId events - if (CheckLevel(4)) { - isyslog ("EEPG: Title:%i, Summary:%i I would put into schedule:", TitleCounter, SummaryCounter); - //isyslog ("C %s-%i-%i-%i\n", *cSource::ToString (C->Src[eq]), C->Nid[eq], C->Tid[eq], C->Sid[eq]); - isyslog ("E %u %u %u 01 FF\n", EventId, StartTime, Duration * 60); - isyslog ("T %s\n", Text); - isyslog ("S %s - %d\'\n", Themes[ThemeId], Duration); - isyslog ("D %s\n", SummText); - isyslog ("e\nc\n.\n"); - } - } //if ps[eq] - } //for eq - if (WrittenTitle) - TitleCounter++; - if (WrittenSummary) - SummaryCounter++; -} - -/** - * \brief Get Nagra titles - * - * \param TableIdExtension the TIE from the relevant summary sections! - */ -void cFilterEEPG::GetTitlesNagra (const u_char * Data, int Length, unsigned short TableIdExtension) -{ - u_char *p = (u_char *) Data; - u_char *DataEnd = (u_char *) Data + Length; - u_char *next_p; - unsigned short int MonthdayTitles = ((TableIdExtension & 0x1ff) >> 4); //Day is coded in day of the month - time_t timeLocal; - struct tm *tmCurrent; - - timeLocal = time (NULL); - tmCurrent = gmtime (&timeLocal); //gmtime gives UTC; only used for getting current year and current day of the month... - unsigned short int CurrentMonthday = tmCurrent->tm_mday; - unsigned short int CurrentYear = tmCurrent->tm_year; - unsigned short int CurrentMonth = tmCurrent->tm_mon; - //esyslog("EEPGDEBUG: CurrentMonthday=%i, TableIdExtension:%04x, MonthdayTitles=%i.",CurrentMonthday,TableIdExtension, MonthdayTitles); - cSchedulesLock SchedulesLock (true); - cSchedules *s = (cSchedules *) cSchedules::Schedules (SchedulesLock); - do { //process each block of titles - sTitleBlockNagraGuide *TB = (sTitleBlockNagraGuide *) p; - int ChannelId = HILO16 (TB->ChannelId); - int Blocklength = HILO16 (TB->Blocklength); - long int NumberOfTitles = HILO32 (TB->NumberOfTitles); - - LogD(3, prep("DEBUG: ChannelId %04x, Blocklength %04x, NumberOfTitles %lu."), ChannelId, Blocklength, - NumberOfTitles); - p += 4; //skip ChannelId and Blocklength - next_p = p + Blocklength; - if (next_p > DataEnd) { //only process if block is complete - LogE(0, prep("ERROR, Block exceeds end of Data. p:%p, Blocklength:%x,DataEnd:%p."), p, Blocklength, DataEnd); - return; //fatal error, this should never happen - } - p += 4; //skip Titlenumber - - sChannel *C = &sChannels[ChannelSeq[ChannelId]]; //find channel - cSchedule *ps[MAX_EQUIVALENCES]; - PrepareToWriteToSchedule (C, s, ps); - - for (int i = 0; i < NumberOfTitles; i++) { //process each title within block - sTitleNagraGuide *Title = (sTitleNagraGuide *) p; - unsigned int EventId = HILO32 (Title->EventId); - - unsigned int StartTime = Title->StartTimeHigh << 5 | Title->StartTimeLow; - int Hours = (StartTime / 60); - int Minutes = StartTime % 60; - - /*StartTime */ - tmCurrent->tm_year = CurrentYear; - tmCurrent->tm_mon = CurrentMonth; - tmCurrent->tm_mday = MonthdayTitles; - tmCurrent->tm_hour = 0; - tmCurrent->tm_min = StartTime; //if starttime is bigger than 1 hour, mktime will correct this! - tmCurrent->tm_sec = 0; - tmCurrent->tm_isdst = -1; //now correct with daylight savings - if (MonthdayTitles < CurrentMonthday - 7) //the titles that are older than one week are not from the past, but from next month! - //at first this was set at -1 day (= yesterday), but sometimes providers send old data which then - //end up in next months schedule ... - tmCurrent->tm_mon++; //if a year border is passed, mktime will take care of this! - StartTime = UTC2LocalTime (mktime (tmCurrent)); //VDR stores its times in UTC, but wants its input in local time... - - char *Text = NULL; - u_char *t = (u_char *) Data + HILO32 (Title->OffsetToText); - //u_char *t2 = (u_char *) Data + HILO32 (Title->OffsetToText2); - if (t >= DataEnd) - LogE(0, prep("ERROR, Title Text out of range: t:%p, DataEnd:%p, Data:%p, Length:%i."), t, DataEnd, Data, - Length); - else { - Asprintf (&Text, "%.*s", *t, t + 1); //FIXME second text string is not processed right now - //asprintf (&Text, "%.*s %.*s", *t, t + 1, *t2, t2 + 1); - - //now get summary texts - u_char *DataStartSummaries = buffer[TableIdExtension] + 4; - unsigned int DataLengthSummaries = bufsize[TableIdExtension] - 4; - char *SummText = NULL; - if (HILO32 (Title->SumDataOffset) >= DataLengthSummaries) - LogE(0, prep("ERROR, SumDataOffset out of range: Title->SumDataOffset:%i, DataLengthSummaries:%i."), HILO32 (Title->SumDataOffset),DataLengthSummaries); - else - SummText = GetSummaryTextNagra (DataStartSummaries, HILO32 (Title->SumDataOffset), EventId); - - LogD(3, prep("DEBUG: Eventid: %08x ChannelId:%x, Starttime %02i:%02i, Duration %i, OffsetToText:%08x, OffsetToText2:%08x, SumDataOffset:%08x ThemeId:%x Title:%s \n SummaryText:%s"), - EventId, ChannelId, Hours, Minutes, Title->Duration, - HILO32 (Title->OffsetToText), HILO32 (Title->OffsetToText2), - HILO32 (Title->SumDataOffset), Title->ThemeId, Text, SummText); - - if (Themes[Title->ThemeId][0] == 0x00) //if detailed themeid is not known, get global themeid - Title->ThemeId &= 0xf0; - WriteToSchedule (ps, C->NumberOfEquivalences, EventId, StartTime, Title->Duration, Text, SummText, - Title->ThemeId, NAGRA_TABLE_ID, Version); - - if (Text != NULL) - free (Text); - Text = NULL; - if (SummText != NULL) - free (SummText); - SummText = NULL; - } - - if (CheckLevel(3)) { - if (Title->AlwaysZero16 != 0) - isyslog ("EEPGDEBUG: TitleAlwaysZero16 (3bits) is NOT ZERO:%x.", Title->AlwaysZero16); - if (Title->AlwaysZero17 != 0) - isyslog ("EEPGDEBUG: TitleAlwaysZero17 is NOT ZERO:%x.", Title->AlwaysZero17); - if (Title->AlwaysZero1 != 0) - isyslog ("EEPGDEBUG: TitleAlwaysZero1 is NOT ZERO:%x.", Title->AlwaysZero1); - if (Title->AlwaysZero2 != 0) - isyslog ("EEPGDEBUG: TitleAlwaysZero2 is NOT ZERO:%x.", Title->AlwaysZero2); - if (Title->AlwaysZero3 != 0) - isyslog ("EEPGDEBUG: TitleAlwaysZero3 is NOT ZERO:%x.", Title->AlwaysZero3); - if (Title->AlwaysZero4 != 0) - isyslog ("EEPGDEBUG: TitleAlwaysZero4 is NOT ZERO:%x.", Title->AlwaysZero4); - if (Title->AlwaysZero5 != 0) - isyslog ("EEPGDEBUG: TitleAlwaysZero5 is NOT ZERO:%x.", Title->AlwaysZero5); - if (Title->AlwaysZero8 != 0) - isyslog ("EEPGDEBUG: TitleAlwaysZero8 is NOT ZERO:%x.", Title->AlwaysZero8); - if (Title->AlwaysZero9 != 0) - isyslog ("EEPGDEBUG: TitleAlwaysZero9 is NOT ZERO:%x.", Title->AlwaysZero9); - if (Title->AlwaysZero10 != 0) - isyslog ("EEPGDEBUG: TitleAlwaysZero10 is NOT ZERO:%x.", Title->AlwaysZero10); - if (Title->AlwaysZero11 != 0) - isyslog ("EEPGDEBUG: TitleAlwaysZero11 is NOT ZERO:%x.", Title->AlwaysZero11); - } - p += 30; //next title - } //end for titles - - FinishWriteToSchedule (C, s, ps); - p = next_p; - } while (p < DataEnd); //end of TitleBlock -} - -int cFilterEEPG::GetThemesNagra (const u_char * Data, int Length, unsigned short TableIdExtension) //return code 0 = fatal error, code 1 = success, code 2 = last item processed -{ - u_char *DataStart = (u_char *) Data; - u_char *p = DataStart; //TODO Language code terminated by 0 is ignored - u_char *DataEnd = DataStart + Length; - u_char *DataStartTitles = buffer[TableIdExtension] + 4; - u_char *DataEndTitles = DataStartTitles + bufsize[TableIdExtension] - 4; - if (Length == 0) { - LogI(1, prep("NO THEMES FOUND")); - return 2; - } - - int NumberOfThemes = (*p << 24) | *(p + 1) << 16 | *(p + 2) << 8 | *(p + 3); - p += 4; //skip number of themes block - //isyslog ("EEPG: found %i themes.", NumberOfThemes); - - if ((nThemes == 0)) - LogI(1, "-------------THEMES FOUND--------------"); - - for (int i = 0; i < NumberOfThemes; i++) { - int Textlength = *p; - p++; //skip textlength byte - u_char *Text = p; - u_char ThemeId = 0; - p += Textlength; //skip text - int NrOfBlocks = (*p << 8) | *(p + 1); - p += 2; //skip nrofblocks - bool AnyDoubt = false; - u_char *p2 = p; - p += NrOfBlocks * 8; - for (int j = 0; j < NrOfBlocks; j++) { - sThemesTitlesNagraGuide *TT = (sThemesTitlesNagraGuide *) p2; - p2 += 8; //skip block - u_char *NewThemeId = DataStartTitles + HILO32 (TT->TitleOffset) + 28; - if (NewThemeId >= DataEndTitles) - LogE(0, prep("ERROR, ThemeId out of range: NewThemeId:%p, DataEndTitles:%p, DataStartTitles:%p."), NewThemeId, - DataEndTitles, DataStartTitles); - else { - //esyslog("EEPGDEBUG: NewThemeId:%02x, Text:%s.",*NewThemeId, Text); - if (Themes[*NewThemeId][0] != 0x00) { //theme is already filled, break off - AnyDoubt = true; - break; - } - if (j == 0) //first block - ThemeId = *NewThemeId; - else if (ThemeId != *NewThemeId) { //different theme ids in block - if ((ThemeId & 0xf0) != (*NewThemeId & 0xf0)) { //major nible of themeid does not correspond - LogE(3, prep("ERROR, Theme has multiple indices which differ in major nibble, old index = %x, new index = %x. Ignoring both indices."), - ThemeId, *NewThemeId); - AnyDoubt = true; - break; - } else if ((ThemeId & 0x0f) != 0) //ThemeId is like 1a, 2a, not like 10,20. So it is minor in tree-structure, and it should be labeled in major part of tree - ThemeId = *NewThemeId; //lets hope new themeid is major, if not, it has not worsened.... - } - } //else NewThemeId >= DataEndTitles - if (CheckLevel(3)) { - if (TT->Always1 != 1) - isyslog ("EEPGDEBUG: TT Always1 is NOT 1:%x.", TT->Always1); - if (TT->AlwaysZero1 != 0) - isyslog ("EEPGDEBUG: TT AlwaysZero1 is NOT ZERO:%x.", TT->AlwaysZero1); - if (TT->AlwaysZero2 != 0) - isyslog ("EEPGDEBUG: TT AlwaysZero2 is NOT ZERO:%x.", TT->AlwaysZero2); - } - } //for nrofblocks - // esyslog("EEPGDEBUG: AnyDoubt:%x.",AnyDoubt); - if (!AnyDoubt) { - if (Textlength > 63) - Textlength = 63; //leave room for trailing NULL - if (Themes[ThemeId][0] != 0) { - LogE(0, prep("Trying to add new theme, but Id already exists. ThemeId = %x, Old theme with this Id:%s, new theme: %s."), - ThemeId, Themes[ThemeId], Text); - continue; - } - memcpy (&Themes[ThemeId], Text, Textlength); - Themes[ThemeId][Textlength] = '\0'; //trailing NULL - CleanString (Themes[ThemeId]); - nThemes++; - LogI(1, prep("%02x %s"), ThemeId, Themes[ThemeId]); - } - } //for NumberOfThemes - if (p != DataEnd) { - LogE(0, prep("Themes error: buffer is smaller or bigger than sum of entries. p:%p,DataEnd:%p"), p, DataEnd); - return 0; - } else - return 2; -} - -/** - * \brief Get Nagra channels - * - * \return 0 = fatal error, code 1 = success, code 2 = last item processed - */ -int cFilterEEPG::GetChannelsNagra (const u_char * Data, int Length) -{ - u_char *DataStart = (u_char *) Data; - u_char *p = DataStart; - u_char *DataEnd = DataStart + Length; - - nChannels = (*p << 24) | *(p + 1) << 16 | *(p + 2) << 8 | *(p + 3); - p += 4; //skip numberofchannels - if (CheckLevel(1)) { - isyslog ("| ID | %-26.26s | %-22.22s | FND | %-8.8s |\n", "Channel ID", "Channel Name", "Sky Num."); - isyslog ("|------|-%-26.26s-|-%-22.22s-|-----|-%-8.8s-|\n", "------------------------------", - "-----------------------------", "--------------------"); - } - - for (int j = 0; j < nChannels; j++) { - sChannelsNagraGuide *Channel = (sChannelsNagraGuide *) p; - sChannel *C = &sChannels[j]; - C->ChannelId = j + 1; //Nagra starts numbering at 1 - ChannelSeq[C->ChannelId] = j; //fill lookup table to go from channel-id to sequence nr in table; lookuptable starts with 0 - C->SkyNumber = 0; - C->NumberOfEquivalences = 1; //there is always an original channel. every equivalence adds 1 - C->Src[0] = Source(); //assume all EPG channels are on same satellite, if not, manage this via equivalents!!! - C->Nid[0] = HILO16 (Channel->NetworkId); - C->Tid[0] = HILO16 (Channel->TransportId); - C->Sid[0] = HILO16 (Channel->ServiceId); - tChannelID channelID = tChannelID(C->Src[0], C->Nid[0], C->Tid[0], C->Sid[0]); - cChannel *VC = GetChannelByID(channelID, true); - bool IsFound = (VC); - if(IsFound) { - strncpy((char*)(C->Name), VC->Name (), 64); - C->Src[0] = VC->Source(); - CleanString (C->Name); - } - else - C->Name[0] = '\0'; //empty string - - LogI(1, "|% 5d | %-26.26s | %-22.22s | %-3.3s | % 6d |\n", C->ChannelId - , *channelID.ToString(), C->Name, IsFound ? "YES" : "NO", C->SkyNumber); - - LogD(4, prep("DEBUG: start : %s"), cs_hexdump (0, p, 9)); - - p += 8; //skip to first 0x8c if non-FTA, or 0x00 if FTA - for (int i = 0; i < Channel->Nr8cBlocks; i++) { - if (*p != 0x8c) { - LogD(0, prep("DEBUG: ERROR in Channel Table, expected value of 0x8c is %02x"), *p); - return 0; //fatal error - } - p++; //skip 8c byte - LogD(4, prep("DEBUG: 8c string: %s"), cs_hexdump (0, p + 1, *p)); - p += *p; //skip 8c block - p++; //forgot to skip length byte - } - //start last non 8c block here - if (*p != 0x00) { - LogE(0, prep("ERROR in Channel Table, expected value of 0x00 is %02x"), *p); - return 0; //fatal error - } - p++; //skip 0x00 byte - LogD(4, prep("DEBUG: endstring: %s"), cs_hexdump (0, p + 1, *p * 4)); - p += (*p * 4); //p points to nrofblocks, each block is 4 bytes - p++; //forgot to skip nrofblocks byte - - /* - if (Channel->AlwaysZero1 != 0) - isyslog ("EEPGDEBUG: AlwaysZero1 is NOT ZERO:%x.", Channel->AlwaysZero1); - if (Channel->AlwaysZero2 != 0) - isyslog ("EEPGDEBUG: AlwaysZero2 is NOT ZERO:%x.", Channel->AlwaysZero2); - if (Channel->Always0x8c != 0x8c) - isyslog ("EEPGDEBUG: Always0x8c is NOT 0x8c:%x.", Channel->Always0x8c); - if (Channel->Always0x08 != 0x08) - isyslog ("EEPGDEBUG: Always0x08 is NOT 0x08:%x.", Channel->Always0x08); - if (Channel->Always0x02 != 0x02) - isyslog ("EEPGDEBUG: Always0x02 is NOT 0x02:%x.", Channel->Always0x02); - if (Channel->Always0x01 != 0x01) - isyslog ("EEPGDEBUG: Always0x01 is NOT 0x01:%x.", Channel->Always0x01); - if (Channel->Always0x20 != 0x20) - isyslog ("EEPGDEBUG: Always0x20 is NOT 0x20:%x.", Channel->Always0x20); - if (Channel->Always0x0a != 0x0a) - isyslog ("EEPGDEBUG: Always0x0a is NOT 0x0a:%x.", Channel->Always0x0a); - if (Channel->Always0x81 != 0x81) - isyslog ("EEPGDEBUG: Always0x81 is NOT 0x81:%x.", Channel->Always0x81); - if (Channel->Always0x44 != 0x44) - isyslog ("EEPGDEBUG: Always0x44 is NOT 0x44:%x.", Channel->Always0x44); - */ - - } - if (p != DataEnd) - LogE(0, prep("Warning, possible problem at end of channel table; p = %p, DataEnd = %p"), p, DataEnd); - LoadEquivalentChannels (); - return 2; //obviously, when you get here, channels are read succesfully, but since all channels are sent at once, you can stop now -} - -/** - * \brief Get Nagra extended EPG - * - * \return 0 = fatal error, code 1 = success, code 2 = last item processed - */ -int cFilterEEPG::GetNagra (const u_char * Data, int Length) -{ - sTitleBlockHeaderNagraGuide *TBH = (sTitleBlockHeaderNagraGuide *) Data; - if (InitialTitle[0] == 0x00) { //InitialTitle is empty, so we are waiting for the start marker - if (TBH->TableIdExtensionHigh == 0x00 && TBH->TableIdExtensionLow == 0x00) { //this is the start of the data - if (TBH->VersionNumber == LastVersionNagra) { - LogI(0, prep("Nagra EEPG already up-to-date with version %i"), LastVersionNagra); - return 2; - } - Version = TBH->VersionNumber; - LogI(0, prep("initialized Nagraguide, version %i"), Version); - //u_char *p = (u_char *) Data + 11; - u_char *p = (u_char *) Data + 8; - if (*p != 0x01) { - LogE(0, prep("Error, Nagra first byte in table_id_extension 0x00 is not 0x01 but %02x. Format unknown, exiting."), - *p); - return 0; //fatal error - } - p++; //skip 0x01 byte - unsigned short int l = ((*p << 8) | *(p + 1)); - u_char *p_end = p + l - 3; //this ensures a full block of 4 bytes is there to process - p += 2; //skip length bytes - while (p < p_end) { - sSection0000BlockNagraGuide *S = (sSection0000BlockNagraGuide *) p; - int TTT = ((S->TableIdExtensionHigh << 10) | (S->TableIdExtensionLow << 3) | (S->TIE200 << 9)); - LogD(4, prep("DEBUG: TableIdExtension %04x, Unknown1 %02x Version %02x Always 0xd6 %02x DayCounter %02x"), - TTT, S->Unknown1, S->VersionNumber, S->Always0xd6, S->DayCounter); - if ((TTT > 0x0400) && (TTT < 0x0600)) //take high byte and compare with summarie tables in 0x0400 and 0x0500 range; - NagraTIE[NagraCounter++] = TTT; //only store TIEs of titlessummaries, all others can be derived; they are stored in the order of the 0x0000 index table, - //lets hope first entry corresponds with today, next with tomorrow etc. - p += 4; - } - buffer.clear (); //clear buffer maps - bufsize.clear (); //clear buffer maps - InitialTitle[0] = 0xff; //copy data into initial title - } - return (1); - } - unsigned short SectionLength = ((TBH->SectionLengthHigh & 0x0f) << 8) | TBH->SectionLengthLow; - unsigned short TableIdExtension = HILO16 (TBH->TableIdExtension); - if (TableIdExtension == 0x0000) { - LastVersionNagra = Version; - return (2); //done - } - /* - Table_id_extensions: - (0x0000) - (0x0010) per channel, nr_of_channels entries, every entry is 8 bytes, first 4 bytes gives nr. of titles in corresponding title table - (0x0020) per channel info day 2 of the month - (0x01F0) per channel info day 31 of the month - (0x0200) leeg; letop op je leest gemakkelijk door naar 0x0210! - (0x0210) titles day 1 of the month - (0x0220) titles day 2 of the month - (0x03F0) titles day 31 of the month - (0x0400) channel info - (0x0410) summaries day 1 of the month - (0x0420) summaries day 2 of the month - (0x05F0) summaries day 31 of the month - (0x0610) themes/title reference sunday, correspond to titles 0x0400 lower... - (0x0620) themes/title reference monday - ... this goes on until 0x07f0 - (0x0810) bouquet info; references to channels within a package, day 1 of the month - (0x0820) same day 2 of the month - (0x09f0) same day 31 of the month - */ - - if (!(TBH->TableIdExtensionHigh >= 0x02 && TBH->TableIdExtensionHigh <= 0x07)) //here we regulate which tables we are testing - return (1); - if (TableIdExtension == 0x0200) //table 0x0200 only contains language code, because it is for day 0 of the month, and 0x0400 is used for channels - return 1; - if (TBH->SectionNumber == 0) { //first section, create a table - buffer[TableIdExtension] = NULL; - bufsize[TableIdExtension] = 0; - NumberOfTables++; - } - //store all sections in core until last section is found; processing incomplete sections is very complex and doesnt save much memory, - //since the data has to be stored anyway; summaries do not seem to have channelid included, so storing titles and separately storing summaries will not work... - //GetEventId only works for a specific Schedule for a specific ChannelId.... - buffer[TableIdExtension] = - (unsigned char *) realloc (buffer[TableIdExtension], SectionLength - 9 + bufsize[TableIdExtension]); - memcpy (buffer[TableIdExtension] + bufsize[TableIdExtension], Data + 8, SectionLength - 9); //append new section - bufsize[TableIdExtension] += SectionLength - 9; - if (TBH->SectionNumber >= TBH->LastSectionNumber) { - LogI(1, prep("found %04x lastsection nr:%i."), TableIdExtension, TBH->SectionNumber); - // if (TBH->TableIdExtensionHigh == 0x04 || TBH->TableIdExtensionHigh == 0x05) { - if (TableIdExtension == 0x0400) { - int Result = GetChannelsNagra (buffer[TableIdExtension] + 4, bufsize[TableIdExtension] - 4); //TODO language code terminated by 0 is ignored - free (buffer[TableIdExtension]); - buffer[TableIdExtension] = NULL; - NumberOfTables--; - if (Result == 0) - return 0; //fatal error; TODO this exit should also free all other, non-Channel sections that are stored! - } - } //if lastsection read - return (1); //return and continue, nonfatal -} - -void cFilterEEPG::ProcessNagra () -{ - for (int i = 0; i < MAX_THEMES; i++) //clear all themes - Themes[i][0] = '\0'; - - for (int i = 0; i < NagraCounter; i++) { //first prcoess all themes, since they all use the same codes - unsigned short int TableIdExtension = NagraTIE[i]; - int TIE = TableIdExtension + 0x0200; //from 0x0400 to 0x0600 -> themes - LogI(3, prep("Processing Theme with TableIdExtension:%04x"), TIE); - GetThemesNagra (buffer[TIE] + 4, bufsize[TIE] - 4, TableIdExtension - 0x0200); //assume theme is completed //TODO Language code terminatd by 0 is ignored - free (buffer[TIE]); - buffer[TIE] = NULL; - NumberOfTables--; - } - - for (int i = 0; i < NagraCounter; i++) { //first prcoess all themes, since they all use the same codes - unsigned short int TableIdExtension = NagraTIE[i]; - int TIE = TableIdExtension - 0x0200; //from 0x0400 to 0x0200 -> titles - LogI(0, prep("Processing TableIdExtension:%04x"), TableIdExtension); - GetTitlesNagra (buffer[TIE] + 4, bufsize[TIE] - 4, TableIdExtension); //assume title-reading is completed //TODO Language code terminatd by 0 is ignored - free (buffer[TIE]); - buffer[TIE] = NULL; - NumberOfTables--; - - free (buffer[TableIdExtension]); //summaries - buffer[TableIdExtension] = NULL; - NumberOfTables--; - } - if (NumberOfTables != 0) - LogE(0, prep("ERROR, Not all tables processed and stream is already repeating. NumberOfTables = %i."), - NumberOfTables); -} - -/** - * \brief Get MHW1 Titles - * - * \return 0 = fatal error, code 1 = success, code 2 = last item processed - */ -int cFilterEEPG::GetTitlesMHW1 (const u_char * Data, int Length) -{ - if (Length >= 42) { - sTitleMHW1 *Title = (sTitleMHW1 *) Data; - if (Title->ChannelId == 0xff) { //FF is separator packet - if (memcmp (InitialTitle, Data, 46) == 0) { //data is the same as initial title //TODO use easier notation - LogD(1, prep("End procesing titles")); - return 2; - } - if (nTitles == 0) - memcpy (InitialTitle, Data, 46); //copy data into initial title - int Day = Title->Day; - int Hours = Title->Hours; - if (Hours > 15) - Hours -= 4; - else if (Hours > 7) - Hours -= 2; - else - Day++; - if (Day > 6) - Day = Day - 7; - Day -= Yesterday; - if (Day < 1) - Day = 7 + Day; - //if (Day == 1 && Hours < 6) - if (Day == 0 && Hours < 6) - Day = 7; - //Day = 8; - MHWStartTime = (Day * 86400) + (Hours * 3600) + YesterdayEpochUTC; - LogI(3, prep("Titles: FF PACKET, seqnr:%02x."), Data[5]); - } else if (InitialTitle[0] != 0x00) { //if initialized this should always be 0x90 = tableid! - if (nTitles < MAX_TITLES) { - Title_t *T; - T = (Title_t *) malloc (sizeof (Title_t)); - Titles[nTitles] = T; - int Minutes = Title->Minutes; - int StartTime = MHWStartTime + (Minutes * 60); - T->ChannelId = Title->ChannelId - 1; - T->ThemeId = Title->ThemeId; - T->TableId = Title->TableId; - T->MjdTime = 0; //only used at MHW2 and SKY - T->EventId = HILO32 (Title->ProgramId); - T->StartTime = LocalTime2UTC (StartTime); //here also Daylight Savings correction is done - T->Duration = HILO16 (Title->Duration) * 60; - T->SummaryAvailable = Title->SummaryAvailable; - T->Text = (unsigned char *) malloc (47); - if (T->Text == NULL) { - LogE(0, prep("Titles memory allocation error.")); - return 0; - } - T->Text[46] = '\0'; //end string with NULL character - //memcpy (T->Text, &Title->Title, 23); - decodeText2((unsigned char *)&Title->Title, 23, (char*)T->Text, 47); - CleanString (T->Text); - LogI(3, prep("EvId:%08x,ChanId:%x, Titlenr:%d:, StartTime(epoch):%i, SummAv:%x,Name:%s."), T->EventId, - T->ChannelId, nTitles, T->StartTime, T->SummaryAvailable, T->Text); - nTitles++; - } //nTitles < MaxTitles - else { - LogE(0, prep("Error, %i titles found more than %i"), nTitles, MAX_TITLES); - return 0; - } - } //else if InitialTitle - } //Length==46 - else { - LogE(0, prep("Error, length of title package < 42.")); - return 1; //non fatal - } - - return 1; -} - -/** - * \brief Get MHW2 Titles - * - * \return 0 = fatal error, code 1 = success, code 2 = last item processed - */ -int cFilterEEPG::GetTitlesMHW2 (const u_char * Data, int Length) -{ - if (Length > 18) { - int Pos = 18; - int Len = 0; - /*bool Check = false; - while (Pos < Length) { - Check = false; - Pos += 7; - if (Pos < Length) { - Pos += 3; - if (Pos < Length) - if (Data[Pos] > 0xc0) { - Pos += (Data[Pos] - 0xc0); - Pos += 4; - if (Pos < Length) { - if (Data[Pos] == 0xff) { - Pos += 1; - Check = true; - } - } - } - } - if (Check == false){ - isyslog ("EEPGDebug: Check==false"); - return 1; // I assume nonfatal error or success - } - }*/ - if (memcmp (InitialTitle, Data, 16) == 0) { //data is the same as initial title - return 2; //last item processed - } else { - if (nTitles == 0) - memcpy (InitialTitle, Data, 16); //copy data into initial title - //Pos = 18; - while (Pos < Length) { - Title_t *T; - T = (Title_t *) malloc (sizeof (Title_t)); - Titles[nTitles] = T; - T->ChannelId = Data[Pos]; - Pos+=11;//The date time starts here - //isyslog ("EEPGDebug: ChannelID:%d", T->ChannelId); - unsigned int MjdTime = (Data[Pos] << 8) | Data[Pos + 1]; - T->MjdTime = 0; //not used for matching MHW2 - T->StartTime = ((MjdTime - 40587) * 86400) - + (((((Data[Pos + 2] & 0xf0) >> 4) * 10) + (Data[Pos + 2] & 0x0f)) * 3600) - + (((((Data[Pos + 3] & 0xf0) >> 4) * 10) + (Data[Pos + 3] & 0x0f)) * 60); - T->Duration = (((Data[Pos + 5] << 8) | Data[Pos + 6]) >> 4) * 60; - Len = Data[Pos + 7] & 0x3f; - //isyslog ("EEPGDebug: Len:%d", Len); - T->Text = (unsigned char *) malloc (Len + 2); - if (T->Text == NULL) { - LogE(0, prep("Titles memory allocation error.")); - return 0; //fatal error - } - T->Text[Len] = '\0'; //end string with NULL character - decodeText2(&Data[Pos + 8],Len,(char*)T->Text,Len+1); - //memcpy (T->Text, &Data[Pos + 8], Len); - CleanString (T->Text); - Pos += Len + 8; // Sub Theme starts here - T->ThemeId = ((Data[7] & 0x3f) << 6) | (Data[Pos] & 0x3f); - T->EventId = (Data[Pos + 1] << 8) | Data[Pos + 2]; - T->SummaryAvailable = (T->EventId != 0xFFFF); - LogI(3, prep("EventId %04x Titlenr %d:SummAv:%x,Name:%s."), T->EventId, - nTitles, T->SummaryAvailable, T->Text); - Pos += 3; - nTitles++; - if (nTitles > MAX_TITLES) { - LogE(0, prep("Error, %i titles found more than %i"), nTitles, MAX_TITLES); - return 0; //fatal error - } - } - return 1; //success - } //else memcmp - } //if length - return 1; //non fatal error -} - -/** - * \brief Get MHW1 Summaries - * - * \return 0 = fatal error, code 1 = success, code 2 = last item processed - */ -int cFilterEEPG::GetSummariesMHW1 (const u_char * Data, int Length) -{ - sSummaryMHW1 *Summary = (sSummaryMHW1 *) Data; - if (Length >= 11) { - if (Summary->NumReplays < 10) { //Why limit this at 10? - if (Length >= (11 + (Summary->NumReplays * 7))) { - if (Summary->Byte7 == 0xff && Summary->Byte8 == 0xff && Summary->Byte9 == 0xff) { - if (memcmp (InitialSummary, Data, 20) == 0) { //data is equal to initial buffer - return 2; - } else if (nSummaries < MAX_TITLES) { - if (nSummaries == 0) - memcpy (InitialSummary, Data, 20); //copy this data in initial buffer - int SummaryOffset = 11 + (Summary->NumReplays * 7); - int SummaryLength = Length - SummaryOffset; - unsigned char *Text = (unsigned char *) malloc (2*SummaryLength + 1); - if (Text == NULL) { - LogE(0, prep("Summaries memory allocation error.")); - return 0; - } - Text[SummaryLength+1] = '\0'; //end string with NULL character - //memcpy (Text, &Data[SummaryOffset], SummaryLength); - decodeText2(&Data[SummaryOffset], SummaryLength, (char*)Text, 2*SummaryLength + 1); -// CleanString (Text); -// if (Summary->NumReplays != 0) -// esyslog ("EEPG: Number of replays:%i.", Summary->NumReplays); - //int Replays = Summary->NumReplays; - int ReplayOffset = 11; - Summary_t *S; - S = (Summary_t *) malloc (sizeof (Summary_t)); - Summaries[nSummaries] = S; - S->NumReplays = Summary->NumReplays; - S->EventId = HILO32 (Summary->ProgramId); - S->Text = Text; - int i = 0; - do { - S->Replays[i].MjdTime = 0; //only used for SKY - //if (Summary->NumReplays == 0) { - //S->ChannelId = 0xFFFF; //signal that ChannelId is not known; 0 is bad signal value because it is a valid ChannelId... - //S->StartTime = 0; - //} - //else { - S->Replays[i].ChannelId = Data[ReplayOffset++] - 1; - unsigned int Date_hi = Data[ReplayOffset++]; - unsigned int Date_lo = Data[ReplayOffset++]; - unsigned short int Hour = Data[ReplayOffset++]; - unsigned short int Minute = Data[ReplayOffset++]; - unsigned short int Sec = Data[ReplayOffset++]; - ReplayOffset++; //makes total of 7 bytes - - LogI(4, prep("EvId:%08x ChanId:%x, ChanName %s, Time: %02d:%02d:%02d."), - S->EventId, S->Replays[i].ChannelId, - sChannels[ChannelSeq[S->Replays[i].ChannelId]].Name, Hour, Minute, Sec); - - S->Replays[i].StartTime = MjdToEpochTime (Date) + (((((Hour & 0xf0) >> 4) * 10) + (Hour & 0x0f)) * 3600) - + (((((Minute & 0xf0) >> 4) * 10) + (Minute & 0x0f)) * 60) - + ((((Sec & 0xf0) >> 4) * 10) + (Sec & 0x0f)); -// summary -> time[i] = ProviderLocalTime2UTC (summary -> time[i]); - S->Replays[i].StartTime = LocalTime2UTC (S->Replays[i].StartTime); - //} - i++; - } while (i < Summary->NumReplays); - //} while (Replays-- >= 0); - LogI(3, prep("EvId:%08x ChanId:%x, Replays:%d, Summnr %d:%.35s."), S->EventId, - S->Replays[0].ChannelId, S->NumReplays, nSummaries, S->Text); - nSummaries++; - } else { - LogE(0, prep("Error, %i summaries found more than %i"), nSummaries, MAX_TITLES); - return 0; - } - } //0xff - else { - LogE(0, prep("Warning, Summary bytes not as expected.")); - return 1; //it is not a success, but error is not fatal - } - } //numreplays length - else { - LogE(0, prep("Warning, number of replays is not conforming to length.")); - return 1; //nonfatal error - } - } //numreplays <10 - else { - LogE(0, prep("Warning, number of replays %d > 10, cannot process."), - Summary->NumReplays); - return 1; //nonfatal error - } - } //length >11 - else { - LogE(0, prep("Summary length too small:%s"), cs_hexdump (0, Data, Length)); - return 1; //nonfatal error - } - return 1; //success -} - -/** - * \brief Get MHW2 Summaries - * - * \return 0 = fatal error, code 1 = success, code 2 = last item processed - */ -int cFilterEEPG::GetSummariesMHW2 (const u_char * Data, int Length) -{ - if (Length > (Data[14] + 17)) { - if (memcmp (InitialSummary, Data, 16) == 0) //data is equal to initial buffer - return 2; - else { - if (nSummaries == 0) - memcpy (InitialSummary, Data, 16); //copy this data in initial buffer - if (nSummaries < MAX_TITLES) { - int lenText = Data[14]; - int SummaryLength = lenText; - int Pos = 15; - int Loop = Data[Pos + SummaryLength] & 0x0f; - Summary_t *S; - S = (Summary_t *) malloc (sizeof (Summary_t)); - Summaries[nSummaries] = S; - - S->Replays[0].ChannelId = 0xFFFF; //signal that ChannelId is not known; 0 is bad signal value because it is a valid ChannelId... - S->Replays[0].StartTime = 0; //not used - S->Replays[0].MjdTime = 0; //not used - S->NumReplays = 0; //not used - S->EventId = (Data[3] << 8) | Data[4]; - unsigned char tmp[4096]; //TODO do this smarter - memcpy (tmp, &Data[Pos], lenText); - tmp[SummaryLength] = '\n'; - SummaryLength += 1; - Pos += (lenText + 1); - if (Loop > 0) { - while (Loop > 0) { - lenText = Data[Pos]; - Pos += 1; - if ((Pos + lenText) < Length) { - memcpy (&tmp[SummaryLength], &Data[Pos], lenText); - SummaryLength += lenText; - if (Loop > 1) { - tmp[SummaryLength] = '\n'; - SummaryLength += 1; - } - } else - break; - Pos += lenText; - Loop--; - } - } - S->Text = (unsigned char *) malloc (SummaryLength + 2); - S->Text[SummaryLength] = '\0'; //end string with NULL character - if (S->Text == NULL) { - LogE(0, prep("Summaries memory allocation error.")); - return 0; //fatal error - } - //memcpy (S->Text, tmp, SummaryLength); - decodeText2(tmp,SummaryLength,(char*)S->Text,SummaryLength + 1); - CleanString (S->Text); - LogI(3, prep("EventId %08x Summnr %d:%.30s."), S->EventId, nSummaries, S->Text); - nSummaries++; - } else { - LogE(0, prep("Error, %i summaries found more than %i"), nSummaries, MAX_TITLES); - return 0; //fatal error - } - } //else - } //if length - return 1; //succes or nonfatal error -} - -/** - * \brief Get SKYBOX Channels - * - * \return 0 = fatal error, code 1 = success, code 2 = last item processed - */ -int cFilterEEPG::GetChannelsSKYBOX (const u_char * Data, int Length) -{ - - if (memcmp (InitialChannel, Data, 8) == 0) { //data is the same as initial title - LoadEquivalentChannels (); - return 2; - } else { - if (nChannels == 0) - memcpy (InitialChannel, Data, 8); //copy data into initial title - if (nChannels == 0) { - LogI(1, "| ID | %-26.26s | %-22.22s | FND | %-8.8s |\n", "Channel ID", "Channel Name", "Sky Num."); - LogI(1, "|------|-%-26.26s-|-%-22.22s-|-----|-%-8.8s-|\n", "------------------------------", - "-----------------------------", "--------------------"); - } -// unsigned short int BouquetId = (Data[3] << 8) | Data[4]; - int BouquetDescriptorsLength = ((Data[8] & 0x0f) << 8) | Data[9]; - int TransportStreamLoopLength = - ((Data[BouquetDescriptorsLength + 10] & 0x0f) << 8) | Data[BouquetDescriptorsLength + 11]; - int p1 = (BouquetDescriptorsLength + 12); - while (TransportStreamLoopLength > 0) { - unsigned short int Tid = (Data[p1] << 8) | Data[p1 + 1]; - unsigned short int Nid = (Data[p1 + 2] << 8) | Data[p1 + 3]; - int TransportDescriptorsLength = ((Data[p1 + 4] & 0x0f) << 8) | Data[p1 + 5]; - int p2 = (p1 + 6); - p1 += (TransportDescriptorsLength + 6); - TransportStreamLoopLength -= (TransportDescriptorsLength + 6); - while (TransportDescriptorsLength > 0) { - unsigned char DescriptorTag = Data[p2]; - int DescriptorLength = Data[p2 + 1]; - int p3 = (p2 + 2); - p2 += (DescriptorLength + 2); - TransportDescriptorsLength -= (DescriptorLength + 2); - switch (DescriptorTag) { //TODO switch with only 1 case??? replace this by template - case 0xb1: - p3 += 2; - DescriptorLength -= 2; - while (DescriptorLength > 0) { - // 0x01 = Video Channel - // 0x02 = Audio Channel - // 0x05 = Other Channel - //if( Data[p3+2] == 0x01 || Data[p3+2] == 0x02 || Data[p3+2] == 0x05 ) - //{ - unsigned short int Sid = (Data[p3] << 8) | Data[p3 + 1]; - unsigned short int ChannelId = (Data[p3 + 3] << 8) | Data[p3 + 4]; - unsigned short int SkyNumber = (Data[p3 + 5] << 8) | Data[p3 + 6]; - if (SkyNumber > 100 && SkyNumber < 1000) { - if (ChannelId > 0) { - sChannel *C; - if (ChannelSeq.count (ChannelId) == 0) { //not found - C = &sChannels[nChannels]; - C->ChannelId = ChannelId; - C->NumberOfEquivalences = 1; //there is always an original channel. every equivalence adds 1 - C->Src[0] = Source (); //assume all EPG channels are on same satellite, if not, manage this via equivalents!!! - C->Nid[0] = Nid; - C->Tid[0] = Tid; - C->Sid[0] = Sid; - C->SkyNumber = SkyNumber; - tChannelID channelID = tChannelID (C->Src[0], C->Nid[0], C->Tid[0], C->Sid[0]); - cChannel *VC = Channels.GetByChannelID (channelID, true); - bool IsFound = (VC); - if (IsFound) - strncpy ((char *) C->Name, VC->Name (), 64); - else - C->Name[0] = '\0'; //empty string - - LogI(1, "|% 5d | %-26.26s | %-22.22s | %-3.3s | % 6d |\n", C->ChannelId - , *channelID.ToString(), C->Name, IsFound ? "YES" : "NO", C->SkyNumber); - - ChannelSeq[C->ChannelId] = nChannels; //fill lookup table to go from channel-id to sequence nr in table - nChannels++; - if (nChannels >= MAX_CHANNELS) { - LogE(0, prep("Error, %i channels found more than %i"), nChannels, MAX_CHANNELS); - return 0; - } - } - } - } - p3 += 9; - DescriptorLength -= 9; - } - break; - default: - break; - } //switch descriptortag - } - } //while - return 1; - } //else part of memcmp -} - -/** - * \brief Get SKYBOX Titles - * - * \return 0 = fatal error, code 1 = success, code 2 = last item processed - */ -int cFilterEEPG::GetTitlesSKYBOX (const u_char * Data, int Length) -{ - int p; - unsigned short int ChannelId; - unsigned short int MjdTime; - int Len1; - int Len2; - - if (Length < 20) - return 1; //nonfatal error - if (memcmp (InitialTitle, Data, 20) == 0) //data is the same as initial title - return 2; - else { - if (nTitles == 0) - memcpy (InitialTitle, Data, 20); //copy data into initial title - ChannelId = (Data[3] << 8) | Data[4]; - MjdTime = ((Data[8] << 8) | Data[9]); - if (ChannelId > 0) { - if (MjdTime > 0) { - p = 10; - do { - Title_t *T; - T = (Title_t *) malloc (sizeof (Title_t)); - Titles[nTitles] = T; - T->ChannelId = ChannelId; - T->MjdTime = MjdTime; //only date, no time. Is used to match titles and summaries, SKYBOX only - T->EventId = (Data[p] << 8) | Data[p + 1]; - Len1 = ((Data[p + 2] & 0x0f) << 8) | Data[p + 3]; - if (Data[p + 4] != 0xb5) { - LogD(5, prep("Data error signature for title - Data[p + 4] != 0xb5")); - break; - } - if (Len1 > Length) { - LogD(5, prep("Data error signature for title - Len1 > Length")); - break; - } - p += 4; - Len2 = Data[p + 1] - 7; - T->StartTime = ((MjdTime - 40587) * 86400) + ((Data[p + 2] << 9) | (Data[p + 3] << 1)); - T->Duration = ((Data[p + 4] << 9) | (Data[p + 5] << 1)); - T->ThemeId = Data[p + 6]; - //TODO Data[p + 7] is Quality value add it to description - //int quality = Data[p + 7]; - switch (Data[p + 8] & 0x0F) { - case 0x01: - T->Rating = 0x00; //"U" - break; - case 0x02: - T->Rating = 0x08; //"PG" - break; - case 0x03: - T->Rating = 0x0C; //"12" - break; - case 0x04: - T->Rating = 0x0F; //"15" - break; - case 0x05: - T->Rating = 0x12; //"18" - break; - default: - T->Rating = 0x00; //"-" - break; - } - T->Unknown1 = Data[p + 4 - 13]; //FIXME - T->Unknown2 = Data[p + 4 - 12]; //FIXME - T->Unknown3 = Data[p + 4 - 11]; //FIXME - unsigned char tmp[4096]; //TODO smarter - Len2 = DecodeHuffmanCode (&Data[p + 9], Len2, tmp); - if (Len2 == 0) { - LogE(0, prep("Warning, could not huffman-decode title-text, skipping title.")); - return 1; //non-fatal error - } - T->Text = (unsigned char *) malloc (Len2 + 1); - if (T->Text == NULL) { - LogE(0, prep("Titles memory allocation error.")); - return 0; - } - T->Text[Len2] = '\0'; //end string with NULL character - memcpy (T->Text, tmp, Len2); - CleanString (T->Text); - T->SummaryAvailable = 1; //TODO I assume this is true? - - LogI(3, prep("EventId %08x Titlenr %d,Unknown1:%x,Unknown2:%x,Un3:%x,Name:%s."), - T->EventId, nTitles, T->Unknown1, T->Unknown2, T->Unknown3, T->Text); - - p += Len1; - nTitles++; - if (nTitles >= MAX_TITLES) { - LogE(0, prep("Error, %i titles found more than %i"), nTitles, MAX_TITLES); - return 0; //fatal error - } - } while (p < Length); - } - } - } - return 1; //success -} - -/** - * \brief Get SKYBOX Summaries - * - * \return 0 = fatal error, code 1 = success, code 2 = last item processed - */ -int cFilterEEPG::GetSummariesSKYBOX (const u_char * Data, int Length) -{ - int p; - unsigned short int ChannelId; - unsigned short int MjdTime; - int Len1; - int Len2; - - if (Length < 20) { - return 1; //non fatal error I assume - } - if (memcmp (InitialSummary, Data, 20) == 0) //data is equal to initial buffer - return 2; -// else if (nSummaries < MAX_SUMMARIES) { - else { - if (nSummaries == 0) - memcpy (InitialSummary, Data, 20); //copy this data in initial buffer - ChannelId = (Data[3] << 8) | Data[4]; - MjdTime = ((Data[8] << 8) | Data[9]); - if (ChannelId > 0) { - if (MjdTime > 0) { - p = 10; - do { - Summary_t *S; - S = (Summary_t *) malloc (sizeof (Summary_t)); - Summaries[nSummaries] = S; - S->Replays[0].ChannelId = ChannelId; - S->Replays[0].MjdTime = MjdTime; - S->NumReplays = 0; //not used - S->EventId = (Data[p] << 8) | Data[p + 1]; - Len1 = ((Data[p + 2] & 0x0f) << 8) | Data[p + 3]; - if (Data[p + 4] != 0xb9) { - LogD(5, prep("Data error signature for title - Data[p + 4] != 0xb5")); - break; - } - if (Len1 > Length) { - LogD(5, prep("Data error signature for title - Len1 > Length")); - break; - } - p += 4; - Len2 = Data[p + 1]; - unsigned char tmp[4096]; //TODO can this be done better? - Len2 = DecodeHuffmanCode (&Data[p + 2], Len2, tmp); - if (Len2 == 0) { - LogE(0, prep("Warning, could not huffman-decode text, skipping summary.")); - return 1; //non-fatal error - } - S->Text = (unsigned char *) malloc (Len2 + 1); - if (S->Text == NULL) { - LogE(0, prep("Summaries memory allocation error.")); - return 0; - } - memcpy (S->Text, tmp, Len2); - S->Text[Len2] = '\0'; //end string with NULL character - CleanString (S->Text); - LogI(3, prep("EventId %08x Summnr %d:%.30s."), S->EventId, nSummaries, S->Text); - p += Len1; - nSummaries++; - if (nSummaries >= MAX_TITLES) { - LogI(0, prep("Error, %i summaries found more than %i"), nSummaries, MAX_TITLES); - return 0; - } - } while (p < Length); - } - } - } - return 1; -} - -void cFilterEEPG::FreeSummaries (void) -{ - if (Format == MHW1 || Format == MHW2 || Format == SKY_IT || Format == SKY_UK) { - Summary_t *S; //TODO do I need this? - Summary_t *S2; //TODO do I need this? - for (int i = 0; i < nSummaries; i++) { - S = Summaries[i]; - if (i < nSummaries - 1) { - S2 = Summaries[i + 1]; //look at next summary - if (S->Text != S2->Text && S->Text != 0x00) //this is the last summary that points to this textblock; needed in case NumReplays > 1, multiple pointers to same textblock - free (S->Text); - } else if (S->Text != 0x00) - free (S->Text); - free (S); - } - } - nSummaries = 0; -} - -void cFilterEEPG::FreeTitles (void) -{ - if (Format == MHW1 || Format == MHW2 || Format == SKY_IT || Format == SKY_UK) { - Title_t *T; - for (int i = 0; i < nTitles; i++) { - T = Titles[i]; - free (T->Text); - free (T); - } - } - nTitles = 0; -} - -void cFilterEEPG::LoadIntoSchedule (void) -{ - int i, j, k; - i = 0; - j = 0; - k = 0; - bool foundtitle; - foundtitle = false; - Title_t *T; - Summary_t *S; - int remembersummary; -//keep statistics - int SummariesNotFound = 0; - int NoSummary = 0; - int NotMatching = 0; - int LostSync = 0; - remembersummary = -1; - - cSchedulesLock SchedulesLock (true); - cSchedules *s = (cSchedules *) cSchedules::Schedules (SchedulesLock); - if (s) { - - while (i < nTitles) { - T = Titles[i]; - S = Summaries[j]; - foundtitle = false; - - while ((i < nTitles) && (!foundtitle)) { //find next title that has summary - T = Titles[i]; - if (T->SummaryAvailable) - foundtitle = true; - else { - NoSummary++; - i++; - } - } -//esyslog("foundtitle %x for next title that has a summary:%d",foundtitle,i); - - if (!foundtitle) //no more titles with summaries - break; - if ((T->EventId == S->EventId) && (T->MjdTime == S->Replays[0].MjdTime) - && ((T->ChannelId == S->Replays[0].ChannelId) || ((Format != SKY_IT) && (Format != SKY_UK)))) { //should always be true, titles and summaries are broadcasted in order... - LogD(3, prep("T->EventId == S->EventId")); - //MjdTime = 0 for all but SKY - //S->ChannelId must be equal to T->ChannelId only for SKY; in MHW1 S->ChannelId overrides T->ChannelId when NumReplays > 1 - remembersummary = -1; //reset summary searcher - //int Replays = S->NumReplays; - - int index = 0; - do { - unsigned short int ChannelId; - time_t StartTime; - if (S->NumReplays == 0) { - ChannelId = T->ChannelId; - StartTime = T->StartTime; - } else { - ChannelId = S->Replays[index].ChannelId; - StartTime = S->Replays[index].StartTime; - } - - //channelids are sequentially numbered and sent in MHW1 and MHW2, but not in SKY, so we need to lookup the table index - sChannel *C = &sChannels[ChannelSeq[ChannelId]]; //find channel - cSchedule *p[MAX_EQUIVALENCES]; - PrepareToWriteToSchedule (C, s, p); - - char rating = 0x00; - if ((Format == SKY_IT || Format == SKY_UK) && T->Rating) { //TODO only works on OTV for now - rating = T->Rating; - } - unsigned short int TableId = DEFAULT_TABLE_ID; - if (T->TableId) { - TableId = T->TableId; - } - - WriteToSchedule (p, C->NumberOfEquivalences, T->EventId, StartTime, T->Duration / 60, (char *) T->Text, - (char *) S->Text, T->ThemeId, TableId, 0, rating); - - FinishWriteToSchedule (C, s, p); - //Replays--; - //if ((S->NumReplays != 0) && (Replays > 0)) { //when replays are used, all summaries of the replays are stored consecutively; currently only CSAT - //j++; //move to next summary - //if (j >= nSummaries) //do not forget to look in beginning of (ring)buffer - //j = 0; - //S = Summaries[j]; //next summary within replay range - //} - index++; - } //while - while (index < S->NumReplays); - -//TODO: why load events that have already past, and then run Cleanup -//end of putting title and summary in schedule - i++; //move to next title - } //if T->EventId == S->EventId - else { -// esyslog("EEPG ERROR: ProgramIds not matching, title:%d,summary%d, T->EventId:%u, S->Eventid:%u.",i,j,T->EventId,S->EventId); - NotMatching++; - if (remembersummary == -1) { //I am not in search loop yet - remembersummary = j; - if (remembersummary == 0) - remembersummary = nSummaries; //or next test will never be succesfull for remembersummary = 0 - LostSync++; -// esyslog("EEPG Error: lost sync at title %d, summary %d.",i,j); - } else if (j == (remembersummary - 1)) { //the last summary to be checked has failed also - //esyslog ("EEPG Error: could not find summary for summary-available Title %d.", i); - esyslog - ("EEPG: Error, summary not found for EventId %08x Titlenr %d:SummAv:%x,Unknown1:%x,Unknown2:%x,Un3:%x,Name:%s.", - T->EventId, i, T->SummaryAvailable, T->Unknown1, T->Unknown2, T->Unknown3, T->Text); - - /* write Title info to schedule */ - sChannel *C = &sChannels[ChannelSeq[T->ChannelId]]; //find channel - cSchedule *p[MAX_EQUIVALENCES]; - PrepareToWriteToSchedule (C, s, p); - char rating = 0x00; - if ((Format == SKY_IT || Format == SKY_UK) && T->Rating) { //TODO only works on OTV for now - rating = T->Rating; - } - WriteToSchedule (p, C->NumberOfEquivalences, T->EventId, T->StartTime, T->Duration / 60, (char *) T->Text, - NULL, T->ThemeId, DEFAULT_TABLE_ID, 0, rating); - FinishWriteToSchedule (C, s, p); - - SummariesNotFound++; - i++; //move to next title, for this one no summary can be found - } - -// esyslog("Trying again for this title %d, remember summary %d, but advancing one Summary to %d.",i,remembersummary,j); - } - j++; //move to next summary - if (j >= nSummaries) //do not forget to look in beginning of (ring)buffer - j = 0; - } //while title - } // if s - else - esyslog ("EEPG Error: could not lock schedules."); - - cSchedules::Cleanup (true); //deletes all past events - - isyslog ("EEPG: found %i equivalents channels", nEquivChannels); - isyslog ("EEPG: found %i themes", nThemes); - isyslog ("EEPG: found %i channels", nChannels); - isyslog ("EEPG: found %i titles", nTitles); - isyslog ("EEPG: of which %i reported to have no summary available; skipping these BIENTOT titles", NoSummary); - isyslog ("EEPG: found %i summaries", nSummaries); - if (SummariesNotFound != 0) - esyslog ("EEPG: %i summaries not found", SummariesNotFound); - if (NotMatching > nSummaries) - LogI (0, prep("Warning: lost sync %i times, summary did not match %i times."), - LostSync, NotMatching); - - FreeSummaries (); //do NOT free channels, themes and bouquets here because they will be reused in SKY! - FreeTitles (); - if (!((Format == SKY_IT) || (Format == SKY_UK))) { //everything but SKY; SKY is the only protocol where LoadIntoSchedule is called repeatedly - ChannelSeq.clear (); - } -} - -void cFilterEEPG::AddFilter (u_short Pid, u_char Tid) -{ - if (!Matches (Pid, Tid)) { - Add (Pid, Tid); - esyslog (prep("Filter Pid:%x,Tid:%x added."), Pid, Tid); - } -} - -void cFilterEEPG::AddFilter (u_short Pid, u_char Tid, unsigned char Mask) -{ - if (!Matches (Pid, Tid)) { - Add (Pid, Tid, Mask); - esyslog (prep("Filter Pid:%x,Tid:%x,Mask:%x added."), Pid, Tid, 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 OnlyRunningStatus = false); -}; - -cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Data, bool OnlyRunningStatus) - : SI::EIT (Data, false) -{ - //LogD(2, prep("cEIT2::cEIT2")); - if (Tid > 0 && Format == DISH_BEV) 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(4, 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 (); - 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 { - // 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 () == getVersionNumber ()) - 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() == getVersionNumber()) - 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 (getVersionNumber()); - - int LanguagePreferenceShort = -1; - int LanguagePreferenceExt = -1; - 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 ()); - - switch (d->getDescriptorTag ()) { - 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() == 4) { - if (!DishEventDescriptor) { - DishEventDescriptor = new SI::DishDescriptor(); - } - DishEventDescriptor->setEpisodeInfo(d->getData()); - } - } - 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)); - pEvent->SetTitle (buffer); - l = ShortEventDescriptor->text.getLength(); - f = (unsigned char *) ShortEventDescriptor->text.getData().getData(); - decodeText2 (f, l, buffer, sizeof (buffer)); - //ShortEventDescriptor->text.getText(buffer, sizeof(buffer)); - pEvent->SetShortText (buffer); - } else if (!HasExternalData) { - pEvent->SetTitle (NULL); - pEvent->SetShortText (NULL); - } - if (ExtendedEventDescriptors) { - char buffer[Utf8BufSize (ExtendedEventDescriptors->getMaximumTextLength (": ")) + 1]; - pEvent->SetDescription (ExtendedEventDescriptors->getText (buffer, sizeof (buffer), ": ")); - } 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(), DishShortEventDescriptor->getText()); -// pEvent->SetDescription(DishExtendedEventDescriptor->getText()); - char *tmp; - string fmt; - fmt = "%s"; - if (0 != strcmp(DishEventDescriptor->getShortText(),"") && DishEventDescriptor->hasTheme()) { - fmt += " - "; - } - fmt += "%s"; - if (DishEventDescriptor->hasTheme() && DishEventDescriptor->hasCategory()) { - fmt += " ~ "; - } - fmt += "%s"; - - Asprintf (&tmp, fmt.c_str(), DishEventDescriptor->getShortText() - , DishEventDescriptor->getTheme() - , DishEventDescriptor->getCategory()); - pEvent->SetShortText(tmp); - //LogD(2, prep("EEPGDEBUG:DishTheme:%x-DishCategory:%x)"), DishTheme, DishCategory); - free(tmp); - - fmt = "%s"; - if (0 != strcmp(DishEventDescriptor->getDescription(),"") && (0 != strcmp(DishEventDescriptor->getRating(),"") || 0 != strcmp(DishEventDescriptor->getStarRating(),""))) { - fmt += "\nRating: "; - } - fmt += "%s %s"; - if (0 != strcmp(DishEventDescriptor->getProgramId(),"")) { - fmt += "\n Program ID: "; - } - fmt += "%s %s"; - time_t orgAirDate = DishEventDescriptor->getOriginalAirDate(); - fmt += orgAirDate == 0 ? "%s" : " Original Air Date: "; - Asprintf (&tmp, fmt.c_str(), DishEventDescriptor->getDescription() - , DishEventDescriptor->getRating() - , DishEventDescriptor->getStarRating() - , DishEventDescriptor->getProgramId() - , DishEventDescriptor->getSeriesId() - , DishEventDescriptor->getOriginalAirDate() == 0 ? "" : ctime (&orgAirDate)); - pEvent->SetDescription(tmp); - free(tmp); - - - //LogD(2, prep("DishDescription: %s"), DishExtendedEventDescriptor->getText()); - //LogD(2, prep("DishShortText: %s"), DishExtendedEventDescriptor->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 */ - } - 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(2, prep("OnlyRunningStatus")); - return; - } - if (Modified) { - pSchedule->Sort (); - if (!HasExternalData) - pSchedule->DropOutdated (SegmentStart, SegmentEnd, Tid, getVersionNumber ()); - Schedules->SetModified (pSchedule); - } - LogD(2, prep("end of cEIT2")); - -} -//end of cEIT2 - -} //end namespace SI - -void cFilterEEPG::ProcessNextFormat (bool FirstTime = false) -{ - /* for (int i = 0; i <= HIGHEST_FORMAT; i++) - esyslog ("EEPGDEBUG: Format %i on pid %x", i, UnprocessedFormat[i]); */ - - if (!FirstTime) { - isyslog ("EEPG: found %i equivalents channels", nEquivChannels); - isyslog ("EEPG: found %i themes", nThemes); - isyslog ("EEPG: found %i channels", nChannels); - isyslog ("EEPG: found %i titles", nTitles); - isyslog ("EEPG: found %i summaries", nSummaries); - isyslog ("EEPG: written %i titles", TitleCounter); - isyslog ("EEPG: written %i summaries", SummaryCounter); - isyslog ("EEPG: rejected %i titles/summaries because of higher TableId", RejectTableId); - //Send message when finished - if (SetupPE.DisplayMessage) { - char *mesg; - Asprintf(&mesg, "EEPG: written %i summaries", SummaryCounter); - Skins.QueueMessage(mtInfo, mesg, 2); - free(mesg); - } - - TitleCounter = 0; - SummaryCounter = 0; - /*if (SummariesNotFound != 0) - esyslog ("EEPG: %i summaries not found", SummariesNotFound); - else if (VERBOSE >= 1) - isyslog ("EEPG: %i summaries not found", SummariesNotFound); */ - UnprocessedFormat[Format] = 0; //clear previously processed format - - //Next few lines prevent eepg from reading multiple eepg-systems on one transponder e.g. CDNL - //isyslog ("EEPG: Ended all processing"); - //return; - //If you remove these lines, multiple systems WILL be read - - } - TitleCounter = 0; - SummaryCounter = 0; - RejectTableId = 0; - //cleanup mess of last processing - ChannelSeq.clear (); - FreeTitles (); - FreeSummaries (); - - //now start looking for next format to process - int pid; - for (int i = 0; i <= HIGHEST_FORMAT; i++){ //find first format that is detected - if (UnprocessedFormat[i]) { - isyslog ("EEPG: %s Extended EPG detected on pid %x.", FormatName[i], UnprocessedFormat[i]); - Format = (EFormat)i; - // highest format is processed first this way - // make sure that CONT protocols like Premiere, Freesat are processed - // AFTER ONCE protocols like MHW, SKY and NAGRA - break; - } - if (i == HIGHEST_FORMAT) { //there are no formats left to process - isyslog ("EEPG: Ended all processing"); - return; - } - } - - pid = UnprocessedFormat[Format]; //and reinstall its pid - - memset (&InitialChannel, 0, 8); - memset (&InitialTitle, 0, 64); - memset (&InitialSummary, 0, 64); - NagraCounter = 0; - Version = -1; //because 0 can be a valid version number... - nEquivChannels = 0; - nChannels = 0; - nThemes = 0; - EndChannels = false; - EndThemes = false; - switch (Format) { - case PREMIERE: - if (!Matches (pid, 0xA0)) - Add (pid, 0xA0); - break; - case MHW1: - AddFilter (0xd3, 0x92); //ThemesMHW1//TODO: all filters are serialized, strictly speaking Themes is non-fatal... - break; - case MHW2: - AddFilter (0x231, 0xc8); //MHW2 Channels & Themes - break; - case SKY_IT: - case SKY_UK: - AddFilter (0x11, 0x4a); //Sky Channels - break; - case FREEVIEW: //Freeview, CONT mode //TODO streamline this for other modes - ReadFileDictionary (); - AddFilter (pid, 0x4e, 0xfe); //event info, actual(0x4e)/other(0x4f) TS, present/following - AddFilter (pid, 0x50, 0xf0); //event info, actual TS, schedule(0x50)/schedule for future days(0x5X) - AddFilter (pid, 0x60, 0xf0); //event info, other TS, schedule(0x60)/schedule for future days(0x6X) - AddFilter (0x39, 0x50, 0xf0); //event info, actual TS, Viasat - AddFilter (0x39, 0x60, 0xf0); //event info, other TS, Viasat - break; - case NAGRA: - AddFilter (pid, 0xb0); //perhaps TID is equal to first data byte? - break; - case DISH_BEV: - AddFilter (0x12, 0, 0); // event info, actual(0x4e)/other(0x4f) TS, present/following - AddFilter (0x0300, 0, 0); // Dish Network EEPG event info, actual(0x4e)/other(0x4f) TS, present/following - AddFilter (0x0441, 0, 0); // Dish Network EEPG event info, actual(0x4e)/other(0x4f) TS, present/following -// AddFilter (0x0441, 0x50, 0xf0); // Bell ExpressVU EEPG -// AddFilter (0x0441, 0x60, 0xf0); // Bell ExpressVU EEPG - break; - default: - break; - } -} - -void cFilterEEPG::ProccessContinuous(u_short Pid, u_char Tid, int Length, const u_char *Data) -{ - //0x39 Viasat, 0x0300 Dish Network EEPG, 0x0441 Bell ExpressVU EEPG - LogD(4, prep("Pid: 0x%02x Tid: %d Length: %d"), Pid, Tid, Length); - cSchedulesLock SchedulesLock(true, 10); - cSchedules *Schedules = (cSchedules*)(cSchedules::Schedules(SchedulesLock)); - //Look for other satelite positions only if Dish/Bell ExpressVU for the moment hardcoded pid check - if(Schedules) - SI::cEIT2 EIT(Schedules, Source(), Tid, Data); - - else//cEIT EIT (Schedules, Source (), Tid, Data); - { - // If we don't get a write lock, let's at least get a read lock, so - // that we can set the running status and 'seen' timestamp (well, actually - // with a read lock we shouldn't be doing that, but it's only integers that - // get changed, so it should be ok) - cSchedulesLock SchedulesLock; - cSchedules *Schedules = (cSchedules*)(cSchedules::Schedules(SchedulesLock)); - if(Schedules) - SI::cEIT2 EIT(Schedules, Source(), Tid, Data, true); - - //cEIT EIT (Schedules, Source (), Tid, Data, true); - } -} - -void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Length) -{ - int now = time (0); -// LogD(2, prep("Pid: 0x%02x Tid: %d Length: %d PMT pid: 0x%04x"), Pid, Tid, Length, pmtpid); -// LogD(2, prep("Source: %d Transponder: %d"), Source () , Transponder ()); - - if (Pid == 0 && Tid == SI::TableIdPAT) { - if (!pmtnext || now > pmtnext) { - if (pmtpid) - NextPmt (); - if (!pmtpid) { - SI::PAT pat (Data, false); - if (pat.CheckCRCAndParse ()) { - SI::PAT::Association assoc; - int idx = 0; - for (SI::Loop::Iterator it; pat.associationLoop.getNext (assoc, it);) { - if (!assoc.isNITPid ()) { - //if (!assoc.isNITPid () && Scanning) { - if (idx++ == pmtidx) { - pmtpid = assoc.getPid (); - pmtsid = assoc.getServiceId (); - Add (pmtpid, 0x02); - pmtnext = now + PMT_SCAN_TIMEOUT; - LogI(3, prep("PMT pid now 0x%04x (idx=%d)\n"), pmtpid, pmtidx); - break; - } - } - } - if (!pmtpid) { - pmtidx = 0; - pmtnext = now + PMT_SCAN_IDLE; - LogI(1, prep("PMT scan idle\n")); - - Del (0, 0); //this effectively kills the PMT_SCAN_IDLE functionality - - //now after the scan is completed, start processing - ProcessNextFormat (true); //FirstTime flag is set - } - } - } - } - } else if (pmtpid > 0 && Pid == pmtpid && Tid == SI::TableIdPMT && Source () && Transponder ()) { - SI::PMT pmt (Data, false); - if (pmt.CheckCRCAndParse () && pmt.getServiceId () == pmtsid) { - SI::PMT::Stream stream; - for (SI::Loop::Iterator it; pmt.streamLoop.getNext (stream, it);) { - LogD(2, prep("StreamType: 0x%02x"), stream.getStreamType ()); - if (stream.getStreamType () == 0x05 || stream.getStreamType () == 0xc1) { //0x05 = Premiere, SKY, Freeview, Nagra 0xc1 = MHW1,MHW2; - SI::CharArray data = stream.getData (); - if ((data[1] & 0xE0) == 0xE0 && (data[3] & 0xF0) == 0xF0) { - bool prvData = false, usrData = false; - bool prvOTV = false, prvFRV = false; - int usrOTV = 0, usrFRV = 0; - if (data[2]==0x39) {//TODO Test This - prvFRV = true; - usrFRV = 1; - LogD(1, prep("if (data[2]==0x39) {//TODO Test This")); - } - //Format = 0; // 0 = premiere, 1 = MHW1, 2 = MHW2, 3 = Sky Italy (OpenTV), 4 = Sky UK (OpenTV), 5 = Freesat (Freeview), 6 = Nagraguide - SI::Descriptor * d; - for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext (it));) { - LogD(2, prep("EEPGDEBUG:d->getDescriptorTAG():%x,SI::PrivateTag:%x\n"), d->getDescriptorTag (), SI::PrivateDataSpecifierDescriptorTag); - switch (d->getDescriptorTag ()) { - case SI::PrivateDataSpecifierDescriptorTag: - //esyslog ("prv: %d %08x\n", d->getLength (), d->getData ().FourBytes (2)); - if (d->getLength () == 6 && d->getData ().FourBytes (2) == 0x000000be) - prvData = true; - if (d->getLength () == 6 && d->getData ().FourBytes (2) == 0x4f545600) //OpenTV - prvOTV = true; - if (d->getLength () == 6 && d->getData ().FourBytes (2) == 0x46534154) //Freeview - prvFRV = true; - break; - case 0x52: - //if (d->getLength () == 3 && d->getData ().FourBytes (2) == 0xb07ea882) { - if (d->getLength () == 3 && ((d->getData ().TwoBytes (2) & 0xff00) == 0xb000)) - UnprocessedFormat[NAGRA] = stream.getPid (); - break; - case 0x90: - //esyslog ("usr: %d %08x\n", d->getLength (), d->getData ().FourBytes (2)); - if (d->getLength () == 6 && d->getData ().FourBytes (2) == 0x0000ffff) - usrData = true; - if (d->getLength () == 3 && ((d->getData ().TwoBytes (2) & 0xff00) == 0xb600)) //SKY IT //TODO ugly! - //if (d->getLength () == 3 && (d->getData ().TwoBytes (2) == 0xb6a5)) //SKY IT //TODO ugly! - usrOTV = SKY_IT; - //Format = SKY_IT; - if (d->getLength () == 3 && d->getData ().FourBytes (2) == 0xc004e288) //SKY UK - usrOTV = SKY_UK; - //Format = SKY_UK; - break; - case 0xc1: //MHW1, MHW2 -// esyslog("EEPGDEBUG:d->getDescriptorTAG:%d %08x\n",d->getLength(),d->getData().FourBytes(2)); - if (d->getLength () == 10 && d->getData ().FourBytes (2) == 0x50555348) //MHw1 Cyfra - UnprocessedFormat[MHW1] = stream.getPid (); - break; - case 0xc2: //MHW1, MHW2 - if (d->getLength () == 10 && d->getData ().FourBytes (2) == 0x45504700) //MHw1 CanDigNL and CSat - UnprocessedFormat[MHW1] = stream.getPid (); - else if (d->getLength () == 10 && d->getData ().FourBytes (2) == 0x46494348) { //MHW2 - UnprocessedFormat[MHW2] = stream.getPid (); - } - break; - case 0xd1: //Freeview - LogD(1, prep("case 0xd1: //Freeview")); - if (d->getLength () == 3 && ((d->getData ().TwoBytes (2) & 0xff00) == 0x0100)) - usrFRV = 0x01; - //01 = EIT pid 3842 - //03 04 = SDT Service Description Table pid 3841 - //07 = still undocumented, definition of buch of transport streams pid 3840 - //02 = ATSC reserved, find out what huffman encoded text is sent here! pid 3843 - //05 06 = TOT Time Offset Table pid 3844 - break; - - /* case 0xfe: //SKY_IT - if (d->getLength () == 6 && d->getData ().FourBytes (2) == 0x534b5900) { //SKY_IT - Format = SKY_IT; - } - break;*/ - default: - break; - } - delete d; - } - if ((prvOTV) && ((usrOTV == SKY_IT) || (usrOTV == SKY_UK))) - UnprocessedFormat[usrOTV] = stream.getPid (); - else if (prvFRV) - if (usrFRV == 0x01) - UnprocessedFormat[FREEVIEW] = stream.getPid (); - if (prvData && usrData) - UnprocessedFormat[PREMIERE] = stream.getPid (); - //TODO DPE this is not good since the DISH/BEV filters are always on, but have to test somehow. - //EEPG:12472:H:S119.0W:20000:0:0:0:0:36862:4100:18:36862 - if (((Source() == cSource::FromString("S119.0W") - && Transponder() == cChannel::Transponder(12472,'H')) - || (Source() == cSource::FromString("S91.0W") - && Transponder() == cChannel::Transponder(12224,'R'))) - && !UnprocessedFormat[DISH_BEV]) { - UnprocessedFormat[DISH_BEV] = stream.getPid (); - } - } //if data[1] && data [3] - } //if streamtype - /*if (Format != PREMIERE) //any format found - break; //break out for loop */ - } //for loop that walks through streams -// if (Format == PREMIERE) { //FIXME for Premiere you should also stop scanning when found... - NextPmt (); - pmtnext = 0; - /* } - else { - Del (pmtpid, 0x02); - Del (0, 0); - pmtidx = 0; - pmtnext = now + PMT_SCAN_IDLE; - isyslog ("PMT scan forced idle\n"); - }*/ - } //checkCRC - } //if pmtpid - else if (Source ()) { - - if ( Pid == 0x12 || Pid == 0x0300 || Pid == 0x0441 ) { - if (Tid >= 0x4E) - ProccessContinuous(Pid, Tid, Length, Data); - return; - } - int Result; - switch (Tid) { - case 0xA0: //TODO DPE test this missing break but it seems a bug - if ((Pid < 0x30) || (Pid > 0x37)) { - SI::PremiereCIT cit (Data, false); - if (cit.CheckCRCAndParse ()) { - cSchedulesLock SchedulesLock (true, 10); - cSchedules *Schedules = (cSchedules *) cSchedules::Schedules (SchedulesLock); - if (Schedules) { - int nCount = 0; - int nRating = 0; - SI::ExtendedEventDescriptors * ExtendedEventDescriptors = 0; - SI::ShortEventDescriptor * ShortEventDescriptor = 0; - char *order = 0, *rating = 0; - { - time_t firstTime = 0; - SI::Descriptor * d; - bool UseExtendedEventDescriptor = false; - int LanguagePreferenceShort = -1; - int LanguagePreferenceExt = -1; - for (SI::Loop::Iterator it; (d = cit.eventDescriptors.getNext (it));) { - switch (d->getDescriptorTag ()) { - case 0xF0: // order information - if (SetupPE.OrderInfo) { - static const char *text[] = { - trNOOP ("Ordernumber"), - trNOOP ("Price"), - trNOOP ("Ordering"), - trNOOP ("SMS"), - trNOOP ("WWW") - }; - char buff[512]; - int p = 0; - const unsigned char *data = d->getData ().getData () + 2; - for (int i = 0; i < 5; i++) { - int l = data[0]; - if (l > 0) - p += snprintf (&buff[p], sizeof (buff) - p, "\n%s: %.*s", tr (text[i]), l, &data[1]); - data += l + 1; - } - if (p > 0) - order = strdup (buff); - } - break; - case 0xF1: // parental rating - if (SetupPE.RatingInfo) { - char buff[512]; - int p = 0; - const unsigned char *data = d->getData ().getData () + 2; - nRating = data[0] + 3; - p += snprintf (&buff[p], sizeof (buff) - p, "\n%s: %d %s", tr ("Rating"), nRating, tr ("years")); - data += 7; - int l = data[0]; - if (l > 0) - p += snprintf (&buff[p], sizeof (buff) - p, " (%.*s)", l, &data[1]); - if (p > 0) - rating = strdup (buff); - } - break; - case SI::PremiereContentTransmissionDescriptorTag: - if (nCount >= 0) { - SI::PremiereContentTransmissionDescriptor * pct = (SI::PremiereContentTransmissionDescriptor *) d; - nCount++; - SI::PremiereContentTransmissionDescriptor::StartDayEntry sd; - SI::Loop::Iterator it; - if (pct->startDayLoop.getNext (sd, it)) { - SI::PremiereContentTransmissionDescriptor::StartDayEntry::StartTimeEntry st; - SI::Loop::Iterator it2; - if (sd.startTimeLoop.getNext (st, it2)) { - time_t StartTime = st.getStartTime (sd.getMJD ()); - if (nCount == 1) - firstTime = StartTime; - else if (firstTime < StartTime - 5 * 50 || firstTime > StartTime + 5 * 60) - nCount = -1; - } - } - } - break; - 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; - default: - break; - } - delete d; - } - } - - { - bool Modified = false; - int optCount = 0; - unsigned int crc[3]; - crc[0] = cit.getContentId (); - SI::PremiereContentTransmissionDescriptor * pct; - for (SI::Loop::Iterator it; - (pct = - (SI::PremiereContentTransmissionDescriptor *) cit.eventDescriptors.getNext (it, - SI:: - PremiereContentTransmissionDescriptorTag));) { - int nid = pct->getOriginalNetworkId (); - int tid = pct->getTransportStreamId (); - int sid = pct->getServiceId (); - if (SetupPE.FixEpg) { - if (nid == 133) { - if (tid == 0x03 && sid == 0xf0) { - tid = 0x02; - sid = 0xe0; - } else if (tid == 0x03 && sid == 0xf1) { - tid = 0x02; - sid = 0xe1; - } else if (tid == 0x03 && sid == 0xf5) { - tid = 0x03; - sid = 0xdc; - } else if (tid == 0x04 && sid == 0xd2) { - tid = 0x11; - sid = 0xe2; - } else if (tid == 0x11 && sid == 0xd3) { - tid = 0x11; - sid = 0xe3; - } - } - } - tChannelID channelID (Source (), nid, tid, sid); - cChannel *channel = Channels.GetByChannelID (channelID, true); -#ifdef USE_NOEPG - // only use epg from channels not blocked by noEPG-patch - if (!allowedEPG (channelID)) - continue; -#endif /* NOEPG */ - - if (!channel) - continue; - - cSchedule *pSchedule = (cSchedule *) Schedules->GetSchedule (channelID); - if (!pSchedule) { - pSchedule = new cSchedule (channelID); - Schedules->Add (pSchedule); - } - - optCount++; - SI::PremiereContentTransmissionDescriptor::StartDayEntry sd; - int index = 0; - for (SI::Loop::Iterator it; pct->startDayLoop.getNext (sd, it);) { - int mjd = sd.getMJD (); - SI::PremiereContentTransmissionDescriptor::StartDayEntry::StartTimeEntry st; - for (SI::Loop::Iterator it2; sd.startTimeLoop.getNext (st, it2);) { - time_t StartTime = st.getStartTime (mjd); - time_t EndTime = StartTime + cit.getDuration (); - int runningStatus = (StartTime < now - && now < EndTime) ? SI::RunningStatusRunning : ((StartTime - 30 < now - && now < - StartTime) ? SI:: - RunningStatusStartsInAFewSeconds - : SI::RunningStatusNotRunning); - bool isOpt = false; - if (index++ == 0 && nCount > 1) - isOpt = true; - crc[1] = isOpt ? optCount : 0; - crc[2] = StartTime / STARTTIME_BIAS; - tEventID EventId = ((('P' << 8) | 'W') << 16) | crc16 (0, (unsigned char *) crc, sizeof (crc)); - LogI(2, "%s R%d %04x/%.4x %d %d/%d %s +%d ", *channelID.ToString (), runningStatus, - EventId & 0xFFFF, cit.getContentId (), index, isOpt, optCount, - stripspace (ctime (&StartTime)), (int) cit.getDuration () / 60); - if (EndTime + Setup.EPGLinger * 60 < now) { - LogI(2, "(old)\n"); - continue; - } - - bool newEvent = false; - cEvent *pEvent = (cEvent *) pSchedule->GetEvent (EventId, -1); - if (!pEvent) { - LogI(2, "(new)\n"); - pEvent = new cEvent (EventId); - if (!pEvent) - continue; - newEvent = true; - } else { - LogI(2, "(upd)\n"); - pEvent->SetSeen (); - if (pEvent->TableID () == 0x00 || pEvent->Version () == cit.getVersionNumber ()) { - if (pEvent->RunningStatus () != runningStatus) - pSchedule->SetRunningStatus (pEvent, runningStatus, channel); - continue; - } - } - pEvent->SetEventID (EventId); - pEvent->SetTableID (Tid); - pEvent->SetVersion (cit.getVersionNumber ()); - pEvent->SetStartTime (StartTime); - pEvent->SetDuration (cit.getDuration ()); - - if (ShortEventDescriptor) { - char buffer[256]; - ShortEventDescriptor->name.getText (buffer, sizeof (buffer)); - if (isOpt) { - char buffer2[sizeof (buffer) + 32]; - snprintf (buffer2, sizeof (buffer2), optPats[SetupPE.OptPat], buffer, optCount); - pEvent->SetTitle (buffer2); - } else - pEvent->SetTitle (buffer); - LogI(2, "title: %s\n", pEvent->Title ()); - pEvent->SetShortText (ShortEventDescriptor->text.getText (buffer, sizeof (buffer))); - } - if (ExtendedEventDescriptors) { - char buffer[ExtendedEventDescriptors->getMaximumTextLength (": ") + 1]; - pEvent->SetDescription (ExtendedEventDescriptors->getText (buffer, sizeof (buffer), ": ")); - } - if (order || rating) { - int len = (pEvent->Description ()? strlen (pEvent->Description ()) : 0) + - (order ? strlen (order) : 0) + (rating ? strlen (rating) : 0); - char buffer[len + 32]; - buffer[0] = 0; - if (pEvent->Description ()) - strcat (buffer, pEvent->Description ()); - if (rating) { - strcat (buffer, rating); - pEvent->SetParentalRating(nRating); - } - if (order) - strcat (buffer, order); - pEvent->SetDescription (buffer); - } - - if (newEvent) - pSchedule->AddEvent (pEvent); - - pEvent->FixEpgBugs (); - if (pEvent->RunningStatus () != runningStatus) - pSchedule->SetRunningStatus (pEvent, runningStatus, channel); - pSchedule->DropOutdated (StartTime, EndTime, Tid, cit.getVersionNumber ()); - Modified = true; - } - } - if (Modified) { - pSchedule->Sort (); - Schedules->SetModified (pSchedule); - } - delete pct; - } - } - delete ExtendedEventDescriptors; - delete ShortEventDescriptor; - free (order); - free (rating); - } - } //if checkcrcandpars - break; - } //if citpid == 0xb11 Premiere - case 0xa1: - case 0xa2: - case 0xa3: - Result = GetTitlesSKYBOX (Data, Length - 4); - if (Result != 1) //when fatal error or finished - Del (Pid, 0xa0, 0xfc); //kill filter - if (Result == 0) { //fatal error - esyslog ("EEPG: Fatal error reading titles."); - ProcessNextFormat (); //and go process other formats - } - if (Result == 2) - AddFilter (Pid + 0x10, 0xa8, 0xfc); //Set filter that processes summaries of this batch - break; - case 0xa8: - case 0xa9: - case 0xaa: - case 0xab: - Result = GetSummariesSKYBOX (Data, Length - 4); - if (Result != 1) //when fatal error or finished - Del (Pid, 0xa8, 0xfc); //kill filter - if (Result == 0) { - esyslog ("EEPG: Fatal error reading summaries."); - ProcessNextFormat (); - } - if (Result == 2) { - LoadIntoSchedule (); - if (Pid < 0x47) //this is not the last batch//FIXME chaining is easy on the PIDs and the CPU, but error when Pid,Tid is not used at the moment... - AddFilter (Pid - 0x0F, 0xa0, 0xfc); //next pid, first tid - else //last pid was processed - ProcessNextFormat (); - } - break; - case 0x90: - if (Pid == 0xd2) { - Result = GetTitlesMHW1 (Data, Length); - if (Result != 1) //fatal error or last processed - Del (Pid, Tid); - if (Result == 0) { - esyslog ("EEPG: Fatal error reading titles."); - ProcessNextFormat (); - } - if (Result == 2) - AddFilter (0xd3, 0x90); //SummariesMHW1 - } else if (Pid == 0xd3) { - Result = GetSummariesMHW1 (Data, Length); - if (Result != 1) //fatal error or last processed - Del (Pid, Tid); - if (Result == 0) { - esyslog ("EEPG: Fatal error reading summaries."); - } - if (Result == 2) { - LoadIntoSchedule (); - } - if (Result != 1) { - ProcessNextFormat (); - } - } - break; - case 0xc8: //GetChannelsMHW2 or GetThemesMHW2 - if (Pid == 0x231) { - if (Data[3] == 0x01) { //Themes it will be - Result = GetThemesMHW2 (Data, Length); //return code 0 = fatal error, code 1 = sucess, code 2 = last item processed - //break; - if (Result != 1) - EndThemes = true; //also set Endthemes on true on fatal error - } //if Data - else if (Data[3] == 0x00) { //Channels it will be - Result = GetChannelsMHW (Data, Length, 2); //return code 0 = fatal error, code 1 = sucess, code 2 = last item processed - if (Result != 1) - EndChannels = true; //always remove filter, code 1 should never be returned since MHW2 always reads all channels.. - ChannelsOk = (Result == 2); - } - if (EndChannels && EndThemes) { //those are only set withing MHW2 - Del (0x231, 0xc8); //stop reading MHW2 themes and channels - if (ChannelsOk) //No channels = fatal, no themes = nonfatal - AddFilter (0x234, 0xe6); //start reading MHW2 titles - else { - esyslog ("EEPG: Fatal error reading channels."); - ProcessNextFormat (); - } - } - } //if Pid == 0x231 - break; - case 0x91: - Result = GetChannelsMHW (Data, Length, 1); //return code 0 = fatal error, code 1 = sucess, code 2 = last item processed - Del (Pid, Tid); //always remove filter, code 1 should never be returned since MHW1 always reads all channels... - if (Result == 2) - AddFilter (0xd2, 0x90); //TitlesMHW1 - else { - esyslog ("EEPG: Fatal error reading channels."); - ProcessNextFormat (); - } - break; - case 0x92: - Result = GetThemesMHW1 (Data, Length); //return code 0 = fatal error, code 1 = sucess, code 2 = last item processed - if (Result != 1) - Del (Pid, Tid); - if (Result == 2) - AddFilter (0xd3, 0x91); //ChannelsMHW1 - else { - esyslog ("EEPG: Fatal error reading themes."); //doesnt have to be fatal... - ProcessNextFormat (); - } - break; - case 0xe6: //TitlesMHW2 - if (Pid == 0x234) { - Result = GetTitlesMHW2 (Data, Length); //return code 0 = fatal error, code 1 = sucess, code 2 = last item processed - if (Result != 1) - Del (Pid, Tid); - if (Result == 0) { - esyslog ("EEPG: Fatal error reading titles."); - ProcessNextFormat (); - } - if (Result == 2) - AddFilter (0x236, 0x96); //start reading MHW2 summaries.... - } - break; - case 0x96: //Summaries MHW2 - if (Pid == 0x236) { - Result = GetSummariesMHW2 (Data, Length); //return code 0 = fatal error, code 1 = sucess, code 2 = last item processed - if (Result != 1) - Del (Pid, Tid); - if (Result == 0) { - esyslog ("EEPG: Fatal error reading summaries."); - } - if (Result == 2) - LoadIntoSchedule (); - if (Result != 1) { - ProcessNextFormat (); - } - } //if pid - break; - case 0x4a: //Sky channels - if (Pid == 0x11) { - Result = GetChannelsSKYBOX (Data, Length - 4); - if (Result != 1) //only breakoff on completion or error; do NOT clean up after success, because then not all bouquets will be read - Del (Pid, Tid); //Read all channels, clean up filter - if (Result == 2) { - GetThemesSKYBOX (); //Sky Themes from file; must be called AFTER first channels to have lThemes initialized FIXME - if (ReadFileDictionary ()) - AddFilter (0x30, 0xa0, 0xfc); //SKY Titles batch 0 of 7 - else { - esyslog ("EEPG: Fatal error reading huffman table."); - ProcessNextFormat (); - } - } - - } //if Pid == 0x11 - break; - case 0xb0: //NagraGuide - if (Pid == 0xc8) { - Result = GetNagra (Data, Length); - if (Result != 1) - Del (Pid, Tid); - if (Result == 0) { - esyslog ("EEPG: Fatal error processing NagraGuide. End of processing."); - } - if (Result == 2) { - ProcessNagra (); - cSchedules::Cleanup (true); //deletes all past events - isyslog ("EEPG: Ended processing Nagra"); - } - if (Result != 1) { - ProcessNextFormat (); - } - } - break; - - case 0x4E: - case 0x4F: - case 0x50: - case 0x51: - case 0x52: - case 0x53: - case 0x54: - case 0x55: - case 0x56: - case 0x57: - case 0x58: - case 0x59: - case 0x5A: - case 0x5B: - case 0x5C: - case 0x5D: - case 0x5E: - case 0x5F: - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6A: - case 0x6B: - case 0x6C: - case 0x6D: - case 0x6E: - case 0x6F: - // Freesat: - // Set(3842, 0x4E, 0xFE); // event info, actual(0x4E)/other(0x4F) TS, present/following - // Set(3842, 0x50, 0xF0); // event info, actual TS, schedule(0x50)/schedule for future days(0x5X) - // Set(3842, 0x60, 0xF0); // event info, other TS, schedule(0x60)/schedule for future days(0x6X) - // - // PID found: 3841 (0x0f01) [SECTION: Service Description Table (SDT) - other transport stream] - // PID found: 3842 (0x0f02) [SECTION: Event Information Table (EIT) - other transport stream, schedule] - // PID found: 3843 (0x0f03) [SECTION: ATSC reserved] TODO find out what compressed text info is here! - // PID found: 3844 (0x0f04) [SECTION: Time Offset Table (TOT)] - - if (Pid == 3842 || Pid == 0x39) {//0x39 Viasat, 0x0300 Dish Network EEPG, 0x0441 Bell ExpressVU EEPG - ProccessContinuous(Pid, Tid, Length, Data); - } - break; - - default: - break; - } //end switch - } //closes SOURCE() -} //end of closing - -// --- cPluginEEPG ------------------------------------------------------ - -class cPluginEEPG:public cPlugin -{ -private: - struct { - cFilterEEPG *filter; - cDevice *device; - } epg[MAXDVBDEVICES]; - - void CheckCreateFile(const char* Name, const char *fileContent[]); - -public: - cPluginEEPG (void); - virtual const char *Version (void) { - return VERSION; - } - virtual const char *Description (void) { - return tr (DESCRIPTION); - } - virtual bool Start (void); - virtual void Stop (void); - virtual cMenuSetupPage *SetupMenu (void); - virtual bool SetupParse (const char *Name, const char *Value); -}; - -cPluginEEPG::cPluginEEPG (void) -{ - memset (epg, 0, sizeof (epg)); -} - -void cPluginEEPG::CheckCreateFile(const char* Name, const char *fileContent[]) -{ - FILE *File; - string FileName = string(ConfDir) + "/" + Name; - File = fopen(FileName.c_str(), "r"); - if (File == NULL) { - LogE (0, prep("Error opening file '%s', %s"), FileName.c_str(), strerror (errno)); - File = fopen (FileName.c_str(), "w"); - if (File == NULL) { - LogE (0, prep("Error creating file '%s', %s"), FileName.c_str(), strerror (errno)); - } else { - int i = 0; - while (fileContent[i] != NULL) { - fprintf (File, "%s\n", fileContent[i]); - i++; - } - LogI (0, prep("Success creating file '%s'"), FileName.c_str()); - fclose (File); - } - } else { - fclose (File); - } -} - -bool cPluginEEPG::Start (void) -{ -#if APIVERSNUM < 10507 - RegisterI18n (Phrases); -#endif - for (int i = 0; i < MAXDVBDEVICES; i++) { - cDevice *dev = cDevice::GetDevice (i); - if (dev) { - epg[i].device = dev; - dev->AttachFilter (epg[i].filter = new cFilterEEPG); - isyslog ("Attached EEPG filter to device %d", i); - } - } - ConfDir = NULL; - // Initialize any background activities the plugin shall perform. - DIR *ConfigDir; - if (ConfDir == NULL) { - Asprintf (&ConfDir, "%s/eepg", cPlugin::ConfigDirectory ()); - } - ConfigDir = opendir (ConfDir); - if (ConfigDir == NULL) { - esyslog ("EEPG: Error opening directory '%s', %s", ConfDir, strerror (errno)); - if (mkdir (ConfDir, 0777) < 0) { - esyslog ("EEPG: Error creating directory '%s', %s", ConfDir, strerror (errno)); - } else { - isyslog ("EEPG: Success creating directory '%s'", ConfDir); - } - } - CheckCreateFile(EEPG_FILE_EQUIV, FileEquivalences); - CheckCreateFile("sky_it.dict", SkyItDictionary); - CheckCreateFile("sky_uk.dict", SkyUkDictionary); - CheckCreateFile("sky_it.themes", SkyItThemes); - CheckCreateFile("sky_uk.themes", SkyUkThemes); - CheckCreateFile("freesat.t1", FreesatT1); - CheckCreateFile("freesat.t2", FreesatT2); - CheckCreateFile("sky_uk.themes", SkyUkThemes); - - //store all available sources, so when a channel is not found on current satellite, we can look for alternate sat positions. - //perhaps this can be done smarter through existing VDR function??? - for (cChannel * Channel = Channels.First (); Channel; Channel = Channels.Next (Channel)) { - if (!Channel->GroupSep ()) { - bool found = false; - for (int i = 0; (i < NumberOfAvailableSources) && (!found); i++) - found = (Channel->Source () == AvailableSources[i]); - if (!found) - AvailableSources[NumberOfAvailableSources++] = Channel->Source (); - } - } - if (CheckLevel(3)) - for (int i = 0; i < NumberOfAvailableSources; i++) - isyslog ("EEPG: Available sources:%s.", *cSource::ToString (AvailableSources[i])); - - - return true; -} - -void cPluginEEPG::Stop (void) -{ - for (int i = 0; i < MAXDVBDEVICES; i++) { - cDevice *dev = epg[i].device; - if (dev) - dev->Detach (epg[i].filter); - delete epg[i].filter; - epg[i].device = 0; - epg[i].filter = 0; - } - // Clean up after yourself! - if (ConfDir) { - free (ConfDir); - } -} - -cMenuSetupPage *cPluginEEPG::SetupMenu (void) -{ - return new cMenuSetupPremiereEpg; -} - -bool cPluginEEPG::SetupParse (const char *Name, const char *Value) -{ -// LogF(0, "!!!! Dime test LogF"); -// LogF(0, "!!!! Dime test LogF %d", 2); -// LogI(0, "!!!! Dime test LogI"); -// LogI(0, "!!!! Dime test LogI %d", 2); -// LogI(0, prep2("!!!! Dime test prep")); -// LogI(0, prep2("!!!! Dime test prep %d"), 2); -// LogD(0, "!!!! Dime test LogD"); -// LogD(0, "!!!! Dime test LogD %d", 2); -// LogE(0, "!!!! Dime test LogE"); -// LogE(0, "!!!! Dime test LogE %d", 2); - - - if (!strcasecmp (Name, "OptionPattern")) - SetupPE.OptPat = atoi (Value); - else if (!strcasecmp (Name, "OrderInfo")) - SetupPE.OrderInfo = atoi (Value); - else if (!strcasecmp (Name, "RatingInfo")) - SetupPE.RatingInfo = atoi (Value); - else if (!strcasecmp (Name, "FixEpg")) - SetupPE.FixEpg = atoi (Value); - else if (!strcasecmp (Name, "DisplayMessage")) - SetupPE.DisplayMessage = atoi (Value); -#ifdef DEBUG - else if (!strcasecmp (Name, "LogLevel")) - SetupPE.LogLevel = atoi (Value); -#endif - else - return false; - return true; -} - -VDRPLUGINCREATOR (cPluginEEPG); // Don't touch this! +/* + * Extended Epg plugin to VDR (C++) + * + * (C) 2008-2009 Dingo35 + * + * This code is based on: + * -Premiere plugin (C) 2005-2007 Stefan Huelswitt + * -mhwepg program (C) 2002, 2003 Jean-Claude Repetto + * -LoadEpg plugin written by Luca De Pieri + * -Freesat patch written by dom /at/ suborbital.org.uk + * + * + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "eepg.h" +#include "dish.h" + +#include +#include +#include + +#define VERBOSE 3 +/* 0 = only print errors, 1 = print channels and themes, 2 = print channels, themes, titles, summaries 3 = debug mode */ +/* all is logged into /var/log/syslog */ + +#if APIVERSNUM < 10401 +#error You need at least VDR API version 1.4.1 for this plugin +#endif +#if APIVERSNUM < 10507 +#define trNOOP(s) (s) +#endif + +//#define DEBUG +//#define DEBUG2 + +/*#ifdef DEBUG +#define d(x) { (x); } +#else +#define d(x) ; +#endif +#ifdef DEBUG2 +#define d2(x) { (x); } +#else +#define d2(x) ; +#endif*/ + +#define PMT_SCAN_TIMEOUT 10 // seconds +#define PMT_SCAN_IDLE 3600 // seconds + +static const char *VERSION = "0.0.6pre"; +static const char *DESCRIPTION = trNOOP ("Parses Extended EPG data"); + +using namespace std; + +// --- cSetupEEPG ------------------------------------------------------- + +const char *optPats[] = { + "%s", + "%s (Option %d)", + "%s (O%d)", + "#%2$d %1$s", + "[%2$d] %1$s" +}; + +#define NUM_PATS (sizeof(optPats)/sizeof(char *)) + +char *cs_hexdump (int m, const uchar * buf, int n) +{ + int i; + static char dump[1024]; + + dump[i = 0] = '\0'; + m = (m) ? 3 : 2; + if (m * n >= (int) sizeof (dump)) + n = (sizeof (dump) / m) - 1; + while (i < n) + sprintf (dump + (m * i++), "%02X%s", *buf++, (m > 2) ? " " : ""); + return (dump); +} + +class cSetupEEPG +{ +public: + int OptPat; + int OrderInfo; + int RatingInfo; + int FixEpg; + int DisplayMessage; +#ifdef DEBUG + int LogLevel; +#endif + +public: + cSetupEEPG (void); +}; + +cSetupEEPG SetupPE; + +cSetupEEPG::cSetupEEPG (void) +{ + OptPat = 1; + OrderInfo = 1; + RatingInfo = 1; + FixEpg = 0; + DisplayMessage = 1; +#ifdef DEBUG + LogLevel = 0; +#endif + +} + +// --- cMenuSetupPremiereEpg ------------------------------------------------------------ + +class cMenuSetupPremiereEpg:public cMenuSetupPage +{ +private: + cSetupEEPG data; + const char *optDisp[NUM_PATS]; + char buff[NUM_PATS][32]; +protected: + virtual void Store (void); +public: + cMenuSetupPremiereEpg (void); +}; + +cMenuSetupPremiereEpg::cMenuSetupPremiereEpg (void) +{ + data = SetupPE; + SetSection (tr ("PremiereEPG")); + optDisp[0] = tr ("off"); + for (unsigned int i = 1; i < NUM_PATS; i++) { + snprintf (buff[i], sizeof (buff[i]), optPats[i], "Event", 1); + optDisp[i] = buff[i]; + } + Add (new cMenuEditStraItem (tr ("Tag option events"), &data.OptPat, NUM_PATS, optDisp)); + Add (new cMenuEditBoolItem (tr ("Show order information"), &data.OrderInfo)); + Add (new cMenuEditBoolItem (tr ("Show rating information"), &data.RatingInfo)); + Add (new cMenuEditBoolItem (tr ("Fix EPG data"), &data.FixEpg)); + Add (new cMenuEditBoolItem (tr ("Display summary message"), &data.DisplayMessage)); +#ifdef DEBUG + Add (new cMenuEditIntItem (tr ("Level of logging verbosity"), &data.LogLevel, 0, 5)); +#endif +} + +void cMenuSetupPremiereEpg::Store (void) +{ + SetupPE = data; + SetupStore ("OptionPattern", SetupPE.OptPat); + SetupStore ("OrderInfo", SetupPE.OrderInfo); + SetupStore ("RatingInfo", SetupPE.RatingInfo); + SetupStore ("FixEpg", SetupPE.FixEpg); + SetupStore ("DisplayMessage", SetupPE.DisplayMessage); +#ifdef DEBUG + SetupStore ("LogLevel", SetupPE.LogLevel); +#endif +} + +bool CheckLevel(int level) +{ +#ifdef DEBUG + if (SetupPE.LogLevel >= level) +#else + if (VERBOSE >= level) +#endif + { + return true; + } + return false; +} + +const char* PrepareLog(string message) +{ + message = "EEPG: " + message; + return message.c_str(); +} + +#define MAXSYSLOGBUF 256 + +//void LogVsyslog(int errLevel, const char * message, ...) +void LogVsyslog(int errLevel, int const& lineNum, const char * function, const char * message, ...) +{ + va_list ap; + char fmt[MAXSYSLOGBUF]; + if (errLevel == LOG_DEBUG) { + snprintf(fmt, sizeof(fmt), "[%d] %s:%d %s", cThread::ThreadId(), function, lineNum, message); + } else { + snprintf(fmt, sizeof(fmt), "[%d] %s", cThread::ThreadId(), message); + } + va_start(ap,message); + vsyslog ( errLevel, fmt, ap ); + va_end(ap); +} + +#define LogI(a, b...) void( CheckLevel(a) ? LogVsyslog ( LOG_INFO, __LINE__, __FUNCTION__, b ) : void() ) +#define LogE(a, b...) void( CheckLevel(a) ? LogVsyslog ( LOG_ERR, __LINE__, __FUNCTION__, b ) : void() ) +#define LogD(a, b...) void( CheckLevel(a) ? LogVsyslog ( LOG_DEBUG, __LINE__, __FUNCTION__, b ) : void() ) +//#define LogE(a, b...) void( CheckLevel(a) ? esyslog ( b ) : void() ) +//#define LogD(a, b...) void( CheckLevel(a) ? dsyslog ( b ) : void() ) +#define prep(s) PrepareLog(s) +#define prep2(s) s + +//void LogF(int level, const char * message, ...) __attribute__ ((format (printf,2,3))); + +//void LogF(int level, const char * message, ...) +//{ +// if (CheckLevel(level)) { +// va_list ap; +// va_start(ap,message); +// vsyslog (LOG_ERR, PrepareLog(message), ap ); +// va_end(ap); +// } +//} + +#define Asprintf(a, b, c...) void( asprintf(a, b, c) < 0 ? esyslog("memory allocation error - %s", b) : void() ) + +// --- CRC16 ------------------------------------------------------------------- + +#define POLY 0xA001 // CRC16 + +unsigned int crc16 (unsigned int crc, unsigned char const *p, int len) +{ + while (len--) { + crc ^= *p++; + for (int i = 0; i < 8; i++) + crc = (crc & 1) ? (crc >> 1) ^ POLY : (crc >> 1); + } + return crc & 0xFFFF; +} + +// --- cFilterEEPG ------------------------------------------------------ + +#define STARTTIME_BIAS (20*60) + +static int AvailableSources[32]; +static int NumberOfAvailableSources = 0; +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: + int pmtpid, pmtsid, pmtidx, pmtnext; + int UnprocessedFormat[HIGHEST_FORMAT + 1]; //stores the pid when a format is detected on this transponder, and that are not processed yet + int nEquivChannels, nChannels, nThemes, nTitles, nSummaries, NumberOfTables, Version; + int TitleCounter, SummaryCounter, NoSummaryCounter, RejectTableId; + bool EndChannels, EndThemes; //only used for ?? + int MHWStartTime; //only used for MHW1 + bool ChannelsOk; + //int Format; //the format that this filter currently is processing + std::map < int, int >ChannelSeq; // ChannelSeq[ChannelId] returns the recordnumber of the channel + + Summary_t *Summaries[MAX_TITLES]; + Title_t *Titles[MAX_TITLES]; + sChannel sChannels[MAX_CHANNELS]; + unsigned char Themes[MAX_THEMES][64]; + + std::map < unsigned short int, unsigned char *>buffer; //buffer[Table_Extension_Id] returns the pointer to the buffer for this TEI + std::map < unsigned short int, int >bufsize; //bufsize[Table_Extension_Id] returns the buffersize of the buffer for this TEI + unsigned short int NagraTIE[64]; //at this moment a max of 31 table_ids could be used, so 64 should be enough ....stores the Table_Extension_Id's of summaries received, so they can be processed. Processing while receiving somehow drops sections, the 0x0000 marker will be missed ... + unsigned short int NagraCounter; + + unsigned char InitialChannel[8]; + unsigned char InitialTitle[64]; + unsigned char InitialSummary[64]; + + void NextPmt (void); + void ProccessContinuous(u_short Pid, u_char Tid, int Length, const u_char *Data); +protected: + virtual void Process (u_short Pid, u_char Tid, const u_char * Data, int Length); + virtual void AddFilter (u_short Pid, u_char Tid); + virtual void AddFilter (u_short Pid, u_char Tid, unsigned char Mask); + virtual void ProcessNextFormat (bool FirstTime); + virtual int GetChannelsSKYBOX (const u_char * Data, int Length); + virtual bool GetThemesSKYBOX (void); + virtual bool ReadFileDictionary (void); //Reads Huffman tables for SKY + virtual int GetTitlesSKYBOX (const u_char * Data, int Length); + virtual int GetSummariesSKYBOX (const u_char * Data, int Length); + virtual int GetChannelsMHW (const u_char * Data, int Length, int MHW); //TODO replace MHW by Format? + virtual int GetThemesMHW1 (const u_char * Data, int Length); + virtual int GetNagra (const u_char * Data, int Length); + virtual void ProcessNagra (void); + virtual void GetTitlesNagra (const u_char * Data, int Length, unsigned short TableIdExtension); + virtual char *GetSummaryTextNagra (const u_char * DataStart, long int Offset, unsigned int EventId); + virtual int GetChannelsNagra (const u_char * Data, int Length); + virtual int GetThemesNagra (const u_char * Data, int Length, unsigned short TableIdExtension); + virtual int GetTitlesMHW1 (const u_char * Data, int Length); + virtual int GetSummariesMHW1 (const u_char * Data, int Length); + virtual int GetThemesMHW2 (const u_char * Data, int Length); + virtual int GetTitlesMHW2 (const u_char * Data, int Length); + virtual int GetSummariesMHW2 (const u_char * Data, int Length); + virtual void FreeSummaries (void); + virtual void FreeTitles (void); + virtual void PrepareToWriteToSchedule (sChannel * C, cSchedules * s, cSchedule * ps[MAX_EQUIVALENCES]); //gets a channel and returns an array of schedules that WriteToSchedule can write to. Call this routine before a batch of titles with the same ChannelId will be WriteToScheduled; batchsize can be 1 + virtual void FinishWriteToSchedule (sChannel * C, cSchedules * s, cSchedule * ps[MAX_EQUIVALENCES]); + virtual void WriteToSchedule (cSchedule * ps[MAX_EQUIVALENCES], unsigned short int NumberOfEquivalences, + unsigned int EventId, unsigned int StartTime, unsigned int Duration, char *Text, + char *SummText, unsigned short int ThemeId, unsigned short int TableId, + unsigned short int Version, char Rating = 0x00); + virtual void LoadIntoSchedule (void); + virtual void LoadEquivalentChannels (void); +public: + cFilterEEPG (void); + virtual void SetStatus (bool On); + void Trigger (void); +}; + +cFilterEEPG::cFilterEEPG (void) +{ + Trigger (); + //Set (0x00, 0x00); +} + +void cFilterEEPG::Trigger (void) +{ + LogI(3, prep("trigger\n")); + pmtpid = 0; + pmtidx = 0; + pmtnext = 0; +} + +void cFilterEEPG::SetStatus (bool On) +{ + LogI(0, prep("setstatus %d\n"), On); + if (!On) { + FreeSummaries (); + FreeTitles (); + //Format = 0; + ChannelsOk = false; + NumberOfTables = 0; + } else { + //Set(0x00,0x00); + for (int i = 0; i <= HIGHEST_FORMAT; i++) + UnprocessedFormat[i] = 0; //pid 0 is assumed to be nonvalid for EEPG transfers + AddFilter (0, 0); + } + cFilter::SetStatus (On); + Trigger (); +} + +void cFilterEEPG::NextPmt (void) +{ + Del (pmtpid, 0x02); + pmtpid = 0; + pmtidx++; + LogE(3, prep("PMT next\n")); +} + + + +// ------------------- Freesat ------------------- + +/* FreeSat Huffman decoder for VDR + * + * Insert GPL licence + */ + +/* The following features can be controlled: + * + * FREEVIEW_NO_SYSLOG - Disable use of isyslog + */ + +#ifndef FREEVIEW_NO_SYSLOG +#include +/* Logging via vdr */ +#ifndef isyslog +#define isyslog(a...) void( (SysLogLevel > 1) ? syslog_with_tid(LOG_INFO, a) : void() ) +#endif +void syslog_with_tid (int priority, const char *format, ...) __attribute__ ((format (printf, 2, 3))); +#else +#define isyslog(a...) fprintf(stderr,a) +#endif + + + +struct hufftab { + unsigned int value; + short bits; + char next; +}; + +#define START '\0' +#define STOP '\0' +#define ESCAPE '\1' + + +int freesat_decode_error = 0; /* If set an error has occurred during decoding */ + +static struct hufftab *tables[2][256]; +static int table_size[2][256]; + +/** \brief Convert a textual character description into a value + * + * \param str - Encoded (in someway) string + * + * \return Raw character + */ +static unsigned char resolve_char (char *str) +{ + int val; + if (strcmp (str, "ESCAPE") == 0) { + return ESCAPE; + } else if (strcmp (str, "STOP") == 0) { + return STOP; + } else if (strcmp (str, "START") == 0) { + return START; + } else if (sscanf (str, "0x%02x", &val) == 1) { + return val; + } + return str[0]; + + +} + + +/** \brief Decode a binary string into a value + * + * \param binary - Binary string to decode + * + * \return Decoded value + */ +static unsigned long decode_binary (char *binary) +{ + unsigned long mask = 0x80000000; + unsigned long maskval = 0; + unsigned long val = 0; + size_t i; + + for (i = 0; i < strlen (binary); i++) { + if (binary[i] == '1') { + val |= mask; + } + maskval |= mask; + mask >>= 1; + } + return val; +} + +/** \brief Load an individual freesat data file + * + * \param tableid - Table id that should be loaded + * \param filename - Filename to load + */ +static void load_file (int tableid, const char *filename) +{ + char buf[1024]; + char *from, *to, *binary; + FILE *fp; + + tableid--; + if ((fp = fopen (filename, "r")) != NULL) { + LogI(0, prep("Loading table %d Filename <%s>"), tableid + 1, filename); + + while (fgets (buf, sizeof (buf), fp) != NULL) { + from = binary = to = NULL; + int elems = sscanf (buf, "%a[^:]:%a[^:]:%a[^:]:", &from, &binary, &to); + if (elems == 3) { + int bin_len = strlen (binary); + int from_char = resolve_char (from); + char to_char = resolve_char (to); + unsigned long bin = decode_binary (binary); + int i = table_size[tableid][from_char]++; + + tables[tableid][from_char] = + (struct hufftab *) realloc (tables[tableid][from_char], (i + 1) * sizeof (tables[tableid][from_char][0])); + tables[tableid][from_char][i].value = bin; + tables[tableid][from_char][i].next = to_char; + tables[tableid][from_char][i].bits = bin_len; + free (from); + free (to); + free (binary); + } + } + fclose (fp); + } else { + LogE(0, prep("Cannot load <%s> for table %d"), filename, tableid + 1); + } +} + + +/** \brief Decode an EPG string as necessary + * + * \param src - Possibly encoded string + * \param size - Size of the buffer + * + * \retval NULL - Can't decode + * \return A decoded string + */ +char *freesat_huffman_decode (const unsigned char *src, size_t size) +{ + int tableid; + freesat_decode_error = 0; + + if (src[0] == 0x1f && (src[1] == 1 || src[1] == 2)) { + int uncompressed_len = 30; + char *uncompressed = (char *) calloc (1, uncompressed_len + 1); + unsigned value = 0, byte = 2, bit = 0; + int p = 0; + int lastch = START; + + tableid = src[1] - 1; + while (byte < 6 && byte < size) { + value |= src[byte] << ((5 - byte) * 8); + byte++; + } + //freesat_table_load (); /**< Load the tables as necessary */ + + do { + int found = 0; + unsigned bitShift = 0; + if (lastch == ESCAPE) { + char nextCh = (value >> 24) & 0xff; + found = 1; + // Encoded in the next 8 bits. + // Terminated by the first ASCII character. + bitShift = 8; + if ((nextCh & 0x80) == 0) + lastch = nextCh; + if (p >= uncompressed_len) { + uncompressed_len += 10; + uncompressed = (char *) realloc (uncompressed, uncompressed_len + 1); + } + uncompressed[p++] = nextCh; + uncompressed[p] = 0; + } else { + int j; + for (j = 0; j < table_size[tableid][lastch]; j++) { + unsigned mask = 0, maskbit = 0x80000000; + short kk; + for (kk = 0; kk < tables[tableid][lastch][j].bits; kk++) { + mask |= maskbit; + maskbit >>= 1; + } + if ((value & mask) == tables[tableid][lastch][j].value) { + char nextCh = tables[tableid][lastch][j].next; + bitShift = tables[tableid][lastch][j].bits; + if (nextCh != STOP && nextCh != ESCAPE) { + if (p >= uncompressed_len) { + uncompressed_len += 10; + uncompressed = (char *) realloc (uncompressed, uncompressed_len + 1); + } + uncompressed[p++] = nextCh; + uncompressed[p] = 0; + } + found = 1; + lastch = nextCh; + break; + } + } + } + if (found) { + // Shift up by the number of bits. + unsigned b; + for (b = 0; b < bitShift; b++) { + value = (value << 1) & 0xfffffffe; + if (byte < size) + value |= (src[byte] >> (7 - bit)) & 1; + if (bit == 7) { + bit = 0; + byte++; + } else + bit++; + } + } else { + LogE (0, prep("Missing table %d entry: <%s>"), tableid + 1, uncompressed); + // Entry missing in table. + return uncompressed; + } + } while (lastch != STOP && value != 0); + + return uncompressed; + } + return NULL; +} + +//here all declarations for global variables over all devices + +char *ConfDir; + +sNodeH *nH, H; + +//unsigned char DecodeErrorText[4096]; //TODO only used for debugging? + +int Yesterday; +int YesterdayEpoch; +int YesterdayEpochUTC; + +/* + * Convert local time to UTC + */ +time_t LocalTime2UTC (time_t t) +{ + struct tm *temp; + + temp = gmtime (&t); + temp->tm_isdst = -1; + return mktime (temp); +} + +/* + * Convert UTC to local time + */ +time_t UTC2LocalTime (time_t t) +{ + return 2 * t - LocalTime2UTC (t); +} + +void GetLocalTimeOffset (void) +{ + time_t timeLocal; + struct tm *tmCurrent; + + timeLocal = time (NULL); + timeLocal -= 86400; + tmCurrent = gmtime (&timeLocal); + Yesterday = tmCurrent->tm_wday; + tmCurrent->tm_hour = 0; + tmCurrent->tm_min = 0; + tmCurrent->tm_sec = 0; + tmCurrent->tm_isdst = -1; + YesterdayEpoch = mktime (tmCurrent); + YesterdayEpochUTC = UTC2LocalTime (mktime (tmCurrent)); +} + +void CleanString (unsigned char *String) +{ + +// LogD (1, prep("Unclean: %s"), String); + unsigned char *Src; + unsigned char *Dst; + int Spaces; + int pC; + Src = String; + Dst = String; + Spaces = 0; + pC = 0; + while (*Src) { + // corrections + if (*Src == 0x8c) { // iso-8859-2 LATIN CAPITAL LETTER S WITH ACUTE + *Src = 0xa6; + } + if (*Src == 0x8f) { // iso-8859-2 LATIN CAPITAL LETTER Z WITH ACUTE + *Src = 0xac; + } + + if (*Src!=0x0A && *Src < 0x20) { //don't remove newline + *Src = 0x20; + } + if (*Src == 0x20) { + Spaces++; + if (pC == 0) { + Spaces++; + } + } else { + Spaces = 0; + } + if (Spaces < 2) { + *Dst = *Src; + Dst++; + pC++; + } + Src++; + } + if (Spaces > 0) { + Dst--; + *Dst = 0; + } else { + *Dst = 0; + } +// LogD (1, prep("Clean: %s"), String); +} + +cChannel *GetChannelByID(tChannelID & channelID, bool searchOtherPos) +{ + cChannel *VC = Channels.GetByChannelID(channelID, true); + if(!VC && searchOtherPos){ + //look on other satpositions + for(int i = 0;i < NumberOfAvailableSources;i++){ + channelID = tChannelID(AvailableSources[i], channelID.Nid(), channelID.Tid(), channelID.Sid()); + VC = Channels.GetByChannelID(channelID, true); + if(VC){ + //found this actually on satellite nextdoor... + break; + } + } + } + + return VC; +} + +bool cFilterEEPG::GetThemesSKYBOX (void) //TODO can't we read this from the DVB stream? +{ + string FileName = ConfDir; + FILE *FileThemes; + char *Line; + char Buffer[256]; + if (Format == SKY_IT) + FileName += "/sky_it.themes"; + else if (Format == SKY_UK) + FileName += "/sky_uk.themes"; + else { + LogE (0, prep("Error, wrong format detected in GetThemesSKYBOX. Format = %i."), Format); + return false; + } + //asprintf( &FileName, "%s/%s", ConfDir, ( lProviders + CurrentProvider )->Parm3 ); + FileThemes = fopen (FileName.c_str(), "r"); + if (FileThemes == NULL) { + LogE (0, prep("Error opening file '%s'. %s"), FileName.c_str(), strerror (errno)); + return false; + } else { + //int id = 0; + nThemes = 0; + char string1[256]; + char string2[256]; + //sTheme *T; + while ((Line = fgets (Buffer, sizeof (Buffer), FileThemes)) != NULL) { + memset (string1, 0, sizeof (string1)); + memset (string2, 0, sizeof (string2)); + if (!isempty (Line)) { + //T = &Themes[nThemes]; + if (sscanf (Line, "%[^=] =%[^\n] ", string1, string2) == 2) { + snprintf ((char *) Themes[nThemes], 255, "%s", string2); + } else { + Themes[nThemes][0] = '\0'; + } + //id ++; + nThemes++; + } + } + fclose (FileThemes); + } + return true; +} + +bool cFilterEEPG::ReadFileDictionary (void) +{ + string FileName = ConfDir; + FILE *FileDict; + char *Line; + char Buffer[256]; + switch (Format) { + case SKY_IT: + FileName += "/sky_it.dict"; + break; + case SKY_UK: + FileName += "/sky_uk.dict"; + break; + case FREEVIEW: + FileName += "/freesat.t1"; + load_file (1, FileName.c_str()); + FileName = ConfDir; + FileName += "/freesat.t2"; + load_file (2, FileName.c_str()); + break; + default: + LogE (0 ,prep("Error, wrong format detected in ReadFileDictionary. Format = %i."), Format); + return false; + } + if ((Format == SKY_IT) || (Format == SKY_UK)) { //SKY + FileDict = fopen (FileName.c_str(), "r"); + if (FileDict == NULL) { + LogE (0, prep("Error opening file '%s'. %s"), FileName.c_str(), strerror (errno)); + return false; + } else { + int i; + int LenPrefix; + char string1[256]; + char string2[256]; + H.Value = NULL; + H.P0 = NULL; + H.P1 = NULL; + while ((Line = fgets (Buffer, sizeof (Buffer), FileDict)) != NULL) { + if (!isempty (Line)) { + memset (string1, 0, sizeof (string1)); + memset (string2, 0, sizeof (string2)); + if (sscanf (Line, "%c=%[^\n]\n", string1, string2) == 2 + || (sscanf (Line, "%[^=]=%[^\n]\n", string1, string2) == 2)) { + nH = &H; + LenPrefix = strlen (string2); + for (i = 0; i < LenPrefix; i++) { + switch (string2[i]) { + case '0': + if (nH->P0 == NULL) { + nH->P0 = new sNodeH (); + nH = nH->P0; + nH->Value = NULL; + nH->P0 = NULL; + nH->P1 = NULL; + if ((LenPrefix - 1) == i) { + Asprintf (&nH->Value, "%s", string1); + } + } else { + nH = nH->P0; + if (nH->Value != NULL || (LenPrefix - 1) == i) { + LogE (0 ,prep("Error, huffman prefix code already exists for \"%s\"=%s with '%s'"), string1, + string2, nH->Value); + } + } + break; + case '1': + if (nH->P1 == NULL) { + nH->P1 = new sNodeH (); + nH = nH->P1; + nH->Value = NULL; + nH->P0 = NULL; + nH->P1 = NULL; + if ((LenPrefix - 1) == i) { + Asprintf (&nH->Value, "%s", string1); + } + } else { + nH = nH->P1; + if (nH->Value != NULL || (LenPrefix - 1) == i) { + LogE (0, prep("Error, huffman prefix code already exists for \"%s\"=%s with '%s'"), string1, + string2, nH->Value); + } + } + break; + default: + break; + } + } + } + } + } + fclose (FileDict); + } + + // check tree huffman nodes + FileDict = fopen (FileName.c_str(), "r"); + if (FileDict) { + int i; + int LenPrefix; + char string1[256]; + char string2[256]; + while ((Line = fgets (Buffer, sizeof (Buffer), FileDict)) != NULL) { + if (!isempty (Line)) { + memset (string1, 0, sizeof (string1)); + memset (string2, 0, sizeof (string2)); + if (sscanf (Line, "%c=%[^\n]\n", string1, string2) == 2 + || (sscanf (Line, "%[^=]=%[^\n]\n", string1, string2) == 2)) { + nH = &H; + LenPrefix = strlen (string2); + for (i = 0; i < LenPrefix; i++) { + switch (string2[i]) { + case '0': + if (nH->P0 != NULL) { + nH = nH->P0; + } + break; + case '1': + if (nH->P1 != NULL) { + nH = nH->P1; + } + break; + default: + break; + } + } + if (nH->Value != NULL) { + if (memcmp (nH->Value, string1, strlen (nH->Value)) != 0) { + LogE (0, prep("Error, huffman prefix value '%s' not equal to '%s'"), nH->Value, string1); + } + } else { + LogE (0, prep("Error, huffman prefix value is not exists for \"%s\"=%s"), string1, string2); + } + } + } + } + fclose (FileDict); + } + } //if Format == 3 || Format == 4 + return true; +} + +int DecodeHuffmanCode (const u_char * Data, int Length, unsigned char *DecodeText) +{ + int i; + int p; + int q; + bool CodeError; + bool IsFound; + unsigned char Byte; + unsigned char lastByte; + unsigned char Mask; + unsigned char lastMask; + nH = &H; + p = 0; + q = 0; + DecodeText[0] = '\0'; + //DecodeErrorText[0] = '\0'; + CodeError = false; + IsFound = false; + lastByte = 0; + lastMask = 0; + for (i = 0; i < Length; i++) { + Byte = Data[i]; + Mask = 0x80; + if (i == 0) { + Mask = 0x20; + lastByte = i; + lastMask = Mask; + } +loop1: + if (IsFound) { + lastByte = i; + lastMask = Mask; + IsFound = false; + } + if ((Byte & Mask) == 0) { + if (CodeError) { + //DecodeErrorText[q] = 0x30; + q++; + goto nextloop1; + } + if (nH->P0 != NULL) { + nH = nH->P0; + if (nH->Value != NULL) { + memcpy (&DecodeText[p], nH->Value, strlen (nH->Value)); + p += strlen (nH->Value); + nH = &H; + IsFound = true; + } + } else { + memcpy (&DecodeText[p], "<...?...>", 9); + p += 9; + i = lastByte; + Byte = Data[lastByte]; + Mask = lastMask; + CodeError = true; + goto loop1; + } + } else { + if (CodeError) { + //DecodeErrorText[q] = 0x31; + q++; + goto nextloop1; + } + if (nH->P1 != NULL) { + nH = nH->P1; + if (nH->Value != NULL) { + memcpy (&DecodeText[p], nH->Value, strlen (nH->Value)); + p += strlen (nH->Value); + nH = &H; + IsFound = true; + } + } else { + memcpy (&DecodeText[p], "<...?...>", 9); + p += 9; + i = lastByte; + Byte = Data[lastByte]; + Mask = lastMask; + CodeError = true; + goto loop1; + } + } +nextloop1: + Mask = Mask >> 1; + if (Mask > 0) { + goto loop1; + } + } + DecodeText[p] = '\0'; + //DecodeErrorText[q] = '\0'; + return p; +} + +void decodeText2 (const unsigned char *from, int len, char *buffer, int buffsize) +{ + if (from[0] == 0x1f) { + char *temp = freesat_huffman_decode (from, len); + if (temp) { + len = strlen (temp); + len = len < buffsize - 1 ? len : buffsize - 1; + strncpy (buffer, temp, len); + buffer[len] = 0; + free (temp); + return; + } + } + + SI::String convStr; + SI::CharArray charArray; + charArray.assign(from, len); + convStr.setData(charArray, len); + //LogE(5, prep("decodeText2 from %s - length %d"), from, len); + convStr.getText(buffer, buffsize); + //LogE(5, prep("decodeText2 buffer %s - buffsize %d"), buffer, buffsize); +} + +void cFilterEEPG::LoadEquivalentChannels (void) +{ + char Buffer[1024]; + char *Line; + FILE *File; + string FileName = string(ConfDir) + "/" + EEPG_FILE_EQUIV; + + File = fopen (FileName.c_str(), "r"); + if (File) { + memset (Buffer, 0, sizeof (Buffer)); + char string1[256]; + char string2[256]; + char string3[256]; + int int1; + int int2; + int int3; + int int4; + while ((Line = fgets (Buffer, sizeof (Buffer), File)) != NULL) { + Line = compactspace (skipspace (stripspace (Line))); + if (!isempty (Line)) { + if (sscanf (Line, "%[^ ] %[^ ] %[^\n]\n", string1, string2, string3) == 3) { + if (string1[0] != '#' && string1[0] != ';') { + int1 = 0; //TODO: this could be made more readable + int2 = 0; + int3 = 0; + int4 = 0; + if (sscanf (string1, "%[^-]-%i -%i -%i ", string3, &int1, &int2, &int3) == 4) + if (sscanf (string2, "%[^-]-%i -%i -%i ", string3, &int1, &int2, &int3) == 4) { + if (sscanf (string1, "%[^-]-%i -%i -%i -%i ", string3, &int1, &int2, &int3, &int4) != 5) { + int4 = 0; + } + tChannelID OriginalChID = tChannelID (cSource::FromString (string3), int1, int2, int3, int4); + bool found = false; + int i = 0; + sChannel *C = NULL; + while (i < nChannels && (!found)) { + C = &sChannels[i]; + if (C->Src[0] == (unsigned int)cSource::FromString (string3) && C->Nid[0] == int1 + && C->Tid[0] == int2 && C->Sid[0] == int3) + found = true; + else + i++; + } + if (!found) { + LogI(2, prep("Warning: in equivalence file, cannot find original channel %s. Perhaps you are tuned to another transponder right now."), + string1); + } else { + cChannel *OriginalChannel = Channels.GetByChannelID (OriginalChID, false); + if (!OriginalChannel) + LogI(2, prep("Warning, not found epg channel \'%s\' in channels.conf. Equivalency is assumed to be valid, but perhaps you should check the entry in the equivalents file"), string1); //TODO: skip this ing? + if (sscanf (string2, "%[^-]-%i -%i -%i ", string3, &int1, &int2, &int3) == 4) { + if (sscanf (string2, "%[^-]-%i -%i -%i -%i ", string3, &int1, &int2, &int3, &int4) + != 5) { + int4 = 0; + } + tChannelID EquivChID = tChannelID (cSource::FromString (string3), int1, int2, int3, int4); + cChannel *EquivChannel = Channels.GetByChannelID (EquivChID, false); //TODO use valid function? + if (EquivChannel) { + if (C->NumberOfEquivalences < MAX_EQUIVALENCES) { + C->Src[C->NumberOfEquivalences] = EquivChannel->Source (); + C->Nid[C->NumberOfEquivalences] = EquivChannel->Nid (); + C->Tid[C->NumberOfEquivalences] = EquivChannel->Tid (); + C->Sid[C->NumberOfEquivalences] = EquivChannel->Sid (); + C->NumberOfEquivalences++; + nEquivChannels++; + LogI(3, prep("Added equivalent nr %i with Channel Id %s-%i-%i-%i to channel with id %i."), + C->NumberOfEquivalences, *cSource::ToString (C->Src[C->NumberOfEquivalences - 1]), + C->Nid[C->NumberOfEquivalences - 1], C->Tid[C->NumberOfEquivalences - 1], + C->Sid[C->NumberOfEquivalences - 1], i); + } else + LogE(0, prep("Error, channel with id %i has more than %i equivalences. Increase MAX_EQUIVALENCES."), + i, MAX_EQUIVALENCES); + } else + LogI(0, prep("Warning, not found equivalent channel \'%s\' in channels.conf"), string2); + } + } //else !found + } //if scanf string1 + } //if string1 + } //if scanf + } //if isempty + } //while + fclose (File); + } //if file +} //end of loadequiv + +/** + * \brief Get MHW channels + * + * \return 0 = fatal error, code 1 = success, code 2 = last item processed + */ +int cFilterEEPG::GetChannelsMHW (const u_char * Data, int Length, int MHW) +{ + if ((MHW == 1) || (nChannels == 0)) { //prevents MHW2 from reading channels twice while waiting for themes on same filter + sChannelMHW1 *Channel; + int Size, Off; + Size = sizeof (sChannelMHW1); + Off = 4; + if (MHW == 1) { + //Channel = (sChannelMHW1 *) (Data + 4); + nChannels = (Length - Off) / sizeof (sChannelMHW1); + } + if (MHW == 2) { + if (Length > 120) + nChannels = Data[120]; + else { + LogE(0, prep("Error, channels packet too short for MHW2.")); + return 0; + } + int pName = ((nChannels * 8) + 121); + if (Length > pName) { + //Channel = (sChannelMHW1 *) (Data + 120); + Size -= 14; //MHW2 is 14 bytes shorter + Off = 121; //and offset differs + } else { + LogE(0, prep("Error, channels length does not match pname.")); + return 0; + } + } + + if (nChannels > MAX_CHANNELS) { + LogE(0, prep("EEPG: Error, %i channels found more than %i"), nChannels, MAX_CHANNELS); + return 0; + } else { + LogI(1, "| ID | %-26.26s | %-22.22s | FND | %-8.8s |\n", "Channel ID", "Channel Name", "Sky Num."); + LogI(1, "|------|-%-26.26s-|-%-22.22s-|-----|-%-8.8s-|\n", "------------------------------", + "-----------------------------", "--------------------"); + int pName = ((nChannels * 8) + 121); //TODO double ... + for (int i = 0; i < nChannels; i++) { + Channel = (sChannelMHW1 *) (Data + Off); + sChannel *C = &sChannels[i]; + C->ChannelId = i; + ChannelSeq[C->ChannelId] = i; //fill lookup table to go from channel-id to sequence nr in table + C->SkyNumber = 0; + if (MHW == 1) + memcpy (C->Name, &Channel->Name, 16); //MHW1 + else { //MHW2 + int lenName = Data[pName] & 0x0f; + //LogD (1, prep("EEPGDebug: MHW2 lenName:%d"), lenName); + decodeText2(&Data[pName+1],lenName,(char*)C->Name,256); + //memcpy (C->Name, &Data[pName + 1], lenName); + //else + //memcpy (C->Name, &Data[pName + 1], 256); + pName += (lenName + 1); + } + C->NumberOfEquivalences = 1; //there is always an original channel. every equivalence adds 1 + C->Src[0] = Source (); //assume all EPG channels are on same satellite, if not, manage this via equivalents!!! + C->Nid[0] = HILO16 (Channel->NetworkId); + C->Tid[0] = HILO16 (Channel->TransportId); + C->Sid[0] = HILO16 (Channel->ServiceId); + tChannelID channelID = tChannelID (C->Src[0], C->Nid[0], C->Tid[0], C->Sid[0]); + cChannel *VC = GetChannelByID(channelID, true); + bool IsFound = (VC); + if(IsFound) { + C->Src[0] = VC->Source(); + } + CleanString (C->Name); + + LogI(1, "|% 5d | %-26.26s | %-22.22s | %-3.3s | % 6d |\n", C->ChannelId + , *tChannelID (C->Src[0], C->Nid[0], C->Tid[0], C->Sid[0]).ToString() + , C->Name, IsFound ? "YES" : "NO", C->SkyNumber); + + Off += Size; + } //for loop + } //else nChannels > MAX_CHANNELS + LoadEquivalentChannels (); + GetLocalTimeOffset (); //reread timing variables, only used for MHW + return 2; //obviously, when you get here, channels are read succesfully, but since all channels are sent at once, you can stop now + } //if nChannels == 0 + LogE (0, prep("Warning: Trying to read Channels more than once!")); +//you will only get here when GetChannelsMHW is called, and nChannels !=0, e.g. when multiple citpids cause channels to be read multiple times. Second time, do nothing, give error so that the rest of the chain is not restarted also. + return 0; +} + +/** + * \brief Get MHW1 Themes + * + * \return 0 = fatal error, code 1 = success, code 2 = last item processed + */ +int cFilterEEPG::GetThemesMHW1 (const u_char * Data, int Length) +{ //MHW1 Themes + if (Length > 19) { + sThemeMHW1 *Theme = (sThemeMHW1 *) (Data + 19); + nThemes = (Length - 19) / 15; + if (nThemes > MAX_THEMES) { + LogE(1, prep("Error, %i themes found more than %i"), nThemes, MAX_THEMES); + return 0; + } else { + LogI(1, "-------------THEMES FOUND--------------"); + int ThemeId = 0; + int Offset = 0; + const u_char *ThemesIndex = (Data + 3); + for (int i = 0; i < nThemes; i++) { + if (ThemesIndex[ThemeId] == i) { + Offset = (Offset + 15) & 0xf0; //TODO do not understand this + ThemeId++; + } + memcpy (&Themes[Offset][0], &Theme->Name, 15); + Themes[Offset][15] = '\0'; //trailing null + CleanString (Themes[Offset]); + LogI(1, prep("%.15s"), Themes[Offset]); + Offset++; + Theme++; + } + if ((nThemes * 15) + 19 != Length) { + LogE(0, "Themes error: buffer is smaller or bigger than sum of entries."); + return 0; + } else + return 2; + } + } + return 1; +} + +/** + * \brief Get MHW2 Themes + * + * \return 0 = fatal error, code 1 = success, code 2 = last item processed + */ +int cFilterEEPG::GetThemesMHW2 (const u_char * Data, int Length) +{ + if (!EndThemes) { //only process if not processed + int p1; + int p2; + int pThemeName = 0; + int pSubThemeName = 0; + int lenThemeName = 0; + int lenSubThemeName = 0; + int pThemeId = 0; + if (Length > 4) { + LogI(1, "-------------THEMES FOUND--------------"); + for (int i = 0; i < Data[4]; i++) { + p1 = ((Data[5 + i * 2] << 8) | Data[6 + i * 2]) + 3; + if (Length > p1) { + for (int ii = 0; ii <= (Data[p1] & 0x3f); ii++) { + p2 = ((Data[p1 + 1 + ii * 2] << 8) | Data[p1 + 2 + ii * 2]) + 3; + if (Length > p2) { + if (ii == 0) { + pThemeName = p2 + 1; + lenThemeName = Data[p2] & 0x1f; + lenSubThemeName = 0; + } else { + pSubThemeName = p2 + 1; + lenSubThemeName = Data[p2] & 0x1f; + } + if (Length >= (pThemeName + lenThemeName)) { + pThemeId = ((i & 0x3f) << 6) | (ii & 0x3f); + if (pThemeId > MAX_THEMES) { + LogE(1, prep("Error, something wrong with themes id calculation MaxThemes: %i pThemeID:%d"), MAX_THEMES, pThemeId); + return 0; //fatal error + } + if ((lenThemeName + 2) < 256) { + decodeText2(&Data[pThemeName],lenThemeName,(char*)Themes[pThemeId],256); + //memcpy (Themes[pThemeId], &Data[pThemeName], lenThemeName); + if (Length >= (pSubThemeName + lenSubThemeName)) + if (lenSubThemeName > 0) + if ((lenThemeName + lenSubThemeName + 2) < 256) { + Themes[pThemeId][lenThemeName] = ' '; + decodeText2(&Data[pSubThemeName],lenSubThemeName,(char*)&Themes[pThemeId][lenThemeName + 1],256); + //memcpy (&Themes[pThemeId][lenThemeName + 1], &Data[pSubThemeName], lenSubThemeName); + } + CleanString (Themes[pThemeId]); + LogI(1, prep("%.*s"), lenThemeName + 1 + lenSubThemeName, Themes[pThemeId]); + //isyslog ("%.15s", (lThemes + pThemeId)->Name); + nThemes++; + if (nThemes > MAX_THEMES) { + LogE(1, prep("Error, %i themes found more than %i"), nThemes, MAX_THEMES); + return 0; //fatal error + } + } + } else + return 1; //I assume non fatal error or success + } else + return 1; //I assume non fatal error or success + } + } else + return 1; //I assume non fatal error or success + } //for loop + //Del (Pid, Tid); + EndThemes = true; + return 2; //all themes read + } //if length + } //if !EndThemes + return 1; //non fatal or success +} + +/** + * \brief Get Nagra Summary text + * + * \param TitleEventId EventId passed from title, to check it against the eventId of the summary + * \return pointer to reserved part of memory with summary text in it, terminated by NULL + */ +char *cFilterEEPG::GetSummaryTextNagra (const u_char * DataStart, long int Offset, unsigned int TitleEventId) +//EventId is passed from title, to check it against the eventid of the summary +{ + u_char *p = (u_char *) DataStart + Offset; + sSummaryDataNagraGuide *SD = (sSummaryDataNagraGuide *) p; + + if (TitleEventId != HILO32 (SD->EventId)) { + LogI(0, prep("ERROR, Title has EventId %08x and points to Summary with EventId %08x."), TitleEventId, + HILO32 (SD->EventId)); + return 0; //return empty string + } + + if (CheckLevel(3)) { + if (SD->AlwaysZero1 != 0) + isyslog ("EEPGDEBUG: SummAlwaysZero1 is NOT ZERO:%x.", SD->AlwaysZero1); + if (SD->AlwaysZero2 != 0) + isyslog ("EEPGDEBUG: SummAlwaysZero2 is NOT ZERO:%x.", SD->AlwaysZero2); + if (SD->AlwaysZero3 != 0) + isyslog ("EEPGDEBUG: SummAlwaysZero3 is NOT ZERO:%x.", SD->AlwaysZero3); + if (SD->AlwaysZero4 != 0) + isyslog ("EEPGDEBUG: SummAlwaysZero4 is NOT ZERO:%x.", SD->AlwaysZero4); + if (SD->AlwaysZero5 != 0) + isyslog ("EEPGDEBUG: SummAlwaysZero5 is NOT ZERO:%x.", SD->AlwaysZero5); + if (SD->AlwaysZero6 != 0) + isyslog ("EEPGDEBUG: SummAlwaysZero6 is NOT ZERO:%x.", SD->AlwaysZero6); + if (SD->AlwaysZero7 != 0) + isyslog ("EEPGDEBUG: SummAlwaysZero7 is NOT ZERO:%x.", SD->AlwaysZero7); + if (SD->AlwaysZero8 != 0) + isyslog ("EEPGDEBUG: SummAlwaysZero8 is NOT ZERO:%x.", SD->AlwaysZero8); + if (SD->AlwaysZero9 != 0) + isyslog ("EEPGDEBUG: SummAlwaysZero9 is NOT ZERO:%x.", SD->AlwaysZero9); + + if (SD->Always1 != 0x31) //1970 + isyslog ("EEPGDEBUG: SummAlways1:%02x.", SD->Always1); + if (SD->Always9 != 0x39) + isyslog ("EEPGDEBUG: SummAlways9:%02x.", SD->Always9); + if (SD->Always7 != 0x37) + isyslog ("EEPGDEBUG: SummAlways7:%02x.", SD->Always7); + if (SD->Always0 != 0x30) + isyslog ("EEPGDEBUG: SummAlways0:%02x.", SD->Always0); + + if (SD->Always0x01 != 0x01) // 0x01 byte + isyslog ("EEPGDEBUG: Summ0x01 byte:%02x.", SD->Always0x01); + } + u_char *p2 = (u_char *) DataStart + HILO32 (SD->SummTxtOffset); + /* esyslog + ("EEPGDEBUG: EventId %08x NumberOfBlocks %02x BlockId %08x SummTxtOffset %08x *p2: %02x Unkn1:%02x, Unkn2:%02x.", + HILO32 (SD->EventId), SD->NumberOfBlocks, HILO32 (SD->BlockId), HILO32 (SD->SummTxtOffset), *p2, SD->Unknown1, + SD->Unknown2); + */ + unsigned char *Text = NULL; //makes first realloc work like malloc + int TotLength = 0; //and also makes empty summaries if *p2 != 0x4e + if (SD->NumberOfBlocks > 1) { + switch (*p2) { + case 0x4e: //valid summary text follows + { + bool LastTextBlock = false; + + do { //for all text parts + sSummaryTextNagraGuide *ST = (sSummaryTextNagraGuide *) p2; + p2 += 8; //skip fixed block + if (ST->AlwaysZero1 != 0) + LogD(3, prep("DEBUG: ST AlwaysZero1 is NOT ZERO:%x."), ST->AlwaysZero1); + if (ST->Always0x4e != 0x4e) { + LogI(0, prep("DEBUG: ST Always0x4e is NOT 0x4e:%x."), ST->AlwaysZero1); + return 0; //fatal error, empty text + } + LogI(5, prep("DEBUG: Textnr %i, Lasttxt %i."), ST->TextNr, ST->LastTextNr); + int SummaryLength = ST->Textlength; + + Text = (unsigned char *) realloc (Text, SummaryLength + TotLength); + if (Text == NULL) { + LogI(0, prep("Summaries memory allocation error.")); + return 0; //empty text + } + memcpy (Text + TotLength, p2, SummaryLength); //append new textpart + TotLength += SummaryLength; + p2 += ST->Textlength; //skip text + + LastTextBlock = ((ST->LastTextNr == 0) || (ST->TextNr >= ST->LastTextNr)); + } while (!LastTextBlock); + Text = (unsigned char *) realloc (Text, 1 + TotLength); //allocate 1 extra byte + Text[TotLength] = '\0'; //terminate string by NULL char + LogD(5, prep("DEBUG: Full Text:%s."), Text); + + break; + } + case 0x8c: //"Geen uitzending" "Geen informatie beschikbaar" e.d. + { + sSummaryGBRNagraGuide *GBR = (sSummaryGBRNagraGuide *) p2; + + p2 += 16; //skip fixed part, point to byte after Nextlength + if (CheckLevel(3)) { + if (GBR->AlwaysZero1 != 0) + isyslog ("EEPGDEBUG: GBR AlwaysZero1 is NOT ZERO:%x.", GBR->AlwaysZero1); + if (GBR->AlwaysZero2 != 0) + isyslog ("EEPGDEBUG: GBR AlwaysZero2 is NOT ZERO:%x.", GBR->AlwaysZero2); + if (GBR->AlwaysZero3 != 0) + isyslog ("EEPGDEBUG: GBR AlwaysZero3 is NOT ZERO:%x.", GBR->AlwaysZero3); + if (GBR->AlwaysZero4 != 0) + isyslog ("EEPGDEBUG: GBR AlwaysZero4 is NOT ZERO:%x.", GBR->AlwaysZero4); + + isyslog ("EEPGDEBUG: Blocklength: %02x Data %02x %02x %02x %02x %02x %02x %02x %02x %02x", GBR->Blocklength, + GBR->Un1, GBR->Un2, GBR->Un3, GBR->Un4, GBR->Un5, GBR->Un6, GBR->Un7, GBR->Un8, GBR->Un9); + for (int i = 0; i < GBR->Nextlength; i += 2) + isyslog ("GBR Extradata %02x %02x.", *(p2 + i), *(p2 + i + 1)); + } + } + break; + default: + LogE(0, prep("ERROR *p2 has strange value: EventId %08x NumberOfBlocks %02x BlockId %08x SummTxtOffset %08x *p2: %02x Unkn1:%02x, Unkn2:%02x."), + HILO32 (SD->EventId), SD->NumberOfBlocks, HILO32 (SD->BlockId), HILO32 (SD->SummTxtOffset), *p2, + SD->Unknown1, SD->Unknown2); + break; + } //end of switch + } //NrOfBlocks > 1 + + if (TotLength == 0) + Text = NULL; + + p += 29; //skip fixed part of block + if (SD->NumberOfBlocks == 1) + p -= 4; //in this case there is NO summarytext AND no GBR??!! + for (int i = 1; i < (SD->NumberOfBlocks - 1); i++) { + LogD(3, prep("DEBUG: Extra Blockinfo: %02x %02x %02x %02x."), *p, *(p + 1), *(p + 2), *(p + 3)); + p += 4; //skip this extra blockinfo + } + return (char *) Text; +} + +void cFilterEEPG::PrepareToWriteToSchedule (sChannel * C, cSchedules * s, cSchedule * ps[MAX_EQUIVALENCES]) //gets a channel and returns an array of schedules that WriteToSchedule can write to. Call this routine before a batch of titles with the same ChannelId will be WriteToScheduled; batchsize can be 1 +{ + for (int eq = 0; eq < C->NumberOfEquivalences; eq++) { + tChannelID channelID = tChannelID (C->Src[eq], C->Nid[eq], C->Tid[eq], C->Sid[eq]); +#ifdef USE_NOEPG + if (allowedEPG (channelID) && (channelID.Valid ())) +#else + if (channelID.Valid ()) //only add channels that are known to vdr +#endif /* NOEPG */ + ps[eq] = s->AddSchedule (channelID); //open a a schedule for each equivalent channel + else { + ps[eq] = NULL; + LogE(5, prep("ERROR: Titleblock has invalid (equivalent) channel ID: Equivalence: %i, Source:%x, C->Nid:%x,C->Tid:%x,C->Sid:%x."), + eq, C->Src[eq], C->Nid[eq], C->Tid[eq], C->Sid[eq]); + } + } +} + +void cFilterEEPG::FinishWriteToSchedule (sChannel * C, cSchedules * s, cSchedule * ps[MAX_EQUIVALENCES]) +{ + for (int eq = 0; eq < C->NumberOfEquivalences; eq++) + if (ps[eq]) { + ps[eq]->Sort (); + s->SetModified (ps[eq]); + } +} + +/** + * \brief write event to schedule + * + * \param Duration the Duration of the event in minutes + * \param ps points to array of schedules ps[eq], where eq is equivalence number of the channel. If channelId is invalid then ps[eq]=NULL + */ +void cFilterEEPG::WriteToSchedule (cSchedule * ps[MAX_EQUIVALENCES], unsigned short int NumberOfEquivalences, unsigned int EventId, unsigned int StartTime, unsigned int Duration, char *Text, char *SummText, unsigned short int ThemeId, unsigned short int TableId, unsigned short int Version, char Rating) +{ + bool WrittenTitle = false; + bool WrittenSummary = false; + for (int eq = 0; eq < NumberOfEquivalences; eq++) { + if (ps[eq]) { + cEvent *Event = NULL; + + Event = (cEvent *) ps[eq]->GetEvent (EventId); //since Nagra uses consistent EventIds, try this first + bool TableIdMatches = false; + if (Event) + TableIdMatches = (Event->TableID() == TableId); + if (!Event || !TableIdMatches || abs(Event->StartTime() - (time_t) StartTime) > Duration * 60) //if EventId does not match, or it matched with wrong TableId, then try with StartTime + Event = (cEvent *) ps[eq]->GetEvent (EventId, StartTime); + + cEvent *newEvent = NULL; + if (!Event) { //event is new + Event = newEvent = new cEvent (EventId); + Event->SetSeen (); + } else if (Event->TableID() < TableId) { //existing table may not be overwritten + RejectTableId++; + //esyslog ("EEPGDEBUG: Rejecting Event, existing TableID:%x, new TableID:%x.", Event->TableID (), + // TableId); + Event = NULL; + } + + if (Event) { + Event->SetEventID (EventId); //otherwise the summary cannot be added later + Event->SetTableID (TableId); //TableID 0 is reserved for external epg, will not be overwritten; the lower the TableID, the more actual it is + Event->SetVersion (Version); //TODO use version and tableID to decide whether to update; TODO add language code + Event->SetStartTime (StartTime); + Event->SetDuration (Duration * 60); + if (Rating) { + Event->SetParentalRating(Rating); + } + char *tmp; + if (Text != 0x00) { + WrittenTitle = true; + CleanString ((uchar *) Text); + Event->SetTitle (Text); + } + Asprintf (&tmp, "%s - %d\'", Themes[ThemeId], Duration); + Event->SetShortText (tmp); + //strreplace(t, '|', '\n'); + if (SummText != 0x00) { + WrittenSummary = true; + CleanString ((uchar *) SummText); + Event->SetDescription (SummText); + } + free (tmp); + if (newEvent) + ps[eq]->AddEvent (newEvent); + //newEvent->FixEpgBugs (); causes segfault + } + /* else + esyslog ("EEPG: ERROR, somehow not able to add/update event.");*///at this moment only reports RejectTableId events + if (CheckLevel(4)) { + isyslog ("EEPG: Title:%i, Summary:%i I would put into schedule:", TitleCounter, SummaryCounter); + //isyslog ("C %s-%i-%i-%i\n", *cSource::ToString (C->Src[eq]), C->Nid[eq], C->Tid[eq], C->Sid[eq]); + isyslog ("E %u %u %u 01 FF\n", EventId, StartTime, Duration * 60); + isyslog ("T %s\n", Text); + isyslog ("S %s - %d\'\n", Themes[ThemeId], Duration); + isyslog ("D %s\n", SummText); + isyslog ("e\nc\n.\n"); + } + } //if ps[eq] + } //for eq + if (WrittenTitle) + TitleCounter++; + if (WrittenSummary) + SummaryCounter++; +} + +/** + * \brief Get Nagra titles + * + * \param TableIdExtension the TIE from the relevant summary sections! + */ +void cFilterEEPG::GetTitlesNagra (const u_char * Data, int Length, unsigned short TableIdExtension) +{ + u_char *p = (u_char *) Data; + u_char *DataEnd = (u_char *) Data + Length; + u_char *next_p; + unsigned short int MonthdayTitles = ((TableIdExtension & 0x1ff) >> 4); //Day is coded in day of the month + time_t timeLocal; + struct tm *tmCurrent; + + timeLocal = time (NULL); + tmCurrent = gmtime (&timeLocal); //gmtime gives UTC; only used for getting current year and current day of the month... + unsigned short int CurrentMonthday = tmCurrent->tm_mday; + unsigned short int CurrentYear = tmCurrent->tm_year; + unsigned short int CurrentMonth = tmCurrent->tm_mon; + //esyslog("EEPGDEBUG: CurrentMonthday=%i, TableIdExtension:%04x, MonthdayTitles=%i.",CurrentMonthday,TableIdExtension, MonthdayTitles); + cSchedulesLock SchedulesLock (true); + cSchedules *s = (cSchedules *) cSchedules::Schedules (SchedulesLock); + do { //process each block of titles + sTitleBlockNagraGuide *TB = (sTitleBlockNagraGuide *) p; + int ChannelId = HILO16 (TB->ChannelId); + int Blocklength = HILO16 (TB->Blocklength); + long int NumberOfTitles = HILO32 (TB->NumberOfTitles); + + LogD(3, prep("DEBUG: ChannelId %04x, Blocklength %04x, NumberOfTitles %lu."), ChannelId, Blocklength, + NumberOfTitles); + p += 4; //skip ChannelId and Blocklength + next_p = p + Blocklength; + if (next_p > DataEnd) { //only process if block is complete + LogE(0, prep("ERROR, Block exceeds end of Data. p:%p, Blocklength:%x,DataEnd:%p."), p, Blocklength, DataEnd); + return; //fatal error, this should never happen + } + p += 4; //skip Titlenumber + + sChannel *C = &sChannels[ChannelSeq[ChannelId]]; //find channel + cSchedule *ps[MAX_EQUIVALENCES]; + PrepareToWriteToSchedule (C, s, ps); + + for (int i = 0; i < NumberOfTitles; i++) { //process each title within block + sTitleNagraGuide *Title = (sTitleNagraGuide *) p; + unsigned int EventId = HILO32 (Title->EventId); + + unsigned int StartTime = Title->StartTimeHigh << 5 | Title->StartTimeLow; + int Hours = (StartTime / 60); + int Minutes = StartTime % 60; + + /*StartTime */ + tmCurrent->tm_year = CurrentYear; + tmCurrent->tm_mon = CurrentMonth; + tmCurrent->tm_mday = MonthdayTitles; + tmCurrent->tm_hour = 0; + tmCurrent->tm_min = StartTime; //if starttime is bigger than 1 hour, mktime will correct this! + tmCurrent->tm_sec = 0; + tmCurrent->tm_isdst = -1; //now correct with daylight savings + if (MonthdayTitles < CurrentMonthday - 7) //the titles that are older than one week are not from the past, but from next month! + //at first this was set at -1 day (= yesterday), but sometimes providers send old data which then + //end up in next months schedule ... + tmCurrent->tm_mon++; //if a year border is passed, mktime will take care of this! + StartTime = UTC2LocalTime (mktime (tmCurrent)); //VDR stores its times in UTC, but wants its input in local time... + + char *Text = NULL; + u_char *t = (u_char *) Data + HILO32 (Title->OffsetToText); + //u_char *t2 = (u_char *) Data + HILO32 (Title->OffsetToText2); + if (t >= DataEnd) + LogE(0, prep("ERROR, Title Text out of range: t:%p, DataEnd:%p, Data:%p, Length:%i."), t, DataEnd, Data, + Length); + else { + Asprintf (&Text, "%.*s", *t, t + 1); //FIXME second text string is not processed right now + //asprintf (&Text, "%.*s %.*s", *t, t + 1, *t2, t2 + 1); + + //now get summary texts + u_char *DataStartSummaries = buffer[TableIdExtension] + 4; + unsigned int DataLengthSummaries = bufsize[TableIdExtension] - 4; + char *SummText = NULL; + if (HILO32 (Title->SumDataOffset) >= DataLengthSummaries) + LogE(0, prep("ERROR, SumDataOffset out of range: Title->SumDataOffset:%i, DataLengthSummaries:%i."), HILO32 (Title->SumDataOffset),DataLengthSummaries); + else + SummText = GetSummaryTextNagra (DataStartSummaries, HILO32 (Title->SumDataOffset), EventId); + + LogD(3, prep("DEBUG: Eventid: %08x ChannelId:%x, Starttime %02i:%02i, Duration %i, OffsetToText:%08x, OffsetToText2:%08x, SumDataOffset:%08x ThemeId:%x Title:%s \n SummaryText:%s"), + EventId, ChannelId, Hours, Minutes, Title->Duration, + HILO32 (Title->OffsetToText), HILO32 (Title->OffsetToText2), + HILO32 (Title->SumDataOffset), Title->ThemeId, Text, SummText); + + if (Themes[Title->ThemeId][0] == 0x00) //if detailed themeid is not known, get global themeid + Title->ThemeId &= 0xf0; + WriteToSchedule (ps, C->NumberOfEquivalences, EventId, StartTime, Title->Duration, Text, SummText, + Title->ThemeId, NAGRA_TABLE_ID, Version); + + if (Text != NULL) + free (Text); + Text = NULL; + if (SummText != NULL) + free (SummText); + SummText = NULL; + } + + if (CheckLevel(3)) { + if (Title->AlwaysZero16 != 0) + isyslog ("EEPGDEBUG: TitleAlwaysZero16 (3bits) is NOT ZERO:%x.", Title->AlwaysZero16); + if (Title->AlwaysZero17 != 0) + isyslog ("EEPGDEBUG: TitleAlwaysZero17 is NOT ZERO:%x.", Title->AlwaysZero17); + if (Title->AlwaysZero1 != 0) + isyslog ("EEPGDEBUG: TitleAlwaysZero1 is NOT ZERO:%x.", Title->AlwaysZero1); + if (Title->AlwaysZero2 != 0) + isyslog ("EEPGDEBUG: TitleAlwaysZero2 is NOT ZERO:%x.", Title->AlwaysZero2); + if (Title->AlwaysZero3 != 0) + isyslog ("EEPGDEBUG: TitleAlwaysZero3 is NOT ZERO:%x.", Title->AlwaysZero3); + if (Title->AlwaysZero4 != 0) + isyslog ("EEPGDEBUG: TitleAlwaysZero4 is NOT ZERO:%x.", Title->AlwaysZero4); + if (Title->AlwaysZero5 != 0) + isyslog ("EEPGDEBUG: TitleAlwaysZero5 is NOT ZERO:%x.", Title->AlwaysZero5); + if (Title->AlwaysZero8 != 0) + isyslog ("EEPGDEBUG: TitleAlwaysZero8 is NOT ZERO:%x.", Title->AlwaysZero8); + if (Title->AlwaysZero9 != 0) + isyslog ("EEPGDEBUG: TitleAlwaysZero9 is NOT ZERO:%x.", Title->AlwaysZero9); + if (Title->AlwaysZero10 != 0) + isyslog ("EEPGDEBUG: TitleAlwaysZero10 is NOT ZERO:%x.", Title->AlwaysZero10); + if (Title->AlwaysZero11 != 0) + isyslog ("EEPGDEBUG: TitleAlwaysZero11 is NOT ZERO:%x.", Title->AlwaysZero11); + } + p += 30; //next title + } //end for titles + + FinishWriteToSchedule (C, s, ps); + p = next_p; + } while (p < DataEnd); //end of TitleBlock +} + +int cFilterEEPG::GetThemesNagra (const u_char * Data, int Length, unsigned short TableIdExtension) //return code 0 = fatal error, code 1 = success, code 2 = last item processed +{ + u_char *DataStart = (u_char *) Data; + u_char *p = DataStart; //TODO Language code terminated by 0 is ignored + u_char *DataEnd = DataStart + Length; + u_char *DataStartTitles = buffer[TableIdExtension] + 4; + u_char *DataEndTitles = DataStartTitles + bufsize[TableIdExtension] - 4; + if (Length == 0) { + LogI(1, prep("NO THEMES FOUND")); + return 2; + } + + int NumberOfThemes = (*p << 24) | *(p + 1) << 16 | *(p + 2) << 8 | *(p + 3); + p += 4; //skip number of themes block + //isyslog ("EEPG: found %i themes.", NumberOfThemes); + + if ((nThemes == 0)) + LogI(1, "-------------THEMES FOUND--------------"); + + for (int i = 0; i < NumberOfThemes; i++) { + int Textlength = *p; + p++; //skip textlength byte + u_char *Text = p; + u_char ThemeId = 0; + p += Textlength; //skip text + int NrOfBlocks = (*p << 8) | *(p + 1); + p += 2; //skip nrofblocks + bool AnyDoubt = false; + u_char *p2 = p; + p += NrOfBlocks * 8; + for (int j = 0; j < NrOfBlocks; j++) { + sThemesTitlesNagraGuide *TT = (sThemesTitlesNagraGuide *) p2; + p2 += 8; //skip block + u_char *NewThemeId = DataStartTitles + HILO32 (TT->TitleOffset) + 28; + if (NewThemeId >= DataEndTitles) + LogE(0, prep("ERROR, ThemeId out of range: NewThemeId:%p, DataEndTitles:%p, DataStartTitles:%p."), NewThemeId, + DataEndTitles, DataStartTitles); + else { + //esyslog("EEPGDEBUG: NewThemeId:%02x, Text:%s.",*NewThemeId, Text); + if (Themes[*NewThemeId][0] != 0x00) { //theme is already filled, break off + AnyDoubt = true; + break; + } + if (j == 0) //first block + ThemeId = *NewThemeId; + else if (ThemeId != *NewThemeId) { //different theme ids in block + if ((ThemeId & 0xf0) != (*NewThemeId & 0xf0)) { //major nible of themeid does not correspond + LogE(3, prep("ERROR, Theme has multiple indices which differ in major nibble, old index = %x, new index = %x. Ignoring both indices."), + ThemeId, *NewThemeId); + AnyDoubt = true; + break; + } else if ((ThemeId & 0x0f) != 0) //ThemeId is like 1a, 2a, not like 10,20. So it is minor in tree-structure, and it should be labeled in major part of tree + ThemeId = *NewThemeId; //lets hope new themeid is major, if not, it has not worsened.... + } + } //else NewThemeId >= DataEndTitles + if (CheckLevel(3)) { + if (TT->Always1 != 1) + isyslog ("EEPGDEBUG: TT Always1 is NOT 1:%x.", TT->Always1); + if (TT->AlwaysZero1 != 0) + isyslog ("EEPGDEBUG: TT AlwaysZero1 is NOT ZERO:%x.", TT->AlwaysZero1); + if (TT->AlwaysZero2 != 0) + isyslog ("EEPGDEBUG: TT AlwaysZero2 is NOT ZERO:%x.", TT->AlwaysZero2); + } + } //for nrofblocks + // esyslog("EEPGDEBUG: AnyDoubt:%x.",AnyDoubt); + if (!AnyDoubt) { + if (Textlength > 63) + Textlength = 63; //leave room for trailing NULL + if (Themes[ThemeId][0] != 0) { + LogE(0, prep("Trying to add new theme, but Id already exists. ThemeId = %x, Old theme with this Id:%s, new theme: %s."), + ThemeId, Themes[ThemeId], Text); + continue; + } + memcpy (&Themes[ThemeId], Text, Textlength); + Themes[ThemeId][Textlength] = '\0'; //trailing NULL + CleanString (Themes[ThemeId]); + nThemes++; + LogI(1, prep("%02x %s"), ThemeId, Themes[ThemeId]); + } + } //for NumberOfThemes + if (p != DataEnd) { + LogE(0, prep("Themes error: buffer is smaller or bigger than sum of entries. p:%p,DataEnd:%p"), p, DataEnd); + return 0; + } else + return 2; +} + +/** + * \brief Get Nagra channels + * + * \return 0 = fatal error, code 1 = success, code 2 = last item processed + */ +int cFilterEEPG::GetChannelsNagra (const u_char * Data, int Length) +{ + u_char *DataStart = (u_char *) Data; + u_char *p = DataStart; + u_char *DataEnd = DataStart + Length; + + nChannels = (*p << 24) | *(p + 1) << 16 | *(p + 2) << 8 | *(p + 3); + p += 4; //skip numberofchannels + if (CheckLevel(1)) { + isyslog ("| ID | %-26.26s | %-22.22s | FND | %-8.8s |\n", "Channel ID", "Channel Name", "Sky Num."); + isyslog ("|------|-%-26.26s-|-%-22.22s-|-----|-%-8.8s-|\n", "------------------------------", + "-----------------------------", "--------------------"); + } + + for (int j = 0; j < nChannels; j++) { + sChannelsNagraGuide *Channel = (sChannelsNagraGuide *) p; + sChannel *C = &sChannels[j]; + C->ChannelId = j + 1; //Nagra starts numbering at 1 + ChannelSeq[C->ChannelId] = j; //fill lookup table to go from channel-id to sequence nr in table; lookuptable starts with 0 + C->SkyNumber = 0; + C->NumberOfEquivalences = 1; //there is always an original channel. every equivalence adds 1 + C->Src[0] = Source(); //assume all EPG channels are on same satellite, if not, manage this via equivalents!!! + C->Nid[0] = HILO16 (Channel->NetworkId); + C->Tid[0] = HILO16 (Channel->TransportId); + C->Sid[0] = HILO16 (Channel->ServiceId); + tChannelID channelID = tChannelID(C->Src[0], C->Nid[0], C->Tid[0], C->Sid[0]); + cChannel *VC = GetChannelByID(channelID, true); + bool IsFound = (VC); + if(IsFound) { + strncpy((char*)(C->Name), VC->Name (), 64); + C->Src[0] = VC->Source(); + CleanString (C->Name); + } + else + C->Name[0] = '\0'; //empty string + + LogI(1, "|% 5d | %-26.26s | %-22.22s | %-3.3s | % 6d |\n", C->ChannelId + , *channelID.ToString(), C->Name, IsFound ? "YES" : "NO", C->SkyNumber); + + LogD(4, prep("DEBUG: start : %s"), cs_hexdump (0, p, 9)); + + p += 8; //skip to first 0x8c if non-FTA, or 0x00 if FTA + for (int i = 0; i < Channel->Nr8cBlocks; i++) { + if (*p != 0x8c) { + LogD(0, prep("DEBUG: ERROR in Channel Table, expected value of 0x8c is %02x"), *p); + return 0; //fatal error + } + p++; //skip 8c byte + LogD(4, prep("DEBUG: 8c string: %s"), cs_hexdump (0, p + 1, *p)); + p += *p; //skip 8c block + p++; //forgot to skip length byte + } + //start last non 8c block here + if (*p != 0x00) { + LogE(0, prep("ERROR in Channel Table, expected value of 0x00 is %02x"), *p); + return 0; //fatal error + } + p++; //skip 0x00 byte + LogD(4, prep("DEBUG: endstring: %s"), cs_hexdump (0, p + 1, *p * 4)); + p += (*p * 4); //p points to nrofblocks, each block is 4 bytes + p++; //forgot to skip nrofblocks byte + + /* + if (Channel->AlwaysZero1 != 0) + isyslog ("EEPGDEBUG: AlwaysZero1 is NOT ZERO:%x.", Channel->AlwaysZero1); + if (Channel->AlwaysZero2 != 0) + isyslog ("EEPGDEBUG: AlwaysZero2 is NOT ZERO:%x.", Channel->AlwaysZero2); + if (Channel->Always0x8c != 0x8c) + isyslog ("EEPGDEBUG: Always0x8c is NOT 0x8c:%x.", Channel->Always0x8c); + if (Channel->Always0x08 != 0x08) + isyslog ("EEPGDEBUG: Always0x08 is NOT 0x08:%x.", Channel->Always0x08); + if (Channel->Always0x02 != 0x02) + isyslog ("EEPGDEBUG: Always0x02 is NOT 0x02:%x.", Channel->Always0x02); + if (Channel->Always0x01 != 0x01) + isyslog ("EEPGDEBUG: Always0x01 is NOT 0x01:%x.", Channel->Always0x01); + if (Channel->Always0x20 != 0x20) + isyslog ("EEPGDEBUG: Always0x20 is NOT 0x20:%x.", Channel->Always0x20); + if (Channel->Always0x0a != 0x0a) + isyslog ("EEPGDEBUG: Always0x0a is NOT 0x0a:%x.", Channel->Always0x0a); + if (Channel->Always0x81 != 0x81) + isyslog ("EEPGDEBUG: Always0x81 is NOT 0x81:%x.", Channel->Always0x81); + if (Channel->Always0x44 != 0x44) + isyslog ("EEPGDEBUG: Always0x44 is NOT 0x44:%x.", Channel->Always0x44); + */ + + } + if (p != DataEnd) + LogE(0, prep("Warning, possible problem at end of channel table; p = %p, DataEnd = %p"), p, DataEnd); + LoadEquivalentChannels (); + return 2; //obviously, when you get here, channels are read succesfully, but since all channels are sent at once, you can stop now +} + +/** + * \brief Get Nagra extended EPG + * + * \return 0 = fatal error, code 1 = success, code 2 = last item processed + */ +int cFilterEEPG::GetNagra (const u_char * Data, int Length) +{ + sTitleBlockHeaderNagraGuide *TBH = (sTitleBlockHeaderNagraGuide *) Data; + if (InitialTitle[0] == 0x00) { //InitialTitle is empty, so we are waiting for the start marker + if (TBH->TableIdExtensionHigh == 0x00 && TBH->TableIdExtensionLow == 0x00) { //this is the start of the data + if (TBH->VersionNumber == LastVersionNagra) { + LogI(0, prep("Nagra EEPG already up-to-date with version %i"), LastVersionNagra); + return 2; + } + Version = TBH->VersionNumber; + LogI(0, prep("initialized Nagraguide, version %i"), Version); + //u_char *p = (u_char *) Data + 11; + u_char *p = (u_char *) Data + 8; + if (*p != 0x01) { + LogE(0, prep("Error, Nagra first byte in table_id_extension 0x00 is not 0x01 but %02x. Format unknown, exiting."), + *p); + return 0; //fatal error + } + p++; //skip 0x01 byte + unsigned short int l = ((*p << 8) | *(p + 1)); + u_char *p_end = p + l - 3; //this ensures a full block of 4 bytes is there to process + p += 2; //skip length bytes + while (p < p_end) { + sSection0000BlockNagraGuide *S = (sSection0000BlockNagraGuide *) p; + int TTT = ((S->TableIdExtensionHigh << 10) | (S->TableIdExtensionLow << 3) | (S->TIE200 << 9)); + LogD(4, prep("DEBUG: TableIdExtension %04x, Unknown1 %02x Version %02x Always 0xd6 %02x DayCounter %02x"), + TTT, S->Unknown1, S->VersionNumber, S->Always0xd6, S->DayCounter); + if ((TTT > 0x0400) && (TTT < 0x0600)) //take high byte and compare with summarie tables in 0x0400 and 0x0500 range; + NagraTIE[NagraCounter++] = TTT; //only store TIEs of titlessummaries, all others can be derived; they are stored in the order of the 0x0000 index table, + //lets hope first entry corresponds with today, next with tomorrow etc. + p += 4; + } + buffer.clear (); //clear buffer maps + bufsize.clear (); //clear buffer maps + InitialTitle[0] = 0xff; //copy data into initial title + } + return (1); + } + unsigned short SectionLength = ((TBH->SectionLengthHigh & 0x0f) << 8) | TBH->SectionLengthLow; + unsigned short TableIdExtension = HILO16 (TBH->TableIdExtension); + if (TableIdExtension == 0x0000) { + LastVersionNagra = Version; + return (2); //done + } + /* + Table_id_extensions: + (0x0000) + (0x0010) per channel, nr_of_channels entries, every entry is 8 bytes, first 4 bytes gives nr. of titles in corresponding title table + (0x0020) per channel info day 2 of the month + (0x01F0) per channel info day 31 of the month + (0x0200) leeg; letop op je leest gemakkelijk door naar 0x0210! + (0x0210) titles day 1 of the month + (0x0220) titles day 2 of the month + (0x03F0) titles day 31 of the month + (0x0400) channel info + (0x0410) summaries day 1 of the month + (0x0420) summaries day 2 of the month + (0x05F0) summaries day 31 of the month + (0x0610) themes/title reference sunday, correspond to titles 0x0400 lower... + (0x0620) themes/title reference monday + ... this goes on until 0x07f0 + (0x0810) bouquet info; references to channels within a package, day 1 of the month + (0x0820) same day 2 of the month + (0x09f0) same day 31 of the month + */ + + if (!(TBH->TableIdExtensionHigh >= 0x02 && TBH->TableIdExtensionHigh <= 0x07)) //here we regulate which tables we are testing + return (1); + if (TableIdExtension == 0x0200) //table 0x0200 only contains language code, because it is for day 0 of the month, and 0x0400 is used for channels + return 1; + if (TBH->SectionNumber == 0) { //first section, create a table + buffer[TableIdExtension] = NULL; + bufsize[TableIdExtension] = 0; + NumberOfTables++; + } + //store all sections in core until last section is found; processing incomplete sections is very complex and doesnt save much memory, + //since the data has to be stored anyway; summaries do not seem to have channelid included, so storing titles and separately storing summaries will not work... + //GetEventId only works for a specific Schedule for a specific ChannelId.... + buffer[TableIdExtension] = + (unsigned char *) realloc (buffer[TableIdExtension], SectionLength - 9 + bufsize[TableIdExtension]); + memcpy (buffer[TableIdExtension] + bufsize[TableIdExtension], Data + 8, SectionLength - 9); //append new section + bufsize[TableIdExtension] += SectionLength - 9; + if (TBH->SectionNumber >= TBH->LastSectionNumber) { + LogI(1, prep("found %04x lastsection nr:%i."), TableIdExtension, TBH->SectionNumber); + // if (TBH->TableIdExtensionHigh == 0x04 || TBH->TableIdExtensionHigh == 0x05) { + if (TableIdExtension == 0x0400) { + int Result = GetChannelsNagra (buffer[TableIdExtension] + 4, bufsize[TableIdExtension] - 4); //TODO language code terminated by 0 is ignored + free (buffer[TableIdExtension]); + buffer[TableIdExtension] = NULL; + NumberOfTables--; + if (Result == 0) + return 0; //fatal error; TODO this exit should also free all other, non-Channel sections that are stored! + } + } //if lastsection read + return (1); //return and continue, nonfatal +} + +void cFilterEEPG::ProcessNagra () +{ + for (int i = 0; i < MAX_THEMES; i++) //clear all themes + Themes[i][0] = '\0'; + + for (int i = 0; i < NagraCounter; i++) { //first prcoess all themes, since they all use the same codes + unsigned short int TableIdExtension = NagraTIE[i]; + int TIE = TableIdExtension + 0x0200; //from 0x0400 to 0x0600 -> themes + LogI(3, prep("Processing Theme with TableIdExtension:%04x"), TIE); + GetThemesNagra (buffer[TIE] + 4, bufsize[TIE] - 4, TableIdExtension - 0x0200); //assume theme is completed //TODO Language code terminatd by 0 is ignored + free (buffer[TIE]); + buffer[TIE] = NULL; + NumberOfTables--; + } + + for (int i = 0; i < NagraCounter; i++) { //first prcoess all themes, since they all use the same codes + unsigned short int TableIdExtension = NagraTIE[i]; + int TIE = TableIdExtension - 0x0200; //from 0x0400 to 0x0200 -> titles + LogI(0, prep("Processing TableIdExtension:%04x"), TableIdExtension); + GetTitlesNagra (buffer[TIE] + 4, bufsize[TIE] - 4, TableIdExtension); //assume title-reading is completed //TODO Language code terminatd by 0 is ignored + free (buffer[TIE]); + buffer[TIE] = NULL; + NumberOfTables--; + + free (buffer[TableIdExtension]); //summaries + buffer[TableIdExtension] = NULL; + NumberOfTables--; + } + if (NumberOfTables != 0) + LogE(0, prep("ERROR, Not all tables processed and stream is already repeating. NumberOfTables = %i."), + NumberOfTables); +} + +/** + * \brief Get MHW1 Titles + * + * \return 0 = fatal error, code 1 = success, code 2 = last item processed + */ +int cFilterEEPG::GetTitlesMHW1 (const u_char * Data, int Length) +{ + if (Length >= 42) { + sTitleMHW1 *Title = (sTitleMHW1 *) Data; + if (Title->ChannelId == 0xff) { //FF is separator packet + if (memcmp (InitialTitle, Data, 46) == 0) { //data is the same as initial title //TODO use easier notation + LogD(1, prep("End procesing titles")); + return 2; + } + if (nTitles == 0) + memcpy (InitialTitle, Data, 46); //copy data into initial title + int Day = Title->Day; + int Hours = Title->Hours; + if (Hours > 15) + Hours -= 4; + else if (Hours > 7) + Hours -= 2; + else + Day++; + if (Day > 6) + Day = Day - 7; + Day -= Yesterday; + if (Day < 1) + Day = 7 + Day; + //if (Day == 1 && Hours < 6) + if (Day == 0 && Hours < 6) + Day = 7; + //Day = 8; + MHWStartTime = (Day * 86400) + (Hours * 3600) + YesterdayEpochUTC; + LogI(3, prep("Titles: FF PACKET, seqnr:%02x."), Data[5]); + } else if (InitialTitle[0] != 0x00) { //if initialized this should always be 0x90 = tableid! + if (nTitles < MAX_TITLES) { + Title_t *T; + T = (Title_t *) malloc (sizeof (Title_t)); + Titles[nTitles] = T; + int Minutes = Title->Minutes; + int StartTime = MHWStartTime + (Minutes * 60); + T->ChannelId = Title->ChannelId - 1; + T->ThemeId = Title->ThemeId; + T->TableId = Title->TableId; + T->MjdTime = 0; //only used at MHW2 and SKY + T->EventId = HILO32 (Title->ProgramId); + T->StartTime = LocalTime2UTC (StartTime); //here also Daylight Savings correction is done + T->Duration = HILO16 (Title->Duration) * 60; + T->SummaryAvailable = Title->SummaryAvailable; + T->Text = (unsigned char *) malloc (47); + if (T->Text == NULL) { + LogE(0, prep("Titles memory allocation error.")); + return 0; + } + T->Text[46] = '\0'; //end string with NULL character + //memcpy (T->Text, &Title->Title, 23); + decodeText2((unsigned char *)&Title->Title, 23, (char*)T->Text, 47); + CleanString (T->Text); + LogI(3, prep("EvId:%08x,ChanId:%x, Titlenr:%d:, StartTime(epoch):%i, SummAv:%x,Name:%s."), T->EventId, + T->ChannelId, nTitles, T->StartTime, T->SummaryAvailable, T->Text); + nTitles++; + } //nTitles < MaxTitles + else { + LogE(0, prep("Error, %i titles found more than %i"), nTitles, MAX_TITLES); + return 0; + } + } //else if InitialTitle + } //Length==46 + else { + LogE(0, prep("Error, length of title package < 42.")); + return 1; //non fatal + } + + return 1; +} + +/** + * \brief Get MHW2 Titles + * + * \return 0 = fatal error, code 1 = success, code 2 = last item processed + */ +int cFilterEEPG::GetTitlesMHW2 (const u_char * Data, int Length) +{ + if (Length > 18) { + int Pos = 18; + int Len = 0; + /*bool Check = false; + while (Pos < Length) { + Check = false; + Pos += 7; + if (Pos < Length) { + Pos += 3; + if (Pos < Length) + if (Data[Pos] > 0xc0) { + Pos += (Data[Pos] - 0xc0); + Pos += 4; + if (Pos < Length) { + if (Data[Pos] == 0xff) { + Pos += 1; + Check = true; + } + } + } + } + if (Check == false){ + isyslog ("EEPGDebug: Check==false"); + return 1; // I assume nonfatal error or success + } + }*/ + if (memcmp (InitialTitle, Data, 16) == 0) { //data is the same as initial title + return 2; //last item processed + } else { + if (nTitles == 0) + memcpy (InitialTitle, Data, 16); //copy data into initial title + //Pos = 18; + while (Pos < Length) { + Title_t *T; + T = (Title_t *) malloc (sizeof (Title_t)); + Titles[nTitles] = T; + T->ChannelId = Data[Pos]; + Pos+=11;//The date time starts here + //isyslog ("EEPGDebug: ChannelID:%d", T->ChannelId); + unsigned int MjdTime = (Data[Pos] << 8) | Data[Pos + 1]; + T->MjdTime = 0; //not used for matching MHW2 + T->StartTime = ((MjdTime - 40587) * 86400) + + (((((Data[Pos + 2] & 0xf0) >> 4) * 10) + (Data[Pos + 2] & 0x0f)) * 3600) + + (((((Data[Pos + 3] & 0xf0) >> 4) * 10) + (Data[Pos + 3] & 0x0f)) * 60); + T->Duration = (((Data[Pos + 5] << 8) | Data[Pos + 6]) >> 4) * 60; + Len = Data[Pos + 7] & 0x3f; + //isyslog ("EEPGDebug: Len:%d", Len); + T->Text = (unsigned char *) malloc (Len + 2); + if (T->Text == NULL) { + LogE(0, prep("Titles memory allocation error.")); + return 0; //fatal error + } + T->Text[Len] = '\0'; //end string with NULL character + decodeText2(&Data[Pos + 8],Len,(char*)T->Text,Len+1); + //memcpy (T->Text, &Data[Pos + 8], Len); + CleanString (T->Text); + Pos += Len + 8; // Sub Theme starts here + T->ThemeId = ((Data[7] & 0x3f) << 6) | (Data[Pos] & 0x3f); + T->EventId = (Data[Pos + 1] << 8) | Data[Pos + 2]; + T->SummaryAvailable = (T->EventId != 0xFFFF); + LogI(3, prep("EventId %04x Titlenr %d:SummAv:%x,Name:%s."), T->EventId, + nTitles, T->SummaryAvailable, T->Text); + Pos += 3; + nTitles++; + if (nTitles > MAX_TITLES) { + LogE(0, prep("Error, %i titles found more than %i"), nTitles, MAX_TITLES); + return 0; //fatal error + } + } + return 1; //success + } //else memcmp + } //if length + return 1; //non fatal error +} + +/** + * \brief Get MHW1 Summaries + * + * \return 0 = fatal error, code 1 = success, code 2 = last item processed + */ +int cFilterEEPG::GetSummariesMHW1 (const u_char * Data, int Length) +{ + sSummaryMHW1 *Summary = (sSummaryMHW1 *) Data; + if (Length >= 11) { + if (Summary->NumReplays < 10) { //Why limit this at 10? + if (Length >= (11 + (Summary->NumReplays * 7))) { + if (Summary->Byte7 == 0xff && Summary->Byte8 == 0xff && Summary->Byte9 == 0xff) { + if (memcmp (InitialSummary, Data, 20) == 0) { //data is equal to initial buffer + return 2; + } else if (nSummaries < MAX_TITLES) { + if (nSummaries == 0) + memcpy (InitialSummary, Data, 20); //copy this data in initial buffer + int SummaryOffset = 11 + (Summary->NumReplays * 7); + int SummaryLength = Length - SummaryOffset; + unsigned char *Text = (unsigned char *) malloc (2*SummaryLength + 1); + if (Text == NULL) { + LogE(0, prep("Summaries memory allocation error.")); + return 0; + } + Text[SummaryLength+1] = '\0'; //end string with NULL character + //memcpy (Text, &Data[SummaryOffset], SummaryLength); + decodeText2(&Data[SummaryOffset], SummaryLength, (char*)Text, 2*SummaryLength + 1); +// CleanString (Text); +// if (Summary->NumReplays != 0) +// esyslog ("EEPG: Number of replays:%i.", Summary->NumReplays); + //int Replays = Summary->NumReplays; + int ReplayOffset = 11; + Summary_t *S; + S = (Summary_t *) malloc (sizeof (Summary_t)); + Summaries[nSummaries] = S; + S->NumReplays = Summary->NumReplays; + S->EventId = HILO32 (Summary->ProgramId); + S->Text = Text; + int i = 0; + do { + S->Replays[i].MjdTime = 0; //only used for SKY + //if (Summary->NumReplays == 0) { + //S->ChannelId = 0xFFFF; //signal that ChannelId is not known; 0 is bad signal value because it is a valid ChannelId... + //S->StartTime = 0; + //} + //else { + S->Replays[i].ChannelId = Data[ReplayOffset++] - 1; + unsigned int Date_hi = Data[ReplayOffset++]; + unsigned int Date_lo = Data[ReplayOffset++]; + unsigned short int Hour = Data[ReplayOffset++]; + unsigned short int Minute = Data[ReplayOffset++]; + unsigned short int Sec = Data[ReplayOffset++]; + ReplayOffset++; //makes total of 7 bytes + + LogI(4, prep("EvId:%08x ChanId:%x, ChanName %s, Time: %02d:%02d:%02d."), + S->EventId, S->Replays[i].ChannelId, + sChannels[ChannelSeq[S->Replays[i].ChannelId]].Name, Hour, Minute, Sec); + + S->Replays[i].StartTime = MjdToEpochTime (Date) + (((((Hour & 0xf0) >> 4) * 10) + (Hour & 0x0f)) * 3600) + + (((((Minute & 0xf0) >> 4) * 10) + (Minute & 0x0f)) * 60) + + ((((Sec & 0xf0) >> 4) * 10) + (Sec & 0x0f)); +// summary -> time[i] = ProviderLocalTime2UTC (summary -> time[i]); + S->Replays[i].StartTime = LocalTime2UTC (S->Replays[i].StartTime); + //} + i++; + } while (i < Summary->NumReplays); + //} while (Replays-- >= 0); + LogI(3, prep("EvId:%08x ChanId:%x, Replays:%d, Summnr %d:%.35s."), S->EventId, + S->Replays[0].ChannelId, S->NumReplays, nSummaries, S->Text); + nSummaries++; + } else { + LogE(0, prep("Error, %i summaries found more than %i"), nSummaries, MAX_TITLES); + return 0; + } + } //0xff + else { + LogE(0, prep("Warning, Summary bytes not as expected.")); + return 1; //it is not a success, but error is not fatal + } + } //numreplays length + else { + LogE(0, prep("Warning, number of replays is not conforming to length.")); + return 1; //nonfatal error + } + } //numreplays <10 + else { + LogE(0, prep("Warning, number of replays %d > 10, cannot process."), + Summary->NumReplays); + return 1; //nonfatal error + } + } //length >11 + else { + LogE(0, prep("Summary length too small:%s"), cs_hexdump (0, Data, Length)); + return 1; //nonfatal error + } + return 1; //success +} + +/** + * \brief Get MHW2 Summaries + * + * \return 0 = fatal error, code 1 = success, code 2 = last item processed + */ +int cFilterEEPG::GetSummariesMHW2 (const u_char * Data, int Length) +{ + if (Length > (Data[14] + 17)) { + if (memcmp (InitialSummary, Data, 16) == 0) //data is equal to initial buffer + return 2; + else { + if (nSummaries == 0) + memcpy (InitialSummary, Data, 16); //copy this data in initial buffer + if (nSummaries < MAX_TITLES) { + int lenText = Data[14]; + int SummaryLength = lenText; + int Pos = 15; + int Loop = Data[Pos + SummaryLength] & 0x0f; + Summary_t *S; + S = (Summary_t *) malloc (sizeof (Summary_t)); + Summaries[nSummaries] = S; + + S->Replays[0].ChannelId = 0xFFFF; //signal that ChannelId is not known; 0 is bad signal value because it is a valid ChannelId... + S->Replays[0].StartTime = 0; //not used + S->Replays[0].MjdTime = 0; //not used + S->NumReplays = 0; //not used + S->EventId = (Data[3] << 8) | Data[4]; + unsigned char tmp[4096]; //TODO do this smarter + memcpy (tmp, &Data[Pos], lenText); + tmp[SummaryLength] = '\n'; + SummaryLength += 1; + Pos += (lenText + 1); + if (Loop > 0) { + while (Loop > 0) { + lenText = Data[Pos]; + Pos += 1; + if ((Pos + lenText) < Length) { + memcpy (&tmp[SummaryLength], &Data[Pos], lenText); + SummaryLength += lenText; + if (Loop > 1) { + tmp[SummaryLength] = '\n'; + SummaryLength += 1; + } + } else + break; + Pos += lenText; + Loop--; + } + } + S->Text = (unsigned char *) malloc (SummaryLength + 2); + S->Text[SummaryLength] = '\0'; //end string with NULL character + if (S->Text == NULL) { + LogE(0, prep("Summaries memory allocation error.")); + return 0; //fatal error + } + //memcpy (S->Text, tmp, SummaryLength); + decodeText2(tmp,SummaryLength,(char*)S->Text,SummaryLength + 1); + CleanString (S->Text); + LogI(3, prep("EventId %08x Summnr %d:%.30s."), S->EventId, nSummaries, S->Text); + nSummaries++; + } else { + LogE(0, prep("Error, %i summaries found more than %i"), nSummaries, MAX_TITLES); + return 0; //fatal error + } + } //else + } //if length + return 1; //succes or nonfatal error +} + +/** + * \brief Get SKYBOX Channels + * + * \return 0 = fatal error, code 1 = success, code 2 = last item processed + */ +int cFilterEEPG::GetChannelsSKYBOX (const u_char * Data, int Length) +{ + + if (memcmp (InitialChannel, Data, 8) == 0) { //data is the same as initial title + LoadEquivalentChannels (); + return 2; + } else { + if (nChannels == 0) + memcpy (InitialChannel, Data, 8); //copy data into initial title + if (nChannels == 0) { + LogI(1, "| ID | %-26.26s | %-22.22s | FND | %-8.8s |\n", "Channel ID", "Channel Name", "Sky Num."); + LogI(1, "|------|-%-26.26s-|-%-22.22s-|-----|-%-8.8s-|\n", "------------------------------", + "-----------------------------", "--------------------"); + } +// unsigned short int BouquetId = (Data[3] << 8) | Data[4]; + int BouquetDescriptorsLength = ((Data[8] & 0x0f) << 8) | Data[9]; + int TransportStreamLoopLength = + ((Data[BouquetDescriptorsLength + 10] & 0x0f) << 8) | Data[BouquetDescriptorsLength + 11]; + int p1 = (BouquetDescriptorsLength + 12); + while (TransportStreamLoopLength > 0) { + unsigned short int Tid = (Data[p1] << 8) | Data[p1 + 1]; + unsigned short int Nid = (Data[p1 + 2] << 8) | Data[p1 + 3]; + int TransportDescriptorsLength = ((Data[p1 + 4] & 0x0f) << 8) | Data[p1 + 5]; + int p2 = (p1 + 6); + p1 += (TransportDescriptorsLength + 6); + TransportStreamLoopLength -= (TransportDescriptorsLength + 6); + while (TransportDescriptorsLength > 0) { + unsigned char DescriptorTag = Data[p2]; + int DescriptorLength = Data[p2 + 1]; + int p3 = (p2 + 2); + p2 += (DescriptorLength + 2); + TransportDescriptorsLength -= (DescriptorLength + 2); + switch (DescriptorTag) { //TODO switch with only 1 case??? replace this by template + case 0xb1: + p3 += 2; + DescriptorLength -= 2; + while (DescriptorLength > 0) { + // 0x01 = Video Channel + // 0x02 = Audio Channel + // 0x05 = Other Channel + //if( Data[p3+2] == 0x01 || Data[p3+2] == 0x02 || Data[p3+2] == 0x05 ) + //{ + unsigned short int Sid = (Data[p3] << 8) | Data[p3 + 1]; + unsigned short int ChannelId = (Data[p3 + 3] << 8) | Data[p3 + 4]; + unsigned short int SkyNumber = (Data[p3 + 5] << 8) | Data[p3 + 6]; + if (SkyNumber > 100 && SkyNumber < 1000) { + if (ChannelId > 0) { + sChannel *C; + if (ChannelSeq.count (ChannelId) == 0) { //not found + C = &sChannels[nChannels]; + C->ChannelId = ChannelId; + C->NumberOfEquivalences = 1; //there is always an original channel. every equivalence adds 1 + C->Src[0] = Source (); //assume all EPG channels are on same satellite, if not, manage this via equivalents!!! + C->Nid[0] = Nid; + C->Tid[0] = Tid; + C->Sid[0] = Sid; + C->SkyNumber = SkyNumber; + tChannelID channelID = tChannelID (C->Src[0], C->Nid[0], C->Tid[0], C->Sid[0]); + cChannel *VC = Channels.GetByChannelID (channelID, true); + bool IsFound = (VC); + if (IsFound) + strncpy ((char *) C->Name, VC->Name (), 64); + else + C->Name[0] = '\0'; //empty string + + LogI(1, "|% 5d | %-26.26s | %-22.22s | %-3.3s | % 6d |\n", C->ChannelId + , *channelID.ToString(), C->Name, IsFound ? "YES" : "NO", C->SkyNumber); + + ChannelSeq[C->ChannelId] = nChannels; //fill lookup table to go from channel-id to sequence nr in table + nChannels++; + if (nChannels >= MAX_CHANNELS) { + LogE(0, prep("Error, %i channels found more than %i"), nChannels, MAX_CHANNELS); + return 0; + } + } + } + } + p3 += 9; + DescriptorLength -= 9; + } + break; + default: + break; + } //switch descriptortag + } + } //while + return 1; + } //else part of memcmp +} + +/** + * \brief Get SKYBOX Titles + * + * \return 0 = fatal error, code 1 = success, code 2 = last item processed + */ +int cFilterEEPG::GetTitlesSKYBOX (const u_char * Data, int Length) +{ + int p; + unsigned short int ChannelId; + unsigned short int MjdTime; + int Len1; + int Len2; + + if (Length < 20) + return 1; //nonfatal error + if (memcmp (InitialTitle, Data, 20) == 0) //data is the same as initial title + return 2; + else { + if (nTitles == 0) + memcpy (InitialTitle, Data, 20); //copy data into initial title + ChannelId = (Data[3] << 8) | Data[4]; + MjdTime = ((Data[8] << 8) | Data[9]); + if (ChannelId > 0) { + if (MjdTime > 0) { + p = 10; + do { + Title_t *T; + T = (Title_t *) malloc (sizeof (Title_t)); + Titles[nTitles] = T; + T->ChannelId = ChannelId; + T->MjdTime = MjdTime; //only date, no time. Is used to match titles and summaries, SKYBOX only + T->EventId = (Data[p] << 8) | Data[p + 1]; + Len1 = ((Data[p + 2] & 0x0f) << 8) | Data[p + 3]; + if (Data[p + 4] != 0xb5) { + LogD(5, prep("Data error signature for title - Data[p + 4] != 0xb5")); + break; + } + if (Len1 > Length) { + LogD(5, prep("Data error signature for title - Len1 > Length")); + break; + } + p += 4; + Len2 = Data[p + 1] - 7; + T->StartTime = ((MjdTime - 40587) * 86400) + ((Data[p + 2] << 9) | (Data[p + 3] << 1)); + T->Duration = ((Data[p + 4] << 9) | (Data[p + 5] << 1)); + T->ThemeId = Data[p + 6]; + //TODO Data[p + 7] is Quality value add it to description + //int quality = Data[p + 7]; + switch (Data[p + 8] & 0x0F) { + case 0x01: + T->Rating = 0x00; //"U" + break; + case 0x02: + T->Rating = 0x08; //"PG" + break; + case 0x03: + T->Rating = 0x0C; //"12" + break; + case 0x04: + T->Rating = 0x0F; //"15" + break; + case 0x05: + T->Rating = 0x12; //"18" + break; + default: + T->Rating = 0x00; //"-" + break; + } + T->Unknown1 = Data[p + 4 - 13]; //FIXME + T->Unknown2 = Data[p + 4 - 12]; //FIXME + T->Unknown3 = Data[p + 4 - 11]; //FIXME + unsigned char tmp[4096]; //TODO smarter + Len2 = DecodeHuffmanCode (&Data[p + 9], Len2, tmp); + if (Len2 == 0) { + LogE(0, prep("Warning, could not huffman-decode title-text, skipping title.")); + return 1; //non-fatal error + } + T->Text = (unsigned char *) malloc (Len2 + 1); + if (T->Text == NULL) { + LogE(0, prep("Titles memory allocation error.")); + return 0; + } + T->Text[Len2] = '\0'; //end string with NULL character + memcpy (T->Text, tmp, Len2); + CleanString (T->Text); + T->SummaryAvailable = 1; //TODO I assume this is true? + + LogI(3, prep("EventId %08x Titlenr %d,Unknown1:%x,Unknown2:%x,Un3:%x,Name:%s."), + T->EventId, nTitles, T->Unknown1, T->Unknown2, T->Unknown3, T->Text); + + p += Len1; + nTitles++; + if (nTitles >= MAX_TITLES) { + LogE(0, prep("Error, %i titles found more than %i"), nTitles, MAX_TITLES); + return 0; //fatal error + } + } while (p < Length); + } + } + } + return 1; //success +} + +/** + * \brief Get SKYBOX Summaries + * + * \return 0 = fatal error, code 1 = success, code 2 = last item processed + */ +int cFilterEEPG::GetSummariesSKYBOX (const u_char * Data, int Length) +{ + int p; + unsigned short int ChannelId; + unsigned short int MjdTime; + int Len1; + int Len2; + + if (Length < 20) { + return 1; //non fatal error I assume + } + if (memcmp (InitialSummary, Data, 20) == 0) //data is equal to initial buffer + return 2; +// else if (nSummaries < MAX_SUMMARIES) { + else { + if (nSummaries == 0) + memcpy (InitialSummary, Data, 20); //copy this data in initial buffer + ChannelId = (Data[3] << 8) | Data[4]; + MjdTime = ((Data[8] << 8) | Data[9]); + if (ChannelId > 0) { + if (MjdTime > 0) { + p = 10; + do { + Summary_t *S; + S = (Summary_t *) malloc (sizeof (Summary_t)); + Summaries[nSummaries] = S; + S->Replays[0].ChannelId = ChannelId; + S->Replays[0].MjdTime = MjdTime; + S->NumReplays = 0; //not used + S->EventId = (Data[p] << 8) | Data[p + 1]; + Len1 = ((Data[p + 2] & 0x0f) << 8) | Data[p + 3]; + if (Data[p + 4] != 0xb9) { + LogD(5, prep("Data error signature for title - Data[p + 4] != 0xb5")); + break; + } + if (Len1 > Length) { + LogD(5, prep("Data error signature for title - Len1 > Length")); + break; + } + p += 4; + Len2 = Data[p + 1]; + unsigned char tmp[4096]; //TODO can this be done better? + Len2 = DecodeHuffmanCode (&Data[p + 2], Len2, tmp); + if (Len2 == 0) { + LogE(0, prep("Warning, could not huffman-decode text, skipping summary.")); + return 1; //non-fatal error + } + S->Text = (unsigned char *) malloc (Len2 + 1); + if (S->Text == NULL) { + LogE(0, prep("Summaries memory allocation error.")); + return 0; + } + memcpy (S->Text, tmp, Len2); + S->Text[Len2] = '\0'; //end string with NULL character + CleanString (S->Text); + LogI(3, prep("EventId %08x Summnr %d:%.30s."), S->EventId, nSummaries, S->Text); + p += Len1; + nSummaries++; + if (nSummaries >= MAX_TITLES) { + LogI(0, prep("Error, %i summaries found more than %i"), nSummaries, MAX_TITLES); + return 0; + } + } while (p < Length); + } + } + } + return 1; +} + +void cFilterEEPG::FreeSummaries (void) +{ + if (Format == MHW1 || Format == MHW2 || Format == SKY_IT || Format == SKY_UK) { + Summary_t *S; //TODO do I need this? + Summary_t *S2; //TODO do I need this? + for (int i = 0; i < nSummaries; i++) { + S = Summaries[i]; + if (i < nSummaries - 1) { + S2 = Summaries[i + 1]; //look at next summary + if (S->Text != S2->Text && S->Text != 0x00) //this is the last summary that points to this textblock; needed in case NumReplays > 1, multiple pointers to same textblock + free (S->Text); + } else if (S->Text != 0x00) + free (S->Text); + free (S); + } + } + nSummaries = 0; +} + +void cFilterEEPG::FreeTitles (void) +{ + if (Format == MHW1 || Format == MHW2 || Format == SKY_IT || Format == SKY_UK) { + Title_t *T; + for (int i = 0; i < nTitles; i++) { + T = Titles[i]; + free (T->Text); + free (T); + } + } + nTitles = 0; +} + +void cFilterEEPG::LoadIntoSchedule (void) +{ + int i, j, k; + i = 0; + j = 0; + k = 0; + bool foundtitle; + foundtitle = false; + Title_t *T; + Summary_t *S; + int remembersummary; +//keep statistics + int SummariesNotFound = 0; + int NoSummary = 0; + int NotMatching = 0; + int LostSync = 0; + remembersummary = -1; + + cSchedulesLock SchedulesLock (true); + cSchedules *s = (cSchedules *) cSchedules::Schedules (SchedulesLock); + if (s) { + + while (i < nTitles) { + T = Titles[i]; + S = Summaries[j]; + foundtitle = false; + + while ((i < nTitles) && (!foundtitle)) { //find next title that has summary + T = Titles[i]; + if (T->SummaryAvailable) + foundtitle = true; + else { + NoSummary++; + i++; + } + } +//esyslog("foundtitle %x for next title that has a summary:%d",foundtitle,i); + + if (!foundtitle) //no more titles with summaries + break; + if ((T->EventId == S->EventId) && (T->MjdTime == S->Replays[0].MjdTime) + && ((T->ChannelId == S->Replays[0].ChannelId) || ((Format != SKY_IT) && (Format != SKY_UK)))) { //should always be true, titles and summaries are broadcasted in order... + LogD(3, prep("T->EventId == S->EventId")); + //MjdTime = 0 for all but SKY + //S->ChannelId must be equal to T->ChannelId only for SKY; in MHW1 S->ChannelId overrides T->ChannelId when NumReplays > 1 + remembersummary = -1; //reset summary searcher + //int Replays = S->NumReplays; + + int index = 0; + do { + unsigned short int ChannelId; + time_t StartTime; + if (S->NumReplays == 0) { + ChannelId = T->ChannelId; + StartTime = T->StartTime; + } else { + ChannelId = S->Replays[index].ChannelId; + StartTime = S->Replays[index].StartTime; + } + + //channelids are sequentially numbered and sent in MHW1 and MHW2, but not in SKY, so we need to lookup the table index + sChannel *C = &sChannels[ChannelSeq[ChannelId]]; //find channel + cSchedule *p[MAX_EQUIVALENCES]; + PrepareToWriteToSchedule (C, s, p); + + char rating = 0x00; + if ((Format == SKY_IT || Format == SKY_UK) && T->Rating) { //TODO only works on OTV for now + rating = T->Rating; + } + unsigned short int TableId = DEFAULT_TABLE_ID; + if (T->TableId) { + TableId = T->TableId; + } + + WriteToSchedule (p, C->NumberOfEquivalences, T->EventId, StartTime, T->Duration / 60, (char *) T->Text, + (char *) S->Text, T->ThemeId, TableId, 0, rating); + + FinishWriteToSchedule (C, s, p); + //Replays--; + //if ((S->NumReplays != 0) && (Replays > 0)) { //when replays are used, all summaries of the replays are stored consecutively; currently only CSAT + //j++; //move to next summary + //if (j >= nSummaries) //do not forget to look in beginning of (ring)buffer + //j = 0; + //S = Summaries[j]; //next summary within replay range + //} + index++; + } //while + while (index < S->NumReplays); + +//TODO: why load events that have already past, and then run Cleanup +//end of putting title and summary in schedule + i++; //move to next title + } //if T->EventId == S->EventId + else { +// esyslog("EEPG ERROR: ProgramIds not matching, title:%d,summary%d, T->EventId:%u, S->Eventid:%u.",i,j,T->EventId,S->EventId); + NotMatching++; + if (remembersummary == -1) { //I am not in search loop yet + remembersummary = j; + if (remembersummary == 0) + remembersummary = nSummaries; //or next test will never be succesfull for remembersummary = 0 + LostSync++; +// esyslog("EEPG Error: lost sync at title %d, summary %d.",i,j); + } else if (j == (remembersummary - 1)) { //the last summary to be checked has failed also + //esyslog ("EEPG Error: could not find summary for summary-available Title %d.", i); + esyslog + ("EEPG: Error, summary not found for EventId %08x Titlenr %d:SummAv:%x,Unknown1:%x,Unknown2:%x,Un3:%x,Name:%s.", + T->EventId, i, T->SummaryAvailable, T->Unknown1, T->Unknown2, T->Unknown3, T->Text); + + /* write Title info to schedule */ + sChannel *C = &sChannels[ChannelSeq[T->ChannelId]]; //find channel + cSchedule *p[MAX_EQUIVALENCES]; + PrepareToWriteToSchedule (C, s, p); + char rating = 0x00; + if ((Format == SKY_IT || Format == SKY_UK) && T->Rating) { //TODO only works on OTV for now + rating = T->Rating; + } + WriteToSchedule (p, C->NumberOfEquivalences, T->EventId, T->StartTime, T->Duration / 60, (char *) T->Text, + NULL, T->ThemeId, DEFAULT_TABLE_ID, 0, rating); + FinishWriteToSchedule (C, s, p); + + SummariesNotFound++; + i++; //move to next title, for this one no summary can be found + } + +// esyslog("Trying again for this title %d, remember summary %d, but advancing one Summary to %d.",i,remembersummary,j); + } + j++; //move to next summary + if (j >= nSummaries) //do not forget to look in beginning of (ring)buffer + j = 0; + } //while title + } // if s + else + esyslog ("EEPG Error: could not lock schedules."); + + cSchedules::Cleanup (true); //deletes all past events + + isyslog ("EEPG: found %i equivalents channels", nEquivChannels); + isyslog ("EEPG: found %i themes", nThemes); + isyslog ("EEPG: found %i channels", nChannels); + isyslog ("EEPG: found %i titles", nTitles); + isyslog ("EEPG: of which %i reported to have no summary available; skipping these BIENTOT titles", NoSummary); + isyslog ("EEPG: found %i summaries", nSummaries); + if (SummariesNotFound != 0) + esyslog ("EEPG: %i summaries not found", SummariesNotFound); + if (NotMatching > nSummaries) + LogI (0, prep("Warning: lost sync %i times, summary did not match %i times."), + LostSync, NotMatching); + + FreeSummaries (); //do NOT free channels, themes and bouquets here because they will be reused in SKY! + FreeTitles (); + if (!((Format == SKY_IT) || (Format == SKY_UK))) { //everything but SKY; SKY is the only protocol where LoadIntoSchedule is called repeatedly + ChannelSeq.clear (); + } +} + +void cFilterEEPG::AddFilter (u_short Pid, u_char Tid) +{ + if (!Matches (Pid, Tid)) { + Add (Pid, Tid); + esyslog (prep("Filter Pid:%x,Tid:%x added."), Pid, Tid); + } +} + +void cFilterEEPG::AddFilter (u_short Pid, u_char Tid, unsigned char Mask) +{ + if (!Matches (Pid, Tid)) { + Add (Pid, Tid, Mask); + esyslog (prep("Filter Pid:%x,Tid:%x,Mask:%x added."), Pid, Tid, 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 OnlyRunningStatus = false); +}; + +cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Data, bool OnlyRunningStatus) + : SI::EIT (Data, false) +{ + //LogD(2, prep("cEIT2::cEIT2")); + if (Tid > 0 && Format == DISH_BEV) 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(4, 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 (); + 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 { + // 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 () == getVersionNumber ()) + 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() == getVersionNumber()) + 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 (getVersionNumber()); + + int LanguagePreferenceShort = -1; + int LanguagePreferenceExt = -1; + 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 ()); + + switch (d->getDescriptorTag ()) { + 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)); + pEvent->SetTitle (buffer); + l = ShortEventDescriptor->text.getLength(); + f = (unsigned char *) ShortEventDescriptor->text.getData().getData(); + decodeText2 (f, l, buffer, sizeof (buffer)); + //ShortEventDescriptor->text.getText(buffer, sizeof(buffer)); + pEvent->SetShortText (buffer); + } else if (!HasExternalData) { + pEvent->SetTitle (NULL); + pEvent->SetShortText (NULL); + } + if (ExtendedEventDescriptors) { + char buffer[Utf8BufSize (ExtendedEventDescriptors->getMaximumTextLength (": ")) + 1]; + pEvent->SetDescription (ExtendedEventDescriptors->getText (buffer, sizeof (buffer), ": ")); + } 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(), DishShortEventDescriptor->getText()); +// pEvent->SetDescription(DishExtendedEventDescriptor->getText()); + char *tmp; + string fmt; + fmt = "%s"; + if (0 != strcmp(DishEventDescriptor->getShortText(),"") && DishEventDescriptor->hasTheme()) { + fmt += " - "; + } + fmt += "%s"; + if (DishEventDescriptor->hasTheme() && DishEventDescriptor->hasCategory()) { + fmt += " ~ "; + } + fmt += "%s"; + + Asprintf (&tmp, fmt.c_str(), DishEventDescriptor->getShortText() + , DishEventDescriptor->getTheme() + , DishEventDescriptor->getCategory()); + pEvent->SetShortText(tmp); + //LogD(2, prep("EEPGDEBUG:DishTheme:%x-DishCategory:%x)"), DishTheme, DishCategory); + free(tmp); + + fmt = "%s"; + if (0 != strcmp(DishEventDescriptor->getDescription(),"") + && (0 != strcmp(DishEventDescriptor->getRating(),"") + || 0 != strcmp(DishEventDescriptor->getStarRating(),""))) { + fmt += "\nRating: "; + } + fmt += "%s %s"; + if (0 != strcmp(DishEventDescriptor->getProgramId(),"")) { + fmt += "\n Program ID: "; + } + fmt += "%s %s"; + time_t orgAirDate = DishEventDescriptor->getOriginalAirDate(); + fmt += orgAirDate == 0 ? "%s" : " Original Air Date: %s"; + LogD(3, prep("DishSeriesDescriptorTag2: %s %s %d %s)"), DishEventDescriptor->getProgramId() + , DishEventDescriptor->getSeriesId() + , orgAirDate + , orgAirDate == 0 ? "" : asctime(localtime(&orgAirDate))); + Asprintf (&tmp, fmt.c_str(), DishEventDescriptor->getDescription() + , DishEventDescriptor->getRating() + , DishEventDescriptor->getStarRating() + , DishEventDescriptor->getProgramId() + , DishEventDescriptor->getSeriesId() + , orgAirDate == 0 ? "" : asctime(localtime(&orgAirDate)) (&orgAirDate)); + pEvent->SetDescription(tmp); + free(tmp); + + + //LogD(2, prep("DishDescription: %s"), DishExtendedEventDescriptor->getText()); + //LogD(2, prep("DishShortText: %s"), DishExtendedEventDescriptor->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 */ + } + 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(2, prep("OnlyRunningStatus")); + return; + } + if (Modified) { + pSchedule->Sort (); + if (!HasExternalData) + pSchedule->DropOutdated (SegmentStart, SegmentEnd, Tid, getVersionNumber ()); + Schedules->SetModified (pSchedule); + } + LogD(2, prep("end of cEIT2")); + +} +//end of cEIT2 + +} //end namespace SI + +void cFilterEEPG::ProcessNextFormat (bool FirstTime = false) +{ + /* for (int i = 0; i <= HIGHEST_FORMAT; i++) + esyslog ("EEPGDEBUG: Format %i on pid %x", i, UnprocessedFormat[i]); */ + + if (!FirstTime) { + isyslog ("EEPG: found %i equivalents channels", nEquivChannels); + isyslog ("EEPG: found %i themes", nThemes); + isyslog ("EEPG: found %i channels", nChannels); + isyslog ("EEPG: found %i titles", nTitles); + isyslog ("EEPG: found %i summaries", nSummaries); + isyslog ("EEPG: written %i titles", TitleCounter); + isyslog ("EEPG: written %i summaries", SummaryCounter); + isyslog ("EEPG: rejected %i titles/summaries because of higher TableId", RejectTableId); + //Send message when finished + if (SetupPE.DisplayMessage) { + char *mesg; + Asprintf(&mesg, "EEPG: written %i summaries", SummaryCounter); + Skins.QueueMessage(mtInfo, mesg, 2); + free(mesg); + } + + TitleCounter = 0; + SummaryCounter = 0; + /*if (SummariesNotFound != 0) + esyslog ("EEPG: %i summaries not found", SummariesNotFound); + else if (VERBOSE >= 1) + isyslog ("EEPG: %i summaries not found", SummariesNotFound); */ + UnprocessedFormat[Format] = 0; //clear previously processed format + + //Next few lines prevent eepg from reading multiple eepg-systems on one transponder e.g. CDNL + //isyslog ("EEPG: Ended all processing"); + //return; + //If you remove these lines, multiple systems WILL be read + + } + TitleCounter = 0; + SummaryCounter = 0; + RejectTableId = 0; + //cleanup mess of last processing + ChannelSeq.clear (); + FreeTitles (); + FreeSummaries (); + + //now start looking for next format to process + int pid; + for (int i = 0; i <= HIGHEST_FORMAT; i++){ //find first format that is detected + if (UnprocessedFormat[i]) { + isyslog ("EEPG: %s Extended EPG detected on pid %x.", FormatName[i], UnprocessedFormat[i]); + Format = (EFormat)i; + // highest format is processed first this way + // make sure that CONT protocols like Premiere, Freesat are processed + // AFTER ONCE protocols like MHW, SKY and NAGRA + break; + } + if (i == HIGHEST_FORMAT) { //there are no formats left to process + isyslog ("EEPG: Ended all processing"); + return; + } + } + + pid = UnprocessedFormat[Format]; //and reinstall its pid + + memset (&InitialChannel, 0, 8); + memset (&InitialTitle, 0, 64); + memset (&InitialSummary, 0, 64); + NagraCounter = 0; + Version = -1; //because 0 can be a valid version number... + nEquivChannels = 0; + nChannels = 0; + nThemes = 0; + EndChannels = false; + EndThemes = false; + switch (Format) { + case PREMIERE: + if (!Matches (pid, 0xA0)) + Add (pid, 0xA0); + break; + case MHW1: + AddFilter (0xd3, 0x92); //ThemesMHW1//TODO: all filters are serialized, strictly speaking Themes is non-fatal... + break; + case MHW2: + AddFilter (0x231, 0xc8); //MHW2 Channels & Themes + break; + case SKY_IT: + case SKY_UK: + AddFilter (0x11, 0x4a); //Sky Channels + break; + case FREEVIEW: //Freeview, CONT mode //TODO streamline this for other modes + ReadFileDictionary (); + AddFilter (pid, 0x4e, 0xfe); //event info, actual(0x4e)/other(0x4f) TS, present/following + AddFilter (pid, 0x50, 0xf0); //event info, actual TS, schedule(0x50)/schedule for future days(0x5X) + AddFilter (pid, 0x60, 0xf0); //event info, other TS, schedule(0x60)/schedule for future days(0x6X) + AddFilter (0x39, 0x50, 0xf0); //event info, actual TS, Viasat + AddFilter (0x39, 0x60, 0xf0); //event info, other TS, Viasat + break; + case NAGRA: + AddFilter (pid, 0xb0); //perhaps TID is equal to first data byte? + break; + case DISH_BEV: + AddFilter (0x12, 0, 0); // event info, actual(0x4e)/other(0x4f) TS, present/following + AddFilter (0x0300, 0, 0); // Dish Network EEPG event info, actual(0x4e)/other(0x4f) TS, present/following + AddFilter (0x0441, 0, 0); // Dish Network EEPG event info, actual(0x4e)/other(0x4f) TS, present/following +// AddFilter (0x0441, 0x50, 0xf0); // Bell ExpressVU EEPG +// AddFilter (0x0441, 0x60, 0xf0); // Bell ExpressVU EEPG + break; + default: + break; + } +} + +void cFilterEEPG::ProccessContinuous(u_short Pid, u_char Tid, int Length, const u_char *Data) +{ + //0x39 Viasat, 0x0300 Dish Network EEPG, 0x0441 Bell ExpressVU EEPG + LogD(4, prep("Pid: 0x%02x Tid: %d Length: %d"), Pid, Tid, Length); + cSchedulesLock SchedulesLock(true, 10); + cSchedules *Schedules = (cSchedules*)(cSchedules::Schedules(SchedulesLock)); + //Look for other satelite positions only if Dish/Bell ExpressVU for the moment hardcoded pid check + if(Schedules) + SI::cEIT2 EIT(Schedules, Source(), Tid, Data); + + else//cEIT EIT (Schedules, Source (), Tid, Data); + { + // If we don't get a write lock, let's at least get a read lock, so + // that we can set the running status and 'seen' timestamp (well, actually + // with a read lock we shouldn't be doing that, but it's only integers that + // get changed, so it should be ok) + cSchedulesLock SchedulesLock; + cSchedules *Schedules = (cSchedules*)(cSchedules::Schedules(SchedulesLock)); + if(Schedules) + SI::cEIT2 EIT(Schedules, Source(), Tid, Data, true); + + //cEIT EIT (Schedules, Source (), Tid, Data, true); + } +} + +void cFilterEEPG::Process (u_short Pid, u_char Tid, const u_char * Data, int Length) +{ + int now = time (0); +// LogD(2, prep("Pid: 0x%02x Tid: %d Length: %d PMT pid: 0x%04x"), Pid, Tid, Length, pmtpid); +// LogD(2, prep("Source: %d Transponder: %d"), Source () , Transponder ()); + + if (Pid == 0 && Tid == SI::TableIdPAT) { + if (!pmtnext || now > pmtnext) { + if (pmtpid) + NextPmt (); + if (!pmtpid) { + SI::PAT pat (Data, false); + if (pat.CheckCRCAndParse ()) { + SI::PAT::Association assoc; + int idx = 0; + for (SI::Loop::Iterator it; pat.associationLoop.getNext (assoc, it);) { + if (!assoc.isNITPid ()) { + //if (!assoc.isNITPid () && Scanning) { + if (idx++ == pmtidx) { + pmtpid = assoc.getPid (); + pmtsid = assoc.getServiceId (); + Add (pmtpid, 0x02); + pmtnext = now + PMT_SCAN_TIMEOUT; + LogI(3, prep("PMT pid now 0x%04x (idx=%d)\n"), pmtpid, pmtidx); + break; + } + } + } + if (!pmtpid) { + pmtidx = 0; + pmtnext = now + PMT_SCAN_IDLE; + LogI(1, prep("PMT scan idle\n")); + + Del (0, 0); //this effectively kills the PMT_SCAN_IDLE functionality + + //now after the scan is completed, start processing + ProcessNextFormat (true); //FirstTime flag is set + } + } + } + } + } else if (pmtpid > 0 && Pid == pmtpid && Tid == SI::TableIdPMT && Source () && Transponder ()) { + SI::PMT pmt (Data, false); + if (pmt.CheckCRCAndParse () && pmt.getServiceId () == pmtsid) { + SI::PMT::Stream stream; + for (SI::Loop::Iterator it; pmt.streamLoop.getNext (stream, it);) { + LogD(2, prep("StreamType: 0x%02x"), stream.getStreamType ()); + if (stream.getStreamType () == 0x05 || stream.getStreamType () == 0xc1) { //0x05 = Premiere, SKY, Freeview, Nagra 0xc1 = MHW1,MHW2; + SI::CharArray data = stream.getData (); + if ((data[1] & 0xE0) == 0xE0 && (data[3] & 0xF0) == 0xF0) { + bool prvData = false, usrData = false; + bool prvOTV = false, prvFRV = false; + int usrOTV = 0, usrFRV = 0; + if (data[2]==0x39) {//TODO Test This + prvFRV = true; + usrFRV = 1; + LogD(1, prep("if (data[2]==0x39) {//TODO Test This")); + } + //Format = 0; // 0 = premiere, 1 = MHW1, 2 = MHW2, 3 = Sky Italy (OpenTV), 4 = Sky UK (OpenTV), 5 = Freesat (Freeview), 6 = Nagraguide + SI::Descriptor * d; + for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext (it));) { + LogD(2, prep("EEPGDEBUG:d->getDescriptorTAG():%x,SI::PrivateTag:%x\n"), d->getDescriptorTag (), SI::PrivateDataSpecifierDescriptorTag); + switch (d->getDescriptorTag ()) { + case SI::PrivateDataSpecifierDescriptorTag: + //esyslog ("prv: %d %08x\n", d->getLength (), d->getData ().FourBytes (2)); + if (d->getLength () == 6 && d->getData ().FourBytes (2) == 0x000000be) + prvData = true; + if (d->getLength () == 6 && d->getData ().FourBytes (2) == 0x4f545600) //OpenTV + prvOTV = true; + if (d->getLength () == 6 && d->getData ().FourBytes (2) == 0x46534154) //Freeview + prvFRV = true; + break; + case 0x52: + //if (d->getLength () == 3 && d->getData ().FourBytes (2) == 0xb07ea882) { + if (d->getLength () == 3 && ((d->getData ().TwoBytes (2) & 0xff00) == 0xb000)) + UnprocessedFormat[NAGRA] = stream.getPid (); + break; + case 0x90: + //esyslog ("usr: %d %08x\n", d->getLength (), d->getData ().FourBytes (2)); + if (d->getLength () == 6 && d->getData ().FourBytes (2) == 0x0000ffff) + usrData = true; + if (d->getLength () == 3 && ((d->getData ().TwoBytes (2) & 0xff00) == 0xb600)) //SKY IT //TODO ugly! + //if (d->getLength () == 3 && (d->getData ().TwoBytes (2) == 0xb6a5)) //SKY IT //TODO ugly! + usrOTV = SKY_IT; + //Format = SKY_IT; + if (d->getLength () == 3 && d->getData ().FourBytes (2) == 0xc004e288) //SKY UK + usrOTV = SKY_UK; + //Format = SKY_UK; + break; + case 0xc1: //MHW1, MHW2 +// esyslog("EEPGDEBUG:d->getDescriptorTAG:%d %08x\n",d->getLength(),d->getData().FourBytes(2)); + if (d->getLength () == 10 && d->getData ().FourBytes (2) == 0x50555348) //MHw1 Cyfra + UnprocessedFormat[MHW1] = stream.getPid (); + break; + case 0xc2: //MHW1, MHW2 + if (d->getLength () == 10 && d->getData ().FourBytes (2) == 0x45504700) //MHw1 CanDigNL and CSat + UnprocessedFormat[MHW1] = stream.getPid (); + else if (d->getLength () == 10 && d->getData ().FourBytes (2) == 0x46494348) { //MHW2 + UnprocessedFormat[MHW2] = stream.getPid (); + } + break; + case 0xd1: //Freeview + LogD(1, prep("case 0xd1: //Freeview")); + if (d->getLength () == 3 && ((d->getData ().TwoBytes (2) & 0xff00) == 0x0100)) + usrFRV = 0x01; + //01 = EIT pid 3842 + //03 04 = SDT Service Description Table pid 3841 + //07 = still undocumented, definition of buch of transport streams pid 3840 + //02 = ATSC reserved, find out what huffman encoded text is sent here! pid 3843 + //05 06 = TOT Time Offset Table pid 3844 + break; + + /* case 0xfe: //SKY_IT + if (d->getLength () == 6 && d->getData ().FourBytes (2) == 0x534b5900) { //SKY_IT + Format = SKY_IT; + } + break;*/ + default: + break; + } + delete d; + } + if ((prvOTV) && ((usrOTV == SKY_IT) || (usrOTV == SKY_UK))) + UnprocessedFormat[usrOTV] = stream.getPid (); + else if (prvFRV) + if (usrFRV == 0x01) + UnprocessedFormat[FREEVIEW] = stream.getPid (); + if (prvData && usrData) + UnprocessedFormat[PREMIERE] = stream.getPid (); + //TODO DPE this is not good since the DISH/BEV filters are always on, but have to test somehow. + //EEPG:12472:H:S119.0W:20000:0:0:0:0:36862:4100:18:36862 + if (((Source() == cSource::FromString("S119.0W") + && Transponder() == cChannel::Transponder(12472,'H')) + || (Source() == cSource::FromString("S91.0W") + && Transponder() == cChannel::Transponder(12224,'R'))) + && !UnprocessedFormat[DISH_BEV]) { + UnprocessedFormat[DISH_BEV] = stream.getPid (); + } + } //if data[1] && data [3] + } //if streamtype + /*if (Format != PREMIERE) //any format found + break; //break out for loop */ + } //for loop that walks through streams +// if (Format == PREMIERE) { //FIXME for Premiere you should also stop scanning when found... + NextPmt (); + pmtnext = 0; + /* } + else { + Del (pmtpid, 0x02); + Del (0, 0); + pmtidx = 0; + pmtnext = now + PMT_SCAN_IDLE; + isyslog ("PMT scan forced idle\n"); + }*/ + } //checkCRC + } //if pmtpid + else if (Source ()) { + + if ( Pid == 0x12 || Pid == 0x0300 || Pid == 0x0441 ) { + if (Tid >= 0x4E) + ProccessContinuous(Pid, Tid, Length, Data); + return; + } + int Result; + switch (Tid) { + case 0xA0: //TODO DPE test this missing break but it seems a bug + if ((Pid < 0x30) || (Pid > 0x37)) { + SI::PremiereCIT cit (Data, false); + if (cit.CheckCRCAndParse ()) { + cSchedulesLock SchedulesLock (true, 10); + cSchedules *Schedules = (cSchedules *) cSchedules::Schedules (SchedulesLock); + if (Schedules) { + int nCount = 0; + int nRating = 0; + SI::ExtendedEventDescriptors * ExtendedEventDescriptors = 0; + SI::ShortEventDescriptor * ShortEventDescriptor = 0; + char *order = 0, *rating = 0; + { + time_t firstTime = 0; + SI::Descriptor * d; + bool UseExtendedEventDescriptor = false; + int LanguagePreferenceShort = -1; + int LanguagePreferenceExt = -1; + for (SI::Loop::Iterator it; (d = cit.eventDescriptors.getNext (it));) { + switch (d->getDescriptorTag ()) { + case 0xF0: // order information + if (SetupPE.OrderInfo) { + static const char *text[] = { + trNOOP ("Ordernumber"), + trNOOP ("Price"), + trNOOP ("Ordering"), + trNOOP ("SMS"), + trNOOP ("WWW") + }; + char buff[512]; + int p = 0; + const unsigned char *data = d->getData ().getData () + 2; + for (int i = 0; i < 5; i++) { + int l = data[0]; + if (l > 0) + p += snprintf (&buff[p], sizeof (buff) - p, "\n%s: %.*s", tr (text[i]), l, &data[1]); + data += l + 1; + } + if (p > 0) + order = strdup (buff); + } + break; + case 0xF1: // parental rating + if (SetupPE.RatingInfo) { + char buff[512]; + int p = 0; + const unsigned char *data = d->getData ().getData () + 2; + nRating = data[0] + 3; + p += snprintf (&buff[p], sizeof (buff) - p, "\n%s: %d %s", tr ("Rating"), nRating, tr ("years")); + data += 7; + int l = data[0]; + if (l > 0) + p += snprintf (&buff[p], sizeof (buff) - p, " (%.*s)", l, &data[1]); + if (p > 0) + rating = strdup (buff); + } + break; + case SI::PremiereContentTransmissionDescriptorTag: + if (nCount >= 0) { + SI::PremiereContentTransmissionDescriptor * pct = (SI::PremiereContentTransmissionDescriptor *) d; + nCount++; + SI::PremiereContentTransmissionDescriptor::StartDayEntry sd; + SI::Loop::Iterator it; + if (pct->startDayLoop.getNext (sd, it)) { + SI::PremiereContentTransmissionDescriptor::StartDayEntry::StartTimeEntry st; + SI::Loop::Iterator it2; + if (sd.startTimeLoop.getNext (st, it2)) { + time_t StartTime = st.getStartTime (sd.getMJD ()); + if (nCount == 1) + firstTime = StartTime; + else if (firstTime < StartTime - 5 * 50 || firstTime > StartTime + 5 * 60) + nCount = -1; + } + } + } + break; + 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; + default: + break; + } + delete d; + } + } + + { + bool Modified = false; + int optCount = 0; + unsigned int crc[3]; + crc[0] = cit.getContentId (); + SI::PremiereContentTransmissionDescriptor * pct; + for (SI::Loop::Iterator it; + (pct = + (SI::PremiereContentTransmissionDescriptor *) cit.eventDescriptors.getNext (it, + SI:: + PremiereContentTransmissionDescriptorTag));) { + int nid = pct->getOriginalNetworkId (); + int tid = pct->getTransportStreamId (); + int sid = pct->getServiceId (); + if (SetupPE.FixEpg) { + if (nid == 133) { + if (tid == 0x03 && sid == 0xf0) { + tid = 0x02; + sid = 0xe0; + } else if (tid == 0x03 && sid == 0xf1) { + tid = 0x02; + sid = 0xe1; + } else if (tid == 0x03 && sid == 0xf5) { + tid = 0x03; + sid = 0xdc; + } else if (tid == 0x04 && sid == 0xd2) { + tid = 0x11; + sid = 0xe2; + } else if (tid == 0x11 && sid == 0xd3) { + tid = 0x11; + sid = 0xe3; + } + } + } + tChannelID channelID (Source (), nid, tid, sid); + cChannel *channel = Channels.GetByChannelID (channelID, true); +#ifdef USE_NOEPG + // only use epg from channels not blocked by noEPG-patch + if (!allowedEPG (channelID)) + continue; +#endif /* NOEPG */ + + if (!channel) + continue; + + cSchedule *pSchedule = (cSchedule *) Schedules->GetSchedule (channelID); + if (!pSchedule) { + pSchedule = new cSchedule (channelID); + Schedules->Add (pSchedule); + } + + optCount++; + SI::PremiereContentTransmissionDescriptor::StartDayEntry sd; + int index = 0; + for (SI::Loop::Iterator it; pct->startDayLoop.getNext (sd, it);) { + int mjd = sd.getMJD (); + SI::PremiereContentTransmissionDescriptor::StartDayEntry::StartTimeEntry st; + for (SI::Loop::Iterator it2; sd.startTimeLoop.getNext (st, it2);) { + time_t StartTime = st.getStartTime (mjd); + time_t EndTime = StartTime + cit.getDuration (); + int runningStatus = (StartTime < now + && now < EndTime) ? SI::RunningStatusRunning : ((StartTime - 30 < now + && now < + StartTime) ? SI:: + RunningStatusStartsInAFewSeconds + : SI::RunningStatusNotRunning); + bool isOpt = false; + if (index++ == 0 && nCount > 1) + isOpt = true; + crc[1] = isOpt ? optCount : 0; + crc[2] = StartTime / STARTTIME_BIAS; + tEventID EventId = ((('P' << 8) | 'W') << 16) | crc16 (0, (unsigned char *) crc, sizeof (crc)); + LogI(2, "%s R%d %04x/%.4x %d %d/%d %s +%d ", *channelID.ToString (), runningStatus, + EventId & 0xFFFF, cit.getContentId (), index, isOpt, optCount, + stripspace (ctime (&StartTime)), (int) cit.getDuration () / 60); + if (EndTime + Setup.EPGLinger * 60 < now) { + LogI(2, "(old)\n"); + continue; + } + + bool newEvent = false; + cEvent *pEvent = (cEvent *) pSchedule->GetEvent (EventId, -1); + if (!pEvent) { + LogI(2, "(new)\n"); + pEvent = new cEvent (EventId); + if (!pEvent) + continue; + newEvent = true; + } else { + LogI(2, "(upd)\n"); + pEvent->SetSeen (); + if (pEvent->TableID () == 0x00 || pEvent->Version () == cit.getVersionNumber ()) { + if (pEvent->RunningStatus () != runningStatus) + pSchedule->SetRunningStatus (pEvent, runningStatus, channel); + continue; + } + } + pEvent->SetEventID (EventId); + pEvent->SetTableID (Tid); + pEvent->SetVersion (cit.getVersionNumber ()); + pEvent->SetStartTime (StartTime); + pEvent->SetDuration (cit.getDuration ()); + + if (ShortEventDescriptor) { + char buffer[256]; + ShortEventDescriptor->name.getText (buffer, sizeof (buffer)); + if (isOpt) { + char buffer2[sizeof (buffer) + 32]; + snprintf (buffer2, sizeof (buffer2), optPats[SetupPE.OptPat], buffer, optCount); + pEvent->SetTitle (buffer2); + } else + pEvent->SetTitle (buffer); + LogI(2, "title: %s\n", pEvent->Title ()); + pEvent->SetShortText (ShortEventDescriptor->text.getText (buffer, sizeof (buffer))); + } + if (ExtendedEventDescriptors) { + char buffer[ExtendedEventDescriptors->getMaximumTextLength (": ") + 1]; + pEvent->SetDescription (ExtendedEventDescriptors->getText (buffer, sizeof (buffer), ": ")); + } + if (order || rating) { + int len = (pEvent->Description ()? strlen (pEvent->Description ()) : 0) + + (order ? strlen (order) : 0) + (rating ? strlen (rating) : 0); + char buffer[len + 32]; + buffer[0] = 0; + if (pEvent->Description ()) + strcat (buffer, pEvent->Description ()); + if (rating) { + strcat (buffer, rating); + pEvent->SetParentalRating(nRating); + } + if (order) + strcat (buffer, order); + pEvent->SetDescription (buffer); + } + + if (newEvent) + pSchedule->AddEvent (pEvent); + + pEvent->FixEpgBugs (); + if (pEvent->RunningStatus () != runningStatus) + pSchedule->SetRunningStatus (pEvent, runningStatus, channel); + pSchedule->DropOutdated (StartTime, EndTime, Tid, cit.getVersionNumber ()); + Modified = true; + } + } + if (Modified) { + pSchedule->Sort (); + Schedules->SetModified (pSchedule); + } + delete pct; + } + } + delete ExtendedEventDescriptors; + delete ShortEventDescriptor; + free (order); + free (rating); + } + } //if checkcrcandpars + break; + } //if citpid == 0xb11 Premiere + case 0xa1: + case 0xa2: + case 0xa3: + Result = GetTitlesSKYBOX (Data, Length - 4); + if (Result != 1) //when fatal error or finished + Del (Pid, 0xa0, 0xfc); //kill filter + if (Result == 0) { //fatal error + esyslog ("EEPG: Fatal error reading titles."); + ProcessNextFormat (); //and go process other formats + } + if (Result == 2) + AddFilter (Pid + 0x10, 0xa8, 0xfc); //Set filter that processes summaries of this batch + break; + case 0xa8: + case 0xa9: + case 0xaa: + case 0xab: + Result = GetSummariesSKYBOX (Data, Length - 4); + if (Result != 1) //when fatal error or finished + Del (Pid, 0xa8, 0xfc); //kill filter + if (Result == 0) { + esyslog ("EEPG: Fatal error reading summaries."); + ProcessNextFormat (); + } + if (Result == 2) { + LoadIntoSchedule (); + if (Pid < 0x47) //this is not the last batch//FIXME chaining is easy on the PIDs and the CPU, but error when Pid,Tid is not used at the moment... + AddFilter (Pid - 0x0F, 0xa0, 0xfc); //next pid, first tid + else //last pid was processed + ProcessNextFormat (); + } + break; + case 0x90: + if (Pid == 0xd2) { + Result = GetTitlesMHW1 (Data, Length); + if (Result != 1) //fatal error or last processed + Del (Pid, Tid); + if (Result == 0) { + esyslog ("EEPG: Fatal error reading titles."); + ProcessNextFormat (); + } + if (Result == 2) + AddFilter (0xd3, 0x90); //SummariesMHW1 + } else if (Pid == 0xd3) { + Result = GetSummariesMHW1 (Data, Length); + if (Result != 1) //fatal error or last processed + Del (Pid, Tid); + if (Result == 0) { + esyslog ("EEPG: Fatal error reading summaries."); + } + if (Result == 2) { + LoadIntoSchedule (); + } + if (Result != 1) { + ProcessNextFormat (); + } + } + break; + case 0xc8: //GetChannelsMHW2 or GetThemesMHW2 + if (Pid == 0x231) { + if (Data[3] == 0x01) { //Themes it will be + Result = GetThemesMHW2 (Data, Length); //return code 0 = fatal error, code 1 = sucess, code 2 = last item processed + //break; + if (Result != 1) + EndThemes = true; //also set Endthemes on true on fatal error + } //if Data + else if (Data[3] == 0x00) { //Channels it will be + Result = GetChannelsMHW (Data, Length, 2); //return code 0 = fatal error, code 1 = sucess, code 2 = last item processed + if (Result != 1) + EndChannels = true; //always remove filter, code 1 should never be returned since MHW2 always reads all channels.. + ChannelsOk = (Result == 2); + } + if (EndChannels && EndThemes) { //those are only set withing MHW2 + Del (0x231, 0xc8); //stop reading MHW2 themes and channels + if (ChannelsOk) //No channels = fatal, no themes = nonfatal + AddFilter (0x234, 0xe6); //start reading MHW2 titles + else { + esyslog ("EEPG: Fatal error reading channels."); + ProcessNextFormat (); + } + } + } //if Pid == 0x231 + break; + case 0x91: + Result = GetChannelsMHW (Data, Length, 1); //return code 0 = fatal error, code 1 = sucess, code 2 = last item processed + Del (Pid, Tid); //always remove filter, code 1 should never be returned since MHW1 always reads all channels... + if (Result == 2) + AddFilter (0xd2, 0x90); //TitlesMHW1 + else { + esyslog ("EEPG: Fatal error reading channels."); + ProcessNextFormat (); + } + break; + case 0x92: + Result = GetThemesMHW1 (Data, Length); //return code 0 = fatal error, code 1 = sucess, code 2 = last item processed + if (Result != 1) + Del (Pid, Tid); + if (Result == 2) + AddFilter (0xd3, 0x91); //ChannelsMHW1 + else { + esyslog ("EEPG: Fatal error reading themes."); //doesnt have to be fatal... + ProcessNextFormat (); + } + break; + case 0xe6: //TitlesMHW2 + if (Pid == 0x234) { + Result = GetTitlesMHW2 (Data, Length); //return code 0 = fatal error, code 1 = sucess, code 2 = last item processed + if (Result != 1) + Del (Pid, Tid); + if (Result == 0) { + esyslog ("EEPG: Fatal error reading titles."); + ProcessNextFormat (); + } + if (Result == 2) + AddFilter (0x236, 0x96); //start reading MHW2 summaries.... + } + break; + case 0x96: //Summaries MHW2 + if (Pid == 0x236) { + Result = GetSummariesMHW2 (Data, Length); //return code 0 = fatal error, code 1 = sucess, code 2 = last item processed + if (Result != 1) + Del (Pid, Tid); + if (Result == 0) { + esyslog ("EEPG: Fatal error reading summaries."); + } + if (Result == 2) + LoadIntoSchedule (); + if (Result != 1) { + ProcessNextFormat (); + } + } //if pid + break; + case 0x4a: //Sky channels + if (Pid == 0x11) { + Result = GetChannelsSKYBOX (Data, Length - 4); + if (Result != 1) //only breakoff on completion or error; do NOT clean up after success, because then not all bouquets will be read + Del (Pid, Tid); //Read all channels, clean up filter + if (Result == 2) { + GetThemesSKYBOX (); //Sky Themes from file; must be called AFTER first channels to have lThemes initialized FIXME + if (ReadFileDictionary ()) + AddFilter (0x30, 0xa0, 0xfc); //SKY Titles batch 0 of 7 + else { + esyslog ("EEPG: Fatal error reading huffman table."); + ProcessNextFormat (); + } + } + + } //if Pid == 0x11 + break; + case 0xb0: //NagraGuide + if (Pid == 0xc8) { + Result = GetNagra (Data, Length); + if (Result != 1) + Del (Pid, Tid); + if (Result == 0) { + esyslog ("EEPG: Fatal error processing NagraGuide. End of processing."); + } + if (Result == 2) { + ProcessNagra (); + cSchedules::Cleanup (true); //deletes all past events + isyslog ("EEPG: Ended processing Nagra"); + } + if (Result != 1) { + ProcessNextFormat (); + } + } + break; + + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + // Freesat: + // Set(3842, 0x4E, 0xFE); // event info, actual(0x4E)/other(0x4F) TS, present/following + // Set(3842, 0x50, 0xF0); // event info, actual TS, schedule(0x50)/schedule for future days(0x5X) + // Set(3842, 0x60, 0xF0); // event info, other TS, schedule(0x60)/schedule for future days(0x6X) + // + // PID found: 3841 (0x0f01) [SECTION: Service Description Table (SDT) - other transport stream] + // PID found: 3842 (0x0f02) [SECTION: Event Information Table (EIT) - other transport stream, schedule] + // PID found: 3843 (0x0f03) [SECTION: ATSC reserved] TODO find out what compressed text info is here! + // PID found: 3844 (0x0f04) [SECTION: Time Offset Table (TOT)] + + if (Pid == 3842 || Pid == 0x39) {//0x39 Viasat, 0x0300 Dish Network EEPG, 0x0441 Bell ExpressVU EEPG + ProccessContinuous(Pid, Tid, Length, Data); + } + break; + + default: + break; + } //end switch + } //closes SOURCE() +} //end of closing + +// --- cPluginEEPG ------------------------------------------------------ + +class cPluginEEPG:public cPlugin +{ +private: + struct { + cFilterEEPG *filter; + cDevice *device; + } epg[MAXDVBDEVICES]; + + void CheckCreateFile(const char* Name, const char *fileContent[]); + +public: + cPluginEEPG (void); + virtual const char *Version (void) { + return VERSION; + } + virtual const char *Description (void) { + return tr (DESCRIPTION); + } + virtual bool Start (void); + virtual void Stop (void); + virtual cMenuSetupPage *SetupMenu (void); + virtual bool SetupParse (const char *Name, const char *Value); +}; + +cPluginEEPG::cPluginEEPG (void) +{ + memset (epg, 0, sizeof (epg)); +} + +void cPluginEEPG::CheckCreateFile(const char* Name, const char *fileContent[]) +{ + FILE *File; + string FileName = string(ConfDir) + "/" + Name; + File = fopen(FileName.c_str(), "r"); + if (File == NULL) { + LogE (0, prep("Error opening file '%s', %s"), FileName.c_str(), strerror (errno)); + File = fopen (FileName.c_str(), "w"); + if (File == NULL) { + LogE (0, prep("Error creating file '%s', %s"), FileName.c_str(), strerror (errno)); + } else { + int i = 0; + while (fileContent[i] != NULL) { + fprintf (File, "%s\n", fileContent[i]); + i++; + } + LogI (0, prep("Success creating file '%s'"), FileName.c_str()); + fclose (File); + } + } else { + fclose (File); + } +} + +bool cPluginEEPG::Start (void) +{ +#if APIVERSNUM < 10507 + RegisterI18n (Phrases); +#endif + for (int i = 0; i < MAXDVBDEVICES; i++) { + cDevice *dev = cDevice::GetDevice (i); + if (dev) { + epg[i].device = dev; + dev->AttachFilter (epg[i].filter = new cFilterEEPG); + isyslog ("Attached EEPG filter to device %d", i); + } + } + ConfDir = NULL; + // Initialize any background activities the plugin shall perform. + DIR *ConfigDir; + if (ConfDir == NULL) { + Asprintf (&ConfDir, "%s/eepg", cPlugin::ConfigDirectory ()); + } + ConfigDir = opendir (ConfDir); + if (ConfigDir == NULL) { + esyslog ("EEPG: Error opening directory '%s', %s", ConfDir, strerror (errno)); + if (mkdir (ConfDir, 0777) < 0) { + esyslog ("EEPG: Error creating directory '%s', %s", ConfDir, strerror (errno)); + } else { + isyslog ("EEPG: Success creating directory '%s'", ConfDir); + } + } + CheckCreateFile(EEPG_FILE_EQUIV, FileEquivalences); + CheckCreateFile("sky_it.dict", SkyItDictionary); + CheckCreateFile("sky_uk.dict", SkyUkDictionary); + CheckCreateFile("sky_it.themes", SkyItThemes); + CheckCreateFile("sky_uk.themes", SkyUkThemes); + CheckCreateFile("freesat.t1", FreesatT1); + CheckCreateFile("freesat.t2", FreesatT2); + CheckCreateFile("sky_uk.themes", SkyUkThemes); + + //store all available sources, so when a channel is not found on current satellite, we can look for alternate sat positions. + //perhaps this can be done smarter through existing VDR function??? + for (cChannel * Channel = Channels.First (); Channel; Channel = Channels.Next (Channel)) { + if (!Channel->GroupSep ()) { + bool found = false; + for (int i = 0; (i < NumberOfAvailableSources) && (!found); i++) + found = (Channel->Source () == AvailableSources[i]); + if (!found) + AvailableSources[NumberOfAvailableSources++] = Channel->Source (); + } + } + if (CheckLevel(3)) + for (int i = 0; i < NumberOfAvailableSources; i++) + isyslog ("EEPG: Available sources:%s.", *cSource::ToString (AvailableSources[i])); + + + return true; +} + +void cPluginEEPG::Stop (void) +{ + for (int i = 0; i < MAXDVBDEVICES; i++) { + cDevice *dev = epg[i].device; + if (dev) + dev->Detach (epg[i].filter); + delete epg[i].filter; + epg[i].device = 0; + epg[i].filter = 0; + } + // Clean up after yourself! + if (ConfDir) { + free (ConfDir); + } +} + +cMenuSetupPage *cPluginEEPG::SetupMenu (void) +{ + return new cMenuSetupPremiereEpg; +} + +bool cPluginEEPG::SetupParse (const char *Name, const char *Value) +{ +// LogF(0, "!!!! Dime test LogF"); +// LogF(0, "!!!! Dime test LogF %d", 2); +// LogI(0, "!!!! Dime test LogI"); +// LogI(0, "!!!! Dime test LogI %d", 2); +// LogI(0, prep2("!!!! Dime test prep")); +// LogI(0, prep2("!!!! Dime test prep %d"), 2); +// LogD(0, "!!!! Dime test LogD"); +// LogD(0, "!!!! Dime test LogD %d", 2); +// LogE(0, "!!!! Dime test LogE"); +// LogE(0, "!!!! Dime test LogE %d", 2); + + + if (!strcasecmp (Name, "OptionPattern")) + SetupPE.OptPat = atoi (Value); + else if (!strcasecmp (Name, "OrderInfo")) + SetupPE.OrderInfo = atoi (Value); + else if (!strcasecmp (Name, "RatingInfo")) + SetupPE.RatingInfo = atoi (Value); + else if (!strcasecmp (Name, "FixEpg")) + SetupPE.FixEpg = atoi (Value); + else if (!strcasecmp (Name, "DisplayMessage")) + SetupPE.DisplayMessage = atoi (Value); +#ifdef DEBUG + else if (!strcasecmp (Name, "LogLevel")) + SetupPE.LogLevel = atoi (Value); +#endif + else + return false; + return true; +} + +VDRPLUGINCREATOR (cPluginEEPG); // Don't touch this! -- cgit v1.2.3 From 729dfd3b736f3435129b318657765af0057636d1 Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Thu, 27 Oct 2011 21:38:46 +0200 Subject: corect episode and time info --- eepg.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 0c75106..ce44960 100644 --- a/eepg.c +++ b/eepg.c @@ -3218,12 +3218,16 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat } break; case SI::DishSeriesDescriptorTag: { - if (d->getLength() == 4) { + 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: @@ -3281,7 +3285,9 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat free(tmp); fmt = "%s"; - if (0 != strcmp(DishEventDescriptor->getDescription(),"") && (0 != strcmp(DishEventDescriptor->getRating(),"") || 0 != strcmp(DishEventDescriptor->getStarRating(),""))) { + if (0 != strcmp(DishEventDescriptor->getDescription(),"") + && (0 != strcmp(DishEventDescriptor->getRating(),"") + || 0 != strcmp(DishEventDescriptor->getStarRating(),""))) { fmt += "\nRating: "; } fmt += "%s %s"; @@ -3290,13 +3296,17 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat } fmt += "%s %s"; time_t orgAirDate = DishEventDescriptor->getOriginalAirDate(); - fmt += orgAirDate == 0 ? "%s" : " Original Air Date: "; + fmt += orgAirDate == 0 ? "%s" : " Original Air Date: %s"; + LogD(3, prep("DishSeriesDescriptorTag2: %s %s %d %s)"), DishEventDescriptor->getProgramId() + , DishEventDescriptor->getSeriesId() + , orgAirDate + , orgAirDate == 0 ? "" : asctime(localtime(&orgAirDate))); Asprintf (&tmp, fmt.c_str(), DishEventDescriptor->getDescription() , DishEventDescriptor->getRating() , DishEventDescriptor->getStarRating() , DishEventDescriptor->getProgramId() , DishEventDescriptor->getSeriesId() - , DishEventDescriptor->getOriginalAirDate() == 0 ? "" : ctime (&orgAirDate)); + , orgAirDate == 0 ? "" : asctime(localtime(&orgAirDate))); pEvent->SetDescription(tmp); free(tmp); -- cgit v1.2.3 From 7ae1e640e55eea7d0d8d18ef49fe71389a05f1df Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Thu, 27 Oct 2011 22:06:04 +0200 Subject: resolve conflicts --- eepg.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 87d50a5..ce44960 100644 --- a/eepg.c +++ b/eepg.c @@ -3306,9 +3306,8 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat , DishEventDescriptor->getStarRating() , DishEventDescriptor->getProgramId() , DishEventDescriptor->getSeriesId() -<<<<<<< .mine , orgAirDate == 0 ? "" : asctime(localtime(&orgAirDate))); -======= , orgAirDate == 0 ? "" : asctime(localtime(&orgAirDate)) (&orgAirDate)); ->>>>>>> .theirs pEvent->SetDescription(tmp); + , orgAirDate == 0 ? "" : asctime(localtime(&orgAirDate))); + pEvent->SetDescription(tmp); free(tmp); @@ -4374,4 +4373,4 @@ bool cPluginEEPG::SetupParse (const char *Name, const char *Value) return true; } -VDRPLUGINCREATOR (cPluginEEPG); // Don't touch this! \ No newline at end of file +VDRPLUGINCREATOR (cPluginEEPG); // Don't touch this! -- cgit v1.2.3 From 027cb56529ddf9f1788538117687c17b763d4e2a Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Fri, 28 Oct 2011 08:56:02 +0200 Subject: change formating --- eepg.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index ce44960..84695cb 100644 --- a/eepg.c +++ b/eepg.c @@ -3288,25 +3288,25 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat if (0 != strcmp(DishEventDescriptor->getDescription(),"") && (0 != strcmp(DishEventDescriptor->getRating(),"") || 0 != strcmp(DishEventDescriptor->getStarRating(),""))) { - fmt += "\nRating: "; + fmt += "\n\nRating: "; } fmt += "%s %s"; if (0 != strcmp(DishEventDescriptor->getProgramId(),"")) { - fmt += "\n Program ID: "; + fmt += "\n\nProgram ID: "; } - fmt += "%s %s"; + fmt += "%s %s%s"; time_t orgAirDate = DishEventDescriptor->getOriginalAirDate(); - fmt += orgAirDate == 0 ? "%s" : " Original Air Date: %s"; - LogD(3, prep("DishSeriesDescriptorTag2: %s %s %d %s)"), DishEventDescriptor->getProgramId() - , DishEventDescriptor->getSeriesId() - , orgAirDate - , orgAirDate == 0 ? "" : asctime(localtime(&orgAirDate))); + char datestr [80]; + if (orgAirDate == 0 || strftime (datestr,80," Original Air Date: %a %b %d %Y",gmtime(&orgAirDate)) == 0) { + datestr = ""; + } + Asprintf (&tmp, fmt.c_str(), DishEventDescriptor->getDescription() , DishEventDescriptor->getRating() , DishEventDescriptor->getStarRating() , DishEventDescriptor->getProgramId() , DishEventDescriptor->getSeriesId() - , orgAirDate == 0 ? "" : asctime(localtime(&orgAirDate))); + , orgAirDate == 0 ? "" : datestr); pEvent->SetDescription(tmp); free(tmp); -- cgit v1.2.3 From 726d99a3ed5c12242e0e3c6416b3175c18dbedfd Mon Sep 17 00:00:00 2001 From: Dimitar Petrovski Date: Fri, 28 Oct 2011 09:02:08 +0200 Subject: compile fix --- eepg.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'eepg.c') diff --git a/eepg.c b/eepg.c index 84695cb..6ee1c62 100644 --- a/eepg.c +++ b/eepg.c @@ -3297,8 +3297,9 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat fmt += "%s %s%s"; time_t orgAirDate = DishEventDescriptor->getOriginalAirDate(); char datestr [80]; - if (orgAirDate == 0 || strftime (datestr,80," Original Air Date: %a %b %d %Y",gmtime(&orgAirDate)) == 0) { - datestr = ""; + 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(), DishEventDescriptor->getDescription() @@ -3306,7 +3307,7 @@ cEIT2::cEIT2 (cSchedules * Schedules, int Source, u_char Tid, const u_char * Dat , DishEventDescriptor->getStarRating() , DishEventDescriptor->getProgramId() , DishEventDescriptor->getSeriesId() - , orgAirDate == 0 ? "" : datestr); + , orgAirDate == 0 || !dateok ? "" : datestr); pEvent->SetDescription(tmp); free(tmp); -- cgit v1.2.3