# HG changeset patch # User nathan # Date 1198923589 -3600 # Node ID 967afc97e51d39b50e0326582a10472696addd65 # Parent 23e350c6b6d9f1bd2a800b5658c8a901974057ee release 0.0.6 diff -r 23e350c6b6d9 -r 967afc97e51d HISTORY --- a/HISTORY Sat Dec 29 11:19:37 2007 +0100 +++ b/HISTORY Sat Dec 29 11:19:49 2007 +0100 @@ -1,6 +1,15 @@ VDR Plugin 'premiereepg' Revision History ----------------------------------------- +05.06.2006: Version 0.0.6 +- Fixed and improved generation of EventId. You should delete your epg.data file + to avoid duplicate entries. +- Now using libsi code introduced in VDR 1.3.47 and fixed in 1.4.0-3. + Obviously requires VDR with API version 1.4.1 or later. +- Checking for broken PMT entries. +- Setting event running status by looking at the start & end time. +- Updated Makefile according to changes in VDR 1.3.47 (APIVERSION & DVBDIR). + 21.03.2006: Version 0.0.5 - Fixed compiling for VDR < 1.3.18. - Fixed compiling for VDR >= 1.3.43. diff -r 23e350c6b6d9 -r 967afc97e51d Makefile --- a/Makefile Sat Dec 29 11:19:37 2007 +0100 +++ b/Makefile Sat Dec 29 11:19:49 2007 +0100 @@ -1,7 +1,7 @@ # # PremiereEpg plugin to VDR # -# (C) 2005 Stefan Huelswitt +# (C) 2005-2006 Stefan Huelswitt # # This code is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -35,7 +35,6 @@ ### The directory environment: -DVBDIR = ../../../../DVB VDRDIR = ../../.. LIBDIR = ../../lib TMPDIR = /tmp @@ -46,7 +45,11 @@ ### The version number of VDR (taken from VDR's "config.h"): -VDRVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g') +VDRVERSION = $(shell sed -ne '/define VDRVERSION/ s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h) +APIVERSION = $(shell sed -ne '/define APIVERSION/ s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h) +ifeq ($(strip $(APIVERSION)),) + APIVERSION = $(VDRVERSION) +endif ### The name of the distribution archive: @@ -55,7 +58,7 @@ ### Includes and Defines (add further entries here): -INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include +INCLUDES += -I$(VDRDIR)/include DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' @@ -87,7 +90,7 @@ libvdr-$(PLUGIN).so: $(OBJS) $(CXX) $(CXXFLAGS) -shared $(OBJS) -o $@ - @cp $@ $(LIBDIR)/$@.$(VDRVERSION) + @cp $@ $(LIBDIR)/$@.$(APIVERSION) dist: clean @-rm -rf $(TMPDIR)/$(ARCHIVE) diff -r 23e350c6b6d9 -r 967afc97e51d premiereepg.c --- a/premiereepg.c Sat Dec 29 11:19:37 2007 +0100 +++ b/premiereepg.c Sat Dec 29 11:19:49 2007 +0100 @@ -1,7 +1,7 @@ /* * PremiereEpg plugin to VDR (C++) * - * (C) 2005 Stefan Huelswitt + * (C) 2005-2006 Stefan Huelswitt * * This code is base on the commandline tool premiereepg2vdr * (C) 2004-2005 by Axel Katzur software@katzur.de @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -49,9 +50,13 @@ #define PMT_SCAN_TIMEOUT 10 // seconds #define PMT_SCAN_IDLE 300 // seconds -static const char *VERSION = "0.0.5"; +static const char *VERSION = "0.0.6"; static const char *DESCRIPTION = "Parses extended Premiere EPG data"; +#if APIVERSNUM < 10401 +#error You need at least VDR API version 1.4.1 for this plugin +#endif + // --- cSetupPremiereEpg ------------------------------------------------------- const char *optPats[] = { @@ -330,140 +335,27 @@ SetupStore("RatingInfo",SetupPE.RatingInfo); } -// --- CIT --------------------------------------------------------------------- - -namespace SI { - -#define CIT_LEN 17 - -struct cit { - u_char table_id :8; -#if BYTE_ORDER == BIG_ENDIAN - u_char section_syntax_indicator :1; - u_char :3; - u_char section_length_hi :4; -#else - u_char section_length_hi :4; - u_char :3; - u_char section_syntax_indicator :1; -#endif - u_char section_length_lo :8; - u_char service_id_hi :8; - u_char service_id_lo :8; -#if BYTE_ORDER == BIG_ENDIAN - u_char :2; - u_char version_number :5; - u_char current_next_indicator :1; -#else - u_char current_next_indicator :1; - u_char version_number :5; - u_char :2; -#endif - u_char section_number :8; - u_char last_section_number :8; - u_char content_id_hi_hi :8; - u_char content_id_hi_lo :8; - u_char content_id_lo_hi :8; - u_char content_id_lo_lo :8; - u_char duration_h :8; - u_char duration_m :8; - u_char duration_s :8; -#if BYTE_ORDER == BIG_ENDIAN - u_char :4; - u_char descriptors_loop_length_hi :4; -#else - u_char descriptors_loop_length_hi :4; - u_char :4; -#endif - u_char descriptors_loop_length_lo :8; -}; - -class CIT : public NumberedSection { -public: - CIT(const unsigned char *data, bool doCopy=true) : NumberedSection(data, doCopy) {} - CIT() {} - int getContentId(void) const; - time_t getDuration(void) const; - DescriptorLoop eventDescriptors; -protected: - virtual void Parse(void); -private: - const cit *s; -}; - -int CIT::getContentId(void) const { - return (HILO(s->content_id_hi)<<16) | (HILO(s->content_id_lo)); -} - -time_t CIT::getDuration(void) const { - return DVBTime::getDuration(s->duration_h,s->duration_m,s->duration_s); -} - -void CIT::Parse(void) { -#if VDRVERSNUM >= 10343 - int offset=0; -#else - unsigned int offset=0; -#endif - data.setPointerAndOffset(s, offset); - eventDescriptors.setData(data+offset,HILO(s->descriptors_loop_length)); -} - -} // end of namespace - -// --- cDescrF2 ---------------------------------------------------------------- - -class cDescrF2 { -private: - SI::Descriptor *d; - SI::CharArray data; - int idx, loop, nloop, index; -public: - cDescrF2(SI::Descriptor *D); - int TransportStreamId(void) { return data.TwoBytes(2); } - int OrgNetworkId(void) { return data.TwoBytes(4); } - int ServiceId(void) { return data.TwoBytes(6); } - void Start(void); - bool Next(void); - time_t StartTime(void); - int Index(void) { return index; } - }; - -cDescrF2::cDescrF2(SI::Descriptor *D) -{ - d=D; - data=d->getData(); - Start(); -} - -void cDescrF2::Start(void) -{ - idx=8; loop=0; nloop=-3; index=-1; -} - -bool cDescrF2::Next(void) -{ - loop+=3; - if(loop>=nloop) { - idx+=nloop+3; - if(idx>=d->getLength()) return false; - loop=0; nloop=data[idx+2]; +// --- 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); } - index++; - return true; -} - -time_t cDescrF2::StartTime(void) -{ - int off=idx+3+loop; - return SI::DVBTime::getTime(data[idx+0],data[idx+1],data[off+0],data[off+1],data[off+2]); + return crc&0xFFFF; } // --- cFilterPremiereEpg ------------------------------------------------------ +#define STARTTIME_BIAS (20*60) + class cFilterPremiereEpg : public cFilter { private: - int pmtpid, pmtidx, pmtnext; + int pmtpid, pmtsid, pmtidx, pmtnext; // void NextPmt(void); protected: @@ -503,8 +395,8 @@ void cFilterPremiereEpg::Process(u_short Pid, u_char Tid, const u_char *Data, int Length) { + int now=time(0); if(Pid==0 && Tid==SI::TableIdPAT) { - int now=time(0); if(!pmtnext || now>pmtnext) { if(pmtpid) NextPmt(); if(!pmtpid) { @@ -516,6 +408,7 @@ if(!assoc.isNITPid()) { if(idx++==pmtidx) { pmtpid=assoc.getPid(); + pmtsid=assoc.getServiceId(); Add(pmtpid,0x02); pmtnext=now+PMT_SCAN_TIMEOUT; d(printf("PMT pid now 0x%04x (idx=%d)\n",pmtpid,pmtidx)) @@ -534,7 +427,7 @@ } else if(pmtpid>0 && Pid==pmtpid && Tid==SI::TableIdPMT && Source() && Transponder()) { SI::PMT pmt(Data,false); - if(pmt.CheckCRCAndParse()) { + if(pmt.CheckCRCAndParse() && pmt.getServiceId()==pmtsid) { SI::PMT::Stream stream; for(SI::Loop::Iterator it; pmt.streamLoop.getNext(stream,it); ) { if(stream.getStreamType()==0x05) { @@ -575,20 +468,21 @@ } } else if(Tid==0xA0 && Source()) { - SI::CIT cit(Data,false); + SI::PremiereCIT cit(Data,false); if(cit.CheckCRCAndParse()) { cSchedulesLock SchedulesLock(true,10); cSchedules *Schedules=(cSchedules *)cSchedules::Schedules(SchedulesLock); if(Schedules) { int nCount=0; - time_t firstTime=0; - SI::Descriptor *d; - int LanguagePreferenceShort=-1; - int LanguagePreferenceExt=-1; - bool UseExtendedEventDescriptor=false; 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 @@ -623,15 +517,19 @@ if(p>0) rating=strdup(buff); } break; - case 0xF2: // transmisions + case SI::PremiereContentTransmissionDescriptorTag: if(nCount>=0) { + SI::PremiereContentTransmissionDescriptor *pct=(SI::PremiereContentTransmissionDescriptor *)d; nCount++; - cDescrF2 f2(d); - if(f2.Next()) { - if(nCount==1) firstTime=f2.StartTime(); - else { - time_t time=f2.StartTime(); - if(firstTimetime+5*60) + 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(firstTimeStartTime+5*60) nCount=-1; } } @@ -640,11 +538,7 @@ case SI::ExtendedEventDescriptorTag: { SI::ExtendedEventDescriptor *eed=(SI::ExtendedEventDescriptor *)d; -#if VDRVERSNUM < 10332 - if(I18nIsPreferredLanguage(Setup.EPGLanguages,I18nLanguageIndex(eed->languageCode), LanguagePreferenceExt) || !ExtendedEventDescriptors) { -#else if(I18nIsPreferredLanguage(Setup.EPGLanguages,eed->languageCode, LanguagePreferenceExt) || !ExtendedEventDescriptors) { -#endif delete ExtendedEventDescriptors; ExtendedEventDescriptors=new SI::ExtendedEventDescriptors; UseExtendedEventDescriptor=true; @@ -660,11 +554,7 @@ case SI::ShortEventDescriptorTag: { SI::ShortEventDescriptor *sed=(SI::ShortEventDescriptor *)d; -#if VDRVERSNUM < 10332 - if(I18nIsPreferredLanguage(Setup.EPGLanguages,I18nLanguageIndex(sed->languageCode), LanguagePreferenceShort) || !ShortEventDescriptor) { -#else if(I18nIsPreferredLanguage(Setup.EPGLanguages,sed->languageCode, LanguagePreferenceShort) || !ShortEventDescriptor) { -#endif delete ShortEventDescriptor; ShortEventDescriptor=sed; d=NULL; // so that it is not deleted @@ -676,55 +566,64 @@ } delete d; } - + } + + { bool Modified=false; int optCount=0; - for(SI::Loop::Iterator it; (d=cit.eventDescriptors.getNext(it)); ) { - if(d->getDescriptorTag()==0xF2) { - optCount++; - - cDescrF2 f2(d); - tChannelID channelID(Source(),f2.OrgNetworkId(),f2.TransportStreamId(),f2.ServiceId()); - cChannel *channel=Channels.GetByChannelID(channelID,true); - if(!channel) continue; - - cSchedule *pSchedule=(cSchedule *)Schedules->GetSchedule(channelID); - if(!pSchedule) { - pSchedule=new cSchedule(channelID); - Schedules->Add(pSchedule); - } - - for(f2.Start(); f2.Next();) { - u_int16_t EventId=(cit.getContentId()<<4) | f2.Index(); - time_t StartTime=f2.StartTime(); - + 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)); ) { + tChannelID channelID(Source(),pct->getOriginalNetworkId(),pct->getTransportStreamId(),pct->getServiceId()); + cChannel *channel=Channels.GetByChannelID(channelID,true); + 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=(StartTime1) isOpt=true; - - d2(printf("ch=%s id=%04x/%08x %d opt=%d/%d st=%s ",*channelID.ToString(),EventId,cit.getContentId(),f2.Index(),isOpt,optCount,stripspace(ctime(&StartTime)))) - if(StartTime+cit.getDuration()+Setup.EPGLinger*601) 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)); + + d2(printf("%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*60GetEvent(EventId,StartTime); + cEvent *pEvent=(cEvent *)pSchedule->GetEvent(EventId,-1); if(!pEvent) { - d2(printf("(new)\n")) -#if VDRVERSNUM >= 10325 - pEvent=new cEvent(EventId); -#else - pEvent=new cEvent(channelID,EventId); -#endif - if(!pEvent) continue; - newEvent=true; - } + d2(printf("(new)\n")) + pEvent=new cEvent(EventId); + if(!pEvent) continue; + newEvent=true; + } else { - d2(printf("(upd)\n")) - pEvent->SetSeen(); - if(pEvent->TableID()==0x00) continue; - if(Tid==pEvent->TableID() && pEvent->Version()==cit.getVersionNumber()) continue; - } + d2(printf("(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()); @@ -741,6 +640,7 @@ } else pEvent->SetTitle(buffer); + d2(printf("title: %s\n",pEvent->Title())) pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer,sizeof(buffer))); } if(ExtendedEventDescriptors) { @@ -760,19 +660,21 @@ } if(newEvent) pSchedule->AddEvent(pEvent); -#if VDRVERSNUM >= 10318 pEvent->SetComponents(NULL); -#endif 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 d; + if(Modified) { + pSchedule->Sort(); + Schedules->SetModified(pSchedule); + } + delete pct; } + } delete ExtendedEventDescriptors; delete ShortEventDescriptor; free(order);