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

This commit is contained in:
Klaus Schmidinger 2017-05-01 09:32:32 +02:00
parent 7cfce2fffa
commit 073268bd45
11 changed files with 182 additions and 26 deletions

View File

@ -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.

13
ci.c
View File

@ -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;

13
ci.h
View File

@ -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<cCamSlot> {

View File

@ -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

View File

@ -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;

29
mtd.c
View File

@ -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();

3
mtd.h
View File

@ -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:

View File

@ -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);
}

View File

@ -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; }

89
remux.c
View File

@ -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)

19
remux.h
View File

@ -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.