vdr/pat.c
Klaus Schmidinger 5619c0602b Version 1.7.17
- Updated the Estonian OSD texts (thanks to Arthur Konovalov).
- Fixed following symbolic links in RemoveFileOrDir() (cont'd) (thanks to
  Steffen Barszus).
- Changed the description of cDevice::GetSTC() to make it mandatory for devices
  that can replay.
- Removed the check for positive STC values from cDvbSubtitleConverter::Action().
- Added cString::operator=(const char *String) (suggested by Antti Seppälä).
- Some spelling fixes (thanks to Ville Skyttä).
- Passing package name and version to xgettext (thanks to Ville Skyttä).
- Made 'dist' target dependent on up to date *.po (thanks to Ville Skyttä).
- Added Language and fixed Language-Team header of *.po (thanks to Ville Skyttä).
- Updated the Lithuanian OSD texts (thanks to Valdemaras Pipiras).
- Fixed detecting frames on channels that broadcast with 50 or 60 fps.
  This avoids artifacts during fast forward/rewind when replaying recordings from such
  channels. To fix the index of existing recordings from such channels, just delete the
  'index' file of the recording and VDR will generate a new one the next time you play it.
  You should also change the line "F 25" to "F 50" in the 'info' file of that recording.
- Added support for "registration descriptor" to 'libsi' and using it in pat.c (thanks
  to Rolf Ahrenberg).
- Fixed unjustified log entries about changed channel pids (reported by Derek Kelly).
- Added an include of VDR's 'Make.global' to libsi's Makefile (thanks to Rolf
  Ahrenberg).
- Removed displaying the "contents" information from the "Classic VDR" and
  "ST:TNG Panels" skins, because it is often wrong and nothing but irritating.
- Added typecasts to avoid gcc 4.5 warnings in switch statements on eKeys
  variables where additional 'k_...' flags are used.
- Fixed inclusion of <stdarg.h> (thanks to Henning Heinold).
- Changed "frame duration" to "frame rate" in vdr.5 (reported by Tobias Grimm).
- Removing a cRemote from the Remotes list in case its initialization failed (thanks
  to Dominik Strasser).
- Added LDFLAGS to the linker calls in the Makefiles (thanks to Joerg Bornkessel and
  Paul Menzel).
- Now updating the 'frames per second' data in the list of recordings when a new
  recording is started that has a frame rate other than the default.
- The include path to the freetype2 header files is now retrieved via a call to
  'pkg-config --cflags freetype2' (suggested by Andreas Oberritter).
- The OSD now has full TrueColor support. There can be several "pixmaps" that can
  be overlayed with alpha blending. All existing skins should work out of the box
  with the TrueColor OSD - the only exception being cOsd::GetBitmap(). Since the
  TrueColor OSD doesn't use bitmaps, this function will return a dummy bitmap, which
  may not be what the plugin expects. As long as this bitmap is only used for setting
  the palette, there is no problem. However, any other operations on this bitmap will
  have no effect. See the description of the cPixmap functions in osd.h for details
  about the new functionalities.
  The "ST:TNG Panels" skin has been enhanced to automatically use the TrueColor OSD
  if available.
  The "osddemo" plugin has been extended to show some of the possibilities of the
  TrueColor OSD if it is run on a system that actually provides TrueColor support.
  Thanks to Reinhard Nissl for some valuable input, help with debugging, and an
  implementation of the AlphaBlend() function.
- Updated the Slovakian language texts (thanks to Milan Hrala).
- Added Serbian language texts (thanks to Milan Cvijanovic).
- Fixed reallocating memory in the "pictures" plugin (reported by Paul Menzel, with
  input from Oliver Endriss).
- Fixed reallocating memory in cTsToPes::PutTs() (suggested by Oliver Endriss).
- Now checking the result of all realloc() calls.
- Fixed setting up the 'Recordings' menu in case there are several recordings
  with exactly the same name (reported by Marcus Hilbrich).
- Setting the audio type of language descriptors to 0x00 in the PAT/PMT generator
  (thanks to Anssi Hannula).
- Changed the compiler optimization flag to -O3, which gives quite a performance
  boost in the AlphaBlend() function.
- While replaying, the editing marks are now updated every 10 seconds (based on a
  patch from Manuel Reimer).
- Now reducing the thread and I/O priority cCuttingThread::Action() to make the
  foreground process more responsive (suggested by Frank Neumann).
- Removed checking for minimum line length of 21 characters in the LIRC receiver code
  (reported by Gerald Dachs).
- Updated the Romanian OSD texts (thanks to Lucian Muresan).
- Now storing the original display size when handling DVB subtitles (thanks to
  Reinhard Nissl).
- The original display size of subtitles is now used to scale them properly when
  displaying them on an HD OSD.
2011-03-19 19:02:09 +01:00

525 lines
20 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 2.15 2010/11/07 13:47:16 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;
int esPid;
int length;
uchar *data;
public:
cCaDescriptor(int CaSystem, int CaPid, int EsPid, int Length, const uchar *Data);
virtual ~cCaDescriptor();
bool operator== (const cCaDescriptor &arg) const;
int CaSystem(void) { return caSystem; }
int EsPid(void) { return esPid; }
int Length(void) const { return length; }
const uchar *Data(void) const { return data; }
};
cCaDescriptor::cCaDescriptor(int CaSystem, int CaPid, int EsPid, int Length, const uchar *Data)
{
caSystem = CaSystem;
esPid = EsPid;
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 esPid == arg.esPid && 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, int EsPid);
int GetCaDescriptors(const int *CaSystemIds, int BufSize, uchar *Data, int EsPid);
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, int EsPid)
{
cCaDescriptor *nca = new cCaDescriptor(d->getCaType(), d->getCaPid(), EsPid, 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 %04X -", source, transponder, serviceId, d->getCaType(), EsPid);
for (int i = 0; i < nca->Length(); i++)
q += sprintf(q, " %02X", nca->Data()[i]);
dsyslog("%s", buffer);
#endif
}
// EsPid is to select the "type" of CaDescriptor to be returned
// >0 - CaDescriptor for the particular esPid
// =0 - common CaDescriptor
// <0 - all CaDescriptors regardless of type (old default)
int cCaDescriptors::GetCaDescriptors(const int *CaSystemIds, int BufSize, uchar *Data, int EsPid)
{
if (!CaSystemIds || !*CaSystemIds)
return 0;
if (BufSize > 0 && Data) {
int length = 0;
for (cCaDescriptor *d = caDescriptors.First(); d; d = caDescriptors.Next(d)) {
if (EsPid < 0 || d->EsPid() == EsPid) {
const int *caids = CaSystemIds;
do {
if (d->CaSystem() == *caids) {
if (length + d->Length() <= BufSize) {
memcpy(Data + length, d->Data(), d->Length());
length += d->Length();
}
else
return -1;
}
} while (*++caids);
}
}
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 int *CaSystemIds, int BufSize, uchar *Data, int EsPid);
};
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 int *CaSystemIds, int BufSize, uchar *Data, int EsPid)
{
cMutexLock MutexLock(&mutex);
for (cCaDescriptors *ca = First(); ca; ca = Next(ca)) {
if (ca->Is(Source, Transponder, ServiceId))
return ca->GetCaDescriptors(CaSystemIds, BufSize, Data, EsPid);
}
return 0;
}
cCaDescriptorHandler CaDescriptorHandler;
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, uchar *Data, int EsPid)
{
return CaDescriptorHandler.GetCaDescriptors(Source, Transponder, ServiceId, CaSystemIds, BufSize, Data, EsPid);
}
// --- 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, 0);
delete d;
}
// Scan the stream-specific loop:
SI::PMT::Stream stream;
int Vpid = 0;
int Ppid = 0;
int Vtype = 0;
int Apids[MAXAPIDS + 1] = { 0 }; // these lists are zero-terminated
int Atypes[MAXAPIDS + 1] = { 0 };
int Dpids[MAXDPIDS + 1] = { 0 };
int Dtypes[MAXDPIDS + 1] = { 0 };
int Spids[MAXSPIDS + 1] = { 0 };
uchar SubtitlingTypes[MAXSPIDS + 1] = { 0 };
uint16_t CompositionPageIds[MAXSPIDS + 1] = { 0 };
uint16_t AncillaryPageIds[MAXSPIDS + 1] = { 0 };
char ALangs[MAXAPIDS][MAXLANGCODE2] = { "" };
char DLangs[MAXDPIDS][MAXLANGCODE2] = { "" };
char SLangs[MAXSPIDS][MAXLANGCODE2] = { "" };
int Tpid = 0;
int NumApids = 0;
int NumDpids = 0;
int NumSpids = 0;
for (SI::Loop::Iterator it; pmt.streamLoop.getNext(stream, it); ) {
bool ProcessCaDescriptors = false;
int esPid = stream.getPid();
switch (stream.getStreamType()) {
case 1: // STREAMTYPE_11172_VIDEO
case 2: // STREAMTYPE_13818_VIDEO
case 0x1B: // MPEG4
Vpid = esPid;
Ppid = pmt.getPCRPid();
Vtype = stream.getStreamType();
ProcessCaDescriptors = true;
break;
case 3: // STREAMTYPE_11172_AUDIO
case 4: // STREAMTYPE_13818_AUDIO
case 0x0F: // ISO/IEC 13818-7 Audio with ADTS transport sytax
case 0x11: // ISO/IEC 14496-3 Audio with LATM transport syntax
{
if (NumApids < MAXAPIDS) {
Apids[NumApids] = esPid;
Atypes[NumApids] = stream.getStreamType();
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++;
}
ProcessCaDescriptors = true;
}
break;
case 5: // STREAMTYPE_13818_PRIVATE
case 6: // STREAMTYPE_13818_PES_PRIVATE
//XXX case 8: // STREAMTYPE_13818_DSMCC
{
int dpid = 0;
int dtype = 0;
char lang[MAXLANGCODE1] = { 0 };
SI::Descriptor *d;
for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
switch (d->getDescriptorTag()) {
case SI::AC3DescriptorTag:
case SI::EnhancedAC3DescriptorTag:
dpid = esPid;
dtype = d->getDescriptorTag();
ProcessCaDescriptors = true;
break;
case SI::SubtitlingDescriptorTag:
if (NumSpids < MAXSPIDS) {
Spids[NumSpids] = esPid;
SI::SubtitlingDescriptor *sd = (SI::SubtitlingDescriptor *)d;
SI::SubtitlingDescriptor::Subtitling sub;
char *s = SLangs[NumSpids];
int n = 0;
for (SI::Loop::Iterator it; sd->subtitlingLoop.getNext(sub, it); ) {
if (sub.languageCode[0]) {
SubtitlingTypes[NumSpids] = sub.getSubtitlingType();
CompositionPageIds[NumSpids] = sub.getCompositionPageId();
AncillaryPageIds[NumSpids] = sub.getAncillaryPageId();
if (n > 0)
*s++ = '+';
strn0cpy(s, I18nNormalizeLanguageCode(sub.languageCode), MAXLANGCODE1);
s += strlen(s);
if (n++ > 1)
break;
}
}
NumSpids++;
}
break;
case SI::TeletextDescriptorTag:
Tpid = esPid;
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;
Dtypes[NumDpids] = dtype;
strn0cpy(DLangs[NumDpids], lang, MAXLANGCODE1);
NumDpids++;
}
}
}
break;
case 0x80 ... 0xFF: // STREAMTYPE_USER_PRIVATE
{
char lang[MAXLANGCODE1] = { 0 };
bool IsAc3 = false;
SI::Descriptor *d;
for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
switch (d->getDescriptorTag()) {
case SI::RegistrationDescriptorTag: {
SI::RegistrationDescriptor *rd = (SI::RegistrationDescriptor *)d;
// http://www.smpte-ra.org/mpegreg/mpegreg.html
switch (rd->getFormatIdentifier()) {
case 0x44434949: // 'DCII' aka. DigiCipher II
Vpid = esPid;
Ppid = pmt.getPCRPid();
Vtype = 0x02; // DCII compression is based upon MPEG-2
ProcessCaDescriptors = true;
break;
case 0x41432D33: // 'AC-3'
IsAc3 = true;
break;
default:
//printf("Format identifier: 0x08X\n", rd->getFormatIdentifier());
break;
}
}
break;
case SI::ISO639LanguageDescriptorTag: {
SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
strn0cpy(lang, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1);
}
break;
default: ;
}
delete d;
}
if (IsAc3) {
if (NumDpids < MAXDPIDS) {
Dpids[NumDpids] = esPid;
Dtypes[NumDpids] = SI::AC3DescriptorTag;
strn0cpy(DLangs[NumDpids], lang, MAXLANGCODE1);
NumDpids++;
}
ProcessCaDescriptors = true;
}
}
break;
default: ;//printf("PID: %5d %5d %2d %3d %3d\n", pmt.getServiceId(), stream.getPid(), stream.getStreamType(), pmt.getVersionNumber(), Channel->Number());
}
if (ProcessCaDescriptors) {
for (SI::Loop::Iterator it; (d = (SI::CaDescriptor*)stream.streamDescriptors.getNext(it, SI::CaDescriptorTag)); ) {
CaDescriptors->AddCaDescriptor(d, esPid);
delete d;
}
}
}
if (Setup.UpdateChannels >= 2) {
Channel->SetPids(Vpid, Ppid, Vtype, Apids, Atypes, ALangs, Dpids, Dtypes, DLangs, Spids, SLangs, Tpid);
Channel->SetCaIds(CaDescriptors->CaIds());
Channel->SetSubtitlingDescriptors(SubtitlingTypes, CompositionPageIds, AncillaryPageIds);
}
Channel->SetCaDescriptors(CaDescriptorHandler.AddCaDescriptors(CaDescriptors));
}
lastPmtScan = 0; // this triggers the next scan
Channels.Unlock();
}
}