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