premiereepg.c
author nathan
Sat, 29 Dec 2007 11:19:49 +0100
branchtrunk
changeset 10 967afc97e51d
parent 8 b5bd55cfd28f
child 12 5213337a6614
permissions -rw-r--r--
release 0.0.6
nathan@0
     1
/*
nathan@0
     2
 * PremiereEpg plugin to VDR (C++)
nathan@0
     3
 *
nathan@10
     4
 * (C) 2005-2006 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@10
    32
#include <vdr/config.h>
nathan@0
    33
#include <libsi/section.h>
nathan@0
    34
#include <libsi/descriptor.h>
nathan@0
    35
nathan@0
    36
//#define DEBUG
nathan@0
    37
//#define DEBUG2
nathan@0
    38
nathan@0
    39
#ifdef DEBUG
nathan@0
    40
#define d(x) { (x); }
nathan@0
    41
#else
nathan@0
    42
#define d(x) ; 
nathan@0
    43
#endif
nathan@0
    44
#ifdef DEBUG2
nathan@0
    45
#define d2(x) { (x); }
nathan@0
    46
#else
nathan@0
    47
#define d2(x) ; 
nathan@0
    48
#endif
nathan@0
    49
nathan@0
    50
#define PMT_SCAN_TIMEOUT  10  // seconds
nathan@0
    51
#define PMT_SCAN_IDLE     300 // seconds
nathan@0
    52
nathan@10
    53
static const char *VERSION        = "0.0.6";
nathan@2
    54
static const char *DESCRIPTION    = "Parses extended Premiere EPG data";
nathan@2
    55
nathan@10
    56
#if APIVERSNUM < 10401
nathan@10
    57
#error You need at least VDR API version 1.4.1 for this plugin
nathan@10
    58
#endif
nathan@10
    59
nathan@2
    60
// --- cSetupPremiereEpg -------------------------------------------------------
nathan@2
    61
nathan@2
    62
const char *optPats[] = {
nathan@2
    63
  "%s",
nathan@2
    64
  "%s (Option %d)",
nathan@2
    65
  "%s (O%d)",
nathan@2
    66
  "#%2$d %1$s",
nathan@2
    67
  "[%2$d] %1$s"
nathan@2
    68
  };
nathan@2
    69
#define NUM_PATS (sizeof(optPats)/sizeof(char *))
nathan@2
    70
nathan@2
    71
class cSetupPremiereEpg {
nathan@2
    72
public:
nathan@2
    73
  int OptPat;
nathan@6
    74
  int OrderInfo;
nathan@6
    75
  int RatingInfo;
nathan@2
    76
public:
nathan@2
    77
  cSetupPremiereEpg(void);
nathan@2
    78
  };
nathan@2
    79
nathan@2
    80
cSetupPremiereEpg SetupPE;
nathan@2
    81
nathan@2
    82
cSetupPremiereEpg::cSetupPremiereEpg(void)
nathan@2
    83
{
nathan@2
    84
  OptPat=1;
nathan@6
    85
  OrderInfo=1;
nathan@6
    86
  RatingInfo=1;
nathan@2
    87
}
nathan@2
    88
nathan@2
    89
// --- i18n --------------------------------------------------------------------
nathan@2
    90
nathan@2
    91
const tI18nPhrase Phrases[] = {
nathan@2
    92
/*
nathan@2
    93
*/
nathan@2
    94
  { "PremiereEPG",
nathan@2
    95
    "PremiereEPG",
nathan@2
    96
    "", // TODO
nathan@2
    97
    "", // TODO
nathan@2
    98
    "", // TODO
nathan@2
    99
    "", // TODO
nathan@2
   100
    "", // TODO
nathan@2
   101
    "", // TODO
nathan@2
   102
    "", // TODO
nathan@2
   103
    "", // TODO
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
  },
nathan@2
   111
  { "Parses extended Premiere EPG data",
nathan@2
   112
    "Liest erweiterte Premiere EPG Daten ein",
nathan@2
   113
    "", // TODO
nathan@2
   114
    "", // TODO
nathan@2
   115
    "", // TODO
nathan@2
   116
    "", // TODO
nathan@2
   117
    "", // TODO
nathan@2
   118
    "", // TODO
nathan@2
   119
    "", // TODO
nathan@2
   120
    "", // TODO
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
  },
nathan@2
   128
  { "Tag option events",
nathan@2
   129
    "Options Events markieren",
nathan@2
   130
    "", // TODO
nathan@2
   131
    "", // TODO
nathan@2
   132
    "", // TODO
nathan@2
   133
    "", // TODO
nathan@2
   134
    "", // TODO
nathan@2
   135
    "", // TODO
nathan@2
   136
    "", // TODO
nathan@2
   137
    "", // TODO
nathan@2
   138
    "", // TODO
nathan@2
   139
    "", // TODO
nathan@2
   140
    "", // TODO
nathan@2
   141
    "", // TODO
nathan@2
   142
    "", // TODO
nathan@2
   143
    "", // TODO
nathan@2
   144
  },
nathan@6
   145
  { "Show order information",
nathan@6
   146
    "Bestellhinweise anzeigen",
nathan@6
   147
    "", // TODO
nathan@6
   148
    "", // TODO
nathan@6
   149
    "", // TODO
nathan@6
   150
    "", // TODO
nathan@6
   151
    "", // TODO
nathan@6
   152
    "", // TODO
nathan@6
   153
    "", // TODO
nathan@6
   154
    "", // TODO
nathan@6
   155
    "", // TODO
nathan@6
   156
    "", // TODO
nathan@6
   157
    "", // TODO
nathan@6
   158
    "", // TODO
nathan@6
   159
    "", // TODO
nathan@6
   160
    "", // TODO
nathan@6
   161
  },
nathan@6
   162
  { "Show rating information",
nathan@6
   163
    "Altersfreigaben anzeigen",
nathan@6
   164
    "", // TODO
nathan@6
   165
    "", // TODO
nathan@6
   166
    "", // TODO
nathan@6
   167
    "", // TODO
nathan@6
   168
    "", // TODO
nathan@6
   169
    "", // TODO
nathan@6
   170
    "", // TODO
nathan@6
   171
    "", // TODO
nathan@6
   172
    "", // TODO
nathan@6
   173
    "", // TODO
nathan@6
   174
    "", // TODO
nathan@6
   175
    "", // TODO
nathan@6
   176
    "", // TODO
nathan@6
   177
    "", // TODO
nathan@6
   178
  },
nathan@6
   179
nathan@6
   180
  { "Ordernumber",
nathan@6
   181
    "Bestellnummer",
nathan@6
   182
    "", // TODO
nathan@6
   183
    "", // TODO
nathan@6
   184
    "", // TODO
nathan@6
   185
    "", // TODO
nathan@6
   186
    "", // TODO
nathan@6
   187
    "", // TODO
nathan@6
   188
    "", // TODO
nathan@6
   189
    "", // TODO
nathan@6
   190
    "", // TODO
nathan@6
   191
    "", // TODO
nathan@6
   192
    "", // TODO
nathan@6
   193
    "", // TODO
nathan@6
   194
    "", // TODO
nathan@6
   195
    "", // TODO
nathan@6
   196
  },
nathan@6
   197
  { "Price",
nathan@6
   198
    "Preis",
nathan@6
   199
    "", // TODO
nathan@6
   200
    "", // TODO
nathan@6
   201
    "", // TODO
nathan@6
   202
    "", // TODO
nathan@6
   203
    "", // TODO
nathan@6
   204
    "", // TODO
nathan@6
   205
    "", // TODO
nathan@6
   206
    "", // TODO
nathan@6
   207
    "", // TODO
nathan@6
   208
    "", // TODO
nathan@6
   209
    "", // TODO
nathan@6
   210
    "", // TODO
nathan@6
   211
    "", // TODO
nathan@6
   212
    "", // TODO
nathan@6
   213
  },
nathan@6
   214
  { "Ordering",
nathan@6
   215
    "Bestellen",
nathan@6
   216
    "", // TODO
nathan@6
   217
    "", // TODO
nathan@6
   218
    "", // TODO
nathan@6
   219
    "", // TODO
nathan@6
   220
    "", // TODO
nathan@6
   221
    "", // TODO
nathan@6
   222
    "", // TODO
nathan@6
   223
    "", // TODO
nathan@6
   224
    "", // TODO
nathan@6
   225
    "", // TODO
nathan@6
   226
    "", // TODO
nathan@6
   227
    "", // TODO
nathan@6
   228
    "", // TODO
nathan@6
   229
    "", // TODO
nathan@6
   230
  },
nathan@6
   231
  { "SMS",
nathan@6
   232
    "SMS",
nathan@6
   233
    "", // TODO
nathan@6
   234
    "", // TODO
nathan@6
   235
    "", // TODO
nathan@6
   236
    "", // TODO
nathan@6
   237
    "", // TODO
nathan@6
   238
    "", // TODO
nathan@6
   239
    "", // TODO
nathan@6
   240
    "", // TODO
nathan@6
   241
    "", // TODO
nathan@6
   242
    "", // TODO
nathan@6
   243
    "", // TODO
nathan@6
   244
    "", // TODO
nathan@6
   245
    "", // TODO
nathan@6
   246
    "", // TODO
nathan@6
   247
  },
nathan@6
   248
  { "WWW",
nathan@6
   249
    "WWW",
nathan@6
   250
    "", // TODO
nathan@6
   251
    "", // TODO
nathan@6
   252
    "", // TODO
nathan@6
   253
    "", // TODO
nathan@6
   254
    "", // TODO
nathan@6
   255
    "", // TODO
nathan@6
   256
    "", // TODO
nathan@6
   257
    "", // TODO
nathan@6
   258
    "", // TODO
nathan@6
   259
    "", // TODO
nathan@6
   260
    "", // TODO
nathan@6
   261
    "", // TODO
nathan@6
   262
    "", // TODO
nathan@6
   263
    "", // TODO
nathan@6
   264
  },
nathan@6
   265
  { "Rating",
nathan@6
   266
    "Altersfreigabe",
nathan@6
   267
    "", // TODO
nathan@6
   268
    "", // TODO
nathan@6
   269
    "", // TODO
nathan@6
   270
    "", // TODO
nathan@6
   271
    "", // TODO
nathan@6
   272
    "", // TODO
nathan@6
   273
    "", // TODO
nathan@6
   274
    "", // TODO
nathan@6
   275
    "", // TODO
nathan@6
   276
    "", // TODO
nathan@6
   277
    "", // TODO
nathan@6
   278
    "", // TODO
nathan@6
   279
    "", // TODO
nathan@6
   280
    "", // TODO
nathan@6
   281
  },
nathan@6
   282
  { "years",
nathan@6
   283
    "Jahre",
nathan@6
   284
    "", // TODO
nathan@6
   285
    "", // TODO
nathan@6
   286
    "", // TODO
nathan@6
   287
    "", // TODO
nathan@6
   288
    "", // TODO
nathan@6
   289
    "", // TODO
nathan@6
   290
    "", // TODO
nathan@6
   291
    "", // TODO
nathan@6
   292
    "", // TODO
nathan@6
   293
    "", // TODO
nathan@6
   294
    "", // TODO
nathan@6
   295
    "", // TODO
nathan@6
   296
    "", // TODO
nathan@6
   297
    "", // TODO
nathan@6
   298
  },
nathan@2
   299
nathan@2
   300
  { NULL }
nathan@2
   301
  };
nathan@2
   302
nathan@2
   303
// --- cMenuSetupPremiereEpg ------------------------------------------------------------
nathan@2
   304
nathan@2
   305
class cMenuSetupPremiereEpg : public cMenuSetupPage {
nathan@2
   306
private:
nathan@2
   307
  cSetupPremiereEpg data;
nathan@2
   308
  const char *optDisp[NUM_PATS];
nathan@2
   309
  char buff[NUM_PATS][32];
nathan@2
   310
protected:
nathan@2
   311
  virtual void Store(void);
nathan@2
   312
public:
nathan@2
   313
  cMenuSetupPremiereEpg(void);
nathan@2
   314
  };
nathan@2
   315
nathan@2
   316
cMenuSetupPremiereEpg::cMenuSetupPremiereEpg(void)
nathan@2
   317
{
nathan@2
   318
  data=SetupPE;
nathan@2
   319
  SetSection(tr("PremiereEPG"));
nathan@2
   320
  optDisp[0]=tr("off");
nathan@2
   321
  for(unsigned int i=1; i<NUM_PATS; i++) {
nathan@2
   322
    snprintf(buff[i],sizeof(buff[i]),optPats[i],"Event",1);
nathan@2
   323
    optDisp[i]=buff[i];
nathan@2
   324
    }
nathan@2
   325
  Add(new cMenuEditStraItem(tr("Tag option events"),&data.OptPat,NUM_PATS,optDisp));
nathan@6
   326
  Add(new cMenuEditBoolItem(tr("Show order information"),&data.OrderInfo));
nathan@6
   327
  Add(new cMenuEditBoolItem(tr("Show rating information"),&data.RatingInfo));
nathan@2
   328
}
nathan@2
   329
nathan@2
   330
void cMenuSetupPremiereEpg::Store(void)
nathan@2
   331
{
nathan@2
   332
  SetupPE=data;
nathan@2
   333
  SetupStore("OptionPattern",SetupPE.OptPat);
nathan@6
   334
  SetupStore("OrderInfo",SetupPE.OrderInfo);
nathan@6
   335
  SetupStore("RatingInfo",SetupPE.RatingInfo);
nathan@2
   336
}
nathan@0
   337
nathan@10
   338
// --- CRC16 -------------------------------------------------------------------
nathan@10
   339
nathan@10
   340
#define POLY 0xA001 // CRC16
nathan@10
   341
nathan@10
   342
unsigned int crc16(unsigned int crc, unsigned char const *p, int len)
nathan@10
   343
{
nathan@10
   344
  while(len--) {
nathan@10
   345
    crc^=*p++;
nathan@10
   346
    for(int i=0; i<8; i++)
nathan@10
   347
      crc=(crc&1) ? (crc>>1)^POLY : (crc>>1);
nathan@0
   348
    }
nathan@10
   349
  return crc&0xFFFF;
nathan@0
   350
}
nathan@0
   351
nathan@0
   352
// --- cFilterPremiereEpg ------------------------------------------------------
nathan@0
   353
nathan@10
   354
#define STARTTIME_BIAS (20*60)
nathan@10
   355
nathan@0
   356
class cFilterPremiereEpg : public cFilter {
nathan@0
   357
private:
nathan@10
   358
  int pmtpid, pmtsid, pmtidx, pmtnext;
nathan@0
   359
  //
nathan@0
   360
  void NextPmt(void);
nathan@0
   361
protected:
nathan@0
   362
  virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
nathan@0
   363
public:
nathan@0
   364
  cFilterPremiereEpg(void);
nathan@0
   365
  virtual void SetStatus(bool On);
nathan@0
   366
  void Trigger(void);
nathan@0
   367
  };
nathan@0
   368
nathan@0
   369
cFilterPremiereEpg::cFilterPremiereEpg(void)
nathan@0
   370
{
nathan@0
   371
  Trigger();
nathan@0
   372
  Set(0x00,0x00);
nathan@0
   373
}
nathan@0
   374
nathan@0
   375
void cFilterPremiereEpg::Trigger(void)
nathan@0
   376
{
nathan@0
   377
  d(printf("trigger\n"))
nathan@0
   378
  pmtpid=0; pmtidx=0; pmtnext=0;
nathan@0
   379
}
nathan@0
   380
nathan@0
   381
void cFilterPremiereEpg::SetStatus(bool On)
nathan@0
   382
{
nathan@0
   383
  d(printf("setstatus %d\n",On))
nathan@0
   384
  cFilter::SetStatus(On);
nathan@0
   385
  Trigger();
nathan@0
   386
}
nathan@0
   387
nathan@0
   388
void cFilterPremiereEpg::NextPmt(void)
nathan@0
   389
{
nathan@0
   390
  Del(pmtpid,0x02);
nathan@0
   391
  pmtpid=0;
nathan@0
   392
  pmtidx++;
nathan@0
   393
  d(printf("PMT next\n"))
nathan@0
   394
}
nathan@0
   395
nathan@0
   396
void cFilterPremiereEpg::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
nathan@0
   397
{
nathan@10
   398
  int now=time(0);
nathan@0
   399
  if(Pid==0 && Tid==SI::TableIdPAT) {
nathan@0
   400
    if(!pmtnext || now>pmtnext) {
nathan@0
   401
      if(pmtpid) NextPmt();
nathan@0
   402
      if(!pmtpid) {
nathan@0
   403
        SI::PAT pat(Data,false);
nathan@0
   404
        if(pat.CheckCRCAndParse()) {
nathan@0
   405
          SI::PAT::Association assoc;
nathan@0
   406
          int idx=0;
nathan@0
   407
          for(SI::Loop::Iterator it; pat.associationLoop.getNext(assoc,it);) {
nathan@0
   408
            if(!assoc.isNITPid()) {
nathan@0
   409
              if(idx++==pmtidx) {
nathan@0
   410
                pmtpid=assoc.getPid();
nathan@10
   411
                pmtsid=assoc.getServiceId();
nathan@0
   412
                Add(pmtpid,0x02);
nathan@0
   413
                pmtnext=now+PMT_SCAN_TIMEOUT;
nathan@0
   414
                d(printf("PMT pid now 0x%04x (idx=%d)\n",pmtpid,pmtidx))
nathan@0
   415
                break;
nathan@0
   416
                }
nathan@0
   417
              }
nathan@0
   418
            }
nathan@0
   419
          if(!pmtpid) {
nathan@0
   420
            pmtidx=0;
nathan@0
   421
            pmtnext=now+PMT_SCAN_IDLE;
nathan@0
   422
            d(printf("PMT scan idle\n"))
nathan@0
   423
            }
nathan@0
   424
          }
nathan@0
   425
        }
nathan@0
   426
      }
nathan@0
   427
    }
nathan@0
   428
  else if(pmtpid>0 && Pid==pmtpid && Tid==SI::TableIdPMT && Source() && Transponder()) {
nathan@0
   429
    SI::PMT pmt(Data,false);
nathan@10
   430
    if(pmt.CheckCRCAndParse() && pmt.getServiceId()==pmtsid) {
nathan@0
   431
      SI::PMT::Stream stream;
nathan@0
   432
      for(SI::Loop::Iterator it; pmt.streamLoop.getNext(stream,it); ) {
nathan@0
   433
        if(stream.getStreamType()==0x05) {
nathan@0
   434
          SI::CharArray data=stream.getData();
nathan@0
   435
          if((data[1]&0xE0)==0xE0 && (data[3]&0xF0)==0xF0) {
nathan@0
   436
            bool prvData=false, usrData=false;
nathan@0
   437
            SI::Descriptor *d;
nathan@0
   438
            for(SI::Loop::Iterator it; (d=stream.streamDescriptors.getNext(it)); ) {
nathan@0
   439
              switch(d->getDescriptorTag()) {
nathan@0
   440
                case SI::PrivateDataSpecifierDescriptorTag:
nathan@0
   441
                  d(printf("prv: %d %08x\n",d->getLength(),d->getData().FourBytes(2)))
nathan@0
   442
                  if(d->getLength()==6 && d->getData().FourBytes(2)==0x000000be)
nathan@0
   443
                    prvData=true;
nathan@0
   444
                  break;
nathan@0
   445
                case 0x90:
nathan@0
   446
                  d(printf("usr: %d %08x\n",d->getLength(),d->getData().FourBytes(2)))
nathan@0
   447
                  if(d->getLength()==6 && d->getData().FourBytes(2)==0x0000ffff)
nathan@0
   448
                    usrData=true;
nathan@0
   449
                  break;
nathan@0
   450
                default:
nathan@0
   451
                  break;
nathan@0
   452
                }
nathan@0
   453
              delete d;
nathan@0
   454
              }
nathan@0
   455
            if(prvData && usrData) {
nathan@0
   456
              int pid=stream.getPid();
nathan@0
   457
              d(printf("found citpid 0x%04x",pid))
nathan@0
   458
              if(!Matches(pid,0xA0)) {
nathan@0
   459
                Add(pid,0xA0);
nathan@0
   460
                d(printf(" (added)"))
nathan@0
   461
                }
nathan@0
   462
              d(printf("\n"))
nathan@0
   463
              }
nathan@0
   464
            }
nathan@0
   465
          }
nathan@0
   466
        }
nathan@0
   467
      NextPmt(); pmtnext=0;
nathan@0
   468
      }
nathan@0
   469
    }
nathan@0
   470
  else if(Tid==0xA0 && Source()) {
nathan@10
   471
    SI::PremiereCIT cit(Data,false);
nathan@0
   472
    if(cit.CheckCRCAndParse()) {
nathan@0
   473
      cSchedulesLock SchedulesLock(true,10);
nathan@0
   474
      cSchedules *Schedules=(cSchedules *)cSchedules::Schedules(SchedulesLock);
nathan@0
   475
      if(Schedules) {
nathan@0
   476
        int nCount=0;
nathan@0
   477
        SI::ExtendedEventDescriptors *ExtendedEventDescriptors=0;
nathan@0
   478
        SI::ShortEventDescriptor *ShortEventDescriptor=0;
nathan@6
   479
        char *order=0, *rating=0;
nathan@10
   480
        {
nathan@10
   481
        time_t firstTime=0;
nathan@10
   482
        SI::Descriptor *d;
nathan@10
   483
        bool UseExtendedEventDescriptor=false;
nathan@10
   484
        int LanguagePreferenceShort=-1;
nathan@10
   485
        int LanguagePreferenceExt=-1;
nathan@0
   486
        for(SI::Loop::Iterator it; (d=cit.eventDescriptors.getNext(it)); ) {
nathan@0
   487
          switch(d->getDescriptorTag()) {
nathan@6
   488
            case 0xF0: // order information
nathan@6
   489
              if(SetupPE.OrderInfo) {
nathan@6
   490
                static const char *text[] = {
nathan@6
   491
                  "Ordernumber",
nathan@6
   492
                  "Price",
nathan@6
   493
                  "Ordering",
nathan@6
   494
                  "SMS",
nathan@6
   495
                  "WWW"
nathan@6
   496
                  };
nathan@6
   497
                char buff[512];
nathan@6
   498
                int p=0;
nathan@6
   499
                const unsigned char *data=d->getData().getData()+2;
nathan@6
   500
                for(int i=0; i<5; i++) {
nathan@6
   501
                  int l=data[0]; 
nathan@6
   502
                  if(l>0) p+=snprintf(&buff[p],sizeof(buff)-p,"\n%s: %.*s",tr(text[i]),l,&data[1]);
nathan@6
   503
                  data+=l+1;
nathan@6
   504
                  }
nathan@6
   505
                if(p>0) order=strdup(buff);
nathan@6
   506
                }
nathan@6
   507
              break;
nathan@6
   508
            case 0xF1: // parental rating
nathan@6
   509
              if(SetupPE.RatingInfo) {
nathan@6
   510
                char buff[512];
nathan@6
   511
                int p=0;
nathan@6
   512
                const unsigned char *data=d->getData().getData()+2;
nathan@6
   513
                p+=snprintf(&buff[p],sizeof(buff)-p,"\n%s: %d %s",tr("Rating"),data[0]+3,tr("years"));
nathan@6
   514
                data+=7;
nathan@6
   515
                int l=data[0]; 
nathan@6
   516
                if(l>0) p+=snprintf(&buff[p],sizeof(buff)-p," (%.*s)",l,&data[1]);
nathan@6
   517
                if(p>0) rating=strdup(buff);
nathan@6
   518
                }
nathan@6
   519
              break;
nathan@10
   520
            case SI::PremiereContentTransmissionDescriptorTag:
nathan@0
   521
              if(nCount>=0) {
nathan@10
   522
                SI::PremiereContentTransmissionDescriptor *pct=(SI::PremiereContentTransmissionDescriptor *)d;
nathan@0
   523
                nCount++;
nathan@10
   524
                SI::PremiereContentTransmissionDescriptor::StartDayEntry sd;
nathan@10
   525
                SI::Loop::Iterator it;
nathan@10
   526
                if(pct->startDayLoop.getNext(sd,it)) {
nathan@10
   527
                  SI::PremiereContentTransmissionDescriptor::StartDayEntry::StartTimeEntry st;
nathan@10
   528
                  SI::Loop::Iterator it2;
nathan@10
   529
                  if(sd.startTimeLoop.getNext(st,it2)) {
nathan@10
   530
                    time_t StartTime=st.getStartTime(sd.getMJD());
nathan@10
   531
                    if(nCount==1) firstTime=StartTime;
nathan@10
   532
                    else if(firstTime<StartTime-5*50 || firstTime>StartTime+5*60)
nathan@0
   533
                      nCount=-1;
nathan@0
   534
                    }
nathan@0
   535
                  }
nathan@0
   536
                }
nathan@0
   537
              break;
nathan@0
   538
            case SI::ExtendedEventDescriptorTag:
nathan@0
   539
              {
nathan@0
   540
              SI::ExtendedEventDescriptor *eed=(SI::ExtendedEventDescriptor *)d;
nathan@4
   541
              if(I18nIsPreferredLanguage(Setup.EPGLanguages,eed->languageCode, LanguagePreferenceExt) || !ExtendedEventDescriptors) {
nathan@0
   542
                 delete ExtendedEventDescriptors;
nathan@0
   543
                 ExtendedEventDescriptors=new SI::ExtendedEventDescriptors;
nathan@0
   544
                 UseExtendedEventDescriptor=true;
nathan@0
   545
                 }
nathan@0
   546
              if(UseExtendedEventDescriptor) {
nathan@0
   547
                 ExtendedEventDescriptors->Add(eed);
nathan@0
   548
                 d=NULL; // so that it is not deleted
nathan@0
   549
                 }
nathan@0
   550
              if(eed->getDescriptorNumber()==eed->getLastDescriptorNumber())
nathan@0
   551
                 UseExtendedEventDescriptor=false;
nathan@0
   552
              }
nathan@0
   553
              break;
nathan@0
   554
            case SI::ShortEventDescriptorTag:
nathan@0
   555
              {
nathan@0
   556
              SI::ShortEventDescriptor *sed=(SI::ShortEventDescriptor *)d;
nathan@4
   557
              if(I18nIsPreferredLanguage(Setup.EPGLanguages,sed->languageCode, LanguagePreferenceShort) || !ShortEventDescriptor) {
nathan@0
   558
                 delete ShortEventDescriptor;
nathan@0
   559
                 ShortEventDescriptor=sed;
nathan@0
   560
                 d=NULL; // so that it is not deleted
nathan@0
   561
                 }
nathan@0
   562
              }
nathan@0
   563
              break;
nathan@0
   564
            default:
nathan@0
   565
              break;
nathan@0
   566
            }
nathan@0
   567
          delete d;
nathan@0
   568
          }
nathan@10
   569
        }
nathan@10
   570
nathan@10
   571
        {
nathan@0
   572
        bool Modified=false;
nathan@0
   573
        int optCount=0;
nathan@10
   574
        unsigned int crc[3];
nathan@10
   575
        crc[0]=cit.getContentId();
nathan@10
   576
        SI::PremiereContentTransmissionDescriptor *pct;
nathan@10
   577
        for(SI::Loop::Iterator it; (pct=(SI::PremiereContentTransmissionDescriptor *)cit.eventDescriptors.getNext(it,SI::PremiereContentTransmissionDescriptorTag)); ) {
nathan@10
   578
          tChannelID channelID(Source(),pct->getOriginalNetworkId(),pct->getTransportStreamId(),pct->getServiceId());
nathan@10
   579
          cChannel *channel=Channels.GetByChannelID(channelID,true);
nathan@10
   580
          if(!channel) continue;
nathan@10
   581
nathan@10
   582
          cSchedule *pSchedule=(cSchedule *)Schedules->GetSchedule(channelID);
nathan@10
   583
          if(!pSchedule) {
nathan@10
   584
             pSchedule=new cSchedule(channelID);
nathan@10
   585
             Schedules->Add(pSchedule);
nathan@10
   586
             }
nathan@10
   587
nathan@10
   588
          optCount++;
nathan@10
   589
          SI::PremiereContentTransmissionDescriptor::StartDayEntry sd;
nathan@10
   590
          int index=0;
nathan@10
   591
          for(SI::Loop::Iterator it; pct->startDayLoop.getNext(sd,it); ) {
nathan@10
   592
            int mjd=sd.getMJD();
nathan@10
   593
            SI::PremiereContentTransmissionDescriptor::StartDayEntry::StartTimeEntry st;
nathan@10
   594
            for(SI::Loop::Iterator it2; sd.startTimeLoop.getNext(st,it2); ) {
nathan@10
   595
              time_t StartTime=st.getStartTime(mjd);
nathan@10
   596
              time_t EndTime=StartTime+cit.getDuration();
nathan@10
   597
              int runningStatus=(StartTime<now && now<EndTime) ? SI::RunningStatusRunning : ((StartTime-30<now && now<StartTime) ? SI::RunningStatusStartsInAFewSeconds : SI::RunningStatusNotRunning);
nathan@0
   598
              bool isOpt=false;
nathan@10
   599
              if(index++==0 && nCount>1) isOpt=true;
nathan@10
   600
              crc[1]=isOpt ? optCount : 0;
nathan@10
   601
              crc[2]=StartTime / STARTTIME_BIAS;
nathan@10
   602
              tEventID EventId=((('P'<<8)|'W')<<16) | crc16(0,(unsigned char *)crc,sizeof(crc));
nathan@10
   603
nathan@10
   604
              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))
nathan@10
   605
              if(EndTime+Setup.EPGLinger*60<now) {
nathan@0
   606
                d2(printf("(old)\n"))
nathan@0
   607
                continue;
nathan@0
   608
                }
nathan@0
   609
nathan@2
   610
              bool newEvent=false;
nathan@10
   611
              cEvent *pEvent=(cEvent *)pSchedule->GetEvent(EventId,-1);
nathan@0
   612
              if(!pEvent) {
nathan@10
   613
                d2(printf("(new)\n"))
nathan@10
   614
                pEvent=new cEvent(EventId);
nathan@10
   615
                if(!pEvent) continue;
nathan@10
   616
                newEvent=true;
nathan@10
   617
                }
nathan@0
   618
              else {
nathan@10
   619
                d2(printf("(upd)\n"))
nathan@10
   620
                pEvent->SetSeen();
nathan@10
   621
                if(pEvent->TableID()==0x00 || pEvent->Version()==cit.getVersionNumber()) {
nathan@10
   622
                  if(pEvent->RunningStatus()!=runningStatus)
nathan@10
   623
                    pSchedule->SetRunningStatus(pEvent,runningStatus,channel);
nathan@10
   624
                  continue;
nathan@10
   625
                  }
nathan@10
   626
                }
nathan@0
   627
              pEvent->SetEventID(EventId);
nathan@0
   628
              pEvent->SetTableID(Tid);
nathan@0
   629
              pEvent->SetVersion(cit.getVersionNumber());
nathan@0
   630
              pEvent->SetStartTime(StartTime);
nathan@0
   631
              pEvent->SetDuration(cit.getDuration());
nathan@0
   632
nathan@0
   633
              if(ShortEventDescriptor) {
nathan@2
   634
                char buffer[256];
nathan@2
   635
                ShortEventDescriptor->name.getText(buffer,sizeof(buffer));
nathan@0
   636
                if(isOpt) {
nathan@2
   637
                  char buffer2[sizeof(buffer)+32];
nathan@2
   638
                  snprintf(buffer2,sizeof(buffer2),optPats[SetupPE.OptPat],buffer,optCount);
nathan@2
   639
                  pEvent->SetTitle(buffer2);
nathan@0
   640
                  }
nathan@2
   641
                else
nathan@2
   642
                  pEvent->SetTitle(buffer);
nathan@10
   643
                d2(printf("title: %s\n",pEvent->Title()))
nathan@0
   644
                pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer,sizeof(buffer)));
nathan@0
   645
                }
nathan@0
   646
              if(ExtendedEventDescriptors) {
nathan@0
   647
                char buffer[ExtendedEventDescriptors->getMaximumTextLength(": ")+1];
nathan@0
   648
                pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer,sizeof(buffer),": "));
nathan@0
   649
                }
nathan@6
   650
              if(order || rating) {
nathan@6
   651
                int len=(pEvent->Description() ? strlen(pEvent->Description()) : 0) +
nathan@6
   652
                        (order                 ? strlen(order) : 0) +
nathan@6
   653
                        (rating                ? strlen(rating) : 0);
nathan@6
   654
                char buffer[len+32];
nathan@6
   655
                buffer[0]=0;
nathan@6
   656
                if(pEvent->Description()) strcat(buffer,pEvent->Description());
nathan@6
   657
                if(rating)                strcat(buffer,rating);
nathan@6
   658
                if(order)                 strcat(buffer,order);
nathan@6
   659
                pEvent->SetDescription(buffer);
nathan@6
   660
                }
nathan@0
   661
nathan@2
   662
              if(newEvent) pSchedule->AddEvent(pEvent);
nathan@0
   663
              pEvent->SetComponents(NULL);
nathan@0
   664
              pEvent->FixEpgBugs();
nathan@10
   665
              if(pEvent->RunningStatus()!=runningStatus)
nathan@10
   666
                pSchedule->SetRunningStatus(pEvent,runningStatus,channel);
nathan@10
   667
              pSchedule->DropOutdated(StartTime,EndTime,Tid,cit.getVersionNumber());
nathan@0
   668
              Modified=true;
nathan@0
   669
              }
nathan@0
   670
            }
nathan@10
   671
          if(Modified) {
nathan@10
   672
            pSchedule->Sort();
nathan@10
   673
            Schedules->SetModified(pSchedule);
nathan@10
   674
            }
nathan@10
   675
          delete pct;
nathan@0
   676
          }
nathan@10
   677
        }
nathan@0
   678
        delete ExtendedEventDescriptors;
nathan@0
   679
        delete ShortEventDescriptor;
nathan@6
   680
        free(order);
nathan@6
   681
        free(rating);
nathan@0
   682
        }
nathan@0
   683
      }
nathan@0
   684
    }
nathan@0
   685
}
nathan@0
   686
nathan@0
   687
// --- cPluginPremiereEpg ------------------------------------------------------
nathan@0
   688
nathan@0
   689
class cPluginPremiereEpg : public cPlugin {
nathan@0
   690
private:
nathan@0
   691
  struct {
nathan@0
   692
    cFilterPremiereEpg *filter;
nathan@0
   693
    cDevice *device;
nathan@0
   694
    } epg[MAXDVBDEVICES];
nathan@0
   695
public:
nathan@0
   696
  cPluginPremiereEpg(void);
nathan@0
   697
  virtual const char *Version(void) { return VERSION; }
nathan@2
   698
  virtual const char *Description(void) { return tr(DESCRIPTION); }
nathan@0
   699
  virtual bool Start(void);
nathan@0
   700
  virtual void Stop(void);
nathan@2
   701
  virtual cMenuSetupPage *SetupMenu(void);
nathan@2
   702
  virtual bool SetupParse(const char *Name, const char *Value);
nathan@0
   703
  };
nathan@0
   704
nathan@0
   705
cPluginPremiereEpg::cPluginPremiereEpg(void)
nathan@0
   706
{
nathan@0
   707
  memset(epg,0,sizeof(epg));
nathan@0
   708
}
nathan@0
   709
nathan@0
   710
bool cPluginPremiereEpg::Start(void)
nathan@0
   711
{
nathan@2
   712
  RegisterI18n(Phrases);
nathan@0
   713
  for(int i=0; i<MAXDVBDEVICES; i++) {
nathan@0
   714
    cDevice *dev=cDevice::GetDevice(i);
nathan@0
   715
    if(dev) {
nathan@0
   716
      epg[i].device=dev;
nathan@0
   717
      dev->AttachFilter(epg[i].filter=new cFilterPremiereEpg);
nathan@0
   718
      isyslog("Attached premiere EPG filter to device %d",i);
nathan@0
   719
      }
nathan@0
   720
    }
nathan@0
   721
  return true;
nathan@0
   722
}
nathan@0
   723
nathan@0
   724
void cPluginPremiereEpg::Stop(void)
nathan@0
   725
{
nathan@0
   726
  for(int i=0; i<MAXDVBDEVICES; i++) {
nathan@0
   727
    cDevice *dev=epg[i].device;
nathan@0
   728
    if(dev) dev->Detach(epg[i].filter);
nathan@0
   729
    delete epg[i].filter;
nathan@0
   730
    epg[i].device=0;
nathan@0
   731
    epg[i].filter=0;
nathan@0
   732
    }
nathan@0
   733
}
nathan@0
   734
nathan@2
   735
cMenuSetupPage *cPluginPremiereEpg::SetupMenu(void)
nathan@2
   736
{
nathan@2
   737
  return new cMenuSetupPremiereEpg;
nathan@2
   738
}
nathan@2
   739
nathan@2
   740
bool cPluginPremiereEpg::SetupParse(const char *Name, const char *Value)
nathan@2
   741
{
nathan@6
   742
  if      (!strcasecmp(Name, "OptionPattern")) SetupPE.OptPat     = atoi(Value);
nathan@6
   743
  else if (!strcasecmp(Name, "OrderInfo"))     SetupPE.OrderInfo  = atoi(Value);
nathan@6
   744
  else if (!strcasecmp(Name, "RatingInfo"))    SetupPE.RatingInfo = atoi(Value);
nathan@2
   745
  else return false;
nathan@2
   746
  return true;
nathan@2
   747
}
nathan@2
   748
nathan@0
   749
VDRPLUGINCREATOR(cPluginPremiereEpg); // Don't touch this!