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