mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
VDR developer version 2.1.7 is now available at ftp://ftp.tvdr.de/vdr/Developer/vdr-2.1.7.tar.bz2 A 'diff' against the previous version is available at ftp://ftp.tvdr.de/vdr/Developer/vdr-2.1.6-2.1.7.diff MD5 checksums: 1c954bad31ce74cd1cbd7987e62d2a98 vdr-2.1.7.tar.bz2 88a90327a75833b7723942d3bd25f954 vdr-2.1.6-2.1.7.diff WARNING: ======== This is a *developer* version. Even though *I* use it in my productive environment, I strongly recommend that you only use it under controlled conditions and for testing and debugging. From the HISTORY file: - No longer logging an error message in DirSizeMB() if the given directory doesn't exist. This avoids lots of log entries in case several VDRs use the same video directory and one of them has already physically removed a recording directory, while the others still have it in their list of deleted recordings. - Updated the Italian OSD texts (thanks to Diego Pierotto). - A cCamSlot that has WantsTsData set to true in its constructor now also gets the CAT and EMM PIDs data. - Fixed a possible division by zero in frame rate detection. - VDR now reads command line options from *.conf files in /etc/vdr/conf.d (thanks to Lars Hanisch). See vdr.1 and vdr.5 for details. - Fixed a possible crash in the LCARS skin (thanks to Thomas Reufer). - Updated the dvbhddevice plugin source. - Fixed a bug in the Makefile when installing plugins with LCLBLD=1 (thanks to Stefan Huelswitt). - The pid of the PMT in which the CA descriptors of a given channel are broadcast is now stored together with the CA descriptors and can be retrieved by calling GetPmtPid() (this information is only required to receive encrypted channels with the OctopusNet receiver via the 'satip' plugin). - Channels that are not listed in the SDT are now only marked as OBSOLETE if "Setup/DVB/Update channels" is set to a value other than "no" or "PIDs only". - Fixed multiple OBSOLETE marks in channels that are not listed in the SDT in case "Setup/Miscellaneous/Show channel names with source" is set to "yes". - The new function cOsd::DrawScaledBitmap() is now used for drawing subtitles. This function can be reimplemented by high level OSDs which may be able to do the scaling in hardware or otherwise more efficiently (thanks to Thomas Reufer). - Fixed detaching receivers from devices in case a CAM needs to receive the TS (reported by Dietmar Spingler). - Fixed resetting the receiver for EMM pids for CAMs that need to receive the TS (reported by Dietmar Spingler). - Fixed (well, actually worked around) a problem with subtitles not being displayed because the broadcaster doesn't set the data's version numbers as required by the DVB standard (thanks to Rolf Ahrenberg). - Fixed support for systemd (thanks to Christopher Reimer). - Added a missing backslash to the help text of the SVDRP command MOVR (thanks to Lars Hanisch). - Added subsystem id support for DVB devices connected via USB (thanks to Jose Alberto Reguero). - Added the functions IndexOf(), InsertUnique(), AppendUnique() and RemoveElement() to the cVector class (thanks to Stefan Schallenberg). - Fixed a possible out-of-bounds access in cVector::Remove(). - Added functions to set and retrieve the priority of a cReceiver (suggested by Frank Schmirler). - Added the new parameters "Setup/Miscellaneous/Volume steps" and ".../Volume linearize" (thanks to Claus Muus). See the MANUAL for details. - Fixed jumping to an absolute position via the Red key in case replay was paused (reported by Dieter Ferdinand). - Changed the German weekday names from "MonDieMitDonFreSamSon" to "Mo.Di.Mi.Do.Fr.Sa.So." (thanks to Stefan Blochberger). - Now handling CAT sections that consist of more than one TS packet. - Added handling for DTS audio tracks to cPatPmtParser::ParsePmt() (thanks to Thomas Reufer). - Added support for PGS subtitles (thanks to Thomas Reufer). - Use of the function cOsd::GetBitmap() outside of derived classes is now deprecated, and it may be made 'protected' in a future version, since it doesn't work with TrueColor OSDs. Plugin authors may want to modify their code so that it works without this function. - Modified the descriptions of several threads, so that the important information (like device or frontend numbers) is within the first 15 characters of the string, because only these are displayed in thread listings. Plugin authors may want to do the same. - Added the channel name to log messages that reference a channel (suggested by Dietmar Spingler). - Modified the CAM API so that it is possible to implement CAMs that can be freely assigned to any devices (thanks to Jasmin Jessich). - Plugins can now implement the function SetMenuSortMode() in their skin objects derived from cSkinDisplayMenu, to get informed about the currently used sort mode, if applicable (suggested by Martin Schirrmacher). - Added cOsdProvider::OsdSizeChanged(), which plugins that implement an output device can call to signal a change in the OSD that requires a redraw of the currently displayed object (thanks to Thomas Reufer). - Added a comment to cRecorder::Activate() about the need to call Detach() in the destructor (suggested by Eike Sauer). - Now returning from removing deleted recordings after at most 10 seconds, or if the user presses a remote control key, to keep the system from getting unresponsive when removing a huge number of files (reported by Dieter Ferdinand). - Fixed generating the index file of an existing recording in case at the of a TS file there is less data in the buffer than needed by the frame detector. In such a case it was possible that frames were missed, and there was most likely a distortion when replaying that part of a recording. This is mostly a problem for recordings that consist of more than one *.ts file. Single file recordings could only lose some frames at their very end, which probably doesn't matter. At any rate, if you have generated an index file with VDR version 2.0.6, 2.1.5 or 2.1.6, you may want to do so again with this version to make sure the index is OK. - Added the new command line option --updindex, which can be used to update an incomplete index of a recording (based on a patch from Helmut Auer).
647 lines
25 KiB
C
647 lines
25 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 3.5 2015/01/04 13:14:01 kls Exp $
|
|
*/
|
|
|
|
#include "pat.h"
|
|
#include <malloc.h>
|
|
#include "channels.h"
|
|
#include "libsi/section.h"
|
|
#include "libsi/descriptor.h"
|
|
|
|
#define PMT_SCAN_TIMEOUT 1000 // ms
|
|
|
|
// --- cCaDescriptor ---------------------------------------------------------
|
|
|
|
class cCaDescriptor : public cListObject {
|
|
private:
|
|
int caSystem;
|
|
int caPid;
|
|
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 CaPid(void) { return caPid; }
|
|
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;
|
|
caPid = CaPid;
|
|
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 pmtPid; // needed for OctopusNet - otherwise irrelevant!
|
|
int numCaIds;
|
|
int caIds[MAXCAIDS + 1];
|
|
cList<cCaDescriptor> caDescriptors;
|
|
void AddCaId(int CaId);
|
|
public:
|
|
cCaDescriptors(int Source, int Transponder, int ServiceId, int PmtPid);
|
|
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);
|
|
int GetCaPids(const int *CaSystemIds, int BufSize, int *Pids);
|
|
const int GetPmtPid(void) { return pmtPid; };
|
|
const int *CaIds(void) { return caIds; }
|
|
};
|
|
|
|
cCaDescriptors::cCaDescriptors(int Source, int Transponder, int ServiceId, int PmtPid)
|
|
{
|
|
source = Source;
|
|
transponder = Transponder;
|
|
serviceId = ServiceId;
|
|
pmtPid = PmtPid;
|
|
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 (*caids == 0xFFFF || 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;
|
|
}
|
|
|
|
int cCaDescriptors::GetCaPids(const int *CaSystemIds, int BufSize, int *Pids)
|
|
{
|
|
if (!CaSystemIds || !*CaSystemIds)
|
|
return 0;
|
|
if (BufSize > 0 && Pids) {
|
|
int numPids = 0;
|
|
for (cCaDescriptor *d = caDescriptors.First(); d; d = caDescriptors.Next(d)) {
|
|
const int *caids = CaSystemIds;
|
|
do {
|
|
if (*caids == 0xFFFF || d->CaSystem() == *caids) {
|
|
if (numPids + 1 < BufSize) {
|
|
Pids[numPids++] = d->CaPid();
|
|
Pids[numPids] = 0;
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
} while (*++caids);
|
|
}
|
|
return numPids;
|
|
}
|
|
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 GetCaPids(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, int *Pids);
|
|
int GetPmtPid(int Source, int Transponder, int ServiceId);
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
int cCaDescriptorHandler::GetCaPids(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, int *Pids)
|
|
{
|
|
cMutexLock MutexLock(&mutex);
|
|
for (cCaDescriptors *ca = First(); ca; ca = Next(ca)) {
|
|
if (ca->Is(Source, Transponder, ServiceId))
|
|
return ca->GetCaPids(CaSystemIds, BufSize, Pids);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int cCaDescriptorHandler::GetPmtPid(int Source, int Transponder, int ServiceId)
|
|
{
|
|
cMutexLock MutexLock(&mutex);
|
|
for (cCaDescriptors *ca = First(); ca; ca = Next(ca)) {
|
|
if (ca->Is(Source, Transponder, ServiceId))
|
|
return ca->GetPmtPid();
|
|
}
|
|
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);
|
|
}
|
|
|
|
int GetCaPids(int Source, int Transponder, int ServiceId, const int *CaSystemIds, int BufSize, int *Pids)
|
|
{
|
|
return CaDescriptorHandler.GetCaPids(Source, Transponder, ServiceId, CaSystemIds, BufSize, Pids);
|
|
}
|
|
|
|
int GetPmtPid(int Source, int Transponder, int ServiceId)
|
|
{
|
|
return CaDescriptorHandler.GetPmtPid(Source, Transponder, ServiceId);
|
|
}
|
|
|
|
// --- cPatFilter ------------------------------------------------------------
|
|
|
|
//#define DEBUG_PAT_PMT
|
|
#ifdef DEBUG_PAT_PMT
|
|
#define DBGLOG(a...) { cString s = cString::sprintf(a); fprintf(stderr, "%s\n", *s); dsyslog("%s", *s); }
|
|
#else
|
|
#define DBGLOG(a...)
|
|
#endif
|
|
|
|
cPatFilter::cPatFilter(void)
|
|
{
|
|
Trigger(0);
|
|
Set(0x00, 0x00); // PAT
|
|
}
|
|
|
|
void cPatFilter::SetStatus(bool On)
|
|
{
|
|
cMutexLock MutexLock(&mutex);
|
|
DBGLOG("PAT filter set status %d", On);
|
|
cFilter::SetStatus(On);
|
|
Trigger();
|
|
}
|
|
|
|
void cPatFilter::Trigger(int Sid)
|
|
{
|
|
cMutexLock MutexLock(&mutex);
|
|
patVersion = -1;
|
|
pmtIndex = -1;
|
|
numPmtEntries = 0;
|
|
if (Sid >= 0) {
|
|
sid = Sid;
|
|
DBGLOG("PAT filter trigger SID %d", Sid);
|
|
}
|
|
}
|
|
|
|
bool cPatFilter::PmtVersionChanged(int PmtPid, int Sid, int Version, bool SetNewVersion)
|
|
{
|
|
int Id = MakePmtId(PmtPid, Sid);
|
|
for (int i = 0; i < numPmtEntries; i++) {
|
|
if (pmtId[i] == Id) {
|
|
if (pmtVersion[i] != Version) {
|
|
if (SetNewVersion)
|
|
pmtVersion[i] = Version;
|
|
else
|
|
DBGLOG("PMT %d %2d %5d %2d -> %2d", Transponder(), i, PmtPid, pmtVersion[i], Version);
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void cPatFilter::SwitchToNextPmtPid(void)
|
|
{
|
|
if (pmtIndex >= 0) {
|
|
Del(GetPmtPid(pmtIndex), SI::TableIdPMT);
|
|
pmtIndex = (pmtIndex + 1) % numPmtEntries;
|
|
Add(GetPmtPid(pmtIndex), SI::TableIdPMT);
|
|
}
|
|
}
|
|
|
|
void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
|
|
{
|
|
cMutexLock MutexLock(&mutex);
|
|
if (Pid == 0x00) {
|
|
if (Tid == SI::TableIdPAT) {
|
|
SI::PAT pat(Data, false);
|
|
if (!pat.CheckCRCAndParse())
|
|
return;
|
|
if (pat.getVersionNumber() != patVersion) {
|
|
DBGLOG("PAT %d %d -> %d", Transponder(), patVersion, pat.getVersionNumber());
|
|
if (pmtIndex >= 0) {
|
|
Del(GetPmtPid(pmtIndex), SI::TableIdPMT);
|
|
pmtIndex = -1;
|
|
}
|
|
numPmtEntries = 0;
|
|
SI::PAT::Association assoc;
|
|
for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) {
|
|
if (!assoc.isNITPid() && numPmtEntries < MAXPMTENTRIES) {
|
|
DBGLOG(" PMT pid %2d %5d SID %5d", numPmtEntries, assoc.getPid(), assoc.getServiceId());
|
|
pmtId[numPmtEntries] = MakePmtId(assoc.getPid(), assoc.getServiceId());
|
|
pmtVersion[numPmtEntries] = -1;
|
|
if (sid == assoc.getServiceId()) {
|
|
pmtIndex = numPmtEntries;
|
|
DBGLOG("sid = %d pmtIndex = %d", sid, pmtIndex);
|
|
}
|
|
numPmtEntries++;
|
|
}
|
|
}
|
|
if (numPmtEntries > 0 && pmtIndex < 0)
|
|
pmtIndex = 0;
|
|
Add(GetPmtPid(pmtIndex), SI::TableIdPMT);
|
|
patVersion = pat.getVersionNumber();
|
|
timer.Set(PMT_SCAN_TIMEOUT);
|
|
}
|
|
}
|
|
}
|
|
else if (Tid == SI::TableIdPMT && Source() && Transponder()) {
|
|
timer.Set(PMT_SCAN_TIMEOUT);
|
|
SI::PMT pmt(Data, false);
|
|
if (!pmt.CheckCRCAndParse())
|
|
return;
|
|
if (!PmtVersionChanged(Pid, pmt.getTableIdExtension(), pmt.getVersionNumber())) {
|
|
SwitchToNextPmtPid();
|
|
return;
|
|
}
|
|
if (!Channels.Lock(true, 10))
|
|
return;
|
|
PmtVersionChanged(Pid, pmt.getTableIdExtension(), pmt.getVersionNumber(), true);
|
|
SwitchToNextPmtPid();
|
|
cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), pmt.getServiceId());
|
|
if (Channel) {
|
|
SI::CaDescriptor *d;
|
|
cCaDescriptors *CaDescriptors = new cCaDescriptors(Channel->Source(), Channel->Transponder(), Channel->Sid(), Pid);
|
|
// 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: // H.264
|
|
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 syntax
|
|
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: // STREAMTYPE_USER_PRIVATE
|
|
if (Setup.StandardCompliance == STANDARD_ANSISCTE) { // DigiCipher II VIDEO (ANSI/SCTE 57)
|
|
Vpid = esPid;
|
|
Ppid = pmt.getPCRPid();
|
|
Vtype = 0x02; // compression based upon MPEG-2
|
|
ProcessCaDescriptors = true;
|
|
break;
|
|
}
|
|
// fall through
|
|
case 0x81: // STREAMTYPE_USER_PRIVATE
|
|
if (Setup.StandardCompliance == STANDARD_ANSISCTE) { // ATSC A/53 AUDIO (ANSI/SCTE 57)
|
|
char lang[MAXLANGCODE1] = { 0 };
|
|
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;
|
|
strn0cpy(lang, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1);
|
|
}
|
|
break;
|
|
default: ;
|
|
}
|
|
delete d;
|
|
}
|
|
if (NumDpids < MAXDPIDS) {
|
|
Dpids[NumDpids] = esPid;
|
|
Dtypes[NumDpids] = SI::AC3DescriptorTag;
|
|
strn0cpy(DLangs[NumDpids], lang, MAXLANGCODE1);
|
|
NumDpids++;
|
|
}
|
|
ProcessCaDescriptors = true;
|
|
break;
|
|
}
|
|
// fall through
|
|
case 0x82: // STREAMTYPE_USER_PRIVATE
|
|
if (Setup.StandardCompliance == STANDARD_ANSISCTE) { // STANDARD SUBTITLE (ANSI/SCTE 27)
|
|
//TODO
|
|
break;
|
|
}
|
|
// fall through
|
|
case 0x83 ... 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 0x41432D33: // 'AC-3'
|
|
IsAc3 = true;
|
|
break;
|
|
default:
|
|
//printf("Format identifier: 0x%08X (pid: %d)\n", rd->getFormatIdentifier(), esPid);
|
|
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));
|
|
}
|
|
Channels.Unlock();
|
|
}
|
|
if (timer.TimedOut()) {
|
|
if (pmtIndex >= 0)
|
|
DBGLOG("PMT timeout %d", pmtIndex);
|
|
SwitchToNextPmtPid();
|
|
timer.Set(PMT_SCAN_TIMEOUT);
|
|
}
|
|
}
|