premiereepg.c
branchtrunk
changeset 0 a75b9f441157
child 2 3562cacb3b0b
equal deleted inserted replaced
-1:000000000000 0:a75b9f441157
       
     1 /*
       
     2  * PremiereEpg plugin to VDR (C++)
       
     3  *
       
     4  * (C) 2005 Stefan Huelswitt <s.huelswitt@gmx.de>
       
     5  *
       
     6  * This code is base on the commandline tool premiereepg2vdr
       
     7  * (C) 2004-2005 by Axel Katzur software@katzur.de
       
     8  * but has been rewritten from scratch
       
     9  *
       
    10  * This code is free software; you can redistribute it and/or
       
    11  * modify it under the terms of the GNU General Public License
       
    12  * as published by the Free Software Foundation; either version 2
       
    13  * of the License, or (at your option) any later version.
       
    14  *
       
    15  * This code is distributed in the hope that it will be useful,
       
    16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    18  * GNU General Public License for more details.
       
    19  *
       
    20  * You should have received a copy of the GNU General Public License
       
    21  * along with this program; if not, write to the Free Software
       
    22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
       
    23  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
       
    24  */
       
    25 
       
    26 #include <vdr/plugin.h>
       
    27 #include <vdr/filter.h>
       
    28 #include <vdr/epg.h>
       
    29 #include <vdr/channels.h>
       
    30 #include <vdr/dvbdevice.h>
       
    31 #include <libsi/section.h>
       
    32 #include <libsi/descriptor.h>
       
    33 
       
    34 //#define DEBUG
       
    35 //#define DEBUG2
       
    36 
       
    37 #ifdef DEBUG
       
    38 #define d(x) { (x); }
       
    39 #else
       
    40 #define d(x) ; 
       
    41 #endif
       
    42 #ifdef DEBUG2
       
    43 #define d2(x) { (x); }
       
    44 #else
       
    45 #define d2(x) ; 
       
    46 #endif
       
    47 
       
    48 #define PMT_SCAN_TIMEOUT  10  // seconds
       
    49 #define PMT_SCAN_IDLE     300 // seconds
       
    50 
       
    51 static const char *VERSION        = "0.0.1";
       
    52 static const char *DESCRIPTION    = "Parse extended Premiere EPG data";
       
    53 
       
    54 // --- CIT ---------------------------------------------------------------------
       
    55 
       
    56 namespace SI {
       
    57 
       
    58 #define CIT_LEN 17
       
    59 
       
    60 struct cit {
       
    61    u_char table_id                               :8;
       
    62 #if BYTE_ORDER == BIG_ENDIAN
       
    63    u_char section_syntax_indicator               :1;
       
    64    u_char                                        :3;
       
    65    u_char section_length_hi                      :4;
       
    66 #else
       
    67    u_char section_length_hi                      :4;
       
    68    u_char                                        :3;
       
    69    u_char section_syntax_indicator               :1;
       
    70 #endif
       
    71    u_char section_length_lo                      :8;
       
    72    u_char service_id_hi                          :8;
       
    73    u_char service_id_lo                          :8;
       
    74 #if BYTE_ORDER == BIG_ENDIAN
       
    75    u_char                                        :2;
       
    76    u_char version_number                         :5;
       
    77    u_char current_next_indicator                 :1;
       
    78 #else
       
    79    u_char current_next_indicator                 :1;
       
    80    u_char version_number                         :5;
       
    81    u_char                                        :2;
       
    82 #endif
       
    83    u_char section_number                         :8;
       
    84    u_char last_section_number                    :8;
       
    85    u_char content_id_hi_hi                       :8;
       
    86    u_char content_id_hi_lo                       :8;
       
    87    u_char content_id_lo_hi                       :8;
       
    88    u_char content_id_lo_lo                       :8;
       
    89    u_char duration_h                             :8;
       
    90    u_char duration_m                             :8;
       
    91    u_char duration_s                             :8;
       
    92 #if BYTE_ORDER == BIG_ENDIAN
       
    93    u_char reserved                               :4;
       
    94    u_char descriptors_loop_length_hi             :4;
       
    95 #else
       
    96    u_char descriptors_loop_length_hi             :4;
       
    97    u_char reserved                               :4;
       
    98 #endif
       
    99    u_char descriptors_loop_length_lo             :8;
       
   100 };
       
   101 
       
   102 class CIT : public NumberedSection {
       
   103 public:
       
   104    CIT(const unsigned char *data, bool doCopy=true) : NumberedSection(data, doCopy) {}
       
   105    CIT() {}
       
   106    int getContentId(void) const;
       
   107    time_t getDuration(void) const;
       
   108    DescriptorLoop eventDescriptors;
       
   109 protected:
       
   110    virtual void Parse(void);
       
   111 private:
       
   112    const cit *s;
       
   113 };
       
   114 
       
   115 int CIT::getContentId(void) const {
       
   116    return (HILO(s->content_id_hi)<<16) | (HILO(s->content_id_lo));
       
   117 }
       
   118 
       
   119 time_t CIT::getDuration(void) const {
       
   120    return DVBTime::getDuration(s->duration_h,s->duration_m,s->duration_s);
       
   121 }
       
   122 
       
   123 void CIT::Parse(void) {
       
   124    unsigned int offset=0;
       
   125    data.setPointerAndOffset<const cit>(s, offset);
       
   126    eventDescriptors.setData(data+offset,HILO(s->descriptors_loop_length));
       
   127 }
       
   128 
       
   129 } // end of namespace
       
   130 
       
   131 // --- cDescrF2 ----------------------------------------------------------------
       
   132 
       
   133 class cDescrF2 {
       
   134 private:
       
   135   SI::Descriptor *d;
       
   136   SI::CharArray data;
       
   137   int idx, loop, nloop, index;
       
   138 public:
       
   139   cDescrF2(SI::Descriptor *D);
       
   140   int TransportStreamId(void) { return data.TwoBytes(2); }
       
   141   int OrgNetworkId(void)      { return data.TwoBytes(4); }
       
   142   int ServiceId(void)         { return data.TwoBytes(6); }
       
   143   void Start(void);
       
   144   bool Next(void);
       
   145   time_t StartTime(void);
       
   146   int Index(void) { return index; }
       
   147   };
       
   148 
       
   149 cDescrF2::cDescrF2(SI::Descriptor *D)
       
   150 {
       
   151   d=D;
       
   152   data=d->getData();
       
   153   Start();
       
   154 }
       
   155 
       
   156 void cDescrF2::Start(void)
       
   157 {
       
   158   idx=8; loop=0; nloop=-3; index=-1;
       
   159 }
       
   160 
       
   161 bool cDescrF2::Next(void)
       
   162 {
       
   163   loop+=3;
       
   164   if(loop>=nloop) {
       
   165     idx+=nloop+3;
       
   166     if(idx>=d->getLength()) return false;
       
   167     loop=0; nloop=data[idx+2];
       
   168     }
       
   169  index++;
       
   170  return true;
       
   171 }
       
   172 
       
   173 time_t cDescrF2::StartTime(void)
       
   174 {
       
   175   int off=idx+3+loop;
       
   176   return SI::DVBTime::getTime(data[idx+0],data[idx+1],data[off+0],data[off+1],data[off+2]);
       
   177 }
       
   178 
       
   179 // --- cFilterPremiereEpg ------------------------------------------------------
       
   180 
       
   181 class cFilterPremiereEpg : public cFilter {
       
   182 private:
       
   183   int pmtpid, pmtidx, pmtnext;
       
   184   //
       
   185   void NextPmt(void);
       
   186 protected:
       
   187   virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
       
   188 public:
       
   189   cFilterPremiereEpg(void);
       
   190   virtual void SetStatus(bool On);
       
   191   void Trigger(void);
       
   192   };
       
   193 
       
   194 cFilterPremiereEpg::cFilterPremiereEpg(void)
       
   195 {
       
   196   Trigger();
       
   197   Set(0x00,0x00);
       
   198 }
       
   199 
       
   200 void cFilterPremiereEpg::Trigger(void)
       
   201 {
       
   202   d(printf("trigger\n"))
       
   203   pmtpid=0; pmtidx=0; pmtnext=0;
       
   204 }
       
   205 
       
   206 void cFilterPremiereEpg::SetStatus(bool On)
       
   207 {
       
   208   d(printf("setstatus %d\n",On))
       
   209   cFilter::SetStatus(On);
       
   210   Trigger();
       
   211 }
       
   212 
       
   213 void cFilterPremiereEpg::NextPmt(void)
       
   214 {
       
   215   Del(pmtpid,0x02);
       
   216   pmtpid=0;
       
   217   pmtidx++;
       
   218   d(printf("PMT next\n"))
       
   219 }
       
   220 
       
   221 void cFilterPremiereEpg::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
       
   222 {
       
   223   if(Pid==0 && Tid==SI::TableIdPAT) {
       
   224     int now=time(0);
       
   225     if(!pmtnext || now>pmtnext) {
       
   226       if(pmtpid) NextPmt();
       
   227       if(!pmtpid) {
       
   228         SI::PAT pat(Data,false);
       
   229         if(pat.CheckCRCAndParse()) {
       
   230           SI::PAT::Association assoc;
       
   231           int idx=0;
       
   232           for(SI::Loop::Iterator it; pat.associationLoop.getNext(assoc,it);) {
       
   233             if(!assoc.isNITPid()) {
       
   234               if(idx++==pmtidx) {
       
   235                 pmtpid=assoc.getPid();
       
   236                 Add(pmtpid,0x02);
       
   237                 pmtnext=now+PMT_SCAN_TIMEOUT;
       
   238                 d(printf("PMT pid now 0x%04x (idx=%d)\n",pmtpid,pmtidx))
       
   239                 break;
       
   240                 }
       
   241               }
       
   242             }
       
   243           if(!pmtpid) {
       
   244             pmtidx=0;
       
   245             pmtnext=now+PMT_SCAN_IDLE;
       
   246             d(printf("PMT scan idle\n"))
       
   247             }
       
   248           }
       
   249         }
       
   250       }
       
   251     }
       
   252   else if(pmtpid>0 && Pid==pmtpid && Tid==SI::TableIdPMT && Source() && Transponder()) {
       
   253     SI::PMT pmt(Data,false);
       
   254     if(pmt.CheckCRCAndParse()) {
       
   255       SI::PMT::Stream stream;
       
   256       for(SI::Loop::Iterator it; pmt.streamLoop.getNext(stream,it); ) {
       
   257         if(stream.getStreamType()==0x05) {
       
   258           SI::CharArray data=stream.getData();
       
   259           if((data[1]&0xE0)==0xE0 && (data[3]&0xF0)==0xF0) {
       
   260             bool prvData=false, usrData=false;
       
   261             SI::Descriptor *d;
       
   262             for(SI::Loop::Iterator it; (d=stream.streamDescriptors.getNext(it)); ) {
       
   263               switch(d->getDescriptorTag()) {
       
   264                 case SI::PrivateDataSpecifierDescriptorTag:
       
   265                   d(printf("prv: %d %08x\n",d->getLength(),d->getData().FourBytes(2)))
       
   266                   if(d->getLength()==6 && d->getData().FourBytes(2)==0x000000be)
       
   267                     prvData=true;
       
   268                   break;
       
   269                 case 0x90:
       
   270                   d(printf("usr: %d %08x\n",d->getLength(),d->getData().FourBytes(2)))
       
   271                   if(d->getLength()==6 && d->getData().FourBytes(2)==0x0000ffff)
       
   272                     usrData=true;
       
   273                   break;
       
   274                 default:
       
   275                   break;
       
   276                 }
       
   277               delete d;
       
   278               }
       
   279             if(prvData && usrData) {
       
   280               int pid=stream.getPid();
       
   281               d(printf("found citpid 0x%04x",pid))
       
   282               if(!Matches(pid,0xA0)) {
       
   283                 Add(pid,0xA0);
       
   284                 d(printf(" (added)"))
       
   285                 }
       
   286               d(printf("\n"))
       
   287               }
       
   288             }
       
   289           }
       
   290         }
       
   291       NextPmt(); pmtnext=0;
       
   292       }
       
   293     }
       
   294   else if(Tid==0xA0 && Source()) {
       
   295     SI::CIT cit(Data,false);
       
   296     if(cit.CheckCRCAndParse()) {
       
   297       cSchedulesLock SchedulesLock(true,10);
       
   298       cSchedules *Schedules=(cSchedules *)cSchedules::Schedules(SchedulesLock);
       
   299       if(Schedules) {
       
   300         int nCount=0;
       
   301         time_t firstTime=0;
       
   302         SI::Descriptor *d;
       
   303         int LanguagePreferenceShort=-1;
       
   304         int LanguagePreferenceExt=-1;
       
   305         bool UseExtendedEventDescriptor=false;
       
   306         SI::ExtendedEventDescriptors *ExtendedEventDescriptors=0;
       
   307         SI::ShortEventDescriptor *ShortEventDescriptor=0;
       
   308         for(SI::Loop::Iterator it; (d=cit.eventDescriptors.getNext(it)); ) {
       
   309           switch(d->getDescriptorTag()) {
       
   310             case 0xF2:
       
   311               if(nCount>=0) {
       
   312                 nCount++;
       
   313                 cDescrF2 f2(d);
       
   314                 if(f2.Next()) {
       
   315                   if(nCount==1) firstTime=f2.StartTime();
       
   316                   else {
       
   317                     time_t time=f2.StartTime();
       
   318                     if(firstTime<time-5*50 || firstTime>time+5*60)
       
   319                       nCount=-1;
       
   320                     }
       
   321                   }
       
   322                 }
       
   323               break;
       
   324             case SI::ExtendedEventDescriptorTag:
       
   325               {
       
   326               SI::ExtendedEventDescriptor *eed=(SI::ExtendedEventDescriptor *)d;
       
   327               if(I18nIsPreferredLanguage(Setup.EPGLanguages,I18nLanguageIndex(eed->languageCode), LanguagePreferenceExt) || !ExtendedEventDescriptors) {
       
   328                  delete ExtendedEventDescriptors;
       
   329                  ExtendedEventDescriptors=new SI::ExtendedEventDescriptors;
       
   330                  UseExtendedEventDescriptor=true;
       
   331                  }
       
   332               if(UseExtendedEventDescriptor) {
       
   333                  ExtendedEventDescriptors->Add(eed);
       
   334                  d=NULL; // so that it is not deleted
       
   335                  }
       
   336               if(eed->getDescriptorNumber()==eed->getLastDescriptorNumber())
       
   337                  UseExtendedEventDescriptor=false;
       
   338               }
       
   339               break;
       
   340             case SI::ShortEventDescriptorTag:
       
   341               {
       
   342               SI::ShortEventDescriptor *sed=(SI::ShortEventDescriptor *)d;
       
   343               if(I18nIsPreferredLanguage(Setup.EPGLanguages,I18nLanguageIndex(sed->languageCode), LanguagePreferenceShort) || !ShortEventDescriptor) {
       
   344                  delete ShortEventDescriptor;
       
   345                  ShortEventDescriptor=sed;
       
   346                  d=NULL; // so that it is not deleted
       
   347                  }
       
   348               }
       
   349               break;
       
   350             default:
       
   351               break;
       
   352             }
       
   353           delete d;
       
   354           }
       
   355 
       
   356         bool Modified=false;
       
   357         int optCount=0;
       
   358         for(SI::Loop::Iterator it; (d=cit.eventDescriptors.getNext(it)); ) {
       
   359           if(d->getDescriptorTag()==0xF2) {
       
   360             optCount++;
       
   361 
       
   362             cDescrF2 f2(d);
       
   363             tChannelID channelID(Source(),f2.OrgNetworkId(),f2.TransportStreamId(),f2.ServiceId());
       
   364             cChannel *channel=Channels.GetByChannelID(channelID,true);
       
   365             if(!channel) continue;
       
   366 
       
   367             cSchedule *pSchedule=(cSchedule *)Schedules->GetSchedule(channelID);
       
   368             if(!pSchedule) {
       
   369                pSchedule=new cSchedule(channelID);
       
   370                Schedules->Add(pSchedule);
       
   371                }
       
   372 
       
   373             for(f2.Start(); f2.Next();) {
       
   374               u_int16_t EventId=(cit.getContentId()<<4) | f2.Index();
       
   375               time_t StartTime=f2.StartTime();
       
   376 
       
   377               bool isOpt=false;
       
   378               if(f2.Index()==0 && nCount>1) isOpt=true;
       
   379 
       
   380               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))))
       
   381               if(StartTime+cit.getDuration()+Setup.EPGLinger*60<time(0)) {
       
   382                 d2(printf("(old)\n"))
       
   383                 continue;
       
   384                 }
       
   385 
       
   386               cEvent *pEvent=(cEvent *)pSchedule->GetEvent(EventId,StartTime);
       
   387               if(!pEvent) {
       
   388                  d2(printf("(new)\n"))
       
   389                  pEvent=pSchedule->AddEvent(new cEvent(channelID,EventId));
       
   390                  if(!pEvent) continue;
       
   391                  }
       
   392               else {
       
   393                  d2(printf("(upd)\n"))
       
   394                  pEvent->SetSeen();
       
   395                  if(pEvent->TableID()==0x00) continue;
       
   396                  if(Tid==pEvent->TableID() && pEvent->Version()==cit.getVersionNumber()) continue;
       
   397                  }
       
   398               pEvent->SetEventID(EventId);
       
   399               pEvent->SetTableID(Tid);
       
   400               pEvent->SetVersion(cit.getVersionNumber());
       
   401               pEvent->SetStartTime(StartTime);
       
   402               pEvent->SetDuration(cit.getDuration());
       
   403 
       
   404               if(ShortEventDescriptor) {
       
   405                 char buffer[256+32];
       
   406                 ShortEventDescriptor->name.getText(buffer,sizeof(buffer)-32);
       
   407                 if(isOpt) {
       
   408                   char o[32];
       
   409                   snprintf(o,sizeof(o)," (Option %d)",optCount);
       
   410                   strcat(buffer,o);
       
   411                   }
       
   412                 pEvent->SetTitle(buffer);
       
   413                 pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer,sizeof(buffer)));
       
   414                 }
       
   415               if(ExtendedEventDescriptors) {
       
   416                 char buffer[ExtendedEventDescriptors->getMaximumTextLength(": ")+1];
       
   417                 pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer,sizeof(buffer),": "));
       
   418                 }
       
   419 
       
   420               pEvent->SetComponents(NULL);
       
   421               pEvent->FixEpgBugs();
       
   422               Modified=true;
       
   423               }
       
   424             if(Modified) {
       
   425               pSchedule->Sort();
       
   426               Schedules->SetModified(pSchedule);
       
   427               }
       
   428             }
       
   429           delete d;
       
   430           }
       
   431         delete ExtendedEventDescriptors;
       
   432         delete ShortEventDescriptor;
       
   433         }
       
   434       }
       
   435     }
       
   436 }
       
   437 
       
   438 // --- cPluginPremiereEpg ------------------------------------------------------
       
   439 
       
   440 class cPluginPremiereEpg : public cPlugin {
       
   441 private:
       
   442   struct {
       
   443     cFilterPremiereEpg *filter;
       
   444     cDevice *device;
       
   445     } epg[MAXDVBDEVICES];
       
   446 public:
       
   447   cPluginPremiereEpg(void);
       
   448   virtual const char *Version(void) { return VERSION; }
       
   449   virtual const char *Description(void) { return DESCRIPTION; }
       
   450   virtual bool Start(void);
       
   451   virtual void Stop(void);
       
   452   };
       
   453 
       
   454 cPluginPremiereEpg::cPluginPremiereEpg(void)
       
   455 {
       
   456   memset(epg,0,sizeof(epg));
       
   457 }
       
   458 
       
   459 bool cPluginPremiereEpg::Start(void)
       
   460 {
       
   461   for(int i=0; i<MAXDVBDEVICES; i++) {
       
   462     cDevice *dev=cDevice::GetDevice(i);
       
   463     if(dev) {
       
   464       epg[i].device=dev;
       
   465       dev->AttachFilter(epg[i].filter=new cFilterPremiereEpg);
       
   466       isyslog("Attached premiere EPG filter to device %d",i);
       
   467       }
       
   468     }
       
   469   return true;
       
   470 }
       
   471 
       
   472 void cPluginPremiereEpg::Stop(void)
       
   473 {
       
   474   for(int i=0; i<MAXDVBDEVICES; i++) {
       
   475     cDevice *dev=epg[i].device;
       
   476     if(dev) dev->Detach(epg[i].filter);
       
   477     delete epg[i].filter;
       
   478     epg[i].device=0;
       
   479     epg[i].filter=0;
       
   480     }
       
   481 }
       
   482 
       
   483 VDRPLUGINCREATOR(cPluginPremiereEpg); // Don't touch this!