1.1 --- a/HISTORY Sat Dec 29 11:19:37 2007 +0100
1.2 +++ b/HISTORY Sat Dec 29 11:19:49 2007 +0100
1.3 @@ -1,6 +1,15 @@
1.4 VDR Plugin 'premiereepg' Revision History
1.5 -----------------------------------------
1.6
1.7 +05.06.2006: Version 0.0.6
1.8 +- Fixed and improved generation of EventId. You should delete your epg.data file
1.9 + to avoid duplicate entries.
1.10 +- Now using libsi code introduced in VDR 1.3.47 and fixed in 1.4.0-3.
1.11 + Obviously requires VDR with API version 1.4.1 or later.
1.12 +- Checking for broken PMT entries.
1.13 +- Setting event running status by looking at the start & end time.
1.14 +- Updated Makefile according to changes in VDR 1.3.47 (APIVERSION & DVBDIR).
1.15 +
1.16 21.03.2006: Version 0.0.5
1.17 - Fixed compiling for VDR < 1.3.18.
1.18 - Fixed compiling for VDR >= 1.3.43.
2.1 --- a/Makefile Sat Dec 29 11:19:37 2007 +0100
2.2 +++ b/Makefile Sat Dec 29 11:19:49 2007 +0100
2.3 @@ -1,7 +1,7 @@
2.4 #
2.5 # PremiereEpg plugin to VDR
2.6 #
2.7 -# (C) 2005 Stefan Huelswitt <s.huelswitt@gmx.de>
2.8 +# (C) 2005-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
2.9 #
2.10 # This code is free software; you can redistribute it and/or
2.11 # modify it under the terms of the GNU General Public License
2.12 @@ -35,7 +35,6 @@
2.13
2.14 ### The directory environment:
2.15
2.16 -DVBDIR = ../../../../DVB
2.17 VDRDIR = ../../..
2.18 LIBDIR = ../../lib
2.19 TMPDIR = /tmp
2.20 @@ -46,7 +45,11 @@
2.21
2.22 ### The version number of VDR (taken from VDR's "config.h"):
2.23
2.24 -VDRVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
2.25 +VDRVERSION = $(shell sed -ne '/define VDRVERSION/ s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h)
2.26 +APIVERSION = $(shell sed -ne '/define APIVERSION/ s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h)
2.27 +ifeq ($(strip $(APIVERSION)),)
2.28 + APIVERSION = $(VDRVERSION)
2.29 +endif
2.30
2.31 ### The name of the distribution archive:
2.32
2.33 @@ -55,7 +58,7 @@
2.34
2.35 ### Includes and Defines (add further entries here):
2.36
2.37 -INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include
2.38 +INCLUDES += -I$(VDRDIR)/include
2.39
2.40 DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
2.41
2.42 @@ -87,7 +90,7 @@
2.43
2.44 libvdr-$(PLUGIN).so: $(OBJS)
2.45 $(CXX) $(CXXFLAGS) -shared $(OBJS) -o $@
2.46 - @cp $@ $(LIBDIR)/$@.$(VDRVERSION)
2.47 + @cp $@ $(LIBDIR)/$@.$(APIVERSION)
2.48
2.49 dist: clean
2.50 @-rm -rf $(TMPDIR)/$(ARCHIVE)
3.1 --- a/premiereepg.c Sat Dec 29 11:19:37 2007 +0100
3.2 +++ b/premiereepg.c Sat Dec 29 11:19:49 2007 +0100
3.3 @@ -1,7 +1,7 @@
3.4 /*
3.5 * PremiereEpg plugin to VDR (C++)
3.6 *
3.7 - * (C) 2005 Stefan Huelswitt <s.huelswitt@gmx.de>
3.8 + * (C) 2005-2006 Stefan Huelswitt <s.huelswitt@gmx.de>
3.9 *
3.10 * This code is base on the commandline tool premiereepg2vdr
3.11 * (C) 2004-2005 by Axel Katzur software@katzur.de
3.12 @@ -29,6 +29,7 @@
3.13 #include <vdr/channels.h>
3.14 #include <vdr/dvbdevice.h>
3.15 #include <vdr/i18n.h>
3.16 +#include <vdr/config.h>
3.17 #include <libsi/section.h>
3.18 #include <libsi/descriptor.h>
3.19
3.20 @@ -49,9 +50,13 @@
3.21 #define PMT_SCAN_TIMEOUT 10 // seconds
3.22 #define PMT_SCAN_IDLE 300 // seconds
3.23
3.24 -static const char *VERSION = "0.0.5";
3.25 +static const char *VERSION = "0.0.6";
3.26 static const char *DESCRIPTION = "Parses extended Premiere EPG data";
3.27
3.28 +#if APIVERSNUM < 10401
3.29 +#error You need at least VDR API version 1.4.1 for this plugin
3.30 +#endif
3.31 +
3.32 // --- cSetupPremiereEpg -------------------------------------------------------
3.33
3.34 const char *optPats[] = {
3.35 @@ -330,140 +335,27 @@
3.36 SetupStore("RatingInfo",SetupPE.RatingInfo);
3.37 }
3.38
3.39 -// --- CIT ---------------------------------------------------------------------
3.40 -
3.41 -namespace SI {
3.42 -
3.43 -#define CIT_LEN 17
3.44 -
3.45 -struct cit {
3.46 - u_char table_id :8;
3.47 -#if BYTE_ORDER == BIG_ENDIAN
3.48 - u_char section_syntax_indicator :1;
3.49 - u_char :3;
3.50 - u_char section_length_hi :4;
3.51 -#else
3.52 - u_char section_length_hi :4;
3.53 - u_char :3;
3.54 - u_char section_syntax_indicator :1;
3.55 -#endif
3.56 - u_char section_length_lo :8;
3.57 - u_char service_id_hi :8;
3.58 - u_char service_id_lo :8;
3.59 -#if BYTE_ORDER == BIG_ENDIAN
3.60 - u_char :2;
3.61 - u_char version_number :5;
3.62 - u_char current_next_indicator :1;
3.63 -#else
3.64 - u_char current_next_indicator :1;
3.65 - u_char version_number :5;
3.66 - u_char :2;
3.67 -#endif
3.68 - u_char section_number :8;
3.69 - u_char last_section_number :8;
3.70 - u_char content_id_hi_hi :8;
3.71 - u_char content_id_hi_lo :8;
3.72 - u_char content_id_lo_hi :8;
3.73 - u_char content_id_lo_lo :8;
3.74 - u_char duration_h :8;
3.75 - u_char duration_m :8;
3.76 - u_char duration_s :8;
3.77 -#if BYTE_ORDER == BIG_ENDIAN
3.78 - u_char :4;
3.79 - u_char descriptors_loop_length_hi :4;
3.80 -#else
3.81 - u_char descriptors_loop_length_hi :4;
3.82 - u_char :4;
3.83 -#endif
3.84 - u_char descriptors_loop_length_lo :8;
3.85 -};
3.86 -
3.87 -class CIT : public NumberedSection {
3.88 -public:
3.89 - CIT(const unsigned char *data, bool doCopy=true) : NumberedSection(data, doCopy) {}
3.90 - CIT() {}
3.91 - int getContentId(void) const;
3.92 - time_t getDuration(void) const;
3.93 - DescriptorLoop eventDescriptors;
3.94 -protected:
3.95 - virtual void Parse(void);
3.96 -private:
3.97 - const cit *s;
3.98 -};
3.99 -
3.100 -int CIT::getContentId(void) const {
3.101 - return (HILO(s->content_id_hi)<<16) | (HILO(s->content_id_lo));
3.102 -}
3.103 -
3.104 -time_t CIT::getDuration(void) const {
3.105 - return DVBTime::getDuration(s->duration_h,s->duration_m,s->duration_s);
3.106 -}
3.107 -
3.108 -void CIT::Parse(void) {
3.109 -#if VDRVERSNUM >= 10343
3.110 - int offset=0;
3.111 -#else
3.112 - unsigned int offset=0;
3.113 -#endif
3.114 - data.setPointerAndOffset<const cit>(s, offset);
3.115 - eventDescriptors.setData(data+offset,HILO(s->descriptors_loop_length));
3.116 -}
3.117 -
3.118 -} // end of namespace
3.119 -
3.120 -// --- cDescrF2 ----------------------------------------------------------------
3.121 -
3.122 -class cDescrF2 {
3.123 -private:
3.124 - SI::Descriptor *d;
3.125 - SI::CharArray data;
3.126 - int idx, loop, nloop, index;
3.127 -public:
3.128 - cDescrF2(SI::Descriptor *D);
3.129 - int TransportStreamId(void) { return data.TwoBytes(2); }
3.130 - int OrgNetworkId(void) { return data.TwoBytes(4); }
3.131 - int ServiceId(void) { return data.TwoBytes(6); }
3.132 - void Start(void);
3.133 - bool Next(void);
3.134 - time_t StartTime(void);
3.135 - int Index(void) { return index; }
3.136 - };
3.137 -
3.138 -cDescrF2::cDescrF2(SI::Descriptor *D)
3.139 -{
3.140 - d=D;
3.141 - data=d->getData();
3.142 - Start();
3.143 -}
3.144 -
3.145 -void cDescrF2::Start(void)
3.146 -{
3.147 - idx=8; loop=0; nloop=-3; index=-1;
3.148 -}
3.149 -
3.150 -bool cDescrF2::Next(void)
3.151 -{
3.152 - loop+=3;
3.153 - if(loop>=nloop) {
3.154 - idx+=nloop+3;
3.155 - if(idx>=d->getLength()) return false;
3.156 - loop=0; nloop=data[idx+2];
3.157 +// --- CRC16 -------------------------------------------------------------------
3.158 +
3.159 +#define POLY 0xA001 // CRC16
3.160 +
3.161 +unsigned int crc16(unsigned int crc, unsigned char const *p, int len)
3.162 +{
3.163 + while(len--) {
3.164 + crc^=*p++;
3.165 + for(int i=0; i<8; i++)
3.166 + crc=(crc&1) ? (crc>>1)^POLY : (crc>>1);
3.167 }
3.168 - index++;
3.169 - return true;
3.170 -}
3.171 -
3.172 -time_t cDescrF2::StartTime(void)
3.173 -{
3.174 - int off=idx+3+loop;
3.175 - return SI::DVBTime::getTime(data[idx+0],data[idx+1],data[off+0],data[off+1],data[off+2]);
3.176 + return crc&0xFFFF;
3.177 }
3.178
3.179 // --- cFilterPremiereEpg ------------------------------------------------------
3.180
3.181 +#define STARTTIME_BIAS (20*60)
3.182 +
3.183 class cFilterPremiereEpg : public cFilter {
3.184 private:
3.185 - int pmtpid, pmtidx, pmtnext;
3.186 + int pmtpid, pmtsid, pmtidx, pmtnext;
3.187 //
3.188 void NextPmt(void);
3.189 protected:
3.190 @@ -503,8 +395,8 @@
3.191
3.192 void cFilterPremiereEpg::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
3.193 {
3.194 + int now=time(0);
3.195 if(Pid==0 && Tid==SI::TableIdPAT) {
3.196 - int now=time(0);
3.197 if(!pmtnext || now>pmtnext) {
3.198 if(pmtpid) NextPmt();
3.199 if(!pmtpid) {
3.200 @@ -516,6 +408,7 @@
3.201 if(!assoc.isNITPid()) {
3.202 if(idx++==pmtidx) {
3.203 pmtpid=assoc.getPid();
3.204 + pmtsid=assoc.getServiceId();
3.205 Add(pmtpid,0x02);
3.206 pmtnext=now+PMT_SCAN_TIMEOUT;
3.207 d(printf("PMT pid now 0x%04x (idx=%d)\n",pmtpid,pmtidx))
3.208 @@ -534,7 +427,7 @@
3.209 }
3.210 else if(pmtpid>0 && Pid==pmtpid && Tid==SI::TableIdPMT && Source() && Transponder()) {
3.211 SI::PMT pmt(Data,false);
3.212 - if(pmt.CheckCRCAndParse()) {
3.213 + if(pmt.CheckCRCAndParse() && pmt.getServiceId()==pmtsid) {
3.214 SI::PMT::Stream stream;
3.215 for(SI::Loop::Iterator it; pmt.streamLoop.getNext(stream,it); ) {
3.216 if(stream.getStreamType()==0x05) {
3.217 @@ -575,20 +468,21 @@
3.218 }
3.219 }
3.220 else if(Tid==0xA0 && Source()) {
3.221 - SI::CIT cit(Data,false);
3.222 + SI::PremiereCIT cit(Data,false);
3.223 if(cit.CheckCRCAndParse()) {
3.224 cSchedulesLock SchedulesLock(true,10);
3.225 cSchedules *Schedules=(cSchedules *)cSchedules::Schedules(SchedulesLock);
3.226 if(Schedules) {
3.227 int nCount=0;
3.228 - time_t firstTime=0;
3.229 - SI::Descriptor *d;
3.230 - int LanguagePreferenceShort=-1;
3.231 - int LanguagePreferenceExt=-1;
3.232 - bool UseExtendedEventDescriptor=false;
3.233 SI::ExtendedEventDescriptors *ExtendedEventDescriptors=0;
3.234 SI::ShortEventDescriptor *ShortEventDescriptor=0;
3.235 char *order=0, *rating=0;
3.236 + {
3.237 + time_t firstTime=0;
3.238 + SI::Descriptor *d;
3.239 + bool UseExtendedEventDescriptor=false;
3.240 + int LanguagePreferenceShort=-1;
3.241 + int LanguagePreferenceExt=-1;
3.242 for(SI::Loop::Iterator it; (d=cit.eventDescriptors.getNext(it)); ) {
3.243 switch(d->getDescriptorTag()) {
3.244 case 0xF0: // order information
3.245 @@ -623,15 +517,19 @@
3.246 if(p>0) rating=strdup(buff);
3.247 }
3.248 break;
3.249 - case 0xF2: // transmisions
3.250 + case SI::PremiereContentTransmissionDescriptorTag:
3.251 if(nCount>=0) {
3.252 + SI::PremiereContentTransmissionDescriptor *pct=(SI::PremiereContentTransmissionDescriptor *)d;
3.253 nCount++;
3.254 - cDescrF2 f2(d);
3.255 - if(f2.Next()) {
3.256 - if(nCount==1) firstTime=f2.StartTime();
3.257 - else {
3.258 - time_t time=f2.StartTime();
3.259 - if(firstTime<time-5*50 || firstTime>time+5*60)
3.260 + SI::PremiereContentTransmissionDescriptor::StartDayEntry sd;
3.261 + SI::Loop::Iterator it;
3.262 + if(pct->startDayLoop.getNext(sd,it)) {
3.263 + SI::PremiereContentTransmissionDescriptor::StartDayEntry::StartTimeEntry st;
3.264 + SI::Loop::Iterator it2;
3.265 + if(sd.startTimeLoop.getNext(st,it2)) {
3.266 + time_t StartTime=st.getStartTime(sd.getMJD());
3.267 + if(nCount==1) firstTime=StartTime;
3.268 + else if(firstTime<StartTime-5*50 || firstTime>StartTime+5*60)
3.269 nCount=-1;
3.270 }
3.271 }
3.272 @@ -640,11 +538,7 @@
3.273 case SI::ExtendedEventDescriptorTag:
3.274 {
3.275 SI::ExtendedEventDescriptor *eed=(SI::ExtendedEventDescriptor *)d;
3.276 -#if VDRVERSNUM < 10332
3.277 - if(I18nIsPreferredLanguage(Setup.EPGLanguages,I18nLanguageIndex(eed->languageCode), LanguagePreferenceExt) || !ExtendedEventDescriptors) {
3.278 -#else
3.279 if(I18nIsPreferredLanguage(Setup.EPGLanguages,eed->languageCode, LanguagePreferenceExt) || !ExtendedEventDescriptors) {
3.280 -#endif
3.281 delete ExtendedEventDescriptors;
3.282 ExtendedEventDescriptors=new SI::ExtendedEventDescriptors;
3.283 UseExtendedEventDescriptor=true;
3.284 @@ -660,11 +554,7 @@
3.285 case SI::ShortEventDescriptorTag:
3.286 {
3.287 SI::ShortEventDescriptor *sed=(SI::ShortEventDescriptor *)d;
3.288 -#if VDRVERSNUM < 10332
3.289 - if(I18nIsPreferredLanguage(Setup.EPGLanguages,I18nLanguageIndex(sed->languageCode), LanguagePreferenceShort) || !ShortEventDescriptor) {
3.290 -#else
3.291 if(I18nIsPreferredLanguage(Setup.EPGLanguages,sed->languageCode, LanguagePreferenceShort) || !ShortEventDescriptor) {
3.292 -#endif
3.293 delete ShortEventDescriptor;
3.294 ShortEventDescriptor=sed;
3.295 d=NULL; // so that it is not deleted
3.296 @@ -676,55 +566,64 @@
3.297 }
3.298 delete d;
3.299 }
3.300 -
3.301 + }
3.302 +
3.303 + {
3.304 bool Modified=false;
3.305 int optCount=0;
3.306 - for(SI::Loop::Iterator it; (d=cit.eventDescriptors.getNext(it)); ) {
3.307 - if(d->getDescriptorTag()==0xF2) {
3.308 - optCount++;
3.309 -
3.310 - cDescrF2 f2(d);
3.311 - tChannelID channelID(Source(),f2.OrgNetworkId(),f2.TransportStreamId(),f2.ServiceId());
3.312 - cChannel *channel=Channels.GetByChannelID(channelID,true);
3.313 - if(!channel) continue;
3.314 -
3.315 - cSchedule *pSchedule=(cSchedule *)Schedules->GetSchedule(channelID);
3.316 - if(!pSchedule) {
3.317 - pSchedule=new cSchedule(channelID);
3.318 - Schedules->Add(pSchedule);
3.319 - }
3.320 -
3.321 - for(f2.Start(); f2.Next();) {
3.322 - u_int16_t EventId=(cit.getContentId()<<4) | f2.Index();
3.323 - time_t StartTime=f2.StartTime();
3.324 -
3.325 + unsigned int crc[3];
3.326 + crc[0]=cit.getContentId();
3.327 + SI::PremiereContentTransmissionDescriptor *pct;
3.328 + for(SI::Loop::Iterator it; (pct=(SI::PremiereContentTransmissionDescriptor *)cit.eventDescriptors.getNext(it,SI::PremiereContentTransmissionDescriptorTag)); ) {
3.329 + tChannelID channelID(Source(),pct->getOriginalNetworkId(),pct->getTransportStreamId(),pct->getServiceId());
3.330 + cChannel *channel=Channels.GetByChannelID(channelID,true);
3.331 + if(!channel) continue;
3.332 +
3.333 + cSchedule *pSchedule=(cSchedule *)Schedules->GetSchedule(channelID);
3.334 + if(!pSchedule) {
3.335 + pSchedule=new cSchedule(channelID);
3.336 + Schedules->Add(pSchedule);
3.337 + }
3.338 +
3.339 + optCount++;
3.340 + SI::PremiereContentTransmissionDescriptor::StartDayEntry sd;
3.341 + int index=0;
3.342 + for(SI::Loop::Iterator it; pct->startDayLoop.getNext(sd,it); ) {
3.343 + int mjd=sd.getMJD();
3.344 + SI::PremiereContentTransmissionDescriptor::StartDayEntry::StartTimeEntry st;
3.345 + for(SI::Loop::Iterator it2; sd.startTimeLoop.getNext(st,it2); ) {
3.346 + time_t StartTime=st.getStartTime(mjd);
3.347 + time_t EndTime=StartTime+cit.getDuration();
3.348 + int runningStatus=(StartTime<now && now<EndTime) ? SI::RunningStatusRunning : ((StartTime-30<now && now<StartTime) ? SI::RunningStatusStartsInAFewSeconds : SI::RunningStatusNotRunning);
3.349 bool isOpt=false;
3.350 - if(f2.Index()==0 && nCount>1) isOpt=true;
3.351 -
3.352 - 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))))
3.353 - if(StartTime+cit.getDuration()+Setup.EPGLinger*60<time(0)) {
3.354 + if(index++==0 && nCount>1) isOpt=true;
3.355 + crc[1]=isOpt ? optCount : 0;
3.356 + crc[2]=StartTime / STARTTIME_BIAS;
3.357 + tEventID EventId=((('P'<<8)|'W')<<16) | crc16(0,(unsigned char *)crc,sizeof(crc));
3.358 +
3.359 + 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))
3.360 + if(EndTime+Setup.EPGLinger*60<now) {
3.361 d2(printf("(old)\n"))
3.362 continue;
3.363 }
3.364
3.365 bool newEvent=false;
3.366 - cEvent *pEvent=(cEvent *)pSchedule->GetEvent(EventId,StartTime);
3.367 + cEvent *pEvent=(cEvent *)pSchedule->GetEvent(EventId,-1);
3.368 if(!pEvent) {
3.369 - d2(printf("(new)\n"))
3.370 -#if VDRVERSNUM >= 10325
3.371 - pEvent=new cEvent(EventId);
3.372 -#else
3.373 - pEvent=new cEvent(channelID,EventId);
3.374 -#endif
3.375 - if(!pEvent) continue;
3.376 - newEvent=true;
3.377 - }
3.378 + d2(printf("(new)\n"))
3.379 + pEvent=new cEvent(EventId);
3.380 + if(!pEvent) continue;
3.381 + newEvent=true;
3.382 + }
3.383 else {
3.384 - d2(printf("(upd)\n"))
3.385 - pEvent->SetSeen();
3.386 - if(pEvent->TableID()==0x00) continue;
3.387 - if(Tid==pEvent->TableID() && pEvent->Version()==cit.getVersionNumber()) continue;
3.388 - }
3.389 + d2(printf("(upd)\n"))
3.390 + pEvent->SetSeen();
3.391 + if(pEvent->TableID()==0x00 || pEvent->Version()==cit.getVersionNumber()) {
3.392 + if(pEvent->RunningStatus()!=runningStatus)
3.393 + pSchedule->SetRunningStatus(pEvent,runningStatus,channel);
3.394 + continue;
3.395 + }
3.396 + }
3.397 pEvent->SetEventID(EventId);
3.398 pEvent->SetTableID(Tid);
3.399 pEvent->SetVersion(cit.getVersionNumber());
3.400 @@ -741,6 +640,7 @@
3.401 }
3.402 else
3.403 pEvent->SetTitle(buffer);
3.404 + d2(printf("title: %s\n",pEvent->Title()))
3.405 pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer,sizeof(buffer)));
3.406 }
3.407 if(ExtendedEventDescriptors) {
3.408 @@ -760,19 +660,21 @@
3.409 }
3.410
3.411 if(newEvent) pSchedule->AddEvent(pEvent);
3.412 -#if VDRVERSNUM >= 10318
3.413 pEvent->SetComponents(NULL);
3.414 -#endif
3.415 pEvent->FixEpgBugs();
3.416 + if(pEvent->RunningStatus()!=runningStatus)
3.417 + pSchedule->SetRunningStatus(pEvent,runningStatus,channel);
3.418 + pSchedule->DropOutdated(StartTime,EndTime,Tid,cit.getVersionNumber());
3.419 Modified=true;
3.420 }
3.421 - if(Modified) {
3.422 - pSchedule->Sort();
3.423 - Schedules->SetModified(pSchedule);
3.424 - }
3.425 }
3.426 - delete d;
3.427 + if(Modified) {
3.428 + pSchedule->Sort();
3.429 + Schedules->SetModified(pSchedule);
3.430 + }
3.431 + delete pct;
3.432 }
3.433 + }
3.434 delete ExtendedEventDescriptors;
3.435 delete ShortEventDescriptor;
3.436 free(order);