From 073268bd45a4a36e0d20ba1a60d585e9cae13e30 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Mon, 1 May 2017 09:32:32 +0200 Subject: [PATCH] CAMs are now sent a generated EIT packet that contains a single 'present event' for the current SID, in order to avoid any parental rating dialogs --- HISTORY | 5 +++ ci.c | 13 +++++++- ci.h | 13 +++++++- config.h | 10 +++--- device.c | 19 +++++++++++- mtd.c | 29 ++++++++++-------- mtd.h | 3 +- receiver.c | 4 ++- receiver.h | 4 ++- remux.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++- remux.h | 19 +++++++++++- 11 files changed, 182 insertions(+), 26 deletions(-) diff --git a/HISTORY b/HISTORY index a79eef11..3c3b7605 100644 --- a/HISTORY +++ b/HISTORY @@ -8990,3 +8990,8 @@ Video Disk Recorder Revision History - If 0 is given as the channel number in the SVDRP command LSTC, the data of the current channel is listed. - Fixed a possible crash when pulling the CAM while decrypting a channel with MTD. + +2017-05-01: Version 2.3.5 + +- CAMs are now sent a generated EIT packet that contains a single 'present event' for + the current SID, in order to avoid any parental rating dialogs. diff --git a/ci.c b/ci.c index f64c17e3..def1530a 100644 --- a/ci.c +++ b/ci.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: ci.c 4.13 2017/04/26 09:18:26 kls Exp $ + * $Id: ci.c 4.14 2017/05/01 09:26:12 kls Exp $ */ #include "ci.h" @@ -2424,6 +2424,17 @@ uchar *cCamSlot::Decrypt(uchar *Data, int &Count) return Data; } +bool cCamSlot::Inject(uchar *Data, int Count) +{ + return true; +} + +void cCamSlot::InjectEit(int Sid) +{ + cEitGenerator Eit(Sid); + Inject(Eit.Data(), Eit.Length()); +} + // --- cCamSlots ------------------------------------------------------------- cCamSlots CamSlots; diff --git a/ci.h b/ci.h index 8d1323ac..342cfb96 100644 --- a/ci.h +++ b/ci.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: ci.h 4.6 2017/04/10 09:17:56 kls Exp $ + * $Id: ci.h 4.7 2017/05/01 09:21:22 kls Exp $ */ #ifndef __CI_H @@ -377,6 +377,17 @@ public: ///< A derived class that implements this function will also need ///< to set the WantsTsData parameter in the call to the base class ///< constructor to true in order to receive the TS data. + virtual bool Inject(uchar *Data, int Count); + ///< Sends all Count bytes of the given Data to the CAM, and returns true + ///< if this was possible. If the data can't be sent to the CAM completely, + ///< nothing shall be sent and the return value shall be false. + ///< No decrypted packet is returned by this function. + virtual void InjectEit(int Sid); + ///< Injects a generated EIT with a "present event" for the given Sid into + ///< the TS data stream sent to the CAM. This only applies to CAM slots that + ///< have WantsTsData set to true in their constructor. + ///< The default implementation sends an EIT with the minimum event + ///< necessary to disable the CAMs parental rating prompt. }; class cCamSlots : public cList { diff --git a/config.h b/config.h index e9a9f11d..c67b990d 100644 --- a/config.h +++ b/config.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.h 4.8 2017/03/30 13:42:15 kls Exp $ + * $Id: config.h 4.9 2017/04/29 13:33:13 kls Exp $ */ #ifndef __CONFIG_H @@ -22,13 +22,13 @@ // VDR's own version number: -#define VDRVERSION "2.3.4" -#define VDRVERSNUM 20304 // Version * 10000 + Major * 100 + Minor +#define VDRVERSION "2.3.5" +#define VDRVERSNUM 20305 // Version * 10000 + Major * 100 + Minor // The plugin API's version number: -#define APIVERSION "2.3.4" -#define APIVERSNUM 20304 // Version * 10000 + Major * 100 + Minor +#define APIVERSION "2.3.5" +#define APIVERSNUM 20305 // Version * 10000 + Major * 100 + Minor // When loading plugins, VDR searches them by their APIVERSION, which // may be smaller than VDRVERSION in case there have been no changes to diff --git a/device.c b/device.c index 25d16679..185565b5 100644 --- a/device.c +++ b/device.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.c 4.15 2017/04/17 14:47:42 kls Exp $ + * $Id: device.c 4.16 2017/05/01 09:24:49 kls Exp $ */ #include "device.h" @@ -1652,6 +1652,7 @@ bool cDevice::Receiving(bool Dummy) const #define TS_SCRAMBLING_TIMEOUT 3 // seconds to wait until a TS becomes unscrambled #define TS_SCRAMBLING_TIME_OK 10 // seconds before a Channel/CAM combination is marked as known to decrypt +#define EIT_INJECTION_TIME 10 // seconds for which to inject EIT event void cDevice::Action(void) { @@ -1697,6 +1698,18 @@ void cDevice::Action(void) } } } + // Inject EIT event to avoid the CAMs parental rating prompt: + if (Receiver->startEitInjection) { + time_t Now = time(NULL); + if (cCamSlot *cs = CamSlot()) { + if (Now != Receiver->lastEitInjection) { // once per second + cs->InjectEit(Receiver->ChannelID().Sid()); + Receiver->lastEitInjection = Now; + } + } + if (Now - Receiver->startEitInjection > EIT_INJECTION_TIME) + Receiver->startEitInjection = 0; + } } } Unlock(); @@ -1755,6 +1768,10 @@ bool cDevice::AttachReceiver(cReceiver *Receiver) Unlock(); if (camSlot && Receiver->priority > MINPRIORITY) { // priority check to avoid an infinite loop with the CAM slot's caPidReceiver camSlot->StartDecrypting(); + if (camSlot->WantsTsData()) { + Receiver->lastEitInjection = 0; + Receiver->startEitInjection = time(NULL); + } if (CamSlots.NumReadyMasterSlots() > 1) { // don't try different CAMs if there is only one Receiver->startScrambleDetection = time(NULL); Receiver->scramblingTimeout = TS_SCRAMBLING_TIMEOUT; diff --git a/mtd.c b/mtd.c index 29fe520f..345288ec 100644 --- a/mtd.c +++ b/mtd.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: mtd.c 1.10 2017/04/26 08:33:54 kls Exp $ + * $Id: mtd.c 1.11 2017/05/01 09:19:52 kls Exp $ */ #include "mtd.h" @@ -64,22 +64,20 @@ int cMtdHandler::Put(const uchar *Data, int Count) if (int Skipped = TS_SYNC(Data, Count)) return Used + Skipped; int Pid = TsPid(Data); - if (Pid != CATPID) { // the original CAT with mapped PIDs must be skipped here! #ifdef KEEPPIDS - int Index = 0; + int Index = 0; #else - int Index = (Pid >> UNIQ_PID_SHIFT) - 1; + int Index = (Pid >> UNIQ_PID_SHIFT) - 1; #endif // KEEPPIDS - if (Index >= 0 && Index < camSlots.Size()) { - int w = camSlots[Index]->PutData(Data, TS_SIZE); - if (w == 0) - break; - else if (w != TS_SIZE) - esyslog("ERROR: incomplete MTD packet written (%d) in PID %d (%04X)", Index + 1, Pid, Pid); - } - else if (Index >= 0) // we silently ignore Index -1 (i.e. MTD number 0), since there are several hundred empty TS packets when switching to an encrypted channel for the first time since startup - esyslog("ERROR: invalid MTD number (%d) in PID %d (%04X)", Index + 1, Pid, Pid); + if (Index >= 0 && Index < camSlots.Size()) { + int w = camSlots[Index]->PutData(Data, TS_SIZE); + if (w == 0) + break; + else if (w != TS_SIZE) + esyslog("ERROR: incomplete MTD packet written (%d) in PID %d (%04X)", Index + 1, Pid, Pid); } + else if (Index >= 0) // anything with Index -1 (i.e. MTD number 0) is either garbage or an actual CAT or EIT, which need not be returned to the device + esyslog("ERROR: invalid MTD number (%d) in PID %d (%04X)", Index + 1, Pid, Pid); Data += TS_SIZE; Count -= TS_SIZE; Used += TS_SIZE; @@ -329,6 +327,11 @@ uchar *cMtdCamSlot::Decrypt(uchar *Data, int &Count) return d; } +void cMtdCamSlot::InjectEit(int Sid) +{ + MasterSlot()->InjectEit(mtdMapper->RealToUniqSid(Sid)); +} + int cMtdCamSlot::PutData(const uchar *Data, int Count) { int Free = mtdBuffer->Free(); diff --git a/mtd.h b/mtd.h index 7f2151a3..2be5f3dc 100644 --- a/mtd.h +++ b/mtd.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: mtd.h 1.7 2017/04/26 09:17:08 kls Exp $ + * $Id: mtd.h 1.8 2017/05/01 09:19:21 kls Exp $ */ #ifndef __MTD_H @@ -172,6 +172,7 @@ public: virtual void StartDecrypting(void); virtual void StopDecrypting(void); virtual uchar *Decrypt(uchar *Data, int &Count); + virtual void InjectEit(int Sid); int PutData(const uchar *Data, int Count); int PutCat(const uchar *Data, int Count); // The following functions shall not be called for a cMtdCamSlot: diff --git a/receiver.c b/receiver.c index ef3a5cd1..f664da4a 100644 --- a/receiver.c +++ b/receiver.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: receiver.c 4.3 2017/04/02 10:08:49 kls Exp $ + * $Id: receiver.c 4.4 2017/05/01 08:49:20 kls Exp $ */ #include "receiver.h" @@ -19,6 +19,8 @@ cReceiver::cReceiver(const cChannel *Channel, int Priority) lastScrambledPacket = 0; startScrambleDetection = 0; scramblingTimeout = 0; + startEitInjection = 0; + lastEitInjection = 0; SetPids(Channel); } diff --git a/receiver.h b/receiver.h index 0ba8cb44..9bbc6bdb 100644 --- a/receiver.h +++ b/receiver.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: receiver.h 4.2 2017/04/02 10:08:49 kls Exp $ + * $Id: receiver.h 4.3 2017/05/01 08:48:34 kls Exp $ */ #ifndef __RECEIVER_H @@ -25,6 +25,8 @@ private: time_t lastScrambledPacket; time_t startScrambleDetection; int scramblingTimeout; + time_t startEitInjection; + time_t lastEitInjection; bool WantsPid(int Pid); protected: cDevice *Device(void) { return device; } diff --git a/remux.c b/remux.c index 4a3ff143..6993e9b7 100644 --- a/remux.c +++ b/remux.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remux.c 4.6 2017/04/24 14:59:39 kls Exp $ + * $Id: remux.c 4.7 2017/04/29 12:25:09 kls Exp $ */ #include "remux.h" @@ -940,6 +940,93 @@ bool cPatPmtParser::GetVersions(int &PatVersion, int &PmtVersion) const return patVersion >= 0 && pmtVersion >= 0; } +// --- cEitGenerator --------------------------------------------------------- + +cEitGenerator::cEitGenerator(int Sid) +{ + counter = 0; + version = 0; + if (Sid) + Generate(Sid); +} + +uint16_t cEitGenerator::YMDtoMJD(int Y, int M, int D) +{ + int L = (M < 3) ? 1 : 0; + return 14956 + D + int((Y - L) * 365.25) + int((M + 1 + L * 12) * 30.6001); +} + +uchar *cEitGenerator::AddParentalRatingDescriptor(uchar *p, uchar ParentalRating) +{ + *p++ = SI::ParentalRatingDescriptorTag; + *p++ = 0x04; // descriptor length + *p++ = 'D'; // country code + *p++ = 'E'; + *p++ = 'U'; + *p++ = ParentalRating; + return p; +} + +uchar *cEitGenerator::Generate(int Sid) +{ + uchar *PayloadStart; + uchar *SectionStart; + uchar *DescriptorsStart; + memset(eit, 0xFF, sizeof(eit)); + struct tm tm_r; + time_t t = time(NULL) - 3600; // let's have the event start one hour in the past + tm *tm = localtime_r(&t, &tm_r); + uint16_t MJD = YMDtoMJD(tm->tm_year, tm->tm_mon + 1, tm->tm_mday); + uchar *p = eit; + // TS header: + *p++ = TS_SYNC_BYTE; + *p++ = TS_PAYLOAD_START; + *p++ = EITPID; + *p++ = 0x10 | (counter++ & 0x0F); // continuity counter + *p++ = 0x00; // pointer field (payload unit start indicator is set) + // payload: + PayloadStart = p; + *p++ = 0x4E; // TID present/following event on this transponder + *p++ = 0xF0; + *p++ = 0x00; // section length + SectionStart = p; + *p++ = Sid >> 8; + *p++ = Sid & 0xFF; + *p++ = 0xC1 | (version << 1); + *p++ = 0x00; // section number + *p++ = 0x00; // last section number + *p++ = 0x00; // transport stream id + *p++ = 0x00; // ... + *p++ = 0x00; // original network id + *p++ = 0x00; // ... + *p++ = 0x00; // segment last section number + *p++ = 0x4E; // last table id + *p++ = 0x00; // event id + *p++ = 0x01; // ... + *p++ = MJD >> 8; // start time + *p++ = MJD & 0xFF; // ... + *p++ = tm->tm_hour; // ... + *p++ = tm->tm_min; // ... + *p++ = tm->tm_sec; // ... + *p++ = 0x24; // duration (one day, should cover everything) + *p++ = 0x00; // ... + *p++ = 0x00; // ... + *p++ = 0x90; // running status, free/CA mode + *p++ = 0x00; // descriptors loop length + DescriptorsStart = p; + p = AddParentalRatingDescriptor(p); + // fill in lengths: + *(SectionStart - 1) = p - SectionStart + 4; // +4 = length of CRC + *(DescriptorsStart - 1) = p - DescriptorsStart; + // checksum + int crc = SI::CRC32::crc32((char *)PayloadStart, p - PayloadStart, 0xFFFFFFFF); + *p++ = crc >> 24; + *p++ = crc >> 16; + *p++ = crc >> 8; + *p++ = crc; + return eit; +} + // --- cTsToPes -------------------------------------------------------------- cTsToPes::cTsToPes(void) diff --git a/remux.h b/remux.h index 7dd5b163..1bd96d9b 100644 --- a/remux.h +++ b/remux.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remux.h 4.3 2017/03/26 13:06:37 kls Exp $ + * $Id: remux.h 4.4 2017/04/29 11:56:21 kls Exp $ */ #ifndef __REMUX_H @@ -51,6 +51,7 @@ public: #define PATPID 0x0000 // PAT PID (constant 0) #define CATPID 0x0001 // CAT PID (constant 1) +#define EITPID 0x0012 // EIT PID (constant 18) #define MAXPID 0x2000 // for arrays that use a PID as the index #define PTSTICKS 90000 // number of PTS ticks per second @@ -431,6 +432,22 @@ public: uint16_t AncillaryPageId(int i) const { return (0 <= i && i < MAXSPIDS) ? ancillaryPageIds[i] : uint16_t(0); } }; +// EIT Generator: + +class cEitGenerator { +private: + uchar eit[TS_SIZE]; + int counter; + int version; + uint16_t YMDtoMJD(int Y, int M, int D); + uchar *AddParentalRatingDescriptor(uchar *p, uchar ParentalRating = 0); +public: + cEitGenerator(int Sid = 0); + uchar *Generate(int Sid); + uchar *Data(void) { return eit; } + int Length(void) { return sizeof(eit); } + }; + // TS to PES converter: // Puts together the payload of several TS packets that form one PES // packet.