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