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