vdr/pat.c
Klaus Schmidinger 396d0ad367 Version 1.3.46
- Fixed handling broken PMT records (thanks to Marcel Wiesweg for pointing out how
  to detect these).
- Added a missing "Button$" for the Timer button and "Key$" in skinclassic.c
  (thanks to Rolf Ahrenberg).
- Fixed broken entry 'A111.1W' in sources.conf (reported by Luca Olivetti).
- Replaced the obsolete entry 'S21.5E' in the default 'diseqc.conf' with 'S13.0E'
  (reported by Ville Skyttä).
- Fixed learning keys when VDR is already running (thanks to Jurij Retzlaff).
- Fixed handling the system time transponder setting in the Setup/EPG menu, which
  was broken by the min/max fix in cMenuEditIntItem.
- VPS timers now record only events that have exactly the given start time.
  This fix also implements recording several subsequent events that have the
  same VPS time (like a sports event with intermittent news breaks).
- When checking for timers that have entered the "VPS margin", any free devices are
  now used to switch to the needed transponder. This improves cases where more than
  one VPS timer is about to start.
- Fixed handling the VPS margin in case the event's duration is shorter than the
  margin.
- Fixed handling VPS timers in case the primary device needs to switch to the
  timer's transponder.
- Now avoiding the 'actual' device when starting a recording, so that a Transfer
  Mode for live tv isn't interrupted.
- Fixed a typo in skins.h (thanks to Alexander Rieger).
- cSkins::QueueMessage() called from a background thread with an empty message
  now clears all messages that have been previously queued by that thread and have
  not yet beed displayed (thanks to Alexander Rieger).
- Fixed handling the color button texts when switching from the 'Schedule' menu of
  a channel without EPG info to the 'What's on now' menu (reported by Rolf
  Ahrenberg).
- cMenuEditIntItem and cMenuEditChanItem can now be given strings to label the
  minimum and maximum values, and the case that no channel has been selected,
  respectively.
- The initial channel and volume can now be defined in the "Setup/Miscellaneous"
  menu (based on a patch from Thomas Keil).
- When hitting the end of a recording in fast forward mode, VDR no longer switches
  back to normal speed if the recording is already finished (thanks to Reinhard
  Nissl).
- No longer calling cPlugin::ProcessArgs() if VDR is run with the --help or
  --version option, to avoid error messages from plugins (reported by Udo Richter).
- Now checking whether there is any text before calling cStatus::MsgOsdTextItem()
  (reported by Joachim Wilke).
2006-04-09 18:00:00 +02:00

428 lines
15 KiB
C

/*
* pat.c: PAT section filter
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: pat.c 1.16 2006/03/31 12:39:34 kls Exp $
*/
#include "pat.h"
#include <malloc.h>
#include "channels.h"
#include "libsi/section.h"
#include "libsi/descriptor.h"
#include "thread.h"
#define PMT_SCAN_TIMEOUT 10 // seconds
// --- cCaDescriptor ---------------------------------------------------------
class cCaDescriptor : public cListObject {
private:
int caSystem;
bool stream;
int length;
uchar *data;
public:
cCaDescriptor(int CaSystem, int CaPid, bool Stream, int Length, const uchar *Data);
virtual ~cCaDescriptor();
bool operator== (const cCaDescriptor &arg) const;
int CaSystem(void) { return caSystem; }
int Stream(void) { return stream; }
int Length(void) const { return length; }
const uchar *Data(void) const { return data; }
};
cCaDescriptor::cCaDescriptor(int CaSystem, int CaPid, bool Stream, int Length, const uchar *Data)
{
caSystem = CaSystem;
stream = Stream;
length = Length + 6;
data = MALLOC(uchar, length);
data[0] = SI::CaDescriptorTag;
data[1] = length - 2;
data[2] = (caSystem >> 8) & 0xFF;
data[3] = caSystem & 0xFF;
data[4] = ((CaPid >> 8) & 0x1F) | 0xE0;
data[5] = CaPid & 0xFF;
if (Length)
memcpy(&data[6], Data, Length);
}
cCaDescriptor::~cCaDescriptor()
{
free(data);
}
bool cCaDescriptor::operator== (const cCaDescriptor &arg) const
{
return length == arg.length && memcmp(data, arg.data, length) == 0;
}
// --- cCaDescriptors --------------------------------------------------------
class cCaDescriptors : public cListObject {
private:
int source;
int transponder;
int serviceId;
int numCaIds;
int caIds[MAXCAIDS + 1];
cList<cCaDescriptor> caDescriptors;
void AddCaId(int CaId);
public:
cCaDescriptors(int Source, int Transponder, int ServiceId);
bool operator== (const cCaDescriptors &arg) const;
bool Is(int Source, int Transponder, int ServiceId);
bool Is(cCaDescriptors * CaDescriptors);
bool Empty(void) { return caDescriptors.Count() == 0; }
void AddCaDescriptor(SI::CaDescriptor *d, bool Stream);
int GetCaDescriptors(const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
const int *CaIds(void) { return caIds; }
};
cCaDescriptors::cCaDescriptors(int Source, int Transponder, int ServiceId)
{
source = Source;
transponder = Transponder;
serviceId = ServiceId;
numCaIds = 0;
caIds[0] = 0;
}
bool cCaDescriptors::operator== (const cCaDescriptors &arg) const
{
cCaDescriptor *ca1 = caDescriptors.First();
cCaDescriptor *ca2 = arg.caDescriptors.First();
while (ca1 && ca2) {
if (!(*ca1 == *ca2))
return false;
ca1 = caDescriptors.Next(ca1);
ca2 = arg.caDescriptors.Next(ca2);
}
return !ca1 && !ca2;
}
bool cCaDescriptors::Is(int Source, int Transponder, int ServiceId)
{
return source == Source && transponder == Transponder && serviceId == ServiceId;
}
bool cCaDescriptors::Is(cCaDescriptors *CaDescriptors)
{
return Is(CaDescriptors->source, CaDescriptors->transponder, CaDescriptors->serviceId);
}
void cCaDescriptors::AddCaId(int CaId)
{
if (numCaIds < MAXCAIDS) {
for (int i = 0; i < numCaIds; i++) {
if (caIds[i] == CaId)
return;
}
caIds[numCaIds++] = CaId;
caIds[numCaIds] = 0;
}
}
void cCaDescriptors::AddCaDescriptor(SI::CaDescriptor *d, bool Stream)
{
cCaDescriptor *nca = new cCaDescriptor(d->getCaType(), d->getCaPid(), Stream, d->privateData.getLength(), d->privateData.getData());
for (cCaDescriptor *ca = caDescriptors.First(); ca; ca = caDescriptors.Next(ca)) {
if (*ca == *nca) {
delete nca;
return;
}
}
AddCaId(nca->CaSystem());
caDescriptors.Add(nca);
//#define DEBUG_CA_DESCRIPTORS 1
#ifdef DEBUG_CA_DESCRIPTORS
char buffer[1024];
char *q = buffer;
q += sprintf(q, "CAM: %04X %5d %5d %04X %d -", source, transponder, serviceId, d->getCaType(), Stream);
for (int i = 0; i < nca->Length(); i++)
q += sprintf(q, " %02X", nca->Data()[i]);
dsyslog(buffer);
#endif
}
int cCaDescriptors::GetCaDescriptors(const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
{
if (!CaSystemIds || !*CaSystemIds)
return 0;
if (BufSize > 0 && Data) {
int length = 0;
int IsStream = -1;
for (cCaDescriptor *d = caDescriptors.First(); d; d = caDescriptors.Next(d)) {
const unsigned short *caids = CaSystemIds;
do {
if (d->CaSystem() == *caids) {
if (length + d->Length() <= BufSize) {
if (IsStream >= 0 && IsStream != d->Stream())
dsyslog("CAM: different stream flag in CA descriptors");
IsStream = d->Stream();
memcpy(Data + length, d->Data(), d->Length());
length += d->Length();
}
else
return -1;
}
} while (*++caids);
}
StreamFlag = IsStream == 1;
return length;
}
return -1;
}
// --- cCaDescriptorHandler --------------------------------------------------
class cCaDescriptorHandler : public cList<cCaDescriptors> {
private:
cMutex mutex;
public:
int AddCaDescriptors(cCaDescriptors *CaDescriptors);
// Returns 0 if this is an already known descriptor,
// 1 if it is an all new descriptor with actual contents,
// and 2 if an existing descriptor was changed.
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
};
int cCaDescriptorHandler::AddCaDescriptors(cCaDescriptors *CaDescriptors)
{
cMutexLock MutexLock(&mutex);
for (cCaDescriptors *ca = First(); ca; ca = Next(ca)) {
if (ca->Is(CaDescriptors)) {
if (*ca == *CaDescriptors) {
delete CaDescriptors;
return 0;
}
Del(ca);
Add(CaDescriptors);
return 2;
}
}
Add(CaDescriptors);
return CaDescriptors->Empty() ? 0 : 1;
}
int cCaDescriptorHandler::GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
{
cMutexLock MutexLock(&mutex);
StreamFlag = false;
for (cCaDescriptors *ca = First(); ca; ca = Next(ca)) {
if (ca->Is(Source, Transponder, ServiceId))
return ca->GetCaDescriptors(CaSystemIds, BufSize, Data, StreamFlag);
}
return 0;
}
cCaDescriptorHandler CaDescriptorHandler;
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
{
return CaDescriptorHandler.GetCaDescriptors(Source, Transponder, ServiceId, CaSystemIds, BufSize, Data, StreamFlag);
}
// --- cPatFilter ------------------------------------------------------------
cPatFilter::cPatFilter(void)
{
pmtIndex = 0;
pmtPid = 0;
pmtSid = 0;
lastPmtScan = 0;
numPmtEntries = 0;
Set(0x00, 0x00); // PAT
}
void cPatFilter::SetStatus(bool On)
{
cFilter::SetStatus(On);
pmtIndex = 0;
pmtPid = 0;
pmtSid = 0;
lastPmtScan = 0;
numPmtEntries = 0;
}
void cPatFilter::Trigger(void)
{
numPmtEntries = 0;
}
bool cPatFilter::PmtVersionChanged(int PmtPid, int Sid, int Version)
{
uint64_t v = Version;
v <<= 32;
uint64_t id = (PmtPid | (Sid << 16)) & 0x00000000FFFFFFFFLL;
for (int i = 0; i < numPmtEntries; i++) {
if ((pmtVersion[i] & 0x00000000FFFFFFFFLL) == id) {
bool Changed = (pmtVersion[i] & 0x000000FF00000000LL) != v;
if (Changed)
pmtVersion[i] = id | v;
return Changed;
}
}
if (numPmtEntries < MAXPMTENTRIES)
pmtVersion[numPmtEntries++] = id | v;
return true;
}
void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
{
if (Pid == 0x00) {
if (Tid == 0x00) {
if (pmtPid && time(NULL) - lastPmtScan > PMT_SCAN_TIMEOUT) {
Del(pmtPid, 0x02);
pmtPid = 0;
pmtIndex++;
lastPmtScan = time(NULL);
}
if (!pmtPid) {
SI::PAT pat(Data, false);
if (!pat.CheckCRCAndParse())
return;
SI::PAT::Association assoc;
int Index = 0;
for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) {
if (!assoc.isNITPid()) {
if (Index++ >= pmtIndex && Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId())) {
pmtPid = assoc.getPid();
pmtSid = assoc.getServiceId();
Add(pmtPid, 0x02);
break;
}
}
}
if (!pmtPid)
pmtIndex = 0;
}
}
}
else if (Pid == pmtPid && Tid == SI::TableIdPMT && Source() && Transponder()) {
SI::PMT pmt(Data, false);
if (!pmt.CheckCRCAndParse())
return;
if (pmt.getServiceId() != pmtSid)
return; // skip broken PMT records
if (!PmtVersionChanged(pmtPid, pmt.getTableIdExtension(), pmt.getVersionNumber())) {
lastPmtScan = 0; // this triggers the next scan
return;
}
if (!Channels.Lock(true, 10)) {
numPmtEntries = 0; // to make sure we try again
return;
}
cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), pmt.getServiceId());
if (Channel) {
SI::CaDescriptor *d;
cCaDescriptors *CaDescriptors = new cCaDescriptors(Channel->Source(), Channel->Transponder(), Channel->Sid());
// Scan the common loop:
for (SI::Loop::Iterator it; (d = (SI::CaDescriptor*)pmt.commonDescriptors.getNext(it, SI::CaDescriptorTag)); ) {
CaDescriptors->AddCaDescriptor(d, false);
delete d;
}
// Scan the stream-specific loop:
SI::PMT::Stream stream;
int Vpid = 0;
int Ppid = pmt.getPCRPid();
int Apids[MAXAPIDS + 1] = { 0 }; // these lists are zero-terminated
int Dpids[MAXDPIDS + 1] = { 0 };
char ALangs[MAXAPIDS][MAXLANGCODE2] = { "" };
char DLangs[MAXDPIDS][MAXLANGCODE2] = { "" };
int Tpid = 0;
int NumApids = 0;
int NumDpids = 0;
for (SI::Loop::Iterator it; pmt.streamLoop.getNext(stream, it); ) {
switch (stream.getStreamType()) {
case 1: // STREAMTYPE_11172_VIDEO
case 2: // STREAMTYPE_13818_VIDEO
Vpid = stream.getPid();
break;
case 3: // STREAMTYPE_11172_AUDIO
case 4: // STREAMTYPE_13818_AUDIO
{
if (NumApids < MAXAPIDS) {
Apids[NumApids] = stream.getPid();
SI::Descriptor *d;
for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
switch (d->getDescriptorTag()) {
case SI::ISO639LanguageDescriptorTag: {
SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
SI::ISO639LanguageDescriptor::Language l;
char *s = ALangs[NumApids];
int n = 0;
for (SI::Loop::Iterator it; ld->languageLoop.getNext(l, it); ) {
if (*ld->languageCode != '-') { // some use "---" to indicate "none"
if (n > 0)
*s++ = '+';
strn0cpy(s, I18nNormalizeLanguageCode(l.languageCode), MAXLANGCODE1);
s += strlen(s);
if (n++ > 1)
break;
}
}
}
break;
default: ;
}
delete d;
}
NumApids++;
}
}
break;
case 5: // STREAMTYPE_13818_PRIVATE
case 6: // STREAMTYPE_13818_PES_PRIVATE
//XXX case 8: // STREAMTYPE_13818_DSMCC
{
int dpid = 0;
char lang[MAXLANGCODE1] = { 0 };
SI::Descriptor *d;
for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
switch (d->getDescriptorTag()) {
case SI::AC3DescriptorTag:
dpid = stream.getPid();
break;
case SI::TeletextDescriptorTag:
Tpid = stream.getPid();
break;
case SI::ISO639LanguageDescriptorTag: {
SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
strn0cpy(lang, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1);
}
break;
default: ;
}
delete d;
}
if (dpid) {
if (NumDpids < MAXDPIDS) {
Dpids[NumDpids] = dpid;
strn0cpy(DLangs[NumDpids], lang, MAXLANGCODE1);
NumDpids++;
}
}
}
break;
//default: printf("PID: %5d %5d %2d %3d %3d\n", pmt.getServiceId(), stream.getPid(), stream.getStreamType(), pmt.getVersionNumber(), Channel->Number());//XXX
}
for (SI::Loop::Iterator it; (d = (SI::CaDescriptor*)stream.streamDescriptors.getNext(it, SI::CaDescriptorTag)); ) {
CaDescriptors->AddCaDescriptor(d, true);
delete d;
}
}
if (Setup.UpdateChannels >= 2) {
Channel->SetPids(Vpid, Vpid ? Ppid : 0, Apids, ALangs, Dpids, DLangs, Tpid);
Channel->SetCaIds(CaDescriptors->CaIds());
}
Channel->SetCaDescriptors(CaDescriptorHandler.AddCaDescriptors(CaDescriptors));
}
lastPmtScan = 0; // this triggers the next scan
Channels.Unlock();
}
}